diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md index 036a24e2e..9259163f5 100644 --- a/.github/ISSUE_TEMPLATE.md +++ b/.github/ISSUE_TEMPLATE.md @@ -7,8 +7,8 @@ only tracks infrastructure issues regarding the following items: * Reference Source Browser (http://referencesource.microsoft.com/) * Debugging the .NET Framework using Reference Source -For issues regarding functionality, please use the following resources: +This repository does not accept feature requests or bug reports. To submit +those, you need to go elsewhere: -* BCL. https://github.com/dotnet/corefx -* ASP.NET. https://github.com/aspnet/home -* Everything else. https://connect.microsoft.com/VisualStudio/feedback/LoadSubmitFeedbackForm +* [.NET Framework](https://developercommunity.visualstudio.com/dotnet) +* [.NET Core](https://github.com/dotnet/core) diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md new file mode 100644 index 000000000..1eb3c758b --- /dev/null +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -0,0 +1,3 @@ +# READ BEFORE FILING + +This repository is read-only and doesn't accept community pull requests. \ No newline at end of file diff --git a/Microsoft.Activities.Build/Microsoft.Activities.Build.csproj b/Microsoft.Activities.Build/Microsoft.Activities.Build.csproj new file mode 100644 index 000000000..95bfe6105 --- /dev/null +++ b/Microsoft.Activities.Build/Microsoft.Activities.Build.csproj @@ -0,0 +1,60 @@ + + + + + $([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildProjectDirectory), 'Microsoft.CDF.Settings.targets')) + + + + true + Microsoft.Activities.Build + Library + $(AssemblyName) + true + MSSHARED + false + true + {B3C9BB78-FC5F-4644-B898-E237C8ACF854} + $(ADPDocumentationPath)\$(AssemblyName).xml + $(DefineConstants);NONAPTCA + + + SR.resx + $(AssemblyName) + $(AssemblyName) + + + + + + + + + + + + + + + Microsoft\Activities\Build\Validation + Microsoft\Activities\Build\Expressions + Microsoft\Activities\Build\Debugger + Microsoft\Activities\Build + + + + + + + + + + + + + true + + + + + \ No newline at end of file diff --git a/Microsoft.Activities.Build/Microsoft.WorkflowBuildExtensions.targets b/Microsoft.Activities.Build/Microsoft.WorkflowBuildExtensions.targets new file mode 100644 index 000000000..8878dbd98 --- /dev/null +++ b/Microsoft.Activities.Build/Microsoft.WorkflowBuildExtensions.targets @@ -0,0 +1,126 @@ + + + + + + + ValidationExtension; + ExpressionBuildExtension; + $(PrepareResourcesDependsOn) + + + + + + GenerateCompiledExpressionsTempFile; + $(CoreCompileDependsOn) + + + + + 4.0.0.0 + 31bf3856ad364e35 + Microsoft.Activities.Build, Version=$(WorkflowBuildExtensionVersion), Culture=neutral, PublicKeyToken=$(WorkflowBuildExtensionKeyToken) + + $(IntermediateOutputPath)\TemporaryGeneratedFile_E7A71F73-0F8D-4B9B-B56E-8E70B10BC5D3.cs + $(IntermediateOutputPath)\TemporaryGeneratedFile_036C0B5B-1481-4323-8D20-8F5ADCB23D92.cs + $(IntermediateOutputPath)\TemporaryGeneratedFile_5937a670-0e60-4077-877b-f7221da3dda1.cs + + $(IntermediateOutputPath)\AC2C1ABA-CCF6-44D4-8127-588FD4D0A860-DeferredValidationErrors.xml + + + + + + + + + $(WorkflowBuildExtensionAssemblyName) + false + + + $(WorkflowBuildExtensionAssemblyName) + false + + + + + + + + + $(WorkflowBuildExtensionAssemblyName) + false + + + + + + + + + + + $(WorkflowBuildExtensionAssemblyName) + false + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Microsoft.Activities.Build/Microsoft/Activities/Build/BeforeInitializeComponentExtension.cs b/Microsoft.Activities.Build/Microsoft/Activities/Build/BeforeInitializeComponentExtension.cs new file mode 100644 index 000000000..f9be54d97 --- /dev/null +++ b/Microsoft.Activities.Build/Microsoft/Activities/Build/BeforeInitializeComponentExtension.cs @@ -0,0 +1,242 @@ +//------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +//------------------------------------------------------------ + +namespace Microsoft.Activities.Build +{ + using System; + using System.Activities; + using System.CodeDom; + using System.CodeDom.Compiler; + using System.Diagnostics.CodeAnalysis; + using System.Globalization; + using System.IO; + using System.Linq; + using System.Reflection; + using System.Xaml; + using System.Xaml.Schema; + using Microsoft.Build.Framework; + using Microsoft.Build.Tasks.Xaml; + using System.Runtime; + using System.Text; + + class BeforeInitializeComponentExtension : IXamlBuildTypeGenerationExtension + { + const string FileNameSuffix = "BeforeInitializeComponentHelper"; + + XamlSchemaContext schemaContext; + + public bool Execute(ClassData classData, XamlBuildTypeGenerationExtensionContext buildContext) + { + string className = !string.IsNullOrEmpty(classData.Namespace) ? classData.Namespace + "." + classData.Name : classData.Name; + buildContext.XamlBuildLogger.LogMessage(MessageImportance.Low, SR.InspectingClass(typeof(BeforeInitializeComponentExtension).Name, className)); + + this.schemaContext = classData.EmbeddedResourceXaml.Writer.SchemaContext; + + if (!IsAssignableTo(classData.BaseType, typeof(Activity))) + { + return true; + } + + if (ArePartialMethodsSupported(buildContext)) + { + buildContext.XamlBuildLogger.LogMessage(MessageImportance.Low, SR.GeneratingBeforeInitializeComponent(typeof(BeforeInitializeComponentExtension).Name, className)); + + CodeCompileUnit myCodeCompileUnit = GenerateCompileUnit(classData.Namespace ?? string.Empty, classData, buildContext.Language); + WriteGeneratedCode(classData, buildContext, myCodeCompileUnit, buildContext.Language); + } + else + { + buildContext.XamlBuildLogger.LogWarning(SR.UnsupportedLanguage(typeof(BeforeInitializeComponentExtension).Name, className)); + } + + return true; + } + + static void WriteGeneratedCode(ClassData classData, XamlBuildTypeGenerationExtensionContext buildContext, CodeCompileUnit compileUnit, string language) + { + using (CodeDomProvider codeDomProvider = CodeDomProvider.CreateProvider(buildContext.Language)) + { + string codeFileName = string.Format(CultureInfo.InvariantCulture, "{0}_{1}_{2}.{3}", classData.Namespace, classData.Name, FileNameSuffix, codeDomProvider.FileExtension); + string codeFilePath = Path.Combine(buildContext.OutputPath, codeFileName); + + using (StreamWriter fileStream = new StreamWriter(codeFilePath)) + { + using (IndentedTextWriter tw = new IndentedTextWriter(fileStream)) + { + codeDomProvider.GenerateCodeFromCompileUnit(compileUnit, tw, new CodeGeneratorOptions()); + } + } + + buildContext.AddGeneratedFile(codeFilePath); + + // Generate a resource file that contains the name of the resource holding the XAML. + string resourceFilePath = Path.Combine(buildContext.OutputPath, GenerateHelperResourceFilename(classData, buildContext, language)); + + string xamlResourceName = classData.EmbeddedResourceFileName; + + using (StreamWriter fileStream = new StreamWriter(resourceFilePath)) + { + // The first line of the resource is the Xaml resource name. + fileStream.WriteLine(xamlResourceName); + // The second line of the resource is the full class name of the Xaml helper class. + // In VB, that name is prepended with the root namespace. + string helperClassName = null; + if (string.Equals(language, "VB", StringComparison.OrdinalIgnoreCase)) + { + helperClassName = string.Format(CultureInfo.InvariantCulture, "{0}.{1}", buildContext.RootNamespace, classData.HelperClassFullName); + } + else + { + helperClassName = classData.HelperClassFullName; + } + fileStream.WriteLine(helperClassName); + } + buildContext.AddGeneratedResourceFile(resourceFilePath); + + } + } + + static string GenerateHelperResourceFilename(ClassData classData, XamlBuildTypeGenerationExtensionContext buildContext, string language) + { + // Generate a resource file that contains the name of the resource holding the XAML. + // [ 0) + { + builder.Append("."); + } + builder.Append(classData.Namespace); + } + + if (builder.Length > 0) + { + builder.Append("_"); + } + } + else + { + if (!string.IsNullOrWhiteSpace(classData.Namespace)) + { + builder.Append(classData.Namespace); + } + + if (builder.Length > 0) + { + builder.Append("_"); + } + } + builder.Append(string.Format(CultureInfo.InvariantCulture, "{0}_{1}.{2}", classData.Name, FileNameSuffix, "txt")); + return builder.ToString(); + } + + [SuppressMessage(FxCop.Category.Globalization, FxCop.Rule.DoNotPassLiteralsAsLocalizedParameters, + Justification = "The string literals are code snippets, not localizable values.")] + static CodeCompileUnit GenerateCompileUnit(string clrNamespace, ClassData classData, string language) + { + CodeTypeDeclaration typeDeclaration = new CodeTypeDeclaration + { + Name = classData.Name, + IsPartial = true, + TypeAttributes = classData.IsPublic ? TypeAttributes.Public : TypeAttributes.NotPublic, + }; + + // Partial declarations are only supported in C# and VB + string namespaceAndClassNameSnippet = null; + if (string.Equals(language, "C#", StringComparison.OrdinalIgnoreCase)) + { + namespaceAndClassNameSnippet = CodeDomSnippets.BeforeInitializeComponentCS; + } + else if (string.Equals(language, "VB", StringComparison.OrdinalIgnoreCase)) + { + namespaceAndClassNameSnippet = CodeDomSnippets.BeforeInitializeComponentVB; + } + else + { + throw Fx.AssertAndThrow("This method should only have been called for VB or C#"); + } + + namespaceAndClassNameSnippet = namespaceAndClassNameSnippet.Replace("ClassNameGoesHere", classData.Name); + typeDeclaration.Members.Add(new CodeSnippetTypeMember(namespaceAndClassNameSnippet)); + + return new CodeCompileUnit + { + Namespaces = + { + new CodeNamespace + { + Name = clrNamespace, + Types = + { + typeDeclaration + } + } + } + }; + } + + bool IsAssignableTo(XamlType type, Type assignableTo) + { + XamlType liveXamlType = this.schemaContext.GetXamlType(assignableTo); + XamlType rolXamlType = this.schemaContext.GetXamlType(new XamlTypeName(liveXamlType)); + if (rolXamlType == null) + { + // assignableTo is not in the project references, so project must not be deriving from it + return false; + } + return type.CanAssignTo(rolXamlType); + } + + bool ArePartialMethodsSupported(XamlBuildTypeGenerationExtensionContext buildContext) + { + return string.Equals(buildContext.Language, "C#", StringComparison.OrdinalIgnoreCase) || + string.Equals(buildContext.Language, "VB", StringComparison.OrdinalIgnoreCase); + } + + static class CodeDomSnippets + { + public const string BeforeInitializeComponentCS = +@" partial void BeforeInitializeComponent(ref bool isInitialized) + { + if (isInitialized == true) { + return; + } + + System.Activities.XamlIntegration.ActivityXamlServices.InitializeComponent( + typeof(ClassNameGoesHere), + this + ); + + // Setting this will turn InitializeComponent into a no-op. + isInitialized = true; + } +"; + + public const string BeforeInitializeComponentVB = +@" Private Sub BeforeInitializeComponent(ByRef isInitialized as Boolean) + If (isInitialized) + return + End If + + System.Activities.XamlIntegration.ActivityXamlServices.InitializeComponent( + GetType(ClassNameGoesHere), + Me + ) + + isInitialized = true + End Sub +"; + } + } +} + diff --git a/Microsoft.Activities.Build/Microsoft/Activities/Build/Debugger/DebugBuildExtension.cs b/Microsoft.Activities.Build/Microsoft/Activities/Build/Debugger/DebugBuildExtension.cs new file mode 100644 index 000000000..0ac5cc912 --- /dev/null +++ b/Microsoft.Activities.Build/Microsoft/Activities/Build/Debugger/DebugBuildExtension.cs @@ -0,0 +1,101 @@ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// + +namespace Microsoft.Activities.Build.Debugger +{ + using System; + using System.Activities.Debugger.Symbol; + using System.CodeDom; + using System.IO; + using System.Runtime; + using System.Xaml; + using Microsoft.Build.Framework; + using Microsoft.Build.Tasks.Xaml; + + /// + /// Build Extension for Workflow debugger support. + /// + public class DebugBuildExtension : IXamlBuildTypeGenerationExtension + { + private static string debugSymbolTypeFullName = typeof(DebugSymbol).FullName; + + /// + /// Update debug symbol with check sum. + /// + /// Class data to add the checksum. + /// Build context + /// Whether the execution succeeds + public bool Execute(ClassData classData, XamlBuildTypeGenerationExtensionContext buildContext) + { + string className = !string.IsNullOrEmpty(classData.Namespace) ? classData.Namespace + "." + classData.Name : classData.Name; + buildContext.XamlBuildLogger.LogMessage(MessageImportance.Low, SR.InspectingClass(typeof(DebugBuildExtension).Name, className)); + this.UpdateDebugSymbol(classData, buildContext); + return true; + } + + private void UpdateDebugSymbol(ClassData classData, XamlBuildTypeGenerationExtensionContext buildContext) + { + string path = Path.GetFullPath(classData.FileName); + try + { + using (XamlReader reader = classData.EmbeddedResourceXaml.GetReader()) + { + XamlNodeList newList = new XamlNodeList(reader.SchemaContext); + using (XamlWriter writer = newList.Writer) + { + bool nodesAvailable = reader.Read(); + while (nodesAvailable) + { + if (reader.NodeType == XamlNodeType.StartMember) + { + writer.WriteNode(reader); + if (reader.Member.DeclaringType != null && + reader.Member.DeclaringType.UnderlyingType != null && + string.CompareOrdinal(reader.Member.DeclaringType.UnderlyingType.FullName, debugSymbolTypeFullName) == 0) + { + reader.Read(); + string symbolString = reader.Value as string; + if (!string.IsNullOrEmpty(symbolString)) + { + WorkflowSymbol symbol = WorkflowSymbol.Decode(symbolString); + symbol.FileName = path; + symbol.CalculateChecksum(); + writer.WriteValue(symbol.Encode()); + } + else + { + writer.WriteValue(reader.Value); + } + } + + nodesAvailable = reader.Read(); + } + else + { + writer.WriteNode(reader); + nodesAvailable = reader.Read(); + } + } + } + + classData.EmbeddedResourceXaml = newList; + } + } + catch (Exception e) + { + if (Fx.IsFatal(e)) + { + throw; + } + + buildContext.XamlBuildLogger.LogMessage( + MessageImportance.High, + SR.DebugBuildExtensionExceptionPrefix( + typeof(DebugBuildExtension).Name, + path, + e.Message)); + } + } + } +} diff --git a/Microsoft.Activities.Build/Microsoft/Activities/Build/Expressions/ExpressionsBuildExtension.cs b/Microsoft.Activities.Build/Microsoft/Activities/Build/Expressions/ExpressionsBuildExtension.cs new file mode 100644 index 000000000..5eae886d6 --- /dev/null +++ b/Microsoft.Activities.Build/Microsoft/Activities/Build/Expressions/ExpressionsBuildExtension.cs @@ -0,0 +1,209 @@ +//---------------------------------------------------------------- +// Copyright (c) Microsoft Corporation. All rights reserved. +//---------------------------------------------------------------- + +[module: System.Diagnostics.CodeAnalysis.SuppressMessage(System.Runtime.FxCop.Category.Performance, System.Runtime.FxCop.Rule.AvoidUncalledPrivateCode, Scope = "member", Target = "Microsoft.Activities.Build.SR.get_InvalidValueForDisableWorkflowCompiledExpressions():System.String", Justification = "This resource string is only referenced from the targets file.")] + +namespace Microsoft.Activities.Build.Expressions +{ + using System; + using System.Collections.Generic; + using Microsoft.Build.Tasks.Xaml; + using System.Activities; + using System.Activities.XamlIntegration; + using System.Reflection; + using System.IO; + using System.Runtime; + using System.CodeDom.Compiler; + using Microsoft.Activities.Build.Validation; + + public class ExpressionsBuildExtension : IXamlBuildTypeInspectionExtension + { + static string fileNameSuffix = "_CompiledExpressionRoot"; + XamlBuildTypeInspectionExtensionContext buildContext; + List generatedFiles; + List> messages; + + public ExpressionsBuildExtension() + { + } + + public bool Execute(XamlBuildTypeInspectionExtensionContext buildContext) + { + if (buildContext == null) + { + throw FxTrace.Exception.AsError(new ArgumentNullException("buildContext")); + } + + this.buildContext = buildContext; + string deferredValidationErrorsFilePath = Path.Combine(this.buildContext.OutputPath, ValidationBuildExtension.DeferredValidationErrorsFileName); + if (string.Equals(this.buildContext.Language, "VB", StringComparison.OrdinalIgnoreCase) && File.Exists(deferredValidationErrorsFilePath)) + { + List violations = ReportDeferredValidationErrorsTask.LoadDeferredValidationErrors(deferredValidationErrorsFilePath); + if (violations != null && violations.Count > 0) + { + // ValidationBuildExtension must have run prior to ExpressionBuildExtension. + // If ValidationBuildExtension had cached any validation errors including VB Hosted compiler errors, + // then we do not generate the compiled expression code for VB. + return true; + } + } + + this.generatedFiles = new List(); + this.messages = new List>(); + + try + { + bool success = Execute(); + + foreach (string fileName in this.generatedFiles) + { + buildContext.AddGeneratedFile(fileName); + } + + foreach (Tuple message in this.messages) + { + if (message.Item2) + { + buildContext.XamlBuildLogger.LogError(message.Item1); + } + else + { + buildContext.XamlBuildLogger.LogMessage(message.Item1); + } + } + + return success; + } + catch (BadImageFormatException bex) + { + buildContext.XamlBuildLogger.LogWarning(SR.BadImageFormat_Expression(bex.FileName)); + + // We don't want to add the generated files to the project, since compilation was incomplete; + // so we're responsible for cleaning them up ourselves. + try + { + foreach (string fileName in this.generatedFiles) + { + File.Delete(fileName); + } + } + catch (IOException) + { + // cleanup is best-effort + } + + return true; + } + } + + bool Execute() + { + Assembly localAssembly = Utilities.GetLocalAssembly(buildContext, SR.LocalAssemblyNotLoaded_Expressions); + + foreach (Type type in Utilities.GetTypes(localAssembly)) + { + if (Utilities.IsTypeAuthoredInXaml(type)) + { + Exception ctorException; + Activity activity = Utilities.CreateActivity(type, out ctorException); + if (ctorException != null) + { + LogError(SR.ExpressionBuildExtensionConstructorFailed(type.FullName, ctorException != null ? ctorException.Message : string.Empty)); + } + else if (activity != null) + { + string activityName = activity.GetType().Name; + + string activityNamespace = ""; + string fullActivityNamespace = activity.GetType().Namespace; + if (string.Equals(buildContext.Language, "VB", StringComparison.OrdinalIgnoreCase)) + { + if (string.IsNullOrWhiteSpace(buildContext.RootNamespace)) + { + activityNamespace = fullActivityNamespace; + } + else + { + int firstIndex = fullActivityNamespace.IndexOf(buildContext.RootNamespace, StringComparison.Ordinal); + if (firstIndex != -1) + { + int subStringIndex = firstIndex + buildContext.RootNamespace.Length + 1; + if (subStringIndex < fullActivityNamespace.Length) + { + activityNamespace = fullActivityNamespace.Substring(subStringIndex); + } + else + { + activityNamespace = ""; + } + } + else + { + activityNamespace = ""; + } + } + } + else + { + activityNamespace = fullActivityNamespace; + } + + + TextExpressionCompiler compiler = new TextExpressionCompiler( + new TextExpressionCompilerSettings() + { + Activity = activity, + ActivityName = activityName, + ActivityNamespace = activityNamespace, + Language = buildContext.Language, + RootNamespace = buildContext.RootNamespace, + LogSourceGenerationMessage = LogMessage, + AlwaysGenerateSource = false + }); + + string filePath = Path.GetFullPath(buildContext.OutputPath); + string codeFileName = Path.Combine(filePath, activityNamespace + "_" + activityName + fileNameSuffix + "." + CodeDomProvider.CreateProvider(this.buildContext.Language).FileExtension); + + bool fileWritten = false; + using (StreamWriter fileStream = new StreamWriter(codeFileName)) + { + try + { + fileWritten = compiler.GenerateSource(fileStream); + } + catch (Exception ex) + { + if (Fx.IsFatal(ex)) + { + throw; + } + LogError(ex.Message); + } + } + + if (fileWritten) + { + // Batch up all file generation until the end, because we don't want to emit + // any source if we can't complete compilation due to an unloadable reference. + this.generatedFiles.Add(codeFileName); + } + } + } + } + return true; + } + + // Batch up all errors and warnings, because we don't want to emit any messages + // if we can't complete compilation due to an unloadable reference. + void LogMessage(string message) + { + this.messages.Add(Tuple.Create(message, false)); + } + + void LogError(string message) + { + this.messages.Add(Tuple.Create(message, true)); + } + } +} diff --git a/Microsoft.Activities.Build/Microsoft/Activities/Build/Utilities.cs b/Microsoft.Activities.Build/Microsoft/Activities/Build/Utilities.cs new file mode 100644 index 000000000..acbb5be36 --- /dev/null +++ b/Microsoft.Activities.Build/Microsoft/Activities/Build/Utilities.cs @@ -0,0 +1,111 @@ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// + +namespace Microsoft.Activities.Build +{ + using System; + using System.Activities; + using System.Collections.Generic; + using System.Diagnostics.CodeAnalysis; + using System.IO; + using System.Linq; + using System.Reflection; + using System.Runtime; + using Microsoft.Build.Tasks.Xaml; + + internal static class Utilities + { + private const string InitializeComponentMethodName = "InitializeComponent"; + + internal static Activity CreateActivity(Type type, out Exception ctorException) + { + try + { + ctorException = null; + Activity result = null; + if (!type.ContainsGenericParameters) + { + ConstructorInfo defaultConstructor = type.GetConstructor(Type.EmptyTypes); + if (defaultConstructor != null) + { + result = (Activity)defaultConstructor.Invoke(null); + } + } + + return result; + } + catch (TargetInvocationException tie) + { + Exception ex = tie; + while (ex != null && ex is TargetInvocationException) + { + ex = ex.InnerException; + } + + if (ex is BadImageFormatException) + { + // there's an unloadable reference, this will be handled by the Task's Execute method + throw Fx.Exception.AsError(ex); + } + + ctorException = ex; + return null; + } + } + + [SuppressMessage(FxCop.Category.Reliability, FxCop.Rule.AvoidCallingProblematicMethods, + Justification = "Using LoadFile to avoid loading through Fusion and load the exact local assembly")] + internal static Assembly GetLocalAssembly(BuildExtensionContext context, string errorMessage) + { + try + { + string path = Path.GetFullPath(context.LocalAssembly); + return Assembly.LoadFile(path); + } + catch (Exception e) + { + if (Fx.IsFatal(e) || e is BadImageFormatException) + { + throw; + } + + throw FxTrace.Exception.AsError(new FileLoadException(errorMessage)); + } + } + + internal static Type[] GetTypes(Assembly assembly) + { + try + { + return assembly.GetTypes(); + } + catch (ReflectionTypeLoadException rtle) + { + foreach (Exception exception in rtle.LoaderExceptions) + { + if (exception is BadImageFormatException) + { + throw FxTrace.Exception.AsError(exception); + } + } + + throw; + } + } + + internal static bool IsTypeAuthoredInXaml(Type type) + { + if (type.BaseType != null && (type.BaseType == typeof(Activity) || + (type.BaseType.IsGenericType && type.BaseType.GetGenericTypeDefinition() == typeof(Activity<>)))) + { + if (type.GetMethod(InitializeComponentMethodName, BindingFlags.DeclaredOnly | BindingFlags.Public | BindingFlags.Instance) != null) + { + return true; + } + } + + return false; + } + } +} diff --git a/Microsoft.Activities.Build/Microsoft/Activities/Build/Validation/DeferredValidationTask.cs b/Microsoft.Activities.Build/Microsoft/Activities/Build/Validation/DeferredValidationTask.cs new file mode 100644 index 000000000..9b1e8a79d --- /dev/null +++ b/Microsoft.Activities.Build/Microsoft/Activities/Build/Validation/DeferredValidationTask.cs @@ -0,0 +1,67 @@ +//---------------------------------------------------------------- +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//---------------------------------------------------------------- + +namespace Microsoft.Activities.Build.Validation +{ + using System; + using System.Collections.Generic; + using System.IO; + using System.Runtime; + using Microsoft.Build.Framework; + using Microsoft.Build.Utilities; + + /// + /// Wait until Compile target runs successfully, + /// and then if validation errors's been reported, simply return failure + /// so that the entire build process can terminate. + /// + public class DeferredValidationTask : Task + { + /// + /// Gets or sets the DeferredValidationErrorsFilePath property. + /// + [Required] + public string DeferredValidationErrorsFilePath { get; set; } + + /// + /// Executes to check to see if validation errors' been already reported, + /// and if yes, fail the entire build now. + /// + /// Returns true if validation errors's already been reported. Returns false otherwise. + public override bool Execute() + { + if (File.Exists(this.DeferredValidationErrorsFilePath)) + { + List violations = ReportDeferredValidationErrorsTask.LoadDeferredValidationErrors(this.DeferredValidationErrorsFilePath); + + if (ErrorExists(violations)) + { + // the validation errors must have already been reported/emitted in ReportDeferredValidationErrorsTask. + // the goal of this task is to simply fail the entire build process after CoreCompile target has succeeded. + return false; + } + } + + return true; + } + + private static bool ErrorExists(List violations) + { + if (violations != null && violations.Count > 0) + { + foreach (ValidationBuildExtension.Violation violation in violations) + { + if (!violation.IsWarning) + { + return true; + } + } + } + + return false; + } + } +} diff --git a/Microsoft.Activities.Build/Microsoft/Activities/Build/Validation/ReportDeferredValidationErrorsTask.cs b/Microsoft.Activities.Build/Microsoft/Activities/Build/Validation/ReportDeferredValidationErrorsTask.cs new file mode 100644 index 000000000..710f7e66e --- /dev/null +++ b/Microsoft.Activities.Build/Microsoft/Activities/Build/Validation/ReportDeferredValidationErrorsTask.cs @@ -0,0 +1,64 @@ +//---------------------------------------------------------------- +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//---------------------------------------------------------------- + +namespace Microsoft.Activities.Build.Validation +{ + using System; + using System.Collections.Generic; + using System.IO; + using System.Runtime; + using System.Runtime.Serialization; + using Microsoft.Build.Framework; + using Microsoft.Build.Utilities; + + /// + /// Reports validation errors cached immediately following XamlMarkupCompilePass2, + /// where ValidationBuildExtesion ran, + /// and always return success so the build contineus. + /// + public class ReportDeferredValidationErrorsTask : Task + { + /// + /// Gets or sets the DeferredValidationErrorsFilePath property. + /// + [Required] + public string DeferredValidationErrorsFilePath { get; set; } + + /// + /// Executes to report validation error. + /// + /// Always returns true. + public override bool Execute() + { + if (File.Exists(this.DeferredValidationErrorsFilePath)) + { + List violations = LoadDeferredValidationErrors(this.DeferredValidationErrorsFilePath); + + if (violations != null && violations.Count > 0) + { + foreach (ValidationBuildExtension.Violation violation in violations) + { + violation.Emit(this.Log); + } + } + } + + return true; + } + + internal static List LoadDeferredValidationErrors(string filePath) + { + List violations = null; + using (FileStream stream = new FileStream(filePath, FileMode.Open)) + { + DataContractSerializer serializer = new DataContractSerializer(typeof(List)); + violations = (List)serializer.ReadObject(stream); + } + + return violations; + } + } +} diff --git a/Microsoft.Activities.Build/Microsoft/Activities/Build/Validation/ValidationBuildExtension.cs b/Microsoft.Activities.Build/Microsoft/Activities/Build/Validation/ValidationBuildExtension.cs new file mode 100644 index 000000000..4e21be367 --- /dev/null +++ b/Microsoft.Activities.Build/Microsoft/Activities/Build/Validation/ValidationBuildExtension.cs @@ -0,0 +1,354 @@ +//----------------------------------------------------------------------------- +// Copyright (c) Microsoft Corporation. All rights reserved. +//----------------------------------------------------------------------------- + +namespace Microsoft.Activities.Build.Validation +{ + using System; + using System.Activities; + using System.Activities.Debugger; + using System.Activities.Debugger.Symbol; + using System.Activities.Validation; + using System.Collections.Generic; + using System.Diagnostics.CodeAnalysis; + using System.IO; + using System.Reflection; + using System.Runtime; + using System.Runtime.Serialization; + using Microsoft.Build.Framework; + using Microsoft.Build.Tasks.Xaml; + using Microsoft.Build.Utilities; + + [SuppressMessage(FxCop.Category.Performance, FxCop.Rule.AvoidUninstantiatedInternalClasses, + Justification = "This type is used in targets file. It is instantiated while running XBT pass 2 extensions.")] + class ValidationBuildExtension : IXamlBuildTypeInspectionExtension + { + public const string DeferredValidationErrorsFileName = "AC2C1ABA-CCF6-44D4-8127-588FD4D0A860-DeferredValidationErrors.xml"; + + XamlBuildTypeInspectionExtensionContext buildContext; + List violations; + + public ValidationBuildExtension() + { + } + + public bool Execute(XamlBuildTypeInspectionExtensionContext buildContext) + { + if (buildContext == null) + { + throw FxTrace.Exception.AsError(new ArgumentNullException("buildContext")); + } + this.buildContext = buildContext; + this.violations = new List(); + + try + { + this.Execute(); + + // Delay validation report and always returns true in order to let CoreCompile to compile first. + // CoreCompile will report expression compile errors and duplicated errors will be merged by Microsoft.VisualStudio.Activities.BuildHelper later. + // ReportValidationBuildExtensionErrors will report validation errors after CoreCompile done. + this.WriteViolationsToDeferredErrorsFile(); + + return true; + } + catch (BadImageFormatException bex) + { + buildContext.XamlBuildLogger.LogWarning(SR.BadImageFormat_Validation(bex.FileName)); + return true; + } + } + + void WriteViolationsToDeferredErrorsFile() + { + // OutputPath here is the intermediate output path + string filePath = Path.Combine(this.buildContext.OutputPath, DeferredValidationErrorsFileName); + using (FileStream stream = new FileStream(filePath, FileMode.Create, FileAccess.Write)) + { + DataContractSerializer serializer = new DataContractSerializer(typeof(List)); + serializer.WriteObject(stream, this.violations); + } + } + + void Execute() + { + Assembly localAssembly = Utilities.GetLocalAssembly(this.buildContext, SR.LocalAssemblyNotLoaded); + + foreach (Type type in Utilities.GetTypes(localAssembly)) + { + // Check if the type is authored in xaml + if (Utilities.IsTypeAuthoredInXaml(type)) + { + // Check if the file is marked with SkipWorkflowValidation = true + ITaskItem inputTaskItem = null; + buildContext.MarkupItemsByTypeName.TryGetValue(type.FullName, out inputTaskItem); + if (!SkipValidationForFile(inputTaskItem)) + { + string fileName = inputTaskItem != null ? inputTaskItem.ItemSpec : String.Empty; + Exception ex; + Activity activity = Utilities.CreateActivity(type, out ex); + if (ex != null) + { + string message = SR.ValidationBuildExtensionConstructorFailed(type.FullName, ex != null ? ex.Message : string.Empty); + this.violations.Add(new Violation(fileName, message)); + } + else if (activity != null) + { + this.Validate(activity, fileName); + } + } + } + } + } + + static IDictionary GetParentChildRelationships(Activity activity) + { + IDictionary parentChildMappings = new Dictionary(); + InternalGetParentChildRelationships(activity, null, parentChildMappings); + return parentChildMappings; + } + + static void InternalGetParentChildRelationships(Activity activity, Activity parent, IDictionary parentChildMappings) + { + if (!parentChildMappings.ContainsKey(activity)) + { + // the very first parent declaring the child activity + // and the rest are parent to reference child relationships + parentChildMappings.Add(activity, parent); + } + + foreach (Activity child in WorkflowInspectionServices.GetActivities(activity)) + { + InternalGetParentChildRelationships(child, activity, parentChildMappings); + } + } + + void Validate(Activity activity, string fileName) + { + List validationErrors = new List(); + + ValidationSettings settings = new ValidationSettings() + { + SkipValidatingRootConfiguration = true + }; + ValidationResults results = null; + try + { + results = ActivityValidationServices.Validate(activity, settings); + } + catch (Exception e) + { + if (Fx.IsFatal(e)) + { + throw; + } + + ValidationError error = new ValidationError(SR.ValidationBuildExtensionExceptionPrefix(typeof(ValidationBuildExtension).Name, activity.DisplayName, e.Message)); + validationErrors.Add(error); + } + + if (results != null) + { + validationErrors.AddRange(results.Errors); + validationErrors.AddRange(results.Warnings); + } + + if (validationErrors.Count > 0) + { + Dictionary sourceLocations; + + sourceLocations = GetErrorInformation(activity); + + IDictionary parentChildMappings = GetParentChildRelationships(activity); + + Activity errorSource; + foreach (ValidationError violation in validationErrors) + { + bool foundSourceLocation = false; + SourceLocation violationLocation = null; + + errorSource = violation.Source; + + if (sourceLocations != null) + { + if (violation.SourceDetail != null) + { + foundSourceLocation = sourceLocations.TryGetValue(violation.SourceDetail, out violationLocation); + } + // SourceLocation points to the erroneous activity + // If the errorneous activity does not have SourceLocation attached, + // for instance, debugger does not attach SourceLocations for expressions + // then the SourceLocation points to the first parent activity in the + // parent chain which has SourceLocation attached. + + while (!foundSourceLocation && errorSource != null) + { + foundSourceLocation = sourceLocations.TryGetValue(errorSource, out violationLocation); + if (!foundSourceLocation) + { + Activity parent; + if (!parentChildMappings.TryGetValue(errorSource, out parent)) + { + parent = null; + } + errorSource = parent; + } + } + } + + this.violations.Add(new Violation(fileName, violation, violationLocation)); + } + } + } + + Dictionary GetErrorInformation(Activity activity) + { + Dictionary sourceLocations = null; + + Activity implementationRoot = null; + IEnumerable children = WorkflowInspectionServices.GetActivities(activity); + foreach (Activity child in children) + { + // Check if the child is the root of the activity's implementation + // When an activity is an implementation child of another activity, the IDSpace for + // the implementation child is different than it's parent activity and parent's public + // children. The IDs for activities in the root activity's IDSpace are 1, 2 + // etc and for the root implementation child it is 1.1 and for its implementation + // child it is 1.1.1 and so on. + // As the activity can have just one implementation root, we just check + // for '.' to identify the root of the implementation. + if (child.Id.Contains(".")) + { + implementationRoot = child; + break; + } + } + + if (implementationRoot == null) + { + return sourceLocations; + } + + // We use the workflow debug symbol to get the line and column number information for a + // erroneous activity. + // We do not rely on the workflow debug symbol to get the file name. This is to enable cases + // where the xaml was hand written outside of the workflow designer. The hand written xaml + // file will not have the workflow debug symbol unless it was saved in the workflow designer. + string symbolString = DebugSymbol.GetSymbol(implementationRoot) as String; + + if (!string.IsNullOrEmpty(symbolString)) + { + try + { + WorkflowSymbol wfSymbol = WorkflowSymbol.Decode(symbolString); + if (wfSymbol != null) + { + sourceLocations = SourceLocationProvider.GetSourceLocations(activity, wfSymbol); + } + } + catch (Exception e) + { + if (Fx.IsFatal(e)) + { + throw; + } + // Ignore invalid symbol. + } + } + return sourceLocations; + } + + bool SkipValidationForFile(ITaskItem inputTaskItem) + { + if (inputTaskItem == null) + { + return false; + } + + string skipWorkflowValidationValue = inputTaskItem.GetMetadata("SkipWorkflowValidation"); + if (String.IsNullOrEmpty(skipWorkflowValidationValue) || String.Equals(skipWorkflowValidationValue, "false", StringComparison.OrdinalIgnoreCase)) + { + return false; + } + else if (String.Equals(skipWorkflowValidationValue, "true", StringComparison.OrdinalIgnoreCase)) + { + return true; + } + else + { + this.buildContext.XamlBuildLogger.LogWarning(SR.InvalidValueForSkipWorkflowValidation, null); + return false; + } + } + + [DataContract] + internal class Violation + { + private const int DefaultSourceLocationValue = 1; + + public Violation(string fileName, string message) + : this(fileName, message, false, null) + { + } + + public Violation(string fileName, ValidationError validationError, SourceLocation sourceLoation) + : this(fileName, validationError.Message, validationError.IsWarning, sourceLoation) + { + } + + public Violation(string fileName, string message, bool isWarning, SourceLocation sourceLocation) + { + this.FileName = fileName; + this.Message = message; + this.IsWarning = isWarning; + if (sourceLocation != null) + { + this.StartLineNumber = sourceLocation.StartLine; + this.StartColumnNumber = sourceLocation.StartColumn; + this.EndLineNumber = sourceLocation.EndLine; + this.EndColumnNumber = sourceLocation.EndColumn; + } + else + { + this.StartLineNumber = DefaultSourceLocationValue; + this.StartColumnNumber = DefaultSourceLocationValue; + this.EndLineNumber = DefaultSourceLocationValue; + this.EndColumnNumber = DefaultSourceLocationValue; + } + } + + [DataMember] + public string FileName { get; private set; } + + [DataMember] + public string Message { get; private set; } + + [DataMember] + public bool IsWarning { get; private set; } + + [DataMember] + public int StartLineNumber { get; private set; } + + [DataMember] + public int StartColumnNumber { get; private set; } + + [DataMember] + public int EndLineNumber { get; private set; } + + [DataMember] + public int EndColumnNumber { get; private set; } + + public void Emit(TaskLoggingHelper logger) + { + if (this.IsWarning) + { + logger.LogWarning(null, null, null, this.FileName, this.StartLineNumber, this.StartColumnNumber, this.EndLineNumber, this.EndColumnNumber, this.Message, null); + } + else + { + logger.LogError(null, null, null, this.FileName, this.StartLineNumber, this.StartColumnNumber, this.EndLineNumber, this.EndColumnNumber, this.Message, null); + } + } + } + } +} diff --git a/Microsoft.Activities.Build/Microsoft/Activities/Build/WorkflowBuildMessageTask.cs b/Microsoft.Activities.Build/Microsoft/Activities/Build/WorkflowBuildMessageTask.cs new file mode 100644 index 000000000..ae6d62df7 --- /dev/null +++ b/Microsoft.Activities.Build/Microsoft/Activities/Build/WorkflowBuildMessageTask.cs @@ -0,0 +1,71 @@ +//----------------------------------------------------------------------- +// Copyright (c) Microsoft Corporation. All rights reserved. +//----------------------------------------------------------------------- + +namespace Microsoft.Activities.Build +{ + using System; + using System.Collections.Generic; + using System.Diagnostics.CodeAnalysis; + using System.Runtime; + using Microsoft.Build.Framework; + using Microsoft.Build.Utilities; + + public sealed class WorkflowBuildMessageTask : Task + { + public WorkflowBuildMessageTask() + : base(new System.Resources.ResourceManager("Microsoft.Activities.Build.SR", System.Reflection.Assembly.GetExecutingAssembly())) + { + } + + [Required()] + public string ResourceName + { + get; + set; + } + + public string MessageType + { + get; + set; + } + + public override bool Execute() + { + if (string.IsNullOrWhiteSpace(this.MessageType)) + { + this.MessageType = "Message"; + } + + try + { + if (string.Equals(this.MessageType, "Error", StringComparison.OrdinalIgnoreCase)) + { + Log.LogErrorFromResources(this.ResourceName, null); + return false; + } + else if (string.Equals(this.MessageType, "Warning", StringComparison.OrdinalIgnoreCase)) + { + Log.LogWarningFromResources(this.ResourceName, null); + return true; + } + else if (string.Equals(this.MessageType, "Message", StringComparison.OrdinalIgnoreCase)) + { + Log.LogMessageFromResources(this.ResourceName, null); + return true; + } + else + { + Log.LogError(SR.InvalidType(this.MessageType)); + return false; + } + } + catch (ArgumentException) + { + Log.LogError(SR.InvalidCode(this.ResourceName)); + return false; + } + } + } +} diff --git a/Microsoft.Activities.Build/SR.resx b/Microsoft.Activities.Build/SR.resx new file mode 100644 index 000000000..c042b1ba6 --- /dev/null +++ b/Microsoft.Activities.Build/SR.resx @@ -0,0 +1,166 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Validation during build failed to run. Please try rebuilding the project. + + + XAML build extension '{0}' is inspecting class '{1}'. + + + Expression compilation during build failed to run. Please try rebuilding the project. + + + XAML build extension '{0}' threw the following exception while validating '{1}' activity: {2} + + + XAML build extension '{0}' threw the following exception while updating debug symbol for {1}: {2} + + + Cannot execute Xaml Build extension '{0}' for class '{1}' because the project language does not support partial methods. + + + Xaml Build extension '{0}' generating BeforeInitializeComponent method for class '{1}'. + + + Project property 'SkipWorkflowValidation' is set to an invalid value and will be defaulted to FALSE. The 'SkipWorkflowValidation' property is of type System.Boolean. + + + Type '{0}' cannot be inspected for expressions that need compilation. The constructor threw exception: '{1}' + + + Validation cannot be run for type '{0}'. The constructor threw exception: '{1}' + + + The value '{0}' is invalid for the ResourceName parameter of the WorkflowBuildMessageTask. + + + The value '{0}' is invalid for the MessageType parameter of the WorkflowBuildMessageTask. Valid values are 'Error', 'Warning' and 'Message'. + + + Could not run workflow validation because file '{0}' has an incorrect format. This will not prevent workflows from running; but any workflow that has a validation error will fail at runtime. If the file is a platform-specific library or executable, consider building the project using MSBuild.exe from a command prompt of the targeted platform. + + + Could not compile workflow expressions because file '{0}' has an incorrect format. Workflows in this project may still run, if they do not require expression compilation. If the file is a platform-specific library or executable, consider building the project using MSBuild.exe from a command prompt of the targeted platform. + + + Project property 'DisableWorkflowCompiledExpressions' is set to an invalid value and will be defaulted to FALSE. The 'DisableWorkflowCompiledExpressions' property is of type System.Boolean. + + \ No newline at end of file diff --git a/Microsoft.Activities.Build/Settings.StyleCop b/Microsoft.Activities.Build/Settings.StyleCop new file mode 100644 index 000000000..34b360746 --- /dev/null +++ b/Microsoft.Activities.Build/Settings.StyleCop @@ -0,0 +1,16 @@ + + + + + + False + Linked + %_NTDRIVE%%_NTROOT%\ndp\cdf\src\Legacy.StyleCop + + + CompositionImportsBuildExtension.cs + BeforeInitializeComponentExtension.cs + ValidationBuildExtension.cs + ExpressionsBuildExtension.cs + + \ No newline at end of file diff --git a/Microsoft.CSharp/Microsoft/CSharp/Binder.cs b/Microsoft.CSharp/Microsoft/CSharp/Binder.cs new file mode 100644 index 000000000..11ee1b058 --- /dev/null +++ b/Microsoft.CSharp/Microsoft/CSharp/Binder.cs @@ -0,0 +1,271 @@ +// ==++== +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// ==--== + +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Linq.Expressions; +using System.Runtime.CompilerServices; + +namespace Microsoft.CSharp.RuntimeBinder +{ + /// + /// Contains factory methods to create dynamic call site binders for CSharp. + /// + [EditorBrowsable(EditorBrowsableState.Never)] + public static class Binder + { + ////////////////////////////////////////////////////////////////////// + + /// + /// Initializes a new CSharp binary operation binder. + /// + /// The flags with which to initialize the binder. + /// The binary operation kind. + /// The that indicates where this operation is used. + /// The sequence of instances for the arguments to this operation. + /// Returns a new CSharp binary operation binder. + public static CallSiteBinder BinaryOperation( + CSharpBinderFlags flags, + ExpressionType operation, + Type context, + IEnumerable argumentInfo) + { + bool isChecked = (flags & CSharpBinderFlags.CheckedContext) != 0; + bool isLogical = (flags & CSharpBinderFlags.BinaryOperationLogical) != 0; + + CSharpBinaryOperationFlags binaryOperationFlags = 0; + if (isLogical) + { + binaryOperationFlags |= CSharpBinaryOperationFlags.LogicalOperation; + } + + return new CSharpBinaryOperationBinder(operation, isChecked, binaryOperationFlags, context, argumentInfo); + } + + + ////////////////////////////////////////////////////////////////////// + + /// + /// Initializes a new CSharp convert binder. + /// + /// The flags with which to initialize the binder. + /// The type to convert to. + /// The that indicates where this operation is used. + /// Returns a new CSharp convert binder. + public static CallSiteBinder Convert( + CSharpBinderFlags flags, + Type type, + Type context) + { + CSharpConversionKind conversionKind = + ((flags & CSharpBinderFlags.ConvertExplicit) != 0) ? + CSharpConversionKind.ExplicitConversion : + ((flags & CSharpBinderFlags.ConvertArrayIndex) != 0) ? + CSharpConversionKind.ArrayCreationConversion : + CSharpConversionKind.ImplicitConversion; + bool isChecked = (flags & CSharpBinderFlags.CheckedContext) != 0; + + return new CSharpConvertBinder(type, conversionKind, isChecked, context); + } + + + ////////////////////////////////////////////////////////////////////// + + /// + /// Initializes a new CSharp get index binder. + /// + /// The flags with which to initialize the binder. + /// The that indicates where this operation is used. + /// The sequence of instances for the arguments to this operation. + /// Returns a new CSharp get index binder. + public static CallSiteBinder GetIndex( + CSharpBinderFlags flags, + Type context, + IEnumerable argumentInfo) + { + return new CSharpGetIndexBinder(context, argumentInfo); + } + + ////////////////////////////////////////////////////////////////////// + + /// + /// Initializes a new CSharp get member binder. + /// + /// The flags with which to initialize the binder. + /// The name of the member to get. + /// The that indicates where this operation is used. + /// The sequence of instances for the arguments to this operation. + /// Returns a new CSharp get member binder. + public static CallSiteBinder GetMember( + CSharpBinderFlags flags, + string name, + Type context, + IEnumerable argumentInfo) + { + bool allowCallables = (flags & CSharpBinderFlags.ResultIndexed) != 0; + return new CSharpGetMemberBinder(name, allowCallables, context, argumentInfo); + } + + ////////////////////////////////////////////////////////////////////// + + /// + /// Initializes a new CSharp invoke binder. + /// + /// The flags with which to initialize the binder. + /// The that indicates where this operation is used. + /// The sequence of instances for the arguments to this operation. + /// Returns a new CSharp invoke binder. + public static CallSiteBinder Invoke( + CSharpBinderFlags flags, + Type context, + IEnumerable argumentInfo) + { + bool resultDiscarded = (flags & CSharpBinderFlags.ResultDiscarded) != 0; + + CSharpCallFlags callFlags = 0; + if (resultDiscarded) + { + callFlags |= CSharpCallFlags.ResultDiscarded; + } + + return new CSharpInvokeBinder(callFlags, context, argumentInfo); + } + + ////////////////////////////////////////////////////////////////////// + + /// + /// Initializes a new CSharp invoke member binder. + /// + /// The flags with which to initialize the binder. + /// The name of the member to invoke. + /// The list of type arguments specified for this invoke. + /// The that indicates where this operation is used. + /// The sequence of instances for the arguments to this operation. + /// Returns a new CSharp invoke member binder. + public static CallSiteBinder InvokeMember( + CSharpBinderFlags flags, + string name, + IEnumerable typeArguments, + Type context, + IEnumerable argumentInfo) + { + bool invokeSimpleName = (flags & CSharpBinderFlags.InvokeSimpleName) != 0; + bool invokeSpecialName = (flags & CSharpBinderFlags.InvokeSpecialName) != 0; + bool resultDiscarded = (flags & CSharpBinderFlags.ResultDiscarded) != 0; + + CSharpCallFlags callFlags = 0; + if (invokeSimpleName) + { + callFlags |= CSharpCallFlags.SimpleNameCall; + } + if (invokeSpecialName) + { + callFlags |= CSharpCallFlags.EventHookup; + } + if (resultDiscarded) + { + callFlags |= CSharpCallFlags.ResultDiscarded; + } + + return new CSharpInvokeMemberBinder(callFlags, name, context, typeArguments, argumentInfo); + } + + ////////////////////////////////////////////////////////////////////// + + /// + /// Initializes a new CSharp invoke constructor binder. + /// + /// The flags with which to initialize the binder. + /// The that indicates where this operation is used. + /// The sequence of instances for the arguments to this operation. + /// Returns a new CSharp invoke constructor binder. + public static CallSiteBinder InvokeConstructor( + CSharpBinderFlags flags, + Type context, + IEnumerable argumentInfo) + { + return new CSharpInvokeConstructorBinder(CSharpCallFlags.None, context, argumentInfo); + } + + ////////////////////////////////////////////////////////////////////// + + /// + /// Initializes a new CSharp is event binder. + /// + /// The flags with which to initialize the binder. + /// The name of the event to look for. + /// The that indicates where this operation is used. + /// Returns a new CSharp is event binder. + public static CallSiteBinder IsEvent( + CSharpBinderFlags flags, + string name, + Type context) + { + return new CSharpIsEventBinder(name, context); + } + + ////////////////////////////////////////////////////////////////////// + + /// + /// Initializes a new CSharp set index binder. + /// + /// The flags with which to initialize the binder. + /// The that indicates where this operation is used. + /// The sequence of instances for the arguments to this operation. + /// Returns a new CSharp set index binder. + public static CallSiteBinder SetIndex( + CSharpBinderFlags flags, + Type context, + IEnumerable argumentInfo) + { + bool isCompoundAssignment = (flags & CSharpBinderFlags.ValueFromCompoundAssignment) != 0; + bool isChecked = (flags & CSharpBinderFlags.CheckedContext) != 0; + return new CSharpSetIndexBinder(isCompoundAssignment, isChecked, context, argumentInfo); + } + + ////////////////////////////////////////////////////////////////////// + + /// + /// Initializes a new CSharp set member binder. + /// + /// The flags with which to initialize the binder. + /// The name of the member to set. + /// The that indicates where this operation is used. + /// The sequence of instances for the arguments to this operation. + /// Returns a new CSharp set member binder. + public static CallSiteBinder SetMember( + CSharpBinderFlags flags, + string name, + Type context, + IEnumerable argumentInfo) + { + bool isCompoundAssignment = (flags & CSharpBinderFlags.ValueFromCompoundAssignment) != 0; + bool isChecked = (flags & CSharpBinderFlags.CheckedContext) != 0; + return new CSharpSetMemberBinder(name, isCompoundAssignment, isChecked, context, argumentInfo); + } + + ////////////////////////////////////////////////////////////////////// + + /// + /// Initializes a new CSharp unary operation binder. + /// + /// The flags with which to initialize the binder. + /// The unary operation kind. + /// The that indicates where this operation is used. + /// The sequence of instances for the arguments to this operation. + /// Returns a new CSharp unary operation binder. + public static CallSiteBinder UnaryOperation( + CSharpBinderFlags flags, + ExpressionType operation, + Type context, + IEnumerable argumentInfo) + { + bool isChecked = (flags & CSharpBinderFlags.CheckedContext) != 0; + return new CSharpUnaryOperationBinder(operation, isChecked, context, argumentInfo); + } + } +} \ No newline at end of file diff --git a/Microsoft.CSharp/Microsoft/CSharp/BinderHelper.cs b/Microsoft.CSharp/Microsoft/CSharp/BinderHelper.cs new file mode 100644 index 000000000..531db92f5 --- /dev/null +++ b/Microsoft.CSharp/Microsoft/CSharp/BinderHelper.cs @@ -0,0 +1,435 @@ +// ==++== +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// ==--== + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Dynamic; +using System.Linq; +using System.Linq.Expressions; +using System.Runtime.InteropServices; +#if FEATURE_NETCORE +using System.Security; +#else +using System.Runtime.Remoting; +#endif + +namespace Microsoft.CSharp.RuntimeBinder +{ + internal static class BinderHelper + { + internal static DynamicMetaObject Bind( + DynamicMetaObjectBinder action, + RuntimeBinder binder, + IEnumerable args, + IEnumerable arginfos, + DynamicMetaObject onBindingError) + { + List parameters = new List(); + BindingRestrictions restrictions = BindingRestrictions.Empty; + ICSharpInvokeOrInvokeMemberBinder callPayload = action as ICSharpInvokeOrInvokeMemberBinder; + ParameterExpression tempForIncrement = null; + IEnumerator arginfosEnum = arginfos == null ? null : arginfos.GetEnumerator(); + + int index = 0; + foreach (DynamicMetaObject o in args) + { + // Our contract with the DLR is such that we will not enter a bind unless we have + // values for the meta-objects involved. + + if (!o.HasValue) + { + Debug.Assert(false, "The runtime binder is being asked to bind a metaobject without a value"); + throw Error.InternalCompilerError(); + } + CSharpArgumentInfo info = null; + if (arginfosEnum != null && arginfosEnum.MoveNext()) + info = arginfosEnum.Current; + + if (index == 0 && IsIncrementOrDecrementActionOnLocal(action)) + { + // We have an inc or a dec operation. Insert the temp local instead. + // + // We need to do this because for value types, the object will come + // in boxed, and we'd need to unbox it to get the original type in order + // to increment. The only way to do that is to create a new temporary. + tempForIncrement = Expression.Variable(o.Value != null ? o.Value.GetType() : typeof(object), "t0"); + parameters.Add(tempForIncrement); + } + else + { + parameters.Add(o.Expression); + } + + BindingRestrictions r = DeduceArgumentRestriction(index, callPayload, o, info); + restrictions = restrictions.Merge(r); + + // Here we check the argument info. If the argument info shows that the current argument + // is a literal constant, then we also add an instance restriction on the value of + // the constant. + if (info != null && info.LiteralConstant) + { + if ((o.Value is float && float.IsNaN((float)o.Value)) + || o.Value is double && double.IsNaN((double)o.Value)) + { + // We cannot create an equality restriction for NaN, because equality is implemented + // in such a way that NaN != NaN and the rule we make would be unsatisfiable. + } + else + { + Expression e = Expression.Equal(o.Expression, Expression.Constant(o.Value, o.Expression.Type)); + r = BindingRestrictions.GetExpressionRestriction(e); + restrictions = restrictions.Merge(r); + } + } + + ++index; + } + + // Get the bound expression. + try + { + DynamicMetaObject deferredBinding; + Expression expression = binder.Bind(action, parameters, args.ToArray(), out deferredBinding); + + if (deferredBinding != null) + { + expression = ConvertResult(deferredBinding.Expression, action); + restrictions = deferredBinding.Restrictions.Merge(restrictions); + return new DynamicMetaObject(expression, restrictions); + } + + if (tempForIncrement != null) + { + // If we have a ++ or -- payload, we need to do some temp rewriting. + // We rewrite to the following: + // + // temp = (type)o; + // temp++; + // o = temp; + // return o; + + DynamicMetaObject arg0 = args.First(); + + Expression assignTemp = Expression.Assign( + tempForIncrement, + Expression.Convert(arg0.Expression, arg0.Value.GetType())); + Expression assignResult = Expression.Assign( + arg0.Expression, + Expression.Convert(tempForIncrement, arg0.Expression.Type)); + List expressions = new List(); + + expressions.Add(assignTemp); + expressions.Add(expression); + expressions.Add(assignResult); + + expression = Expression.Block(new ParameterExpression[] { tempForIncrement }, expressions); + } + + expression = ConvertResult(expression, action); + + return new DynamicMetaObject(expression, restrictions); + } + catch (RuntimeBinderException e) + { + if (onBindingError != null) + { + return onBindingError; + } + + return new DynamicMetaObject( + Expression.Throw( + Expression.New( + typeof(RuntimeBinderException).GetConstructor(new Type[] { typeof(string) }), + Expression.Constant(e.Message) + ), + GetTypeForErrorMetaObject(action, args.FirstOrDefault()) + ), + restrictions + ); + } + } + + ///////////////////////////////////////////////////////////////////////////////// + + private static bool IsTypeOfStaticCall( + int parameterIndex, + ICSharpInvokeOrInvokeMemberBinder callPayload) + { + return parameterIndex == 0 && callPayload != null && callPayload.StaticCall; + } + + ///////////////////////////////////////////////////////////////////////////////// + +#if FEATURE_NETCORE + [SecuritySafeCritical] +#endif + private static bool IsComObject(object obj) + { +#if SILVERLIGHT && !FEATURE_NETCORE + return false; +#else + return obj != null && Marshal.IsComObject(obj); +#endif + } + + ///////////////////////////////////////////////////////////////////////////////// + + // Try to determine if this object represents a WindowsRuntime object - i.e. it either + // is coming from a WinMD file or is derived from a class coming from a WinMD. + // The logic here matches the CLR's logic of finding a WinRT object. + internal static bool IsWindowsRuntimeObject(DynamicMetaObject obj) + { + if (obj != null && obj.RuntimeType != null) + { + Type curType = obj.RuntimeType; + while(curType != null) + { + if (curType.Attributes.HasFlag(System.Reflection.TypeAttributes.WindowsRuntime)) + { + // Found a WinRT COM object + return true; + } + if (curType.Attributes.HasFlag(System.Reflection.TypeAttributes.Import)) + { + // Found a class that is actually imported from COM but not WinRT + // this is definitely a non-WinRT COM object + return false; + } + curType = curType.BaseType; + } + } + return false; + } + + ///////////////////////////////////////////////////////////////////////////////// + + private static bool IsTransparentProxy(object obj) + { +#if SILVERLIGHT + return false; +#else + return obj != null && RemotingServices.IsTransparentProxy(obj); +#endif + } + + ///////////////////////////////////////////////////////////////////////////////// + + private static bool IsDynamicallyTypedRuntimeProxy(DynamicMetaObject argument, CSharpArgumentInfo info) + { + // This detects situations where, although the argument has a value with + // a given type, that type is insufficient to determine, statically, the + // set of reference conversions that are going to exist at bind time for + // different values. For instance, one __ComObject may allow a conversion + // to IFoo while another does not. + + bool isDynamicObject = + info != null && + !info.UseCompileTimeType && + (IsComObject(argument.Value) || IsTransparentProxy(argument.Value)); + + return isDynamicObject; + } + + ///////////////////////////////////////////////////////////////////////////////// + + private static BindingRestrictions DeduceArgumentRestriction( + int parameterIndex, + ICSharpInvokeOrInvokeMemberBinder callPayload, + DynamicMetaObject argument, + CSharpArgumentInfo info) + { + // Here we deduce what predicates the DLR can apply to future calls in order to + // determine whether to use the previously-computed-and-cached delegate, or + // whether we need to bind the site again. Ideally we would like the + // predicate to be as broad as is possible; if we can re-use analysis based + // solely on the type of the argument, that is preferable to re-using analysis + // based on object identity with a previously-analyzed argument. + + // The times when we need to restrict re-use to a particular instance, rather + // than its type, are: + // + // * if the argument is a null reference then we have no type information. + // + // * if we are making a static call then the first argument is + // going to be a Type object. In this scenario we should always check + // for a specific Type object rather than restricting to the Type type. + // + // * if the argument was dynamic at compile time and it is a dynamic proxy + // object that the runtime manages, such as COM RCWs and transparent + // proxies. + // + // ** there is also a case for constant values (such as literals) to use + // something like value restrictions, and that is accomplished in Bind(). + + bool useValueRestriction = + argument.Value == null || + IsTypeOfStaticCall(parameterIndex, callPayload) || + IsDynamicallyTypedRuntimeProxy(argument, info); + + return useValueRestriction ? + BindingRestrictions.GetInstanceRestriction(argument.Expression, argument.Value) : + BindingRestrictions.GetTypeRestriction(argument.Expression, argument.RuntimeType); + } + + ///////////////////////////////////////////////////////////////////////////////// + + private static Expression ConvertResult(Expression binding, DynamicMetaObjectBinder action) + { + // Need to handle the following cases: + // (1) Call to a constructor: no conversions. + // (2) Call to a void-returning method: return null iff result is discarded. + // (3) Call to a value-type returning method: box to object. + // + // In all other cases, binding.Type should be equivalent or + // reference assignable to resultType. + + var invokeConstructor = action as CSharpInvokeConstructorBinder; + if (invokeConstructor != null) + { + // No conversions needed, the call site has the correct type. + return binding; + } + + if (binding.Type == typeof(void)) + { + var invoke = action as ICSharpInvokeOrInvokeMemberBinder; + if (invoke != null && invoke.ResultDiscarded) + { + Debug.Assert(action.ReturnType == typeof(object)); + return Expression.Block(binding, Expression.Default(action.ReturnType)); + } + else + { + throw Error.BindToVoidMethodButExpectResult(); + } + } + + if (binding.Type.IsValueType && !action.ReturnType.IsValueType) + { + Debug.Assert(action.ReturnType == typeof(object)); + return Expression.Convert(binding, action.ReturnType); + } + +#if !SILVERLIGHT + Debug.Assert(binding.Type.IsEquivalentTo(action.ReturnType) || + !binding.Type.IsValueType && !action.ReturnType.IsValueType && action.ReturnType.IsAssignableFrom(binding.Type)); +#endif + return binding; + } + + ///////////////////////////////////////////////////////////////////////////////// + + private static Type GetTypeForErrorMetaObject(DynamicMetaObjectBinder action, DynamicMetaObject arg0) + { + // This is similar to ConvertResult but has fewer things to worry about. + + var invokeConstructor = action as CSharpInvokeConstructorBinder; + if (invokeConstructor != null) + { + if (arg0 == null || !(arg0.Value is System.Type)) + { + Debug.Assert(false); + return typeof(object); + } + + Type result = arg0.Value as System.Type; + + return result; + } + + return action.ReturnType; + } + + ///////////////////////////////////////////////////////////////////////////////// + + private static bool IsIncrementOrDecrementActionOnLocal(DynamicMetaObjectBinder action) + { + CSharpUnaryOperationBinder operatorPayload = action as CSharpUnaryOperationBinder; + + return operatorPayload != null && + (operatorPayload.Operation == ExpressionType.Increment || operatorPayload.Operation == ExpressionType.Decrement); + } + + ///////////////////////////////////////////////////////////////////////////////// + + internal static IEnumerable Cons(T sourceHead, IEnumerable sourceTail) + { + yield return sourceHead; + + if (sourceTail != null) + { + foreach (T x in sourceTail) + { + yield return x; + } + } + } + + internal static IEnumerable Cons(T sourceHead, IEnumerable sourceMiddle, T sourceLast) + { + yield return sourceHead; + + if (sourceMiddle != null) + { + foreach (T x in sourceMiddle) + { + yield return x; + } + } + + yield return sourceLast; + } + + ///////////////////////////////////////////////////////////////////////////////// + + internal static List ToList(IEnumerable source) + { + if (source == null) + { + return new List(); + } + + return source.ToList(); + } + + ///////////////////////////////////////////////////////////////////////////////// + + internal static CallInfo CreateCallInfo(IEnumerable argInfos, int discard) + { + // This function converts the C# Binder's notion of argument information to the + // DLR's notion. The DLR counts arguments differently than C#. Here are some + // examples: + + // Expression Binder C# ArgInfos DLR CallInfo + // + // d.M(1, 2, 3); CSharpInvokeMemberBinder 4 3 + // d(1, 2, 3); CSharpInvokeBinder 4 3 + // d[1, 2] = 3; CSharpSetIndexBinder 4 2 + // d[1, 2, 3] CSharpGetIndexBinder 4 3 + // + // The "discard" parameter tells this function how many of the C# arg infos it + // should not count as DLR arguments. + + int argCount = 0; + List argNames = new List(); + + foreach (CSharpArgumentInfo info in argInfos) + { + if (info.NamedArgument) + { + argNames.Add(info.Name); + } + ++argCount; + } + + Debug.Assert(discard <= argCount); + Debug.Assert(argNames.Count <= argCount - discard); + + return new CallInfo(argCount - discard, argNames); + } + } +} diff --git a/Microsoft.CSharp/Microsoft/CSharp/CSharpArgumentInfo.cs b/Microsoft.CSharp/Microsoft/CSharp/CSharpArgumentInfo.cs new file mode 100644 index 000000000..fe866667b --- /dev/null +++ b/Microsoft.CSharp/Microsoft/CSharp/CSharpArgumentInfo.cs @@ -0,0 +1,59 @@ +// ==++== +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// ==--== + +using System.Collections.Generic; +using System.ComponentModel; +using System.Dynamic; + +namespace Microsoft.CSharp.RuntimeBinder +{ + /// + /// Represents information about C# dynamic operations that are specific to particular arguments at a call site. + /// Instances of this class are generated by the C# compiler. + /// + [EditorBrowsable(EditorBrowsableState.Never)] + public sealed class CSharpArgumentInfo + { + // Create a singleton static instance. + internal static readonly CSharpArgumentInfo None = new CSharpArgumentInfo(CSharpArgumentInfoFlags.None, null); + + /// + /// The flags for the argument. + /// + internal CSharpArgumentInfoFlags Flags { get { return m_flags; } } + private CSharpArgumentInfoFlags m_flags; + + /// + /// The name of the argument, if named; otherwise null. + /// + internal string Name { get { return m_name; } } + private string m_name; + + private CSharpArgumentInfo(CSharpArgumentInfoFlags flags, string name) + { + m_flags = flags; + m_name = name; + } + + /// + /// Initializes a new instance of the class. + /// + /// The flags for the argument. + /// The name of the argument, if named; otherwise null. + public static CSharpArgumentInfo Create(CSharpArgumentInfoFlags flags, string name) + { + return new CSharpArgumentInfo(flags, name); + } + + // Accessor helpers. + internal bool UseCompileTimeType { get { return (Flags & CSharpArgumentInfoFlags.UseCompileTimeType) != 0; } } + internal bool LiteralConstant { get { return (Flags & CSharpArgumentInfoFlags.Constant) != 0; } } + internal bool NamedArgument { get { return (Flags & CSharpArgumentInfoFlags.NamedArgument) != 0; } } + internal bool IsByRef { get { return (Flags & CSharpArgumentInfoFlags.IsRef) != 0; } } + internal bool IsOut { get { return (Flags & CSharpArgumentInfoFlags.IsOut) != 0; } } + internal bool IsStaticType { get { return (Flags & CSharpArgumentInfoFlags.IsStaticType) != 0; } } + } +} diff --git a/Microsoft.CSharp/Microsoft/CSharp/CSharpArgumentInfoFlags.cs b/Microsoft.CSharp/Microsoft/CSharp/CSharpArgumentInfoFlags.cs new file mode 100644 index 000000000..77c0c3d94 --- /dev/null +++ b/Microsoft.CSharp/Microsoft/CSharp/CSharpArgumentInfoFlags.cs @@ -0,0 +1,54 @@ +// ==++== +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// ==--== + +using System; +using System.ComponentModel; + +namespace Microsoft.CSharp.RuntimeBinder +{ + /// + /// Represents information about C# dynamic operations that are specific to particular arguments at a call site. + /// Instances of this class are generated by the C# compiler. + /// + [Flags, EditorBrowsable(EditorBrowsableState.Never)] + public enum CSharpArgumentInfoFlags + { + /// + /// No additional information to represent. + /// + None = 0x00000000, + + /// + /// The argument's compile-time type should be considered during binding. + /// + UseCompileTimeType = 0x00000001, + + /// + /// The argument is a constant. + /// + Constant = 0x00000002, + + /// + /// The argument is a named argument. + /// + NamedArgument = 0x00000004, + + /// + /// The argument is passed to a ref parameter. + /// + IsRef = 0x00000008, + + /// + /// The argument is passed to an out parameter. + /// + IsOut = 0x00000010, + + /// + /// The argument is a indicating an actual typename used in source. Used only for target objects in static calls. + /// + IsStaticType = 0x00000020, + } +} diff --git a/Microsoft.CSharp/Microsoft/CSharp/CSharpBinaryOperationBinder.cs b/Microsoft.CSharp/Microsoft/CSharp/CSharpBinaryOperationBinder.cs new file mode 100644 index 000000000..ccd452503 --- /dev/null +++ b/Microsoft.CSharp/Microsoft/CSharp/CSharpBinaryOperationBinder.cs @@ -0,0 +1,73 @@ +// ==++== +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// ==--== + +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Diagnostics; +using System.Dynamic; +using System.Linq.Expressions; + +namespace Microsoft.CSharp.RuntimeBinder +{ + /// + /// Represents a dynamic binary operation in C#, providing the binding semantics and the details about the operation. + /// Instances of this class are generated by the C# compiler. + /// + internal sealed class CSharpBinaryOperationBinder : BinaryOperationBinder + { + internal bool IsChecked { get { return m_isChecked; } } + private bool m_isChecked; + + internal bool IsLogicalOperation { get { return (m_binopFlags & CSharpBinaryOperationFlags.LogicalOperation) != 0; } } + private CSharpBinaryOperationFlags m_binopFlags; + + internal Type CallingContext { get { return m_callingContext; } } + private Type m_callingContext; + + internal IList ArgumentInfo { get { return m_argumentInfo.AsReadOnly(); } } + private List m_argumentInfo; + + private RuntimeBinder m_binder; + + ////////////////////////////////////////////////////////////////////// + + /// + /// Initializes a new instance of the class. + /// + /// The binary operation kind. + /// True if the operation is defined in a checked context; otherwise false. + /// The flags associated with this binary operation. + /// The sequence of instances for the arguments to this operation. + public CSharpBinaryOperationBinder( + ExpressionType operation, + bool isChecked, + CSharpBinaryOperationFlags binaryOperationFlags, + Type callingContext, + IEnumerable argumentInfo) : + base(operation) + { + m_isChecked = isChecked; + m_binopFlags = binaryOperationFlags; + m_callingContext = callingContext; + m_argumentInfo = BinderHelper.ToList(argumentInfo); + Debug.Assert(m_argumentInfo.Count == 2); + m_binder = RuntimeBinder.GetInstance(); + } + + /// + /// Performs the binding of the binary dynamic operation if the target dynamic object cannot bind. + /// + /// The target of the dynamic binary operation. + /// The right hand side operand of the dynamic binary operation. + /// The binding result in case the binding fails, or null. + /// The representing the result of the binding. + public sealed override DynamicMetaObject FallbackBinaryOperation(DynamicMetaObject target, DynamicMetaObject arg, DynamicMetaObject errorSuggestion) + { + return BinderHelper.Bind(this, m_binder, BinderHelper.Cons(target, null, arg), m_argumentInfo, errorSuggestion); + } + } +} diff --git a/Microsoft.CSharp/Microsoft/CSharp/CSharpBinaryOperationFlags.cs b/Microsoft.CSharp/Microsoft/CSharp/CSharpBinaryOperationFlags.cs new file mode 100644 index 000000000..a0a4faad2 --- /dev/null +++ b/Microsoft.CSharp/Microsoft/CSharp/CSharpBinaryOperationFlags.cs @@ -0,0 +1,31 @@ +// ==++== +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// ==--== + +using System; +using System.ComponentModel; + +namespace Microsoft.CSharp.RuntimeBinder +{ + /// + /// Represents the set of binary operation flags in C# for use with instances. + /// Instances of this enum are generated by the C# compiler. + /// + internal enum CSharpBinaryOperationFlags + { + None = 0, + + /// + /// The operation is a binary compound operation on a member access. + /// + MemberAccess = 1, + + /// + /// The operation is a logical binary operation. + /// + LogicalOperation = 2, + } +} + diff --git a/Microsoft.CSharp/Microsoft/CSharp/CSharpBinderFlags.cs b/Microsoft.CSharp/Microsoft/CSharp/CSharpBinderFlags.cs new file mode 100644 index 000000000..b18d5e76d --- /dev/null +++ b/Microsoft.CSharp/Microsoft/CSharp/CSharpBinderFlags.cs @@ -0,0 +1,69 @@ +// ==++== +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// ==--== + +using System; +using System.ComponentModel; + +namespace Microsoft.CSharp.RuntimeBinder +{ + /// + /// Represents information about C# dynamic operations that are not specific to particular arguments at a call site. + /// Instances of this class are generated by the C# compiler. + /// + [Flags, EditorBrowsable(EditorBrowsableState.Never)] + public enum CSharpBinderFlags + { + /// + /// There is no additional information required for this binder. + /// + None = 0x00000000, + + /// + /// The evaluation of this binder happens in a checked context. + /// + CheckedContext = 0x00000001, + + /// + /// The binder represents an invoke on a simple name. + /// + InvokeSimpleName = 0x00000002, + + /// + /// The binder represents an invoke on a specialname. + /// + InvokeSpecialName = 0x00000004, + + /// + /// The binder represents a logical AND or logical OR that is part of a conditional logical operator evaluation. + /// + BinaryOperationLogical = 0x00000008, + + /// + /// The binder represents an explicit conversion. + /// + ConvertExplicit = 0x00000010, + + /// + /// The binder represents an implicit conversion for use in an array creation expression. + /// + ConvertArrayIndex = 0x00000020, + + /// + /// The result of any bind is going to be indexed get a set index or get index binder. + /// + ResultIndexed = 0x00000040, + + /// + /// The value in this set index or set member comes a compound assignment operator. + /// + ValueFromCompoundAssignment = 0x00000080, + + /// + /// The binder is used in a position that does not require a result, and can therefore bind to a void returning method. + /// + ResultDiscarded = 0x00000100, + } +} diff --git a/Microsoft.CSharp/Microsoft/CSharp/CSharpCallFlags.cs b/Microsoft.CSharp/Microsoft/CSharp/CSharpCallFlags.cs new file mode 100644 index 000000000..894a8ce6b --- /dev/null +++ b/Microsoft.CSharp/Microsoft/CSharp/CSharpCallFlags.cs @@ -0,0 +1,35 @@ +// ==++== +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// ==--== + +using System; +using System.ComponentModel; + +namespace Microsoft.CSharp.RuntimeBinder +{ + /// + /// Represents information about a or that + /// is not specific to any particular argument given to those operations. + /// + internal enum CSharpCallFlags + { + /// + /// No extra information. + /// + None = 0, + + /// + /// The method was called given only a simple name, such as M(), unlike x.M(). + /// + SimpleNameCall = 1, + + /// + /// The call is permitted to bind against special names + /// + EventHookup = 2, + + ResultDiscarded = 4, + } +} diff --git a/Microsoft.CSharp/Microsoft/CSharp/CSharpConversionKind.cs b/Microsoft.CSharp/Microsoft/CSharp/CSharpConversionKind.cs new file mode 100644 index 000000000..a14a42968 --- /dev/null +++ b/Microsoft.CSharp/Microsoft/CSharp/CSharpConversionKind.cs @@ -0,0 +1,32 @@ +// ==++== +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// ==--== + +using System.ComponentModel; + +namespace Microsoft.CSharp.RuntimeBinder +{ + /// + /// Represents the set of conversion kinds in C# for use with instances. + /// Instances of this enum are generated by the C# compiler. + /// + internal enum CSharpConversionKind + { + /// + /// Implicit conversion in C#. + /// + ImplicitConversion, + + /// + /// Explicit conversion in C#. + /// + ExplicitConversion, + + /// + /// Array creation conversion in C#. + /// + ArrayCreationConversion, + } +} diff --git a/Microsoft.CSharp/Microsoft/CSharp/CSharpConvertBinder.cs b/Microsoft.CSharp/Microsoft/CSharp/CSharpConvertBinder.cs new file mode 100644 index 000000000..edb469140 --- /dev/null +++ b/Microsoft.CSharp/Microsoft/CSharp/CSharpConvertBinder.cs @@ -0,0 +1,67 @@ +// ==++== +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// ==--== + +using System; +using System.ComponentModel; +using System.Dynamic; + +namespace Microsoft.CSharp.RuntimeBinder +{ + /// + /// Represents a dynamic conversion in C#, providing the binding semantics and the details about the operation. + /// Instances of this class are generated by the C# compiler. + /// + internal sealed class CSharpConvertBinder : ConvertBinder + { + internal CSharpConversionKind ConversionKind { get { return m_conversionKind; } } + private CSharpConversionKind m_conversionKind; + + internal bool IsChecked { get { return m_isChecked; } } + private bool m_isChecked; + + internal Type CallingContext { get { return m_callingContext; } } + private Type m_callingContext; + + private RuntimeBinder m_binder; + + /// + /// Initializes a new intsance of the . + /// + /// The type to convert to. + /// The kind of conversion for this operation. + /// True if the operation is defined in a checked context; otherwise false. + public CSharpConvertBinder( + Type type, + CSharpConversionKind conversionKind, + bool isChecked, + Type callingContext) : + base(type, conversionKind == CSharpConversionKind.ExplicitConversion) + { + m_conversionKind = conversionKind; + m_isChecked = isChecked; + m_callingContext = callingContext; + m_binder = RuntimeBinder.GetInstance(); + } + + /// + /// Performs the binding of the dynamic convert operation if the target dynamic object cannot bind. + /// + /// The target of the dynamic convert operation. + /// The binding result to use if binding fails, or null. + /// The representing the result of the binding. + public override DynamicMetaObject FallbackConvert(DynamicMetaObject target, DynamicMetaObject errorSuggestion) + { +#if !SILVERLIGHT + DynamicMetaObject com; + if (!BinderHelper.IsWindowsRuntimeObject(target) && ComBinder.TryConvert(this, target, out com)) + { + return com; + } +#endif + return BinderHelper.Bind(this, m_binder, new[] { target }, null, errorSuggestion); + } + } +} diff --git a/Microsoft.CSharp/Microsoft/CSharp/CSharpGetIndexBinder.cs b/Microsoft.CSharp/Microsoft/CSharp/CSharpGetIndexBinder.cs new file mode 100644 index 000000000..996b8afc5 --- /dev/null +++ b/Microsoft.CSharp/Microsoft/CSharp/CSharpGetIndexBinder.cs @@ -0,0 +1,62 @@ +// ==++== +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// ==--== + +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Dynamic; + +namespace Microsoft.CSharp.RuntimeBinder +{ + /// + /// Represents a dynamic indexer access in C#, providing the binding semantics and the details about the operation. + /// Instances of this class are generated by the C# compiler. + /// + internal sealed class CSharpGetIndexBinder : GetIndexBinder + { + internal Type CallingContext { get { return m_callingContext; } } + private Type m_callingContext; + + internal IList ArgumentInfo { get { return m_argumentInfo.AsReadOnly(); } } + private List m_argumentInfo; + + private RuntimeBinder m_binder; + + /// + /// Initializes a new instance of the . + /// + /// The that indicates where this operation is defined. + /// The sequence of instances for the arguments to this operation. + public CSharpGetIndexBinder( + Type callingContext, + IEnumerable argumentInfo) : + base(BinderHelper.CreateCallInfo(argumentInfo, 1)) // discard 1 argument: the target object + { + m_callingContext = callingContext; + m_argumentInfo = BinderHelper.ToList(argumentInfo); + m_binder = RuntimeBinder.GetInstance(); + } + + /// + /// Performs the binding of the dynamic get index operation if the target dynamic object cannot bind. + /// + /// The target of the dynamic get index operation. + /// The arguments of the dynamic get index operation. + /// The binding result to use if binding fails, or null. + /// The representing the result of the binding. + public override DynamicMetaObject FallbackGetIndex(DynamicMetaObject target, DynamicMetaObject[] indexes, DynamicMetaObject errorSuggestion) + { +#if !SILVERLIGHT + DynamicMetaObject com; + if (!BinderHelper.IsWindowsRuntimeObject(target) && ComBinder.TryBindGetIndex(this, target, indexes, out com)) + { + return com; + } +#endif + return BinderHelper.Bind(this, m_binder, BinderHelper.Cons(target, indexes), m_argumentInfo, errorSuggestion); + } + } +} diff --git a/Microsoft.CSharp/Microsoft/CSharp/CSharpGetMemberBinder.cs b/Microsoft.CSharp/Microsoft/CSharp/CSharpGetMemberBinder.cs new file mode 100644 index 000000000..cd60f88ad --- /dev/null +++ b/Microsoft.CSharp/Microsoft/CSharp/CSharpGetMemberBinder.cs @@ -0,0 +1,71 @@ +// ==++== +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// ==--== + +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Dynamic; + +namespace Microsoft.CSharp.RuntimeBinder +{ + /// + /// Represents a dynamic property access in C#, providing the binding semantics and the details about the operation. + /// Instances of this class are generated by the C# compiler. + /// + internal sealed class CSharpGetMemberBinder : GetMemberBinder, IInvokeOnGetBinder + { + internal Type CallingContext { get { return m_callingContext; } } + private Type m_callingContext; + + internal IList ArgumentInfo { get { return m_argumentInfo.AsReadOnly(); } } + private List m_argumentInfo; + + bool IInvokeOnGetBinder.InvokeOnGet { get { return !m_bResultIndexed; } } + + internal bool ResultIndexed { get { return m_bResultIndexed; } } + private bool m_bResultIndexed; + + private RuntimeBinder m_binder; + + /// + /// Initializes a new instance of the . + /// + /// The name of the member to get. + /// Determines if COM binder should return a callable object. + /// The that indicates where this operation is defined. + /// The sequence of instances for the arguments to this operation. + public CSharpGetMemberBinder( + string name, + bool resultIndexed, + Type callingContext, + IEnumerable argumentInfo) : + base(name, false /*caseInsensitive*/) + { + m_bResultIndexed = resultIndexed; + m_callingContext = callingContext; + m_argumentInfo = BinderHelper.ToList(argumentInfo); + m_binder = RuntimeBinder.GetInstance(); + } + + /// + /// Performs the binding of the dynamic get member operation if the target dynamic object cannot bind. + /// + /// The target of the dynamic get member operation. + /// The binding result to use if binding fails, or null. + /// The representing the result of the binding. + public override DynamicMetaObject FallbackGetMember(DynamicMetaObject target, DynamicMetaObject errorSuggestion) + { +#if !SILVERLIGHT + DynamicMetaObject com; + if (!BinderHelper.IsWindowsRuntimeObject(target) && ComBinder.TryBindGetMember(this, target, out com, ResultIndexed)) + { + return com; + } +#endif + return BinderHelper.Bind(this, m_binder, new[] { target }, m_argumentInfo, errorSuggestion); + } + } +} diff --git a/Microsoft.CSharp/Microsoft/CSharp/CSharpInvokeBinder.cs b/Microsoft.CSharp/Microsoft/CSharp/CSharpInvokeBinder.cs new file mode 100644 index 000000000..090784284 --- /dev/null +++ b/Microsoft.CSharp/Microsoft/CSharp/CSharpInvokeBinder.cs @@ -0,0 +1,75 @@ +// ==++== +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// ==--== + +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Dynamic; + +namespace Microsoft.CSharp.RuntimeBinder +{ + /// + /// Represents a dynamic delegate-like call in C#, providing the binding semantics and the details about the operation. + /// Instances of this class are generated by the C# compiler. + /// + internal sealed class CSharpInvokeBinder : InvokeBinder, ICSharpInvokeOrInvokeMemberBinder + { + bool ICSharpInvokeOrInvokeMemberBinder.StaticCall { get { return m_argumentInfo[0] != null && m_argumentInfo[0].IsStaticType; } } + string ICSharpInvokeOrInvokeMemberBinder.Name { get { return "Invoke"; } } + IList ICSharpInvokeOrInvokeMemberBinder.TypeArguments { get { return new Type[0]; } } + + CSharpCallFlags ICSharpInvokeOrInvokeMemberBinder.Flags { get { return m_flags; } } + private CSharpCallFlags m_flags; + + Type ICSharpInvokeOrInvokeMemberBinder.CallingContext { get { return m_callingContext; } } + private Type m_callingContext; + + IList ICSharpInvokeOrInvokeMemberBinder.ArgumentInfo { get { return m_argumentInfo.AsReadOnly(); } } + private List m_argumentInfo; + + bool ICSharpInvokeOrInvokeMemberBinder.ResultDiscarded { get { return (m_flags & CSharpCallFlags.ResultDiscarded) != 0; } } + + private RuntimeBinder m_binder; + + /// + /// Initializes a new instance of the . + /// + /// Extra information about this operation that is not specific to any particular argument. + /// The that indicates where this operation is defined. + /// The sequence of instances for the arguments to this operation. + public CSharpInvokeBinder( + CSharpCallFlags flags, + Type callingContext, + IEnumerable argumentInfo) : + base(BinderHelper.CreateCallInfo(argumentInfo, 1)) // discard 1 argument: the target object (even if static, arg is type) + { + m_flags = flags; + m_callingContext = callingContext; + m_argumentInfo = BinderHelper.ToList(argumentInfo); + m_binder = RuntimeBinder.GetInstance(); + } + + /// + /// Performs the binding of the dynamic invoke operation if the target dynamic object cannot bind. + /// + /// The target of the dynamic invoke operation. + /// The arguments of the dynamic invoke operation. + /// The binding result to use if binding fails, or null. + /// The representing the result of the binding. + public override DynamicMetaObject FallbackInvoke(DynamicMetaObject target, DynamicMetaObject[] args, DynamicMetaObject errorSuggestion) + { +#if !SILVERLIGHT + + DynamicMetaObject com; + if (!BinderHelper.IsWindowsRuntimeObject(target) && ComBinder.TryBindInvoke(this, target, args, out com)) + { + return com; + } +#endif + return BinderHelper.Bind(this, m_binder, BinderHelper.Cons(target, args), m_argumentInfo, errorSuggestion); + } + } +} diff --git a/Microsoft.CSharp/Microsoft/CSharp/CSharpInvokeConstructorBinder.cs b/Microsoft.CSharp/Microsoft/CSharp/CSharpInvokeConstructorBinder.cs new file mode 100644 index 000000000..c8110d730 --- /dev/null +++ b/Microsoft.CSharp/Microsoft/CSharp/CSharpInvokeConstructorBinder.cs @@ -0,0 +1,49 @@ +// ==++== +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// ==--== + +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Dynamic; + +namespace Microsoft.CSharp.RuntimeBinder +{ + internal sealed class CSharpInvokeConstructorBinder : DynamicMetaObjectBinder, ICSharpInvokeOrInvokeMemberBinder + { + public CSharpCallFlags Flags { get { return m_flags; } } + private CSharpCallFlags m_flags; + + public Type CallingContext { get { return m_callingContext; } } + private Type m_callingContext; + + public IList ArgumentInfo { get { return m_argumentInfo.AsReadOnly(); } } + private List m_argumentInfo; + + public bool StaticCall { get { return true; } } + public IList TypeArguments { get { return new Type[0]; } } + public string Name { get { return ".ctor"; } } + + bool ICSharpInvokeOrInvokeMemberBinder.ResultDiscarded { get { return false; } } + + private RuntimeBinder m_binder; + + public CSharpInvokeConstructorBinder( + CSharpCallFlags flags, + Type callingContext, + IEnumerable argumentInfo) + { + m_flags = flags; + m_callingContext = callingContext; + m_argumentInfo = BinderHelper.ToList(argumentInfo); + m_binder = RuntimeBinder.GetInstance(); + } + + public sealed override DynamicMetaObject Bind(DynamicMetaObject target, DynamicMetaObject[] args) + { + return BinderHelper.Bind(this, m_binder, BinderHelper.Cons(target, args), m_argumentInfo, null); + } + } +} diff --git a/Microsoft.CSharp/Microsoft/CSharp/CSharpInvokeMemberBinder.cs b/Microsoft.CSharp/Microsoft/CSharp/CSharpInvokeMemberBinder.cs new file mode 100644 index 000000000..c37f4b237 --- /dev/null +++ b/Microsoft.CSharp/Microsoft/CSharp/CSharpInvokeMemberBinder.cs @@ -0,0 +1,93 @@ +// ==++== +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// ==--== + +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Dynamic; + +namespace Microsoft.CSharp.RuntimeBinder +{ + /// + /// Represents a dynamic method call in C#, providing the binding semantics and the details about the operation. + /// Instances of this class are generated by the C# compiler. + /// + internal sealed class CSharpInvokeMemberBinder : InvokeMemberBinder, ICSharpInvokeOrInvokeMemberBinder + { + bool ICSharpInvokeOrInvokeMemberBinder.StaticCall { get { return m_argumentInfo[0] != null && m_argumentInfo[0].IsStaticType; } } + + CSharpCallFlags ICSharpInvokeOrInvokeMemberBinder.Flags { get { return m_flags; } } + private CSharpCallFlags m_flags; + + Type ICSharpInvokeOrInvokeMemberBinder.CallingContext { get { return m_callingContext; } } + private Type m_callingContext; + + IList ICSharpInvokeOrInvokeMemberBinder.TypeArguments { get { return m_typeArguments.AsReadOnly(); } } + private List m_typeArguments; + + IList ICSharpInvokeOrInvokeMemberBinder.ArgumentInfo { get { return m_argumentInfo.AsReadOnly(); } } + private List m_argumentInfo; + + bool ICSharpInvokeOrInvokeMemberBinder.ResultDiscarded { get { return (m_flags & CSharpCallFlags.ResultDiscarded) != 0; } } + + private RuntimeBinder m_binder; + + /// + /// Initializes a new instance of the . + /// + /// Extra information about this operation that is not specific to any particular argument. + /// The name of the member to invoke. + /// The that indicates where this operation is defined. + /// The list of user-specified type arguments to this call. + /// The sequence of instances for the arguments to this operation. + public CSharpInvokeMemberBinder( + CSharpCallFlags flags, + string name, + Type callingContext, + IEnumerable typeArguments, + IEnumerable argumentInfo) : + base(name, false, BinderHelper.CreateCallInfo(argumentInfo, 1)) // discard 1 argument: the target object (even if static, arg is type) + { + m_flags = flags; + m_callingContext = callingContext; + m_typeArguments = BinderHelper.ToList(typeArguments); + m_argumentInfo = BinderHelper.ToList(argumentInfo); + m_binder = RuntimeBinder.GetInstance(); + } + + /// + /// Performs the binding of the dynamic invoke member operation if the target dynamic object cannot bind. + /// + /// The target of the dynamic invoke member operation. + /// The arguments of the dynamic invoke member operation. + /// The binding result to use if binding fails, or null. + /// The representing the result of the binding. + public override DynamicMetaObject FallbackInvokeMember(DynamicMetaObject target, DynamicMetaObject[] args, DynamicMetaObject errorSuggestion) + { +#if !SILVERLIGHT + DynamicMetaObject com; + if (!BinderHelper.IsWindowsRuntimeObject(target) && ComBinder.TryBindInvokeMember(this, target, args, out com)) + { + return com; + } +#endif + return BinderHelper.Bind(this, m_binder, BinderHelper.Cons(target, args), m_argumentInfo, errorSuggestion); + } + + /// + /// Performs the binding of the dynamic invoke operation if the target dynamic object cannot bind. + /// + /// The target of the dynamic invoke operation. + /// The arguments of the dynamic invoke operation. + /// The binding result to use if binding fails, or null. + /// The representing the result of the binding. + public override DynamicMetaObject FallbackInvoke(DynamicMetaObject target, DynamicMetaObject[] args, DynamicMetaObject errorSuggestion) + { + CSharpInvokeBinder c = new CSharpInvokeBinder(m_flags, m_callingContext, m_argumentInfo); + return c.Defer(target, args); + } + } +} diff --git a/Microsoft.CSharp/Microsoft/CSharp/CSharpIsEventBinder.cs b/Microsoft.CSharp/Microsoft/CSharp/CSharpIsEventBinder.cs new file mode 100644 index 000000000..582d3f750 --- /dev/null +++ b/Microsoft.CSharp/Microsoft/CSharp/CSharpIsEventBinder.cs @@ -0,0 +1,58 @@ +// ==++== +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// ==--== + +using System; +using System.ComponentModel; +using System.Dynamic; + +namespace Microsoft.CSharp.RuntimeBinder +{ + /// + /// Used to test whether a dynamic member over which += or -= is used is an event member. + /// + internal sealed class CSharpIsEventBinder : DynamicMetaObjectBinder + { + internal string Name { get { return m_name; } } + private string m_name; + + internal Type CallingContext { get { return m_callingContext; } } + private Type m_callingContext; + + private RuntimeBinder m_binder; + + /// + /// Initializes a new instance of the class. + /// + /// The name of the member to test. + /// The that indicates where this operation is defined. + public CSharpIsEventBinder( + string name, + Type callingContext) + { + m_name = name; + m_callingContext = callingContext; + m_binder = RuntimeBinder.GetInstance(); + } + + /// + /// The result type of the operation. + /// + public override sealed Type ReturnType { + get { return typeof(bool); } + } + + /// + /// Performs the binding of the binary dynamic operation if the target dynamic object cannot bind. + /// + /// The target of the dynamic binary operation. + /// The arguments to the dynamic event test. + /// The representing the result of the binding. + public sealed override DynamicMetaObject Bind(DynamicMetaObject target, DynamicMetaObject[] args) + { + return BinderHelper.Bind(this, m_binder, new DynamicMetaObject[] { target }, null, null); + } + } +} diff --git a/Microsoft.CSharp/Microsoft/CSharp/CSharpSetIndexBinder.cs b/Microsoft.CSharp/Microsoft/CSharp/CSharpSetIndexBinder.cs new file mode 100644 index 000000000..e369c1034 --- /dev/null +++ b/Microsoft.CSharp/Microsoft/CSharp/CSharpSetIndexBinder.cs @@ -0,0 +1,76 @@ +// ==++== +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// ==--== + +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Dynamic; + +namespace Microsoft.CSharp.RuntimeBinder +{ + /// + /// Represents a dynamic indexer access in C#, providing the binding semantics and the details about the operation. + /// Instances of this class are generated by the C# compiler. + /// + internal sealed class CSharpSetIndexBinder : SetIndexBinder + { + internal bool IsCompoundAssignment { get { return m_bIsCompoundAssignment; } } + private bool m_bIsCompoundAssignment; + + internal bool IsChecked { get { return m_isChecked; } } + private bool m_isChecked; + + internal Type CallingContext { get { return m_callingContext; } } + private Type m_callingContext; + + internal IList ArgumentInfo { get { return m_argumentInfo.AsReadOnly(); } } + private List m_argumentInfo; + + private RuntimeBinder m_binder; + + ////////////////////////////////////////////////////////////////////// + + /// + /// Initializes a new instance of the . + /// + /// True if the assignment comes from a compound assignment in source. + /// The that indicates where this operation is defined. + /// The sequence of instances for the arguments to this operation. + public CSharpSetIndexBinder( + bool isCompoundAssignment, + bool isChecked, + Type callingContext, + IEnumerable argumentInfo) : + base(BinderHelper.CreateCallInfo(argumentInfo, 2)) // discard 2 arguments: the target object and the value + { + m_bIsCompoundAssignment = isCompoundAssignment; + m_isChecked = isChecked; + m_callingContext = callingContext; + m_argumentInfo = BinderHelper.ToList(argumentInfo); + m_binder = RuntimeBinder.GetInstance(); + } + + /// + /// Performs the binding of the dynamic set index operation if the target dynamic object cannot bind. + /// + /// The target of the dynamic set index operation. + /// The arguments of the dynamic set index operation. + /// The value to set to the collection. + /// The binding result to use if binding fails, or null. + /// The representing the result of the binding. + public override DynamicMetaObject FallbackSetIndex(DynamicMetaObject target, DynamicMetaObject[] indexes, DynamicMetaObject value, DynamicMetaObject errorSuggestion) + { +#if !SILVERLIGHT + DynamicMetaObject com; + if (!BinderHelper.IsWindowsRuntimeObject(target) && ComBinder.TryBindSetIndex(this, target, indexes, value, out com)) + { + return com; + } +#endif + return BinderHelper.Bind(this, m_binder, BinderHelper.Cons(target, indexes, value), m_argumentInfo, errorSuggestion); + } + } +} diff --git a/Microsoft.CSharp/Microsoft/CSharp/CSharpSetMemberBinder.cs b/Microsoft.CSharp/Microsoft/CSharp/CSharpSetMemberBinder.cs new file mode 100644 index 000000000..97f02074c --- /dev/null +++ b/Microsoft.CSharp/Microsoft/CSharp/CSharpSetMemberBinder.cs @@ -0,0 +1,78 @@ +// ==++== +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// ==--== + +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Dynamic; + +namespace Microsoft.CSharp.RuntimeBinder +{ + /// + /// Represents a dynamic property access in C#, providing the binding semantics and the details about the operation. + /// Instances of this class are generated by the C# compiler. + /// + internal sealed class CSharpSetMemberBinder : SetMemberBinder + { + internal bool IsCompoundAssignment { get { return m_bIsCompoundAssignment; } } + private bool m_bIsCompoundAssignment; + + internal bool IsChecked { get { return m_isChecked; } } + private bool m_isChecked; + + internal Type CallingContext { get { return m_callingContext; } } + private Type m_callingContext; + + internal IList ArgumentInfo { get { return m_argumentInfo.AsReadOnly(); } } + private List m_argumentInfo; + + private RuntimeBinder m_binder; + + ////////////////////////////////////////////////////////////////////// + + + /// + /// Initializes a new instance of the . + /// + /// The name of the member to get. + /// True if the assignment comes from a compound assignment in source. + /// The that indicates where this operation is defined. + /// The sequence of instances for the arguments to this operation. + public CSharpSetMemberBinder( + string name, + bool isCompoundAssignment, + bool isChecked, + Type callingContext, + IEnumerable argumentInfo) : + base(name, false) + { + m_bIsCompoundAssignment = isCompoundAssignment; + m_isChecked = isChecked; + m_callingContext = callingContext; + m_argumentInfo = BinderHelper.ToList(argumentInfo); + m_binder = RuntimeBinder.GetInstance(); + } + + /// + /// Performs the binding of the dynamic set member operation if the target dynamic object cannot bind. + /// + /// The target of the dynamic set member operation. + /// The value to set to the member. + /// The binding result to use if binding fails, or null. + /// The representing the result of the binding. + public override DynamicMetaObject FallbackSetMember(DynamicMetaObject target, DynamicMetaObject value, DynamicMetaObject errorSuggestion) + { +#if !SILVERLIGHT + DynamicMetaObject com; + if (!BinderHelper.IsWindowsRuntimeObject(target) && ComBinder.TryBindSetMember(this, target, value, out com)) + { + return com; + } +#endif + return BinderHelper.Bind(this, m_binder, new[] { target, value }, m_argumentInfo, errorSuggestion); + } + } +} diff --git a/Microsoft.CSharp/Microsoft/CSharp/CSharpUnaryOperationBinder.cs b/Microsoft.CSharp/Microsoft/CSharp/CSharpUnaryOperationBinder.cs new file mode 100644 index 000000000..5fe5d6f18 --- /dev/null +++ b/Microsoft.CSharp/Microsoft/CSharp/CSharpUnaryOperationBinder.cs @@ -0,0 +1,64 @@ +// ==++== +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// ==--== + +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Diagnostics; +using System.Dynamic; +using System.Linq.Expressions; + +namespace Microsoft.CSharp.RuntimeBinder +{ + /// + /// Represents a dynamic unary operation in C#, providing the binding semantics and the details about the operation. + /// Instances of this class are generated by the C# compiler. + /// + internal sealed class CSharpUnaryOperationBinder : UnaryOperationBinder + { + internal bool IsChecked { get { return m_isChecked; } } + private bool m_isChecked; + + internal Type CallingContext { get { return m_callingContext; } } + private Type m_callingContext; + + internal IList ArgumentInfo { get { return m_argumentInfo.AsReadOnly(); } } + private List m_argumentInfo; + + private RuntimeBinder m_binder; + + /// + /// Initializes a new instance of the class. + /// + /// The unary operation kind. + /// True if the operation is defined in a checked context; otherwise, false. + /// The sequence of instances for the arguments to this operation. + public CSharpUnaryOperationBinder( + ExpressionType operation, + bool isChecked, + Type callingContext, + IEnumerable argumentInfo) : + base(operation) + { + m_isChecked = isChecked; + m_callingContext = callingContext; + m_argumentInfo = BinderHelper.ToList(argumentInfo); + Debug.Assert(m_argumentInfo.Count == 1); + m_binder = RuntimeBinder.GetInstance(); + } + + /// + /// Performs the binding of the unary dynamic operation if the target dynamic object cannot bind. + /// + /// The target of the dynamic unary operation. + /// The binding result in case the binding fails, or null. + /// The representing the result of the binding. + public sealed override DynamicMetaObject FallbackUnaryOperation(DynamicMetaObject target, DynamicMetaObject errorSuggestion) + { + return BinderHelper.Bind(this, m_binder, BinderHelper.Cons(target, null), m_argumentInfo, errorSuggestion); + } + } +} diff --git a/Microsoft.CSharp/Microsoft/CSharp/DynamicDebuggerProxy.cs b/Microsoft.CSharp/Microsoft/CSharp/DynamicDebuggerProxy.cs new file mode 100644 index 000000000..f1fac59e9 --- /dev/null +++ b/Microsoft.CSharp/Microsoft/CSharp/DynamicDebuggerProxy.cs @@ -0,0 +1,516 @@ +// ==++== +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// ==--== + +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Diagnostics; +using System.Dynamic; +using System.Linq; +using System.Linq.Expressions; +using System.Runtime.CompilerServices; +using System.Runtime.Serialization; + +namespace Microsoft.CSharp.RuntimeBinder +{ +#if !SILVERLIGHT + [Serializable] +#endif + [EditorBrowsable(EditorBrowsableState.Never)] + internal sealed class DynamicBindingFailedException : Exception + { + public DynamicBindingFailedException() + : base() + { + } + +#if !SILVERLIGHT + private DynamicBindingFailedException(SerializationInfo info, StreamingContext context) + : base(info, context) + { + } +#endif + } + + internal sealed class GetMemberValueBinder : GetMemberBinder + { + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + public GetMemberValueBinder(string name, bool ignoreCase) + : base(name, ignoreCase) + { + } + + public override DynamicMetaObject FallbackGetMember(DynamicMetaObject self, DynamicMetaObject onBindingError) + { + if (onBindingError == null) + { + var v = new List { self }; + var error = new DynamicMetaObject(System.Linq.Expressions.Expression.Throw( + System.Linq.Expressions.Expression.Constant(new DynamicBindingFailedException(), typeof(Exception)), typeof(object)), System.Dynamic.BindingRestrictions.Combine(v)); + return error; + } + return onBindingError; + } + } + + internal sealed class DynamicMetaObjectProviderDebugView + { + [System.Diagnostics.DebuggerDisplay("{value}", Name = "{name, nq}", Type = "{type, nq}")] + internal class DynamicProperty + { + [System.Diagnostics.DebuggerBrowsable(System.Diagnostics.DebuggerBrowsableState.Never)] + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1823:AvoidUnusedPrivateFields")] + readonly string name; + + [System.Diagnostics.DebuggerBrowsable(System.Diagnostics.DebuggerBrowsableState.Never)] + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1823:AvoidUnusedPrivateFields")] + readonly object value; + + [System.Diagnostics.DebuggerBrowsable(System.Diagnostics.DebuggerBrowsableState.Never)] + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1823:AvoidUnusedPrivateFields")] + readonly string type; + + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + public DynamicProperty(string name, object value) + { + this.name = name; + this.value = value; + this.type = value == null ? "" : value.GetType().ToString(); + } + } + + [System.Diagnostics.DebuggerBrowsable(System.Diagnostics.DebuggerBrowsableState.Never)] + private IList> results = null; + + [System.Diagnostics.DebuggerBrowsable(System.Diagnostics.DebuggerBrowsableState.Never)] + private object obj; + + [System.Diagnostics.DebuggerBrowsable(System.Diagnostics.DebuggerBrowsableState.RootHidden)] + internal DynamicProperty[] Items + { + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + get + { + if (results == null || results.Count == 0) + { + results = QueryDynamicObject(obj); + if (results == null || results.Count == 0) + { + throw new DynamicDebugViewEmptyException(); + } + } + DynamicProperty[] pairArray = new DynamicProperty[results.Count]; + for (int i = 0; i < results.Count; i++) + { + pairArray[i] = new DynamicProperty(results[i].Key, results[i].Value); + } + return pairArray; + } + } + + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + public DynamicMetaObjectProviderDebugView(object arg) + { + this.obj = arg; + } + + [System.Diagnostics.DebuggerBrowsable(System.Diagnostics.DebuggerBrowsableState.Never)] + private static readonly Type ComObjectType = typeof(object).Assembly.GetType("System.__ComObject"); + + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + private static bool IsComObject(object obj) + { + return (obj != null && ComObjectType.IsAssignableFrom(obj.GetType())); + } + + [System.Diagnostics.DebuggerBrowsable(System.Diagnostics.DebuggerBrowsableState.Never)] + private static readonly ParameterExpression parameter = Expression.Parameter(typeof(object), "debug"); + + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + public static object TryEvalBinaryOperators( + T1 arg1, + T2 arg2, + CSharpArgumentInfoFlags arg1Flags, + CSharpArgumentInfoFlags arg2Flags, + ExpressionType opKind, + Type accessibilityContext) + { + CSharpArgumentInfo arg1Info = CSharpArgumentInfo.Create(arg1Flags, null); + CSharpArgumentInfo arg2Info = CSharpArgumentInfo.Create(arg2Flags, null); + + CSharpBinaryOperationBinder binder = new CSharpBinaryOperationBinder( + opKind, + false, // isChecked + CSharpBinaryOperationFlags.None, + accessibilityContext, + new CSharpArgumentInfo[] { arg1Info, arg2Info }); + + var site = CallSite>.Create(binder); + return site.Target(site, arg1, arg2); + } + + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + public static object TryEvalUnaryOperators(T obj, ExpressionType oper, Type accessibilityContext) + { + if (oper == ExpressionType.IsTrue || oper == ExpressionType.IsFalse) + { + var trueFalseSite = CallSite> + .Create(new Microsoft.CSharp.RuntimeBinder.CSharpUnaryOperationBinder(oper, + false, + accessibilityContext, + new CSharpArgumentInfo[] { CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null) })); + return trueFalseSite.Target(trueFalseSite, obj); + } + + var site = CallSite> + .Create(new Microsoft.CSharp.RuntimeBinder.CSharpUnaryOperationBinder(oper, + false, + accessibilityContext, + new CSharpArgumentInfo[] { CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null) })); + return site.Target(site, obj); + } + + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + public static K TryEvalCast(T obj, Type type, CSharpBinderFlags kind, Type accessibilityContext) + { + var site = CallSite>.Create(Binder.Convert(kind, type, accessibilityContext)); + return site.Target(site, obj); + } + + /// + /// Creates array of types that describes delegate's signature and array of + /// CSharpArgumentInfoFlags that describe each of the arguments. + /// + private static void CreateDelegateSignatureAndArgumentInfos( + object[] args, + Type[] argTypes, + CSharpArgumentInfoFlags[] argFlags, + out Type[] delegateSignatureTypes, + out CSharpArgumentInfo[] argInfos) + { + int numberOfArguments = args.Length; + Debug.Assert((numberOfArguments == argTypes.Length) && (numberOfArguments == argFlags.Length), "Argument arrays size mismatch."); + + delegateSignatureTypes = new Type[numberOfArguments + 2]; + delegateSignatureTypes[0] = typeof(CallSite); + + argInfos = new CSharpArgumentInfo[numberOfArguments]; + + for (int i = 0; i < numberOfArguments; i++) + { + if (argTypes[i] != null) + { + delegateSignatureTypes[i + 1] = argTypes[i]; + } + else if (args[i] != null) + { + delegateSignatureTypes[i + 1] = args[i].GetType(); + } + else + { + delegateSignatureTypes[i + 1] = typeof(object); + } + + argInfos[i] = CSharpArgumentInfo.Create(argFlags[i], null); + } + + delegateSignatureTypes[numberOfArguments + 1] = typeof(object); // type of return value + } + + /// + /// Creates a delegate based on type array that describe its signature and invokes it. + /// + /// Result of invoking the delegate. + private static object CreateDelegateAndInvoke(Type[] delegateSignatureTypes, CallSiteBinder binder, object[] args) + { + Type delegateType = Expression.GetDelegateType(delegateSignatureTypes); + var site = CallSite.Create(delegateType, binder); + + Delegate target = (Delegate)site.GetType().GetField("Target").GetValue(site); + + object[] argsWithSite = new object[args.Length + 1]; + argsWithSite[0] = site; + args.CopyTo(argsWithSite, 1); + + object result = target.DynamicInvoke(argsWithSite); + return result; + } + + + /// + /// DynamicOperatorRewriter in EE generates call to this method to dynamically invoke a method. + /// + /// Array that contains method arguments. The first element is an object on which method should be called. + /// Type of each argument in methodArgs. + /// Flags describing each argument. + /// Name of a method to invoke. + /// Type that determines context in which method should be called. + /// Generic type arguments if there are any. + /// Result of method invocation. + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + public static object TryEvalMethodVarArgs( + object[] methodArgs, + Type[] argTypes, + CSharpArgumentInfoFlags[] argFlags, + string methodName, + Type accessibilityContext, + Type[] typeArguments) + { + Type[] delegateSignatureTypes = null; + CSharpArgumentInfo[] argInfos = null; + + CreateDelegateSignatureAndArgumentInfos( + methodArgs, + argTypes, + argFlags, + out delegateSignatureTypes, + out argInfos); + + CallSiteBinder binder; + if (String.IsNullOrEmpty(methodName)) + { + //null or empty indicates delegate invocation. + binder = new CSharpInvokeBinder( + CSharpCallFlags.ResultDiscarded, + accessibilityContext, + argInfos); + } + else + { + binder = new CSharpInvokeMemberBinder( + CSharpCallFlags.ResultDiscarded, + methodName, + accessibilityContext, + typeArguments, + argInfos); + } + + return CreateDelegateAndInvoke(delegateSignatureTypes, binder, methodArgs); + } + + /// + /// DynamicOperatorRewriter in EE generates call to this method to dynamically invoke a property getter + /// with no arguments. + /// + /// Type of object on which property is defined. + /// Object on which property is defined. + /// Name of a property to invoke. + /// Type that determines context in which method should be called. + /// Determines if COM binder should return a callable object. + /// Result of property invocation. + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + public static object TryGetMemberValue(T obj, string propName, Type accessibilityContext, bool isResultIndexed) + { + // In most cases it's ok to use CSharpArgumentInfoFlags.None since target of property call is dynamic. + // The only possible case when target is not dynamic but we still treat is as dynamic access is when + // one of arguments is dynamic. This is only possible for indexed properties since we call this method and + // TryGetMemberValueVarArgs afterwards. + + CSharpGetMemberBinder binder = new CSharpGetMemberBinder( + propName, + isResultIndexed, + accessibilityContext, + new CSharpArgumentInfo[] { CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null) }); + + var site = CallSite>.Create(binder); + return site.Target(site, obj); + } + + /// + /// DynamicOperatorRewriter in EE generates call to this method to dynamically invoke a property/indexer getter. + /// + /// Array that contains property arguments. The first element is an object on + /// which indexer should be called or call to TryGetMemberValue that selects the right property in case of + /// indexed properties. + /// Type of each argument in propArgs. + /// Flags describing each argument. + /// Type that determines context in which method should be called. + /// Result of property invocation. + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + public static object TryGetMemberValueVarArgs( + object[] propArgs, + Type[] argTypes, + CSharpArgumentInfoFlags[] argFlags, + Type accessibilityContext) + { + Type[] delegateSignatureTypes = null; + CSharpArgumentInfo[] argInfos = null; + + CreateDelegateSignatureAndArgumentInfos( + propArgs, + argTypes, + argFlags, + out delegateSignatureTypes, + out argInfos); + + CallSiteBinder binder = new CSharpGetIndexBinder(accessibilityContext, argInfos); + + return CreateDelegateAndInvoke(delegateSignatureTypes, binder, propArgs); + } + + /// + /// DynamicOperatorRewriter in EE generates call to this method to dynamically invoke a property setter + /// with no arguments. + /// + /// Type of object on which property is defined. + /// Type of value property needs to be set to. + /// Object on which property is defined. + /// Name of a property to invoke. + /// Value property needs to be set to. + /// Type that determines context in which method should be called. + /// Result of property invocation. + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + public static object TrySetMemberValue( + TObject obj, + string propName, + TValue value, + CSharpArgumentInfoFlags valueFlags, + Type accessibilityContext) + { + CSharpArgumentInfo targetArgInfo = CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null); + CSharpArgumentInfo valueArgInfo = CSharpArgumentInfo.Create(valueFlags, null); + + CSharpSetMemberBinder binder = new CSharpSetMemberBinder( + propName, + false, // isCompoundAssignment + false, // isChecked + accessibilityContext, + new CSharpArgumentInfo[] { targetArgInfo, valueArgInfo }); + + var site = CallSite>.Create(binder); + return site.Target(site, obj, value); + } + + /// + /// DynamicOperatorRewriter in EE generates call to this method to dynamically invoke a property/indexer setter. + /// + /// Array that contains property arguments. The first element is an object on + /// which indexer should be called or call to TrySetMemberValue that selects the right property in case of + /// indexed properties. The last argument is value that property should be set to. + /// Type of each argument in propArgs. + /// Flags describing each argument. + /// Type that determines context in which method should be called. + /// Result of property invocation. + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + public static object TrySetMemberValueVarArgs( + object[] propArgs, + Type[] argTypes, + CSharpArgumentInfoFlags[] argFlags, + Type accessibilityContext) + { + Type[] delegateSignatureTypes = null; + CSharpArgumentInfo[] argInfos = null; + + CreateDelegateSignatureAndArgumentInfos( + propArgs, + argTypes, + argFlags, + out delegateSignatureTypes, + out argInfos); + + CallSiteBinder binder = new CSharpSetIndexBinder(/*isCompoundAssignment */ false, /* isChecked */ false, accessibilityContext, argInfos); + + return CreateDelegateAndInvoke(delegateSignatureTypes, binder, propArgs); + } + + + //Called when we don't know if the member is a property or a method + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + internal static object TryGetMemberValue(object obj, string name, bool ignoreException) + { + // if you want to ignore case for VB, this is how you set it .. make it a member and add a ctor to init it + bool ignoreCase = false; + object value = null; + + var site = CallSite>.Create(new GetMemberValueBinder(name, ignoreCase)); + + try + { + value = site.Target(site, obj); + } + catch (DynamicBindingFailedException exp) + { + if (ignoreException) + value = null; + else + throw exp; + } + catch (MissingMemberException exp) + { + if (ignoreException) + value = Strings.GetValueonWriteOnlyProperty; + else + throw exp; + } + return value; + } + + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + private static IList> QueryDynamicObject(object obj) + { + IDynamicMetaObjectProvider ido = obj as IDynamicMetaObjectProvider; + if (ido != null) + { + DynamicMetaObject mo = ido.GetMetaObject(parameter); + List names = new List(mo.GetDynamicMemberNames()); + names.Sort(); + if (names != null) + { + var result = new List>(); + foreach (string name in names) + { + object value; + if ((value = TryGetMemberValue(obj, name, true)) != null) + { + result.Add(new KeyValuePair(name, value)); + } + } + return result; + } + } +#if !SILVERLIGHT + else if (IsComObject(obj)) + { + var comExclusionList = new string[] { "MailEnvelope" }; //add any com names to be excluded from dynamic view here + + IEnumerable names = System.Dynamic.ComBinder.GetDynamicDataMemberNames(obj); + names = from name in names + where !comExclusionList.Contains(name) + select name; + var sortedNames = new List(names); + sortedNames.Sort(); + return System.Dynamic.ComBinder.GetDynamicDataMembers(obj, sortedNames); + } +#endif + return new KeyValuePair[0]; + } + +#if !SILVERLIGHT + [Serializable] +#endif + internal class DynamicDebugViewEmptyException : Exception + { + public DynamicDebugViewEmptyException() + { + } + +#if !SILVERLIGHT + protected DynamicDebugViewEmptyException(SerializationInfo info, StreamingContext context) + : base(info, context) + { + } +#endif + public string Empty + { + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + get + { + return Strings.EmptyDynamicView; + } + } + } + } +} diff --git a/Microsoft.CSharp/Microsoft/CSharp/ExpressionTreeCallRewriter.cs b/Microsoft.CSharp/Microsoft/CSharp/ExpressionTreeCallRewriter.cs new file mode 100644 index 000000000..65a0b2007 --- /dev/null +++ b/Microsoft.CSharp/Microsoft/CSharp/ExpressionTreeCallRewriter.cs @@ -0,0 +1,1204 @@ +// ==++== +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// ==--== + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; +using System.Linq.Expressions; +using System.Reflection; +using Microsoft.CSharp.RuntimeBinder.Semantics; + +namespace Microsoft.CSharp.RuntimeBinder +{ + internal class ExpressionTreeCallRewriter : ExprVisitorBase + { + ///////////////////////////////////////////////////////////////////////////////// + // Members + + private class ExpressionEXPR : EXPR + { + public Expression Expression ; + public ExpressionEXPR(Expression e) + { + Expression = e; + } + } + + private Dictionary DictionaryOfParameters; + private IEnumerable ListOfParameters; + private TypeManager m_typeManager; + // Counts how many EXPRSAVEs we've encountered so we know which index into the + // parameter list we should be taking. + private int currentParameterIndex; + + ///////////////////////////////////////////////////////////////////////////////// + + protected ExpressionTreeCallRewriter(TypeManager typeManager, IEnumerable listOfParameters) + { + m_typeManager = typeManager; + DictionaryOfParameters = new Dictionary(); + ListOfParameters = listOfParameters; + } + + ///////////////////////////////////////////////////////////////////////////////// + + public static Expression Rewrite(TypeManager typeManager, EXPR pExpr, IEnumerable listOfParameters) + { + ExpressionTreeCallRewriter rewriter = new ExpressionTreeCallRewriter(typeManager, listOfParameters); + + // We should have a EXPRBINOP thats an EK_SEQUENCE. The RHS of our sequence + // should be a call to PM_EXPRESSION_LAMBDA. The LHS of our sequence is the + // set of declarations for the parameters that we'll need. + // Assert all of these first, and then unwrap them. + + Debug.Assert(pExpr != null); + Debug.Assert(pExpr.isBIN()); + Debug.Assert(pExpr.kind == ExpressionKind.EK_SEQUENCE); + Debug.Assert(pExpr.asBIN().GetOptionalRightChild() != null); + Debug.Assert(pExpr.asBIN().GetOptionalRightChild().isCALL()); + Debug.Assert(pExpr.asBIN().GetOptionalRightChild().asCALL().PredefinedMethod == PREDEFMETH.PM_EXPRESSION_LAMBDA); + Debug.Assert(pExpr.asBIN().GetOptionalLeftChild() != null); + + // Visit the left to generate the parameter construction. + rewriter.Visit(pExpr.asBIN().GetOptionalLeftChild()); + EXPRCALL call = pExpr.asBIN().GetOptionalRightChild().asCALL(); + + ExpressionEXPR e = rewriter.Visit(call) as ExpressionEXPR; + return e.Expression; + } + + ///////////////////////////////////////////////////////////////////////////////// + + protected override EXPR VisitSAVE(EXPRBINOP pExpr) + { + // Saves should have a LHS that is a CALL to PM_EXPRESSION_PARAMETER + // and a RHS that is a WRAP of that call. + Debug.Assert(pExpr.GetOptionalLeftChild() != null); + Debug.Assert(pExpr.GetOptionalLeftChild().isCALL()); + Debug.Assert(pExpr.GetOptionalLeftChild().asCALL().PredefinedMethod == PREDEFMETH.PM_EXPRESSION_PARAMETER); + Debug.Assert(pExpr.GetOptionalRightChild() != null); + Debug.Assert(pExpr.GetOptionalRightChild().isWRAP()); + + EXPRCALL call = pExpr.GetOptionalLeftChild().asCALL(); + EXPRTYPEOF TypeOf = call.GetOptionalArguments().asLIST().GetOptionalElement().asTYPEOF(); + Expression parameter = ListOfParameters.ElementAt(currentParameterIndex++); + DictionaryOfParameters.Add(call, parameter); + + return null; + } + + ///////////////////////////////////////////////////////////////////////////////// + + protected override EXPR VisitCAST(EXPRCAST pExpr) + { + return base.VisitCAST(pExpr); + } + + ///////////////////////////////////////////////////////////////////////////////// + + protected override EXPR VisitCALL(EXPRCALL pExpr) + { + if (pExpr.PredefinedMethod != PREDEFMETH.PM_FIRST) + { + switch (pExpr.PredefinedMethod) + { + case PREDEFMETH.PM_EXPRESSION_LAMBDA: + return GenerateLambda(pExpr); + + case PREDEFMETH.PM_EXPRESSION_CALL: + return GenerateCall(pExpr); + + case PREDEFMETH.PM_EXPRESSION_ARRAYINDEX: + case PREDEFMETH.PM_EXPRESSION_ARRAYINDEX2: + return GenerateArrayIndex(pExpr); + + case PREDEFMETH.PM_EXPRESSION_CONVERT: + case PREDEFMETH.PM_EXPRESSION_CONVERT_USER_DEFINED: + case PREDEFMETH.PM_EXPRESSION_CONVERTCHECKED: + case PREDEFMETH.PM_EXPRESSION_CONVERTCHECKED_USER_DEFINED: + return GenerateConvert(pExpr); + + case PREDEFMETH.PM_EXPRESSION_PROPERTY: + return GenerateProperty(pExpr); + + case PREDEFMETH.PM_EXPRESSION_FIELD: + return GenerateField(pExpr); + + case PREDEFMETH.PM_EXPRESSION_INVOKE: + return GenerateInvoke(pExpr); + + case PREDEFMETH.PM_EXPRESSION_NEW: + return GenerateNew(pExpr); + + case PREDEFMETH.PM_EXPRESSION_ADD: + case PREDEFMETH.PM_EXPRESSION_AND: + case PREDEFMETH.PM_EXPRESSION_DIVIDE: + case PREDEFMETH.PM_EXPRESSION_EQUAL: + case PREDEFMETH.PM_EXPRESSION_EXCLUSIVEOR: + case PREDEFMETH.PM_EXPRESSION_GREATERTHAN: + case PREDEFMETH.PM_EXPRESSION_GREATERTHANOREQUAL: + case PREDEFMETH.PM_EXPRESSION_LEFTSHIFT: + case PREDEFMETH.PM_EXPRESSION_LESSTHAN: + case PREDEFMETH.PM_EXPRESSION_LESSTHANOREQUAL: + case PREDEFMETH.PM_EXPRESSION_MODULO: + case PREDEFMETH.PM_EXPRESSION_MULTIPLY: + case PREDEFMETH.PM_EXPRESSION_NOTEQUAL: + case PREDEFMETH.PM_EXPRESSION_OR: + case PREDEFMETH.PM_EXPRESSION_RIGHTSHIFT: + case PREDEFMETH.PM_EXPRESSION_SUBTRACT: + case PREDEFMETH.PM_EXPRESSION_ORELSE: + case PREDEFMETH.PM_EXPRESSION_ANDALSO: + // Checked + case PREDEFMETH.PM_EXPRESSION_ADDCHECKED: + case PREDEFMETH.PM_EXPRESSION_MULTIPLYCHECKED: + case PREDEFMETH.PM_EXPRESSION_SUBTRACTCHECKED: + return GenerateBinaryOperator(pExpr); + + case PREDEFMETH.PM_EXPRESSION_ADD_USER_DEFINED: + case PREDEFMETH.PM_EXPRESSION_AND_USER_DEFINED: + case PREDEFMETH.PM_EXPRESSION_DIVIDE_USER_DEFINED: + case PREDEFMETH.PM_EXPRESSION_EQUAL_USER_DEFINED: + case PREDEFMETH.PM_EXPRESSION_EXCLUSIVEOR_USER_DEFINED: + case PREDEFMETH.PM_EXPRESSION_GREATERTHAN_USER_DEFINED: + case PREDEFMETH.PM_EXPRESSION_GREATERTHANOREQUAL_USER_DEFINED: + case PREDEFMETH.PM_EXPRESSION_LEFTSHIFT_USER_DEFINED: + case PREDEFMETH.PM_EXPRESSION_LESSTHAN_USER_DEFINED: + case PREDEFMETH.PM_EXPRESSION_LESSTHANOREQUAL_USER_DEFINED: + case PREDEFMETH.PM_EXPRESSION_MODULO_USER_DEFINED: + case PREDEFMETH.PM_EXPRESSION_MULTIPLY_USER_DEFINED: + case PREDEFMETH.PM_EXPRESSION_NOTEQUAL_USER_DEFINED: + case PREDEFMETH.PM_EXPRESSION_OR_USER_DEFINED: + case PREDEFMETH.PM_EXPRESSION_RIGHTSHIFT_USER_DEFINED: + case PREDEFMETH.PM_EXPRESSION_SUBTRACT_USER_DEFINED: + case PREDEFMETH.PM_EXPRESSION_ORELSE_USER_DEFINED: + case PREDEFMETH.PM_EXPRESSION_ANDALSO_USER_DEFINED: + // Checked + case PREDEFMETH.PM_EXPRESSION_ADDCHECKED_USER_DEFINED: + case PREDEFMETH.PM_EXPRESSION_MULTIPLYCHECKED_USER_DEFINED: + case PREDEFMETH.PM_EXPRESSION_SUBTRACTCHECKED_USER_DEFINED: + return GenerateUserDefinedBinaryOperator(pExpr); + + case PREDEFMETH.PM_EXPRESSION_NEGATE: + case PREDEFMETH.PM_EXPRESSION_NOT: + case PREDEFMETH.PM_EXPRESSION_NEGATECHECKED: + return GenerateUnaryOperator(pExpr); + + case PREDEFMETH.PM_EXPRESSION_UNARYPLUS_USER_DEFINED: + case PREDEFMETH.PM_EXPRESSION_NEGATE_USER_DEFINED: + case PREDEFMETH.PM_EXPRESSION_NOT_USER_DEFINED: + case PREDEFMETH.PM_EXPRESSION_NEGATECHECKED_USER_DEFINED: + return GenerateUserDefinedUnaryOperator(pExpr); + + case PREDEFMETH.PM_EXPRESSION_CONSTANT_OBJECT_TYPE: + return GenerateConstantType(pExpr); + + case PREDEFMETH.PM_EXPRESSION_ASSIGN: + return GenerateAssignment(pExpr); + + default: + Debug.Assert(false, "RuntimeBinderInternalCompilerException", "Invalid Predefined Method in VisitCALL"); + throw Error.InternalCompilerError(); + } + } + return pExpr; + } + + #region Generators + ///////////////////////////////////////////////////////////////////////////////// + + private ExpressionEXPR GenerateLambda(EXPRCALL pExpr) + { + // We always call Lambda(body, arrayinit) where the arrayinit + // is the initialization of the parameters. + // + // TODO: What do we do with the initializer? + ExpressionEXPR body = Visit(pExpr.GetOptionalArguments().asLIST().GetOptionalElement()) as ExpressionEXPR; + + Expression e = body.Expression; + + /* + * // TODO: Do we need to do this? + if (e.Type.IsValueType) + { + // If we have a value type, convert it to object so that boxing + // can happen. + + e = Expression.Convert(body.Expression, typeof(object)); + } + * */ + return new ExpressionEXPR(e); + } + + ///////////////////////////////////////////////////////////////////////////////// + + private ExpressionEXPR GenerateCall(EXPRCALL pExpr) + { + // Our arguments are: object, methodinfo, parameters. + // The object is either an EXPRWRAP of a CALL, or a CALL that is a PM_CONVERT, whose + // argument is the WRAP of a CALL. Deal with that first. + + EXPRMETHODINFO methinfo; + EXPRARRINIT arrinit; + + EXPRLIST list = pExpr.GetOptionalArguments().asLIST(); + if (list.GetOptionalNextListNode().isLIST()) + { + methinfo = list.GetOptionalNextListNode().asLIST().GetOptionalElement().asMETHODINFO(); + arrinit = list.GetOptionalNextListNode().asLIST().GetOptionalNextListNode().asARRINIT(); + } + else + { + methinfo = list.GetOptionalNextListNode().asMETHODINFO(); + arrinit = null; + } + + Expression obj = null; + MethodInfo m = GetMethodInfoFromExpr(methinfo); + Expression[] arguments = GetArgumentsFromArrayInit(arrinit); + + if (m == null) + { + Debug.Assert(false, "RuntimeBinderInternalCompilerException", "How did we get a call that doesn't have a methodinfo?"); + throw Error.InternalCompilerError(); + } + + // The DLR is expecting the instance for a static invocation to be null. If we have + // an instance method, fetch the object. + if (!m.IsStatic) + { + obj = GetExpression(pExpr.GetOptionalArguments().asLIST().GetOptionalElement()); + } + + return new ExpressionEXPR(Expression.Call(obj, m, arguments)); + } + + ///////////////////////////////////////////////////////////////////////////////// + + private ExpressionEXPR GenerateArrayIndex(EXPRCALL pExpr) + { + // We have two possibilities here - we're either a single index array, in which + // case we'll be PM_EXPRESSION_ARRAYINDEX, or we have multiple dimensions, + // in which case we are PM_EXPRESSION_ARRAYINDEX2. + // + // Our arguments then, are: object, index or object, indicies. + EXPRLIST list = pExpr.GetOptionalArguments().asLIST(); + Expression obj = GetExpression(list.GetOptionalElement()); + Expression[] indicies; + + if (pExpr.PredefinedMethod == PREDEFMETH.PM_EXPRESSION_ARRAYINDEX) + { + indicies = new Expression[] { GetExpression(list.GetOptionalNextListNode()) }; + } + else + { + Debug.Assert(pExpr.PredefinedMethod == PREDEFMETH.PM_EXPRESSION_ARRAYINDEX2); + indicies = GetArgumentsFromArrayInit(list.GetOptionalNextListNode().asARRINIT()); + } + return new ExpressionEXPR(Expression.ArrayAccess(obj, indicies)); + } + + ///////////////////////////////////////////////////////////////////////////////// + + private ExpressionEXPR GenerateConvert(EXPRCALL pExpr) + { + PREDEFMETH pm = pExpr.PredefinedMethod; + Expression e; + Type t; + + if (pm == PREDEFMETH.PM_EXPRESSION_CONVERT_USER_DEFINED || + pm == PREDEFMETH.PM_EXPRESSION_CONVERTCHECKED_USER_DEFINED) + { + // If we have a user defined conversion, then we'll have the object + // as the first element, and another list as a second element. This list + // contains a TYPEOF as the first element, and the METHODINFO for the call + // as the second. + + EXPRLIST list = pExpr.asCALL().GetOptionalArguments().asLIST(); + EXPRLIST list2 = list.GetOptionalNextListNode().asLIST(); + e = GetExpression(list.GetOptionalElement()); + t = list2.GetOptionalElement().asTYPEOF().SourceType.type.AssociatedSystemType; + + if (e.Type.MakeByRefType() == t) + { + // We're trying to convert from a type to its by ref type. Dont do that. + return new ExpressionEXPR(e); + } + Debug.Assert((pExpr.flags & EXPRFLAG.EXF_UNBOXRUNTIME) == 0); + + MethodInfo m = GetMethodInfoFromExpr(list2.GetOptionalNextListNode().asMETHODINFO()); + + if (pm == PREDEFMETH.PM_EXPRESSION_CONVERT_USER_DEFINED) + { + return new ExpressionEXPR(Expression.Convert(e, t, m)); + } + return new ExpressionEXPR(Expression.ConvertChecked(e, t, m)); + } + else + { + Debug.Assert(pm == PREDEFMETH.PM_EXPRESSION_CONVERT || + pm == PREDEFMETH.PM_EXPRESSION_CONVERTCHECKED); + + // If we have a standard conversion, then we'll have some object as + // the first list element (ie a WRAP or a CALL), and then a TYPEOF + // as the second list element. + EXPRLIST list = pExpr.asCALL().GetOptionalArguments().asLIST(); + + e = GetExpression(list.GetOptionalElement()); + t = list.GetOptionalNextListNode().asTYPEOF().SourceType.type.AssociatedSystemType; + + if (e.Type.MakeByRefType() == t) + { + // We're trying to convert from a type to its by ref type. Dont do that. + return new ExpressionEXPR(e); + } + + if ((pExpr.flags & EXPRFLAG.EXF_UNBOXRUNTIME) != 0) + { + // If we want to unbox this thing, return that instead of the convert. + return new ExpressionEXPR(Expression.Unbox(e, t)); + } + + if (pm == PREDEFMETH.PM_EXPRESSION_CONVERT) + { + return new ExpressionEXPR(Expression.Convert(e, t)); + } + return new ExpressionEXPR(Expression.ConvertChecked(e, t)); + } + } + + ///////////////////////////////////////////////////////////////////////////////// + + private ExpressionEXPR GenerateProperty(EXPRCALL pExpr) + { + EXPRLIST list = pExpr.asCALL().GetOptionalArguments().asLIST(); + + EXPR instance = list.GetOptionalElement(); + EXPRPropertyInfo propinfo = list.GetOptionalNextListNode().isLIST() ? + list.GetOptionalNextListNode().asLIST().GetOptionalElement().asPropertyInfo() : + list.GetOptionalNextListNode().asPropertyInfo(); + EXPRARRINIT arguments = list.GetOptionalNextListNode().isLIST() ? + list.GetOptionalNextListNode().asLIST().GetOptionalNextListNode().asARRINIT() : null; + + PropertyInfo p = GetPropertyInfoFromExpr(propinfo); + + if (p == null) + { + Debug.Assert(false, "RuntimeBinderInternalCompilerException", "How did we get a prop that doesn't have a propinfo?"); + throw Error.InternalCompilerError(); + } + + if (arguments == null) + { + return new ExpressionEXPR(Expression.Property(GetExpression(instance), p)); + } + return new ExpressionEXPR(Expression.Property(GetExpression(instance), p, GetArgumentsFromArrayInit(arguments))); + } + + ///////////////////////////////////////////////////////////////////////////////// + + private ExpressionEXPR GenerateField(EXPRCALL pExpr) + { + EXPRLIST list = pExpr.asCALL().GetOptionalArguments().asLIST(); + Type t = list.GetOptionalNextListNode().asFIELDINFO().FieldType().AssociatedSystemType; + FieldInfo f = list.GetOptionalNextListNode().asFIELDINFO().Field().AssociatedFieldInfo; + + // This is to ensure that for embedded nopia types, we have the + // appropriate local type from the member itself; this is possible + // because nopia types are not generic or nested. + if (!t.IsGenericType && !t.IsNested) + { + t = f.DeclaringType; + } + + // Now find the generic'ed one if we're generic. + if (t.IsGenericType) + { + f = t.GetField(f.Name, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static); + } + + return new ExpressionEXPR(Expression.Field(GetExpression(list.GetOptionalElement()), f)); + } + + ///////////////////////////////////////////////////////////////////////////////// + + private ExpressionEXPR GenerateInvoke(EXPRCALL pExpr) + { + EXPRLIST list = pExpr.asCALL().GetOptionalArguments().asLIST(); + + return new ExpressionEXPR(Expression.Invoke( + GetExpression(list.GetOptionalElement()), + GetArgumentsFromArrayInit(list.GetOptionalNextListNode().asARRINIT()))); + } + + ///////////////////////////////////////////////////////////////////////////////// + + private ExpressionEXPR GenerateNew(EXPRCALL pExpr) + { + EXPRLIST list = pExpr.asCALL().GetOptionalArguments().asLIST(); + + var constructor = GetConstructorInfoFromExpr(list.GetOptionalElement().asMETHODINFO()); + var arguments = GetArgumentsFromArrayInit(list.GetOptionalNextListNode().asARRINIT()); + return new ExpressionEXPR(Expression.New(constructor, arguments)); + } + + ///////////////////////////////////////////////////////////////////////////////// + + private ExpressionEXPR GenerateConstantType(EXPRCALL pExpr) + { + EXPRLIST list = pExpr.GetOptionalArguments().asLIST(); + +#if !SILVERLIGHT + return new ExpressionEXPR( + Expression.Constant( + GetObject(list.GetOptionalElement()), + list.GetOptionalNextListNode().asTYPEOF().SourceType.type.AssociatedSystemType)); +#else + // This is to fix Silverlight Bug #85557, in which we cannot call + // Activator.CreateInstance using a type that is not accessible. + // This will also work on the desktop and should be ported back + // post-Dev10, when the branch opens up. See definition of GetObject + // below as well. + + object v = GetObject(list.GetOptionalElement()); + Type t = list.GetOptionalNextListNode().asTYPEOF().SourceType.type.AssociatedSystemType; + + if (v == null) + { + return new ExpressionEXPR(Expression.Default(t)); + } + + return new ExpressionEXPR(Expression.Constant(v, t)); +#endif + } + + ///////////////////////////////////////////////////////////////////////////////// + + private ExpressionEXPR GenerateAssignment(EXPRCALL pExpr) + { + EXPRLIST list = pExpr.GetOptionalArguments().asLIST(); + + return new ExpressionEXPR(Expression.Assign( + GetExpression(list.GetOptionalElement()), + GetExpression(list.GetOptionalNextListNode()))); + } + + ///////////////////////////////////////////////////////////////////////////////// + + private ExpressionEXPR GenerateBinaryOperator(EXPRCALL pExpr) + { + Expression arg1 = GetExpression(pExpr.GetOptionalArguments().asLIST().GetOptionalElement()); + Expression arg2 = GetExpression(pExpr.GetOptionalArguments().asLIST().GetOptionalNextListNode()); + + switch (pExpr.PredefinedMethod) + { + case PREDEFMETH.PM_EXPRESSION_ADD: + return new ExpressionEXPR(Expression.Add(arg1, arg2)); + case PREDEFMETH.PM_EXPRESSION_AND: + return new ExpressionEXPR(Expression.And(arg1, arg2)); + case PREDEFMETH.PM_EXPRESSION_DIVIDE: + return new ExpressionEXPR(Expression.Divide(arg1, arg2)); + case PREDEFMETH.PM_EXPRESSION_EQUAL: + return new ExpressionEXPR(Expression.Equal(arg1, arg2)); + case PREDEFMETH.PM_EXPRESSION_EXCLUSIVEOR: + return new ExpressionEXPR(Expression.ExclusiveOr(arg1, arg2)); + case PREDEFMETH.PM_EXPRESSION_GREATERTHAN: + return new ExpressionEXPR(Expression.GreaterThan(arg1, arg2)); + case PREDEFMETH.PM_EXPRESSION_GREATERTHANOREQUAL: + return new ExpressionEXPR(Expression.GreaterThanOrEqual(arg1, arg2)); + case PREDEFMETH.PM_EXPRESSION_LEFTSHIFT: + return new ExpressionEXPR(Expression.LeftShift(arg1, arg2)); + case PREDEFMETH.PM_EXPRESSION_LESSTHAN: + return new ExpressionEXPR(Expression.LessThan(arg1, arg2)); + case PREDEFMETH.PM_EXPRESSION_LESSTHANOREQUAL: + return new ExpressionEXPR(Expression.LessThanOrEqual(arg1, arg2)); + case PREDEFMETH.PM_EXPRESSION_MODULO: + return new ExpressionEXPR(Expression.Modulo(arg1, arg2)); + case PREDEFMETH.PM_EXPRESSION_MULTIPLY: + return new ExpressionEXPR(Expression.Multiply(arg1, arg2)); + case PREDEFMETH.PM_EXPRESSION_NOTEQUAL: + return new ExpressionEXPR(Expression.NotEqual(arg1, arg2)); + case PREDEFMETH.PM_EXPRESSION_OR: + return new ExpressionEXPR(Expression.Or(arg1, arg2)); + case PREDEFMETH.PM_EXPRESSION_RIGHTSHIFT: + return new ExpressionEXPR(Expression.RightShift(arg1, arg2)); + case PREDEFMETH.PM_EXPRESSION_SUBTRACT: + return new ExpressionEXPR(Expression.Subtract(arg1, arg2)); + case PREDEFMETH.PM_EXPRESSION_ORELSE: + return new ExpressionEXPR(Expression.OrElse(arg1, arg2)); + case PREDEFMETH.PM_EXPRESSION_ANDALSO: + return new ExpressionEXPR(Expression.AndAlso(arg1, arg2)); + + // Checked + case PREDEFMETH.PM_EXPRESSION_ADDCHECKED: + return new ExpressionEXPR(Expression.AddChecked(arg1, arg2)); + case PREDEFMETH.PM_EXPRESSION_MULTIPLYCHECKED: + return new ExpressionEXPR(Expression.MultiplyChecked(arg1, arg2)); + case PREDEFMETH.PM_EXPRESSION_SUBTRACTCHECKED: + return new ExpressionEXPR(Expression.SubtractChecked(arg1, arg2)); + + default: + Debug.Assert(false, "RuntimeBinderInternalCompilerException", "Invalid Predefined Method in GenerateBinaryOperator"); + throw Error.InternalCompilerError(); + } + } + + ///////////////////////////////////////////////////////////////////////////////// + + private ExpressionEXPR GenerateUserDefinedBinaryOperator(EXPRCALL pExpr) + { + EXPRLIST list = pExpr.GetOptionalArguments().asLIST(); + Expression arg1 = GetExpression(list.GetOptionalElement()); + Expression arg2 = GetExpression(list.GetOptionalNextListNode().asLIST().GetOptionalElement()); + + list = list.GetOptionalNextListNode().asLIST(); + MethodInfo methodInfo; + bool bIsLifted = false; + if (list.GetOptionalNextListNode().isLIST()) + { + EXPRCONSTANT isLifted = list.GetOptionalNextListNode().asLIST().GetOptionalElement().asCONSTANT(); + bIsLifted = isLifted.getVal().iVal == 1; + methodInfo = GetMethodInfoFromExpr(list.GetOptionalNextListNode().asLIST().GetOptionalNextListNode().asMETHODINFO()); + } + else + { + methodInfo = GetMethodInfoFromExpr(list.GetOptionalNextListNode().asMETHODINFO()); + } + + switch (pExpr.PredefinedMethod) + { + case PREDEFMETH.PM_EXPRESSION_ADD_USER_DEFINED: + return new ExpressionEXPR(Expression.Add(arg1, arg2, methodInfo)); + case PREDEFMETH.PM_EXPRESSION_AND_USER_DEFINED: + return new ExpressionEXPR(Expression.And(arg1, arg2, methodInfo)); + case PREDEFMETH.PM_EXPRESSION_DIVIDE_USER_DEFINED: + return new ExpressionEXPR(Expression.Divide(arg1, arg2, methodInfo)); + case PREDEFMETH.PM_EXPRESSION_EQUAL_USER_DEFINED: + return new ExpressionEXPR(Expression.Equal(arg1, arg2, bIsLifted, methodInfo)); + case PREDEFMETH.PM_EXPRESSION_EXCLUSIVEOR_USER_DEFINED: + return new ExpressionEXPR(Expression.ExclusiveOr(arg1, arg2, methodInfo)); + case PREDEFMETH.PM_EXPRESSION_GREATERTHAN_USER_DEFINED: + return new ExpressionEXPR(Expression.GreaterThan(arg1, arg2, bIsLifted, methodInfo)); + case PREDEFMETH.PM_EXPRESSION_GREATERTHANOREQUAL_USER_DEFINED: + return new ExpressionEXPR(Expression.GreaterThanOrEqual(arg1, arg2, bIsLifted, methodInfo)); + case PREDEFMETH.PM_EXPRESSION_LEFTSHIFT_USER_DEFINED: + return new ExpressionEXPR(Expression.LeftShift(arg1, arg2, methodInfo)); + case PREDEFMETH.PM_EXPRESSION_LESSTHAN_USER_DEFINED: + return new ExpressionEXPR(Expression.LessThan(arg1, arg2, bIsLifted, methodInfo)); + case PREDEFMETH.PM_EXPRESSION_LESSTHANOREQUAL_USER_DEFINED: + return new ExpressionEXPR(Expression.LessThanOrEqual(arg1, arg2, bIsLifted, methodInfo)); + case PREDEFMETH.PM_EXPRESSION_MODULO_USER_DEFINED: + return new ExpressionEXPR(Expression.Modulo(arg1, arg2, methodInfo)); + case PREDEFMETH.PM_EXPRESSION_MULTIPLY_USER_DEFINED: + return new ExpressionEXPR(Expression.Multiply(arg1, arg2, methodInfo)); + case PREDEFMETH.PM_EXPRESSION_NOTEQUAL_USER_DEFINED: + return new ExpressionEXPR(Expression.NotEqual(arg1, arg2, bIsLifted, methodInfo)); + case PREDEFMETH.PM_EXPRESSION_OR_USER_DEFINED: + return new ExpressionEXPR(Expression.Or(arg1, arg2, methodInfo)); + case PREDEFMETH.PM_EXPRESSION_RIGHTSHIFT_USER_DEFINED: + return new ExpressionEXPR(Expression.RightShift(arg1, arg2, methodInfo)); + case PREDEFMETH.PM_EXPRESSION_SUBTRACT_USER_DEFINED: + return new ExpressionEXPR(Expression.Subtract(arg1, arg2, methodInfo)); + case PREDEFMETH.PM_EXPRESSION_ORELSE_USER_DEFINED: + return new ExpressionEXPR(Expression.OrElse(arg1, arg2, methodInfo)); + case PREDEFMETH.PM_EXPRESSION_ANDALSO_USER_DEFINED: + return new ExpressionEXPR(Expression.AndAlso(arg1, arg2, methodInfo)); + + // Checked + case PREDEFMETH.PM_EXPRESSION_ADDCHECKED_USER_DEFINED: + return new ExpressionEXPR(Expression.AddChecked(arg1, arg2, methodInfo)); + case PREDEFMETH.PM_EXPRESSION_MULTIPLYCHECKED_USER_DEFINED: + return new ExpressionEXPR(Expression.MultiplyChecked(arg1, arg2, methodInfo)); + case PREDEFMETH.PM_EXPRESSION_SUBTRACTCHECKED_USER_DEFINED: + return new ExpressionEXPR(Expression.SubtractChecked(arg1, arg2, methodInfo)); + + default: + Debug.Assert(false, "RuntimeBinderInternalCompilerException", "Invalid Predefined Method in GenerateUserDefinedBinaryOperator"); + throw Error.InternalCompilerError(); + } + } + + ///////////////////////////////////////////////////////////////////////////////// + + private ExpressionEXPR GenerateUnaryOperator(EXPRCALL pExpr) + { + PREDEFMETH pm = pExpr.PredefinedMethod; + Expression arg = GetExpression(pExpr.GetOptionalArguments()); + + switch (pm) + { + case PREDEFMETH.PM_EXPRESSION_NOT: + return new ExpressionEXPR(Expression.Not(arg)); + + case PREDEFMETH.PM_EXPRESSION_NEGATE: + return new ExpressionEXPR(Expression.Negate(arg)); + + case PREDEFMETH.PM_EXPRESSION_NEGATECHECKED: + return new ExpressionEXPR(Expression.NegateChecked(arg)); + + default: + Debug.Assert(false, "RuntimeBinderInternalCompilerException", "Invalid Predefined Method in GenerateUnaryOperator"); + throw Error.InternalCompilerError(); + } + } + + ///////////////////////////////////////////////////////////////////////////////// + + private ExpressionEXPR GenerateUserDefinedUnaryOperator(EXPRCALL pExpr) + { + PREDEFMETH pm = pExpr.PredefinedMethod; + EXPRLIST list = pExpr.GetOptionalArguments().asLIST(); + Expression arg = GetExpression(list.GetOptionalElement()); + MethodInfo methodInfo = GetMethodInfoFromExpr(list.GetOptionalNextListNode().asMETHODINFO()); + + switch (pm) + { + case PREDEFMETH.PM_EXPRESSION_NOT_USER_DEFINED: + return new ExpressionEXPR(Expression.Not(arg, methodInfo)); + + case PREDEFMETH.PM_EXPRESSION_NEGATE_USER_DEFINED: + return new ExpressionEXPR(Expression.Negate(arg, methodInfo)); + + case PREDEFMETH.PM_EXPRESSION_UNARYPLUS_USER_DEFINED: + return new ExpressionEXPR(Expression.UnaryPlus(arg, methodInfo)); + + case PREDEFMETH.PM_EXPRESSION_NEGATECHECKED_USER_DEFINED: + return new ExpressionEXPR(Expression.NegateChecked(arg, methodInfo)); + + default: + Debug.Assert(false, "RuntimeBinderInternalCompilerException", "Invalid Predefined Method in GenerateUserDefinedUnaryOperator"); + throw Error.InternalCompilerError(); + } + } + #endregion + + #region Helpers + ///////////////////////////////////////////////////////////////////////////////// + + private Expression GetExpression(EXPR pExpr) + { + if (pExpr.isWRAP()) + { + return DictionaryOfParameters[pExpr.asWRAP().GetOptionalExpression().asCALL()]; + } + else if (pExpr.isCONSTANT()) + { + Debug.Assert(pExpr.type.IsNullType()); + return null; + } + else + { + // We can have a convert node or a call of a user defined conversion. + Debug.Assert(pExpr.isCALL()); + EXPRCALL call = pExpr.asCALL(); + PREDEFMETH pm = call.PredefinedMethod; + Debug.Assert(pm == PREDEFMETH.PM_EXPRESSION_CONVERT || + pm == PREDEFMETH.PM_EXPRESSION_CONVERT_USER_DEFINED || + pm == PREDEFMETH.PM_EXPRESSION_NEWARRAYINIT || + pm == PREDEFMETH.PM_EXPRESSION_CALL || + pm == PREDEFMETH.PM_EXPRESSION_PROPERTY || + pm == PREDEFMETH.PM_EXPRESSION_FIELD || + pm == PREDEFMETH.PM_EXPRESSION_ARRAYINDEX || + pm == PREDEFMETH.PM_EXPRESSION_ARRAYINDEX2 || + pm == PREDEFMETH.PM_EXPRESSION_CONSTANT_OBJECT_TYPE || + pm == PREDEFMETH.PM_EXPRESSION_NEW || + + // Binary operators. + pm == PREDEFMETH.PM_EXPRESSION_ASSIGN || + pm == PREDEFMETH.PM_EXPRESSION_ADD || + pm == PREDEFMETH.PM_EXPRESSION_AND || + pm == PREDEFMETH.PM_EXPRESSION_DIVIDE || + pm == PREDEFMETH.PM_EXPRESSION_EQUAL || + pm == PREDEFMETH.PM_EXPRESSION_EXCLUSIVEOR || + pm == PREDEFMETH.PM_EXPRESSION_GREATERTHAN || + pm == PREDEFMETH.PM_EXPRESSION_GREATERTHANOREQUAL || + pm == PREDEFMETH.PM_EXPRESSION_LEFTSHIFT || + pm == PREDEFMETH.PM_EXPRESSION_LESSTHAN || + pm == PREDEFMETH.PM_EXPRESSION_LESSTHANOREQUAL || + pm == PREDEFMETH.PM_EXPRESSION_MODULO || + pm == PREDEFMETH.PM_EXPRESSION_MULTIPLY || + pm == PREDEFMETH.PM_EXPRESSION_NOTEQUAL || + pm == PREDEFMETH.PM_EXPRESSION_OR || + pm == PREDEFMETH.PM_EXPRESSION_RIGHTSHIFT || + pm == PREDEFMETH.PM_EXPRESSION_SUBTRACT || + pm == PREDEFMETH.PM_EXPRESSION_ORELSE || + pm == PREDEFMETH.PM_EXPRESSION_ANDALSO || + pm == PREDEFMETH.PM_EXPRESSION_ADD_USER_DEFINED || + pm == PREDEFMETH.PM_EXPRESSION_AND_USER_DEFINED || + pm == PREDEFMETH.PM_EXPRESSION_DIVIDE_USER_DEFINED || + pm == PREDEFMETH.PM_EXPRESSION_EQUAL_USER_DEFINED || + pm == PREDEFMETH.PM_EXPRESSION_EXCLUSIVEOR_USER_DEFINED || + pm == PREDEFMETH.PM_EXPRESSION_GREATERTHAN_USER_DEFINED || + pm == PREDEFMETH.PM_EXPRESSION_GREATERTHANOREQUAL_USER_DEFINED || + pm == PREDEFMETH.PM_EXPRESSION_LEFTSHIFT_USER_DEFINED || + pm == PREDEFMETH.PM_EXPRESSION_LESSTHAN_USER_DEFINED || + pm == PREDEFMETH.PM_EXPRESSION_LESSTHANOREQUAL_USER_DEFINED || + pm == PREDEFMETH.PM_EXPRESSION_MODULO_USER_DEFINED || + pm == PREDEFMETH.PM_EXPRESSION_MULTIPLY_USER_DEFINED || + pm == PREDEFMETH.PM_EXPRESSION_NOTEQUAL_USER_DEFINED || + pm == PREDEFMETH.PM_EXPRESSION_OR_USER_DEFINED || + pm == PREDEFMETH.PM_EXPRESSION_RIGHTSHIFT_USER_DEFINED || + pm == PREDEFMETH.PM_EXPRESSION_SUBTRACT_USER_DEFINED || + pm == PREDEFMETH.PM_EXPRESSION_ORELSE_USER_DEFINED || + pm == PREDEFMETH.PM_EXPRESSION_ANDALSO_USER_DEFINED || + + // Checked binary + pm == PREDEFMETH.PM_EXPRESSION_ADDCHECKED || + pm == PREDEFMETH.PM_EXPRESSION_MULTIPLYCHECKED || + pm == PREDEFMETH.PM_EXPRESSION_SUBTRACTCHECKED || + pm == PREDEFMETH.PM_EXPRESSION_ADDCHECKED_USER_DEFINED || + pm == PREDEFMETH.PM_EXPRESSION_MULTIPLYCHECKED_USER_DEFINED || + pm == PREDEFMETH.PM_EXPRESSION_SUBTRACTCHECKED_USER_DEFINED || + + // Unary operators. + pm == PREDEFMETH.PM_EXPRESSION_NOT || + pm == PREDEFMETH.PM_EXPRESSION_NEGATE || + pm == PREDEFMETH.PM_EXPRESSION_NOT_USER_DEFINED || + pm == PREDEFMETH.PM_EXPRESSION_NEGATE_USER_DEFINED || + pm == PREDEFMETH.PM_EXPRESSION_UNARYPLUS_USER_DEFINED || + + // Checked unary + pm == PREDEFMETH.PM_EXPRESSION_NEGATECHECKED || + pm == PREDEFMETH.PM_EXPRESSION_CONVERTCHECKED || + pm == PREDEFMETH.PM_EXPRESSION_NEGATECHECKED_USER_DEFINED || + pm == PREDEFMETH.PM_EXPRESSION_CONVERTCHECKED_USER_DEFINED + ); + + switch (pm) + { + case PREDEFMETH.PM_EXPRESSION_CALL: + return GenerateCall(call).Expression; + + case PREDEFMETH.PM_EXPRESSION_CONVERT: + case PREDEFMETH.PM_EXPRESSION_CONVERT_USER_DEFINED: + case PREDEFMETH.PM_EXPRESSION_CONVERTCHECKED: + case PREDEFMETH.PM_EXPRESSION_CONVERTCHECKED_USER_DEFINED: + return GenerateConvert(call).Expression; + + case PREDEFMETH.PM_EXPRESSION_NEWARRAYINIT: + { + EXPRLIST list = call.GetOptionalArguments().asLIST(); + return Expression.NewArrayInit( + list.GetOptionalElement().asTYPEOF().SourceType.type.AssociatedSystemType, + GetArgumentsFromArrayInit(list.GetOptionalNextListNode().asARRINIT())); + } + + case PREDEFMETH.PM_EXPRESSION_ARRAYINDEX: + case PREDEFMETH.PM_EXPRESSION_ARRAYINDEX2: + return GenerateArrayIndex(call).Expression; + + case PREDEFMETH.PM_EXPRESSION_NEW: + return GenerateNew(call).Expression; + + case PREDEFMETH.PM_EXPRESSION_PROPERTY: + return GenerateProperty(call).Expression; + + case PREDEFMETH.PM_EXPRESSION_FIELD: + return GenerateField(call).Expression; + + case PREDEFMETH.PM_EXPRESSION_CONSTANT_OBJECT_TYPE: + return GenerateConstantType(call).Expression; + + case PREDEFMETH.PM_EXPRESSION_ASSIGN: + return GenerateAssignment(call).Expression; + + case PREDEFMETH.PM_EXPRESSION_ADD: + case PREDEFMETH.PM_EXPRESSION_AND: + case PREDEFMETH.PM_EXPRESSION_DIVIDE: + case PREDEFMETH.PM_EXPRESSION_EQUAL: + case PREDEFMETH.PM_EXPRESSION_EXCLUSIVEOR: + case PREDEFMETH.PM_EXPRESSION_GREATERTHAN: + case PREDEFMETH.PM_EXPRESSION_GREATERTHANOREQUAL: + case PREDEFMETH.PM_EXPRESSION_LEFTSHIFT: + case PREDEFMETH.PM_EXPRESSION_LESSTHAN: + case PREDEFMETH.PM_EXPRESSION_LESSTHANOREQUAL: + case PREDEFMETH.PM_EXPRESSION_MODULO: + case PREDEFMETH.PM_EXPRESSION_MULTIPLY: + case PREDEFMETH.PM_EXPRESSION_NOTEQUAL: + case PREDEFMETH.PM_EXPRESSION_OR: + case PREDEFMETH.PM_EXPRESSION_RIGHTSHIFT: + case PREDEFMETH.PM_EXPRESSION_SUBTRACT: + case PREDEFMETH.PM_EXPRESSION_ORELSE: + case PREDEFMETH.PM_EXPRESSION_ANDALSO: + // Checked + case PREDEFMETH.PM_EXPRESSION_ADDCHECKED: + case PREDEFMETH.PM_EXPRESSION_MULTIPLYCHECKED: + case PREDEFMETH.PM_EXPRESSION_SUBTRACTCHECKED: + return GenerateBinaryOperator(call).Expression; + + case PREDEFMETH.PM_EXPRESSION_ADD_USER_DEFINED: + case PREDEFMETH.PM_EXPRESSION_AND_USER_DEFINED: + case PREDEFMETH.PM_EXPRESSION_DIVIDE_USER_DEFINED: + case PREDEFMETH.PM_EXPRESSION_EQUAL_USER_DEFINED: + case PREDEFMETH.PM_EXPRESSION_EXCLUSIVEOR_USER_DEFINED: + case PREDEFMETH.PM_EXPRESSION_GREATERTHAN_USER_DEFINED: + case PREDEFMETH.PM_EXPRESSION_GREATERTHANOREQUAL_USER_DEFINED: + case PREDEFMETH.PM_EXPRESSION_LEFTSHIFT_USER_DEFINED: + case PREDEFMETH.PM_EXPRESSION_LESSTHAN_USER_DEFINED: + case PREDEFMETH.PM_EXPRESSION_LESSTHANOREQUAL_USER_DEFINED: + case PREDEFMETH.PM_EXPRESSION_MODULO_USER_DEFINED: + case PREDEFMETH.PM_EXPRESSION_MULTIPLY_USER_DEFINED: + case PREDEFMETH.PM_EXPRESSION_NOTEQUAL_USER_DEFINED: + case PREDEFMETH.PM_EXPRESSION_OR_USER_DEFINED: + case PREDEFMETH.PM_EXPRESSION_RIGHTSHIFT_USER_DEFINED: + case PREDEFMETH.PM_EXPRESSION_SUBTRACT_USER_DEFINED: + case PREDEFMETH.PM_EXPRESSION_ORELSE_USER_DEFINED: + case PREDEFMETH.PM_EXPRESSION_ANDALSO_USER_DEFINED: + // Checked + case PREDEFMETH.PM_EXPRESSION_ADDCHECKED_USER_DEFINED: + case PREDEFMETH.PM_EXPRESSION_MULTIPLYCHECKED_USER_DEFINED: + case PREDEFMETH.PM_EXPRESSION_SUBTRACTCHECKED_USER_DEFINED: + return GenerateUserDefinedBinaryOperator(call).Expression; + + case PREDEFMETH.PM_EXPRESSION_NOT: + case PREDEFMETH.PM_EXPRESSION_NEGATE: + case PREDEFMETH.PM_EXPRESSION_NEGATECHECKED: + return GenerateUnaryOperator(call).Expression; + + case PREDEFMETH.PM_EXPRESSION_NOT_USER_DEFINED: + case PREDEFMETH.PM_EXPRESSION_NEGATE_USER_DEFINED: + case PREDEFMETH.PM_EXPRESSION_UNARYPLUS_USER_DEFINED: + case PREDEFMETH.PM_EXPRESSION_NEGATECHECKED_USER_DEFINED: + return GenerateUserDefinedUnaryOperator(call).Expression; + + default: + Debug.Assert(false, "RuntimeBinderInternalCompilerException", "Invalid Predefined Method in GetExpression"); + throw Error.InternalCompilerError(); + } + } + } + + ///////////////////////////////////////////////////////////////////////////////// + + private object GetObject(EXPR pExpr) + { + if (pExpr.isCAST()) + { + return GetObject(pExpr.asCAST().GetArgument()); + } + else if (pExpr.isTYPEOF()) + { + return pExpr.asTYPEOF().SourceType.type.AssociatedSystemType; + } + else if (pExpr.isMETHODINFO()) + { + return GetMethodInfoFromExpr(pExpr.asMETHODINFO()); + } + else if (pExpr.isCONSTANT()) + { + CONSTVAL val = pExpr.asCONSTANT().Val; + CType underlyingType = pExpr.type; + object objval; + + if (pExpr.type.IsNullType()) + { + return null; + } + + if (pExpr.type.isEnumType()) + { + underlyingType = underlyingType.getAggregate().GetUnderlyingType(); + } + + switch (Type.GetTypeCode(underlyingType.AssociatedSystemType)) + { + case TypeCode.Boolean: + objval = val.boolVal; + break; + case TypeCode.SByte: + objval = val.sbyteVal; + break; + case TypeCode.Byte: + objval = val.byteVal; + break; + case TypeCode.Int16: + objval = val.shortVal; + break; + case TypeCode.UInt16: + objval = val.ushortVal; + break; + case TypeCode.Int32: + objval = val.iVal; + break; + case TypeCode.UInt32: + objval = val.uiVal; + break; + case TypeCode.Int64: + objval = val.longVal; + break; + case TypeCode.UInt64: + objval = val.ulongVal; + break; + case TypeCode.Single: + objval = val.floatVal; + break; + case TypeCode.Double: + objval = val.doubleVal; + break; + case TypeCode.Decimal: + objval = val.decVal; + break; + case TypeCode.Char: + objval = val.cVal; + break; + case TypeCode.String: + objval = val.strVal; + break; + default: + objval = val.objectVal; + break; + } + + if (pExpr.type.isEnumType()) + { + objval = Enum.ToObject(pExpr.type.AssociatedSystemType, objval); + } + + return objval; + } + else if (pExpr.isZEROINIT()) + { + if (pExpr.asZEROINIT().OptionalArgument != null) + { + return GetObject(pExpr.asZEROINIT().OptionalArgument); + } +#if !SILVERLIGHT + return System.Activator.CreateInstance(pExpr.type.AssociatedSystemType); +#else + // This is to fix Silverlight Bug #85557, in which we cannot call + // Activator.CreateInstance using a type that is not accessible. + // This will also work on the desktop and should be ported back + // post-Dev10, when the branch opens up. See call to GetObject above + // as well. + return null; +#endif + } + + Debug.Assert(false, "RuntimeBinderInternalCompilerException", "Invalid EXPR in GetObject"); + throw Error.InternalCompilerError(); + } + + ///////////////////////////////////////////////////////////////////////////////// + + private Expression[] GetArgumentsFromArrayInit(EXPRARRINIT arrinit) + { + List expressions = new List(); + + if (arrinit != null) + { + EXPR list = arrinit.GetOptionalArguments(); + EXPR p = list; + while (list != null) + { + if (list.isLIST()) + { + p = list.asLIST().GetOptionalElement(); + list = list.asLIST().GetOptionalNextListNode(); + } + else + { + p = list; + list = null; + } + expressions.Add(GetExpression(p)); + } + + Debug.Assert(expressions.Count == arrinit.dimSizes[0]); + } + return expressions.ToArray(); + } + + ///////////////////////////////////////////////////////////////////////////////// + + private MethodInfo GetMethodInfoFromExpr(EXPRMETHODINFO methinfo) + { + // To do this, we need to construct a type array of the parameter types, + // get the parent constructed type, and get the method from it. + + AggregateType aggType = methinfo.Method.Ats; + MethodSymbol methSym = methinfo.Method.Meth(); + + TypeArray genericParams = m_typeManager.SubstTypeArray(methSym.Params, aggType, methSym.typeVars); + CType genericReturn = m_typeManager.SubstType(methSym.RetType, aggType, methSym.typeVars); + + Type type = aggType.AssociatedSystemType; + MethodInfo methodInfo = methSym.AssociatedMemberInfo as MethodInfo; + + // This is to ensure that for embedded nopia types, we have the + // appropriate local type from the member itself; this is possible + // because nopia types are not generic or nested. + if (!type.IsGenericType && !type.IsNested) + { + type = methodInfo.DeclaringType; + } + + // We need to find the associated methodinfo on the instantiated type. + foreach (MethodInfo m in type.GetMethods(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static)) + { + if ((m.MetadataToken != methodInfo.MetadataToken) || (m.Module != methodInfo.Module)) + { + continue; + } + Debug.Assert((m.Name == methodInfo.Name) && + (m.GetParameters().Length == genericParams.size) && + (TypesAreEqual(m.ReturnType, genericReturn.AssociatedSystemType))); + + bool bMatch = true; + ParameterInfo[] parameters = m.GetParameters(); + for (int i = 0; i < genericParams.size; i++) + { + if (!TypesAreEqual(parameters[i].ParameterType, genericParams.Item(i).AssociatedSystemType)) + { + bMatch = false; + break; + } + } + if (bMatch) + { + if (m.IsGenericMethod) + { + int size = methinfo.Method.TypeArgs != null ? methinfo.Method.TypeArgs.size : 0; + Type[] typeArgs = new Type[size]; + if (size > 0) + { + for (int i = 0; i < methinfo.Method.TypeArgs.size; i++) + { + typeArgs[i] = methinfo.Method.TypeArgs[i].AssociatedSystemType; + } + } + return m.MakeGenericMethod(typeArgs); + } + + return m; + } + } + + Debug.Assert(false, "RuntimeBinderInternalCompilerException", "Could not find matching method"); + throw Error.InternalCompilerError(); + } + + ///////////////////////////////////////////////////////////////////////////////// + + private ConstructorInfo GetConstructorInfoFromExpr(EXPRMETHODINFO methinfo) + { + // To do this, we need to construct a type array of the parameter types, + // get the parent constructed type, and get the method from it. + + AggregateType aggType = methinfo.Method.Ats; + MethodSymbol methSym = methinfo.Method.Meth(); + + TypeArray genericInstanceParams = m_typeManager.SubstTypeArray(methSym.Params, aggType); + Type type = aggType.AssociatedSystemType; + ConstructorInfo ctorInfo = (ConstructorInfo)methSym.AssociatedMemberInfo; + + // This is to ensure that for embedded nopia types, we have the + // appropriate local type from the member itself; this is possible + // because nopia types are not generic or nested. + if (!type.IsGenericType && !type.IsNested) + { + type = ctorInfo.DeclaringType; + } + + foreach (ConstructorInfo c in type.GetConstructors(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static)) + { + if ((c.MetadataToken != ctorInfo.MetadataToken) || (c.Module != ctorInfo.Module)) + { + continue; + } + Debug.Assert(c.GetParameters() == null || c.GetParameters().Length == genericInstanceParams.size); + + bool bMatch = true; + ParameterInfo[] parameters = c.GetParameters(); + for (int i = 0; i < genericInstanceParams.size; i++) + { + if (!TypesAreEqual(parameters[i].ParameterType, genericInstanceParams.Item(i).AssociatedSystemType)) + { + bMatch = false; + break; + } + } + if (bMatch) + { + return c; + } + + } + + Debug.Assert(false, "RuntimeBinderInternalCompilerException", "Could not find matching constructor"); + throw Error.InternalCompilerError(); + } + + ///////////////////////////////////////////////////////////////////////////////// + + private PropertyInfo GetPropertyInfoFromExpr(EXPRPropertyInfo propinfo) + { + // To do this, we need to construct a type array of the parameter types, + // get the parent constructed type, and get the property from it. + + AggregateType aggType = propinfo.Property.Ats; + PropertySymbol propSym = propinfo.Property.Prop(); + + TypeArray genericInstanceParams = m_typeManager.SubstTypeArray(propSym.Params, aggType, null); + CType genericInstanceReturn = m_typeManager.SubstType(propSym.RetType, aggType, null); + + Type type = aggType.AssociatedSystemType; + PropertyInfo propertyInfo = propSym.AssociatedPropertyInfo; + + // This is to ensure that for embedded nopia types, we have the + // appropriate local type from the member itself; this is possible + // because nopia types are not generic or nested. + if (!type.IsGenericType && !type.IsNested) + { + type = propertyInfo.DeclaringType; + } + + foreach (PropertyInfo p in type.GetProperties(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static)) + { + if ((p.MetadataToken != propertyInfo.MetadataToken) || (p.Module != propertyInfo.Module)) + { + continue; + } + Debug.Assert((p.Name == propertyInfo.Name) && + (p.GetIndexParameters() == null || p.GetIndexParameters().Length == genericInstanceParams.size)); + + bool bMatch = true; + ParameterInfo[] parameters = p.GetSetMethod(true) != null ? + p.GetSetMethod(true).GetParameters() : p.GetGetMethod(true).GetParameters(); + for (int i = 0; i < genericInstanceParams.size; i++) + { + if (!TypesAreEqual(parameters[i].ParameterType, genericInstanceParams.Item(i).AssociatedSystemType)) + { + bMatch = false; + break; + } + } + if (bMatch) + { + return p; + } + } + + Debug.Assert(false, "RuntimeBinderInternalCompilerException", "Could not find matching property"); + throw Error.InternalCompilerError(); + } + + ///////////////////////////////////////////////////////////////////////////////// + + private bool TypesAreEqual(Type t1, Type t2) + { + if (t1 == t2) + { + return true; + } +#if SILVERLIGHT + return false; +#else + return t1.IsEquivalentTo(t2); +#endif + } + #endregion + } +} diff --git a/Microsoft.CSharp/Microsoft/CSharp/ICSharpInvokeOrInvokeMemberBinder.cs b/Microsoft.CSharp/Microsoft/CSharp/ICSharpInvokeOrInvokeMemberBinder.cs new file mode 100644 index 000000000..1c6072d77 --- /dev/null +++ b/Microsoft.CSharp/Microsoft/CSharp/ICSharpInvokeOrInvokeMemberBinder.cs @@ -0,0 +1,25 @@ +// ==++== +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// ==--== + +using System; +using System.Collections.Generic; + +namespace Microsoft.CSharp.RuntimeBinder +{ + internal interface ICSharpInvokeOrInvokeMemberBinder + { + // Helper methods. + bool StaticCall { get; } + bool ResultDiscarded { get; } + + // Members. + Type CallingContext { get; } + CSharpCallFlags Flags { get; } + string Name { get; } + IList TypeArguments { get; } + IList ArgumentInfo { get; } + } +} \ No newline at end of file diff --git a/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Errors/CError.cs b/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Errors/CError.cs new file mode 100644 index 000000000..56b516c78 --- /dev/null +++ b/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Errors/CError.cs @@ -0,0 +1,34 @@ +// ==++== +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// ==--== + +using System; +using System.Globalization; + +namespace Microsoft.CSharp.RuntimeBinder.Errors +{ + //////////////////////////////////////////////////////////////////////////////// + // CError + // + // This object is the implementation of ICSError for all compiler errors, + // including lexer, parser, and compiler errors. + + internal class CError + { + private string m_text; + + private static string ComputeString(ErrorCode code, string[] args) + { + return String.Format(CultureInfo.InvariantCulture, ErrorFacts.GetMessage(code), args); + } + + public void Initialize(ErrorCode code, string[] args) + { + m_text = ComputeString(code, args); + } + + public string Text { get { return m_text; } } + } +} diff --git a/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Errors/CParameterizedError.cs b/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Errors/CParameterizedError.cs new file mode 100644 index 000000000..f8907b70b --- /dev/null +++ b/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Errors/CParameterizedError.cs @@ -0,0 +1,41 @@ +// ==++== +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// ==--== + +namespace Microsoft.CSharp.RuntimeBinder.Errors +{ + //////////////////////////////////////////////////////////////////////////////// + // CParameterizedError + // + // This object is the unrealized error that is generated prior to + // becoming a CError. It only has the spans, error number, and parameters. + + internal class CParameterizedError + { + private ErrorCode m_errorNumber; + private ErrArg[] m_arguments; + + public void Initialize(ErrorCode errorNumber, ErrArg[] arguments) + { + m_errorNumber = errorNumber; + m_arguments = (ErrArg[])arguments.Clone(); + } + + public int GetParameterCount() + { + return m_arguments.Length; + } + + public ErrArg GetParameter(int index) + { + return m_arguments[index]; + } + + public ErrorCode GetErrorNumber() + { + return m_errorNumber; + } + } +} diff --git a/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Errors/ErrorCode.cs b/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Errors/ErrorCode.cs new file mode 100644 index 000000000..24422606b --- /dev/null +++ b/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Errors/ErrorCode.cs @@ -0,0 +1,124 @@ +// ==++== +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// ==--== + +namespace Microsoft.CSharp.RuntimeBinder.Errors +{ + enum ErrorCode + { + ERR_BadBinaryOps = 19, + ERR_IntDivByZero = 20, + ERR_BadIndexLHS = 21, + ERR_BadIndexCount = 22, + ERR_BadUnaryOp = 23, + ERR_NoImplicitConv = 29, + ERR_NoExplicitConv = 30, + ERR_ConstOutOfRange = 31, + ERR_AmbigBinaryOps = 34, + ERR_AmbigUnaryOp = 35, + ERR_ValueCantBeNull = 37, + ERR_WrongNestedThis = 38, + ERR_NoSuchMember = 117, + ERR_ObjectRequired = 120, + ERR_AmbigCall = 121, + ERR_BadAccess = 122, + ERR_MethDelegateMismatch = 123, + ERR_AssgLvalueExpected = 131, + ERR_NoConstructors = 143, + ERR_BadDelegateConstructor = 148, + ERR_PropertyLacksGet = 154, + ERR_ObjectProhibited = 176, + ERR_AssgReadonly = 191, + ERR_RefReadonly = 192, + ERR_AssgReadonlyStatic = 198, + ERR_RefReadonlyStatic = 199, + ERR_AssgReadonlyProp = 200, + ERR_AbstractBaseCall = 205, + ERR_RefProperty = 206, + ERR_ManagedAddr = 208, + ERR_FixedNotNeeded = 213, + ERR_UnsafeNeeded = 214, + ERR_BadBoolOp = 217, + ERR_MustHaveOpTF = 218, + ERR_CheckedOverflow = 220, + ERR_ConstOutOfRangeChecked = 221, + ERR_AmbigMember = 229, + ERR_SizeofUnsafe = 233, + ERR_FieldInitRefNonstatic = 236, + ERR_CallingFinalizeDepracated = 245, + ERR_CallingBaseFinalizeDeprecated = 250, + ERR_BadCastInFixed = 254, + ERR_NoImplicitConvCast = 266, + ERR_InaccessibleGetter = 271, + ERR_InaccessibleSetter = 272, + ERR_BadArity = 305, + ERR_BadTypeArgument = 306, + ERR_TypeArgsNotAllowed = 307, + ERR_HasNoTypeVars = 308, + ERR_NewConstraintNotSatisfied = 310, + ERR_GenericConstraintNotSatisfiedRefType = 311, + ERR_GenericConstraintNotSatisfiedNullableEnum = 312, + ERR_GenericConstraintNotSatisfiedNullableInterface = 313, + ERR_GenericConstraintNotSatisfiedTyVar = 314, + ERR_GenericConstraintNotSatisfiedValType = 315, + ERR_TypeVarCantBeNull = 403, + ERR_BadRetType = 407, + ERR_CantInferMethTypeArgs = 411, + ERR_MethGrpToNonDel = 428, + ERR_RefConstraintNotSatisfied = 452, + ERR_ValConstraintNotSatisfied = 453, + ERR_CircularConstraint = 454, + ERR_BaseConstraintConflict = 455, + ERR_ConWithValCon = 456, + ERR_AmbigUDConv = 457, + ERR_PredefinedTypeNotFound = 518, + ERR_PredefinedTypeBadType = 520, + ERR_BindToBogus = 570, + ERR_CantCallSpecialMethod = 571, + ERR_BogusType = 648, + ERR_MissingPredefinedMember = 656, + ERR_LiteralDoubleCast = 664, + ERR_UnifyingInterfaceInstantiations = 695, + ERR_ConvertToStaticClass = 716, + ERR_GenericArgIsStaticClass = 718, + ERR_PartialMethodToDelegate = 762, + ERR_IncrementLvalueExpected = 1059, + ERR_NoSuchMemberOrExtension = 1061, + ERR_ValueTypeExtDelegate = 1113, + ERR_BadArgCount = 1501, + ERR_BadArgTypes = 1502, + ERR_BadArgType = 1503, + ERR_RefLvalueExpected = 1510, + ERR_BadProtectedAccess = 1540, + ERR_BindToBogusProp2 = 1545, + ERR_BindToBogusProp1 = 1546, + ERR_BadDelArgCount = 1593, + ERR_BadDelArgTypes = 1594, + ERR_AssgReadonlyLocal = 1604, + ERR_RefReadonlyLocal = 1605, + ERR_ReturnNotLValue = 1612, + ERR_BadArgExtraRef = 1615, + ERR_BadArgRef = 1620, + ERR_AssgReadonly2 = 1648, + ERR_RefReadonly2 = 1649, + ERR_AssgReadonlyStatic2 = 1650, + ERR_RefReadonlyStatic2 = 1651, + ERR_AssgReadonlyLocalCause = 1656, + ERR_RefReadonlyLocalCause = 1657, + ERR_ThisStructNotInAnonMeth = 1673, + ERR_DelegateOnNullable = 1728, + ERR_BadCtorArgCount = 1729, + ERR_BadExtensionArgTypes = 1928, + ERR_BadInstanceArgType = 1929, + ERR_BadArgTypesForCollectionAdd = 1950, + ERR_InitializerAddHasParamModifiers = 1954, + ERR_NonInvocableMemberCalled = 1955, + ERR_NamedArgumentSpecificationBeforeFixedArgument = 5002, + ERR_BadNamedArgument = 5003, + ERR_BadNamedArgumentForDelegateInvoke = 5004, + ERR_DuplicateNamedArgument = 5005, + ERR_NamedArgumentUsedInPositional = 5006, + } +} diff --git a/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Errors/ErrorFactory.cs b/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Errors/ErrorFactory.cs new file mode 100644 index 000000000..409ef9852 --- /dev/null +++ b/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Errors/ErrorFactory.cs @@ -0,0 +1,20 @@ +// ==++== +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// ==--== + +using System.Diagnostics; + +namespace Microsoft.CSharp.RuntimeBinder.Errors +{ + internal class CErrorFactory + { + public CError CreateError(ErrorCode iErrorIndex, params string[] args) + { + CError output = new CError(); + output.Initialize(iErrorIndex, args); + return output; + } + } +} diff --git a/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Errors/ErrorFacts.cs b/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Errors/ErrorFacts.cs new file mode 100644 index 000000000..6663d9ea7 --- /dev/null +++ b/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Errors/ErrorFacts.cs @@ -0,0 +1,43 @@ +// ==++== +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// ==--== + +using System; +using System.Diagnostics; + +namespace Microsoft.CSharp.RuntimeBinder.Errors +{ + static class ErrorFacts + { + public static string GetMessage(ErrorCode code) + { + string codeStr = code.ToString(); + + Debug.Assert(codeStr != null); + + if (codeStr == null) + { + return null; + } + + Debug.Assert(codeStr.Length > 4); + Debug.Assert(codeStr.StartsWith("ERR_", StringComparison.Ordinal)); + + if (codeStr.Length <= 4) + { + return null; + } + + // This strips off the "ERR_" and gets the resource with the rest of the name + + return SR.GetString(codeStr.Substring(4)); + } + + public static string GetMessage(MessageID id) + { + return SR.GetString(id.ToString()); + } + } +} diff --git a/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Errors/ErrorFmt.cs b/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Errors/ErrorFmt.cs new file mode 100644 index 000000000..ab12445ac --- /dev/null +++ b/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Errors/ErrorFmt.cs @@ -0,0 +1,315 @@ +// ==++== +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// ==--== + +using System; +using System.Diagnostics; +using Microsoft.CSharp.RuntimeBinder.Semantics; +using Microsoft.CSharp.RuntimeBinder.Syntax; + +namespace Microsoft.CSharp.RuntimeBinder.Errors +{ + // Things related to the construction of a formatted error. Reporting + // an error involves constructing a formatted message, and then passing + // that message on to an object that gets it to the user. The interface + // that declares the error submission API is separate from this. + + internal enum ErrArgKind + { + Int, + Hresult, + Ids, + SymKind, + Sym, + Type, + Name, + Str, + PredefName, + LocNode, + Ptr, + SymWithType, + MethWithInst, + Expr, + Lim + } + + enum ErrArgFlags + { + None = 0x0000, + Ref = 0x0001, // The arg's location should be included in the error message + NoStr = 0x0002, // The arg should NOT be included in the error message, just the location + RefOnly = Ref | NoStr, + Unique = 0x0004, // The string should be distinct from other args marked with Unique + UseGetErrorInfo = 0x0008, + } + + internal class SymWithTypeMemo + { + public Symbol sym; + public AggregateType ats; + } + + internal class MethPropWithInstMemo + { + public Symbol sym; + public AggregateType ats; + public TypeArray typeArgs; + } + + internal class ErrArg + { + public ErrArgKind eak; + public ErrArgFlags eaf; + internal MessageID ids; + internal int n; + internal SYMKIND sk; + internal PredefinedName pdn; + internal Name name; + internal Symbol sym; + internal string psz; + internal CType pType; + internal MethPropWithInstMemo mpwiMemo; + internal SymWithTypeMemo swtMemo; + + public ErrArg() + { + } + public ErrArg(int n) + { + this.eak = ErrArgKind.Int; + this.eaf = ErrArgFlags.None; + this.n = n; + } + public ErrArg(SYMKIND sk) + { + Debug.Assert(sk != SYMKIND.SK_AssemblyQualifiedNamespaceSymbol); + this.eaf = ErrArgFlags.None; + this.eak = ErrArgKind.SymKind; + this.sk = sk; + } // NSAIDSYMs are treated differently based on the Symbol not the SK + + public ErrArg(Name name) + { + this.eak = ErrArgKind.Name; + this.eaf = ErrArgFlags.None; + this.name = name; + } + public ErrArg(PredefinedName pdn) + { + this.eak = ErrArgKind.PredefName; + this.eaf = ErrArgFlags.None; + this.pdn = pdn; + } + + public ErrArg(string psz) + { + this.eak = ErrArgKind.Str; + this.eaf = ErrArgFlags.None; + this.psz = psz; + } + public ErrArg(CType pType) + : this(pType, ErrArgFlags.None) + { + } + public ErrArg(CType pType, ErrArgFlags eaf) + { + this.eak = ErrArgKind.Type; + this.eaf = eaf; + this.pType = pType; + } + public ErrArg(Symbol pSym) + : this(pSym, ErrArgFlags.None) + { + } + public ErrArg(Symbol pSym, ErrArgFlags eaf) + { + this.eak = ErrArgKind.Sym; + this.eaf = eaf; + this.sym = pSym; + } + public ErrArg(SymWithType swt) + { + this.eak = ErrArgKind.SymWithType; + this.eaf = ErrArgFlags.None; + this.swtMemo = new SymWithTypeMemo(); + this.swtMemo.sym = swt.Sym; + this.swtMemo.ats = swt.Ats; + } + public ErrArg(MethPropWithInst mpwi) + { + this.eak = ErrArgKind.MethWithInst; + this.eaf = ErrArgFlags.None; + this.mpwiMemo = new MethPropWithInstMemo(); + this.mpwiMemo.sym = mpwi.Sym; + this.mpwiMemo.ats = mpwi.Ats; + this.mpwiMemo.typeArgs = mpwi.TypeArgs; + } + public static implicit operator ErrArg(int n) + { + return new ErrArg(n); + } + public static implicit operator ErrArg(SYMKIND sk) + { + return new ErrArg(sk); + } + public static implicit operator ErrArg(CType type) + { + return new ErrArg(type); + } + public static implicit operator ErrArg(string psz) + { + return new ErrArg(psz); + } + public static implicit operator ErrArg(PredefinedName pdn) + { + return new ErrArg(pdn); + } + public static implicit operator ErrArg(Name name) + { + return new ErrArg(name); + } + public static implicit operator ErrArg(Symbol pSym) + { + return new ErrArg(pSym); + } + public static implicit operator ErrArg(SymWithType swt) + { + return new ErrArg(swt); + } + public static implicit operator ErrArg(MethPropWithInst mpwi) + { + return new ErrArg(mpwi); + } + } + + + class ErrArgRef : ErrArg + { + public ErrArgRef() + { + } + public ErrArgRef(int n) + : base(n) + { + } + public ErrArgRef(Name name) + : base(name) + { + this.eaf = ErrArgFlags.Ref; + } + public ErrArgRef(string psz) + : base(psz) + { + this.eaf = ErrArgFlags.Ref; + } + public ErrArgRef(Symbol sym) + : base(sym) + { + this.eaf = ErrArgFlags.Ref; + } + public ErrArgRef(CType pType) + : base(pType) + { + this.eaf = ErrArgFlags.Ref; + } + public ErrArgRef(SymWithType swt) + : base(swt) + { + this.eaf = ErrArgFlags.Ref; + } + public ErrArgRef(MethPropWithInst mpwi) + : base(mpwi) + { + this.eaf = ErrArgFlags.Ref; + } + public ErrArgRef(CType pType, ErrArgFlags eaf) + : base(pType) + { + this.eaf = eaf | ErrArgFlags.Ref; + } + public static implicit operator ErrArgRef(string s) + { + return new ErrArgRef(s); + } + public static implicit operator ErrArgRef(Name name) + { + return new ErrArgRef(name); + } + public static implicit operator ErrArgRef(int n) + { + return new ErrArgRef(n); + } + public static implicit operator ErrArgRef(Symbol sym) + { + return new ErrArgRef(sym); + } + public static implicit operator ErrArgRef(CType type) + { + return new ErrArgRef(type); + } + public static implicit operator ErrArgRef(SymWithType swt) + { + return new ErrArgRef(swt); + } + public static implicit operator ErrArgRef(MethPropWithInst mpwi) + { + return new ErrArgRef(mpwi); + } + } + + internal class ErrArgRefOnly : ErrArgRef + { + public ErrArgRefOnly(Symbol sym) + : base(sym) + { + eaf = ErrArgFlags.RefOnly; + } + } + + // This is used with COMPILER_BASE::ErrorRef to indicate no reference. + internal class ErrArgNoRef : ErrArgRef + { + public ErrArgNoRef(CType pType) + { + this.eak = ErrArgKind.Type; + this.eaf = ErrArgFlags.None; + this.pType = pType; + } + } + + internal class ErrArgIds : ErrArgRef + { + public ErrArgIds(MessageID ids) + { + this.eak = ErrArgKind.Ids; + this.eaf = ErrArgFlags.None; + this.ids = ids; + } + } + + sealed internal class ErrArgSymKind : ErrArgRef + { + public ErrArgSymKind(Symbol sym) + { + eak = ErrArgKind.SymKind; + eaf = ErrArgFlags.None; + sk = sym.getKind(); + if (sk == SYMKIND.SK_AssemblyQualifiedNamespaceSymbol) + { + if (!String.IsNullOrEmpty(sym.AsAssemblyQualifiedNamespaceSymbol().GetNS().name.Text)) + { + // Non-empty namespace name means it's not the root + // so treat it like a namespace instead of an alias + sk = SYMKIND.SK_NamespaceSymbol; + } + else + { + // An empty namespace name means it's just an alias for the root + sk = SYMKIND.SK_ExternalAliasDefinitionSymbol; + } + } + } + } +} diff --git a/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Errors/ErrorHandling.cs b/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Errors/ErrorHandling.cs new file mode 100644 index 000000000..0f8bb61f4 --- /dev/null +++ b/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Errors/ErrorHandling.cs @@ -0,0 +1,262 @@ +// ==++== +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// ==--== + +using System; +using System.Diagnostics; +using System.Globalization; +using Microsoft.CSharp.RuntimeBinder.Semantics; + +namespace Microsoft.CSharp.RuntimeBinder.Errors +{ + // Collection of error reporting code. This class contains among other things ptrs to + // interfaces for constructing error locations (EE doesn't need locations), submitting errors + // (EE and compiler have different destinations), and error construction (again EE and + // compiler differ). Further decoupling would be enabled if the construction of substitution + // strings (the values that replace the placeholders in resource-defined error strings) were + // done at the location in which the error is detected. Right now an ErrArg is constructed + // and later is used to construct a string. I can't see any reason why the string creation + // must be deferred, thus causing the error formatting/reporting subsystem to understand a + // whole host of types, many of which may not be relevant to the EE. + + class ErrorHandling + { + private IErrorSink m_errorSink; + private UserStringBuilder m_userStringBuilder; + private CErrorFactory m_errorFactory; + + // By default these DO NOT add related locations. To add a related location, pass an ErrArgRef. + public void Error(ErrorCode id, params ErrArg[] args) + { + ErrorTreeArgs(id, args); + } + + // By default these DO add related locations. + public void ErrorRef(ErrorCode id, params ErrArgRef[] args) + { + ErrorTreeArgs(id, args); + } + + //////////////////////////////////////////////////////////////////////////////// + // + // This function submits the given error to the controller, and if it's a fatal + // error, throws the fatal exception. + + public void SubmitError(CParameterizedError error) + { + if (m_errorSink != null) + { + m_errorSink.SubmitError(error); + } + } + + public void MakeErrorLocArgs(out CParameterizedError error, ErrorCode id, ErrArg[] prgarg) + { + error = new CParameterizedError(); + error.Initialize(id, prgarg); + } + + public virtual void AddRelatedSymLoc(CParameterizedError err, Symbol sym) + { + } + + public virtual void AddRelatedTypeLoc(CParameterizedError err, CType pType) + { + } + + private void MakeErrorTreeArgs(out CParameterizedError error, ErrorCode id, ErrArg[] prgarg) + { + MakeErrorLocArgs(out error, id, prgarg); + } + + // By default these DO NOT add related locations. To add a related location, pass an ErrArgRef. + + public void MakeError(out CParameterizedError error, ErrorCode id, params ErrArg[] args) + { + MakeErrorTreeArgs(out error, id, args); + } + + public ErrorHandling( + UserStringBuilder strBldr, + IErrorSink sink, + CErrorFactory factory) + { + Debug.Assert(factory != null); + + m_userStringBuilder = strBldr; + m_errorSink = sink; + m_errorFactory = factory; + } + + private CError CreateError(ErrorCode iErrorIndex, string[] args) + { + return m_errorFactory.CreateError(iErrorIndex, args); + } + private void ErrorTreeArgs(ErrorCode id, ErrArg[] prgarg) + { + CParameterizedError error; + MakeErrorTreeArgs(out error, id, prgarg); + SubmitError(error); + } + + public CError RealizeError(CParameterizedError parameterizedError) + { + // Create an arg array manually using the type information in the ErrArgs. + string[] prgpsz = new string[parameterizedError.GetParameterCount()]; + int[] prgiarg = new int[parameterizedError.GetParameterCount()]; + + int ppsz = 0; + int piarg = 0; + int cargUnique = 0; + + m_userStringBuilder.ResetUndisplayableStringFlag(); + + for (int iarg = 0; iarg < parameterizedError.GetParameterCount(); iarg++) + { + ErrArg arg = parameterizedError.GetParameter(iarg); + + // If the NoStr bit is set we don't add it to prgpsz. + if (0 != (arg.eaf & ErrArgFlags.NoStr)) + continue; + + bool fUserStrings = false; + + if (!m_userStringBuilder.ErrArgToString(out prgpsz[ppsz], arg, out fUserStrings)) + { + if (arg.eak == ErrArgKind.Int) + { + prgpsz[ppsz] = arg.n.ToString(CultureInfo.InvariantCulture); + } + } + ppsz++; + + int iargRec; + if (!fUserStrings || 0 == (arg.eaf & ErrArgFlags.Unique)) + { + iargRec = -1; + } + else + { + iargRec = iarg; + cargUnique++; + } + prgiarg[piarg] = iargRec; + piarg++; + } + + // don't ever display undisplayable strings to the user + // if this happens we should track down the caller to not display the error + // this should only ever occur in a cascading error situation due to + // error tolerance + if (m_userStringBuilder.HadUndisplayableString()) + { + return null; + } + + int cpsz = ppsz; + + if (cargUnique > 1) + { + // Copy the strings over to another buffer. + string[] prgpszNew = new string[cpsz]; + Array.Copy(prgpsz, 0, prgpszNew, 0, cpsz); ; + + for (int i = 0; i < cpsz; i++) + { + if (prgiarg[i] < 0 || prgpszNew[i] != prgpsz[i]) + continue; + + ErrArg arg = parameterizedError.GetParameter(prgiarg[i]); + Debug.Assert(0 != (arg.eaf & ErrArgFlags.Unique) && 0 == (arg.eaf & ErrArgFlags.NoStr)); + + Symbol sym = null; + CType pType = null; + + switch (arg.eak) + { + case ErrArgKind.Sym: + sym = arg.sym; + break; + case ErrArgKind.Type: + pType = arg.pType; + break; + case ErrArgKind.SymWithType: + sym = arg.swtMemo.sym; + break; + case ErrArgKind.MethWithInst: + sym = arg.mpwiMemo.sym; + break; + default: + Debug.Assert(false, "Shouldn't be here!"); + continue; + } + + bool fMunge = false; + + for (int j = i + 1; j < cpsz; j++) + { + if (prgiarg[j] < 0) + continue; + Debug.Assert(0 != (parameterizedError.GetParameter(prgiarg[j]).eaf & ErrArgFlags.Unique)); + if (prgpsz[i] != prgpsz[j]) + continue; + + // The strings are identical. If they are the same symbol, leave them alone. + // Otherwise, munge both strings. If j has already been munged, just make + // sure we munge i. + if (prgpszNew[j] != prgpsz[j]) + { + fMunge = true; + continue; + } + + ErrArg arg2 = parameterizedError.GetParameter(prgiarg[j]); + Debug.Assert(0 != (arg2.eaf & ErrArgFlags.Unique) && 0 == (arg2.eaf & ErrArgFlags.NoStr)); + + Symbol sym2 = null; + CType pType2 = null; + + switch (arg2.eak) + { + case ErrArgKind.Sym: + sym2 = arg2.sym; + break; + case ErrArgKind.Type: + pType2 = arg2.pType; + break; + case ErrArgKind.SymWithType: + sym2 = arg2.swtMemo.sym; + break; + case ErrArgKind.MethWithInst: + sym2 = arg2.mpwiMemo.sym; + break; + default: + Debug.Assert(false, "Shouldn't be here!"); + continue; + } + + if (sym2 == sym && pType2 == pType && !fMunge) + continue; + + + prgpszNew[j] = prgpsz[j]; + + fMunge = true; + } + + if (fMunge) + { + prgpszNew[i] = prgpsz[i]; + } + } + + prgpsz = prgpszNew; + } + + CError err = CreateError(parameterizedError.GetErrorNumber(), prgpsz); + return err; + } + } +} diff --git a/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Errors/IErrorSink.cs b/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Errors/IErrorSink.cs new file mode 100644 index 000000000..51aa58bc1 --- /dev/null +++ b/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Errors/IErrorSink.cs @@ -0,0 +1,16 @@ +// ==++== +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// ==--== + +namespace Microsoft.CSharp.RuntimeBinder.Errors +{ + // this interface is used to decouple the error reporting + // implementation from the error detection source. + interface IErrorSink + { + void SubmitError(CParameterizedError error); + int ErrorCount(); + } +} diff --git a/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Errors/MessageID.cs b/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Errors/MessageID.cs new file mode 100644 index 000000000..625a920ae --- /dev/null +++ b/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Errors/MessageID.cs @@ -0,0 +1,29 @@ +// ==++== +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// ==--== + +namespace Microsoft.CSharp.RuntimeBinder.Errors +{ + enum MessageID + { + SK_METHOD, + SK_CLASS, + SK_NAMESPACE, + SK_FIELD, + SK_PROPERTY, + SK_UNKNOWN, + SK_VARIABLE, + SK_EVENT, + SK_TYVAR, + SK_ALIAS, + ERRORSYM, + NULL, + GlobalNamespace, + MethodGroup, + AnonMethod, + Lambda, + AnonymousType, + } +} diff --git a/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Errors/Microsoft.CSharp.RuntimeBinder.Errors.txt b/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Errors/Microsoft.CSharp.RuntimeBinder.Errors.txt new file mode 100644 index 000000000..3e8da5b12 --- /dev/null +++ b/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Errors/Microsoft.CSharp.RuntimeBinder.Errors.txt @@ -0,0 +1,142 @@ +; IMPORTANT: This file needs to be kept in sync with ndp\fx\src\csharp.small\Microsoft.CSharp.txt +; +; Silverlight builds currently support only one resource file per project so we need to manually +; keep these files in sync. +; When porting exceptions, they should be added to ndp\fx\src\csharp.small\Error.cs instead +; since SL build puts them in a wrong namespace. + + +#ifdef _SUPPRESS_SR_STRING_GEN + +SK_METHOD=method +SK_CLASS=type +SK_NAMESPACE=namespace +SK_FIELD=field +SK_PROPERTY=property +SK_UNKNOWN=element +SK_VARIABLE=variable +SK_EVENT=event +SK_TYVAR=type parameter +SK_ALIAS=using alias +ERRORSYM= +NULL= +GlobalNamespace= +MethodGroup=method group +AnonMethod=anonymous method +Lambda=lambda expression +AnonymousType=anonymous type + +BadBinaryOps=Operator '{0}' cannot be applied to operands of type '{1}' and '{2}' +IntDivByZero=Division by constant zero +BadIndexLHS=Cannot apply indexing with [] to an expression of type '{0}' +BadIndexCount=Wrong number of indices inside []; expected '{0}' +BadUnaryOp=Operator '{0}' cannot be applied to operand of type '{1}' +NoImplicitConv=Cannot implicitly convert type '{0}' to '{1}' +NoExplicitConv=Cannot convert type '{0}' to '{1}' +ConstOutOfRange=Constant value '{0}' cannot be converted to a '{1}' +AmbigBinaryOps=Operator '{0}' is ambiguous on operands of type '{1}' and '{2}' +AmbigUnaryOp=Operator '{0}' is ambiguous on an operand of type '{1}' +ValueCantBeNull=Cannot convert null to '{0}' because it is a non-nullable value type +WrongNestedThis=Cannot access a non-static member of outer type '{0}' via nested type '{1}' +NoSuchMember='{0}' does not contain a definition for '{1}' +ObjectRequired=An object reference is required for the non-static field, method, or property '{0}' +AmbigCall=The call is ambiguous between the following methods or properties: '{0}' and '{1}' +BadAccess='{0}' is inaccessible due to its protection level +MethDelegateMismatch=No overload for '{0}' matches delegate '{1}' +AssgLvalueExpected=The left-hand side of an assignment must be a variable, property or indexer +NoConstructors=The type '{0}' has no constructors defined +BadDelegateConstructor=The delegate '{0}' does not have a valid constructor +PropertyLacksGet=The property or indexer '{0}' cannot be used in this context because it lacks the get accessor +ObjectProhibited=Member '{0}' cannot be accessed with an instance reference; qualify it with a type name instead +AssgReadonly=A readonly field cannot be assigned to (except in a constructor or a variable initializer) +RefReadonly=A readonly field cannot be passed ref or out (except in a constructor) +AssgReadonlyStatic=A static readonly field cannot be assigned to (except in a static constructor or a variable initializer) +RefReadonlyStatic=A static readonly field cannot be passed ref or out (except in a static constructor) +AssgReadonlyProp=Property or indexer '{0}' cannot be assigned to -- it is read only +AbstractBaseCall=Cannot call an abstract base member: '{0}' +RefProperty=A property or indexer may not be passed as an out or ref parameter +ManagedAddr=Cannot take the address of, get the size of, or declare a pointer to a managed type ('{0}') +FixedNotNeeded=You cannot use the fixed statement to take the address of an already fixed expression +UnsafeNeeded=Dynamic calls cannot be used in conjunction with pointers +BadBoolOp=In order to be applicable as a short circuit operator a user-defined logical operator ('{0}') must have the same return type as the type of its 2 parameters +MustHaveOpTF=The type ('{0}') must contain declarations of operator true and operator false +CheckedOverflow=The operation overflows at compile time in checked mode +ConstOutOfRangeChecked=Constant value '{0}' cannot be converted to a '{1}' (use 'unchecked' syntax to override) +AmbigMember=Ambiguity between '{0}' and '{1}' +SizeofUnsafe='{0}' does not have a predefined size, therefore sizeof can only be used in an unsafe context (consider using System.Runtime.InteropServices.Marshal.SizeOf) +FieldInitRefNonstatic=A field initializer cannot reference the non-static field, method, or property '{0}' +CallingFinalizeDepracated=Destructors and object.Finalize cannot be called directly. Consider calling IDisposable.Dispose if available. +CallingBaseFinalizeDeprecated=Do not directly call your base class Finalize method. It is called automatically from your destructor. +BadCastInFixed=The right hand side of a fixed statement assignment may not be a cast expression +NoImplicitConvCast=Cannot implicitly convert type '{0}' to '{1}'. An explicit conversion exists (are you missing a cast?) +InaccessibleGetter=The property or indexer '{0}' cannot be used in this context because the get accessor is inaccessible +InaccessibleSetter=The property or indexer '{0}' cannot be used in this context because the set accessor is inaccessible +BadArity=Using the generic {1} '{0}' requires '{2}' type arguments +BadTypeArgument=The type '{0}' may not be used as a type argument +TypeArgsNotAllowed=The {1} '{0}' cannot be used with type arguments +HasNoTypeVars=The non-generic {1} '{0}' cannot be used with type arguments +NewConstraintNotSatisfied='{2}' must be a non-abstract type with a public parameterless constructor in order to use it as parameter '{1}' in the generic type or method '{0}' +GenericConstraintNotSatisfiedRefType=The type '{3}' cannot be used as type parameter '{2}' in the generic type or method '{0}'. There is no implicit reference conversion from '{3}' to '{1}'. +GenericConstraintNotSatisfiedNullableEnum=The type '{3}' cannot be used as type parameter '{2}' in the generic type or method '{0}'. The nullable type '{3}' does not satisfy the constraint of '{1}'. +GenericConstraintNotSatisfiedNullableInterface=The type '{3}' cannot be used as type parameter '{2}' in the generic type or method '{0}'. The nullable type '{3}' does not satisfy the constraint of '{1}'. Nullable types can not satisfy any interface constraints. +GenericConstraintNotSatisfiedTyVar=The type '{3}' cannot be used as type parameter '{2}' in the generic type or method '{0}'. There is no boxing conversion or type parameter conversion from '{3}' to '{1}'. +GenericConstraintNotSatisfiedValType=The type '{3}' cannot be used as type parameter '{2}' in the generic type or method '{0}'. There is no boxing conversion from '{3}' to '{1}'. +TypeVarCantBeNull=Cannot convert null to type parameter '{0}' because it could be a non-nullable value type. Consider using 'default({0})' instead. +BadRetType='{1} {0}' has the wrong return type +CantInferMethTypeArgs=The type arguments for method '{0}' cannot be inferred from the usage. Try specifying the type arguments explicitly. +MethGrpToNonDel=Cannot convert method group '{0}' to non-delegate type '{1}'. Did you intend to invoke the method? +RefConstraintNotSatisfied=The type '{2}' must be a reference type in order to use it as parameter '{1}' in the generic type or method '{0}' +ValConstraintNotSatisfied=The type '{2}' must be a non-nullable value type in order to use it as parameter '{1}' in the generic type or method '{0}' +CircularConstraint=Circular constraint dependency involving '{0}' and '{1}' +BaseConstraintConflict=Type parameter '{0}' inherits conflicting constraints '{1}' and '{2}' +ConWithValCon=Type parameter '{1}' has the 'struct' constraint so '{1}' cannot be used as a constraint for '{0}' +AmbigUDConv=Ambiguous user defined conversions '{0}' and '{1}' when converting from '{2}' to '{3}' +PredefinedTypeNotFound=Predefined type '{0}' is not defined or imported +PredefinedTypeBadType=Predefined type '{0}' is declared incorrectly +BindToBogus='{0}' is not supported by the language +CantCallSpecialMethod='{0}': cannot explicitly call operator or accessor +BogusType='{0}' is a type not supported by the language +MissingPredefinedMember=Missing compiler required member '{0}.{1}' +LiteralDoubleCast=Literal of type double cannot be implicitly converted to type '{1}'; use an '{0}' suffix to create a literal of this type +UnifyingInterfaceInstantiations='{0}' cannot implement both '{1}' and '{2}' because they may unify for some type parameter substitutions +ConvertToStaticClass=Cannot convert to static type '{0}' +GenericArgIsStaticClass='{0}': static types cannot be used as type arguments +PartialMethodToDelegate=Cannot create delegate from method '{0}' because it is a partial method without an implementing declaration +IncrementLvalueExpected=The operand of an increment or decrement operator must be a variable, property or indexer +NoSuchMemberOrExtension='{0}' does not contain a definition for '{1}' and no extension method '{1}' accepting a first argument of type '{0}' could be found (are you missing a using directive or an assembly reference?) +ValueTypeExtDelegate=Extension methods '{0}' defined on value type '{1}' cannot be used to create delegates +BadArgCount=No overload for method '{0}' takes '{1}' arguments +BadArgTypes=The best overloaded method match for '{0}' has some invalid arguments +BadArgType=Argument '{0}': cannot convert from '{1}' to '{2}' +RefLvalueExpected=A ref or out argument must be an assignable variable +BadProtectedAccess=Cannot access protected member '{0}' via a qualifier of type '{1}'; the qualifier must be of type '{2}' (or derived from it) +BindToBogusProp2=Property, indexer, or event '{0}' is not supported by the language; try directly calling accessor methods '{1}' or '{2}' +BindToBogusProp1=Property, indexer, or event '{0}' is not supported by the language; try directly calling accessor method '{1}' +BadDelArgCount=Delegate '{0}' does not take '{1}' arguments +BadDelArgTypes=Delegate '{0}' has some invalid arguments +AssgReadonlyLocal=Cannot assign to '{0}' because it is read-only +RefReadonlyLocal=Cannot pass '{0}' as a ref or out argument because it is read-only +ReturnNotLValue=Cannot modify the return value of '{0}' because it is not a variable +BadArgExtraRef=Argument '{0}' should not be passed with the '{1}' keyword +BadArgRef=Argument '{0}' must be passed with the '{1}' keyword +AssgReadonly2=Members of readonly field '{0}' cannot be modified (except in a constructor or a variable initializer) +RefReadonly2=Members of readonly field '{0}' cannot be passed ref or out (except in a constructor) +AssgReadonlyStatic2=Fields of static readonly field '{0}' cannot be assigned to (except in a static constructor or a variable initializer) +RefReadonlyStatic2=Fields of static readonly field '{0}' cannot be passed ref or out (except in a static constructor) +AssgReadonlyLocalCause=Cannot assign to '{0}' because it is a '{1}' +RefReadonlyLocalCause=Cannot pass '{0}' as a ref or out argument because it is a '{1}' +ThisStructNotInAnonMeth=Anonymous methods, lambda expressions, and query expressions inside structs cannot access instance members of 'this'. Consider copying 'this' to a local variable outside the anonymous method, lambda expression or query expression and using the local instead. +DelegateOnNullable=Cannot bind delegate to '{0}' because it is a member of 'System.Nullable' +BadCtorArgCount='{0}' does not contain a constructor that takes '{1}' arguments +BadExtensionArgTypes='{0}' does not contain a definition for '{1}' and the best extension method overload '{2}' has some invalid arguments +BadInstanceArgType=Instance argument: cannot convert from '{0}' to '{1}' +BadArgTypesForCollectionAdd=The best overloaded Add method '{0}' for the collection initializer has some invalid arguments +InitializerAddHasParamModifiers=The best overloaded method match '{0}' for the collection initializer element cannot be used. Collection initializer 'Add' methods cannot have ref or out parameters. +NonInvocableMemberCalled=Non-invocable member '{0}' cannot be used like a method. +NamedArgumentSpecificationBeforeFixedArgument=Named argument specifications must appear after all fixed arguments have been specified +BadNamedArgument=The best overload for '{0}' does not have a parameter named '{1}' +BadNamedArgumentForDelegateInvoke=The delegate '{0}' does not have a parameter named '{1}' +DuplicateNamedArgument=Named argument '{0}' cannot be specified multiple times +NamedArgumentUsedInPositional=Named argument '{0}' specifies a parameter for which a positional argument has already been given + +#endif \ No newline at end of file diff --git a/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Errors/UserStringBuilder.cs b/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Errors/UserStringBuilder.cs new file mode 100644 index 000000000..f00424e34 --- /dev/null +++ b/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Errors/UserStringBuilder.cs @@ -0,0 +1,815 @@ +// ==++== +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// ==--== + +using System; +using System.Diagnostics; +using System.Globalization; +using System.Text; +using Microsoft.CSharp.RuntimeBinder.Semantics; +using Microsoft.CSharp.RuntimeBinder.Syntax; + +namespace Microsoft.CSharp.RuntimeBinder.Errors +{ + internal class UserStringBuilder + { + protected bool fHadUndisplayableStringInError; + protected bool m_buildingInProgress; + protected GlobalSymbolContext m_globalSymbols; + protected StringBuilder m_strBuilder; + + public UserStringBuilder( + GlobalSymbolContext globalSymbols) + { + Debug.Assert(globalSymbols != null); + fHadUndisplayableStringInError = false; + m_buildingInProgress = false; + m_globalSymbols = globalSymbols; + } + + protected void BeginString() + { + Debug.Assert(!m_buildingInProgress); + m_buildingInProgress = true; + m_strBuilder = new StringBuilder(); + } + protected void EndString(out string s) + { + Debug.Assert(m_buildingInProgress); + m_buildingInProgress = false; + s = m_strBuilder.ToString(); + m_strBuilder = null; + } + + + public bool HadUndisplayableString() + { + return fHadUndisplayableStringInError; + } + + public void ResetUndisplayableStringFlag() + { + fHadUndisplayableStringInError = false; + } + + protected void ErrSK(out string psz, SYMKIND sk) + { + MessageID id; + switch (sk) + { + case SYMKIND.SK_MethodSymbol: + id = MessageID.SK_METHOD; + break; + case SYMKIND.SK_AggregateSymbol: + id = MessageID.SK_CLASS; + break; + case SYMKIND.SK_NamespaceSymbol: + id = MessageID.SK_NAMESPACE; + break; + case SYMKIND.SK_FieldSymbol: + id = MessageID.SK_FIELD; + break; + case SYMKIND.SK_LocalVariableSymbol: + id = MessageID.SK_VARIABLE; + break; + case SYMKIND.SK_PropertySymbol: + id = MessageID.SK_PROPERTY; + break; + case SYMKIND.SK_EventSymbol: + id = MessageID.SK_EVENT; + break; + case SYMKIND.SK_TypeParameterSymbol: + id = MessageID.SK_TYVAR; + break; + case SYMKIND.SK_AssemblyQualifiedNamespaceSymbol: + Debug.Assert(false, "Illegal sk"); + id = MessageID.SK_ALIAS; + break; + default: + Debug.Assert(false, "impossible sk"); + id = MessageID.SK_UNKNOWN; + break; + } + + ErrId(out psz, id); + } + /* + * Create a fill-in string describing a parameter list. + * Does NOT include () + */ + protected void ErrAppendParamList(TypeArray @params, bool isVarargs, bool isParamArray) + { + if (null == @params) + return; + + for (int i = 0; i < @params.size; i++) + { + if (i > 0) + { + ErrAppendString(", "); + } + + if (isParamArray && i == @params.size - 1) + { + ErrAppendString("params "); + } + + // parameter type name + ErrAppendType(@params.Item(i), null); + } + + if (isVarargs) + { + if (@params.size != 0) + { + ErrAppendString(", "); + } + + ErrAppendString("..."); + } + } + + public void ErrAppendString(string str) + { + m_strBuilder.Append(str); + } + public void ErrAppendChar(char ch) + { + m_strBuilder.Append(ch); + } + + public void ErrAppendPrintf(string format, params object[] args) + { + ErrAppendString(String.Format(CultureInfo.InvariantCulture, format, args)); + } + public void ErrAppendName(Name name) + { + CheckDisplayableName(name); + + if (name == GetNameManager().GetPredefName(PredefinedName.PN_INDEXERINTERNAL)) + { + ErrAppendString("this"); + } + else + { + ErrAppendString(name.Text); + } + } + + protected void ErrAppendMethodParentSym(MethodSymbol sym, SubstContext pcxt, out TypeArray substMethTyParams) + { + substMethTyParams = null; + ErrAppendParentSym(sym, pcxt); + } + + protected void ErrAppendParentSym(Symbol sym, SubstContext pctx) + { + ErrAppendParentCore(sym.parent, pctx); + } + protected void ErrAppendParentType(CType pType, SubstContext pctx) + { + if (pType.IsErrorType()) + { + if (pType.AsErrorType().HasTypeParent()) + { + ErrAppendType(pType.AsErrorType().GetTypeParent(), null); + ErrAppendChar('.'); + } + else + { + ErrAppendParentCore(pType.AsErrorType().GetNSParent(), pctx); + } + } + else if (pType.IsAggregateType()) + { + ErrAppendParentCore(pType.AsAggregateType().GetOwningAggregate(), pctx); + } + else if (pType.GetBaseOrParameterOrElementType() != null) + { + ErrAppendType(pType.GetBaseOrParameterOrElementType(), null); + ErrAppendChar('.'); + } + } + protected void ErrAppendParentCore(Symbol parent, SubstContext pctx) + { + if (null == parent) + return; + + if (parent == getBSymmgr().GetRootNS()) + return; + + if (pctx != null && !pctx.FNop() && parent.IsAggregateSymbol() && 0 != parent.AsAggregateSymbol().GetTypeVarsAll().size) + { + CType pType = GetTypeManager().SubstType(parent.AsAggregateSymbol().getThisType(), pctx); + ErrAppendType(pType, null); + } + else + { + ErrAppendSym(parent, null); + } + ErrAppendChar('.'); + } + protected void ErrAppendTypeParameters(TypeArray @params, SubstContext pctx, bool forClass) + { + if (@params != null && @params.size != 0) + { + ErrAppendChar('<'); + ErrAppendType(@params.Item(0), pctx); + for (int i = 1; i < @params.size; i++) + { + ErrAppendString(","); + ErrAppendType(@params.Item(i), pctx); + } + ErrAppendChar('>'); + } + } + protected void ErrAppendMethod(MethodSymbol meth, SubstContext pctx, bool fArgs) + { + if (meth.IsExpImpl() && meth.swtSlot) + { + ErrAppendParentSym(meth, pctx); + + // Get the type args from the explicit impl type and substitute using pctx (if there is one). + SubstContext ctx = new SubstContext(GetTypeManager().SubstType(meth.swtSlot.GetType(), pctx).AsAggregateType()); + ErrAppendSym(meth.swtSlot.Sym, ctx, fArgs); + + // args already added + return; + } + + if (meth.isPropertyAccessor()) + { + PropertySymbol prop = meth.getProperty(); + + // this includes the parent class + ErrAppendSym(prop, pctx); + + // add accessor name + if (prop.methGet == meth) + { + ErrAppendString(".get"); + } + else + { + Debug.Assert(meth == prop.methSet); + ErrAppendString(".set"); + } + + // args already added + return; + } + + if (meth.isEventAccessor()) + { + EventSymbol @event = meth.getEvent(); + + // this includes the parent class + ErrAppendSym(@event, pctx); + + // add accessor name + if (@event.methAdd == meth) + { + ErrAppendString(".add"); + } + else + { + Debug.Assert(meth == @event.methRemove); + ErrAppendString(".remove"); + } + + // args already added + return; + } + + TypeArray replacementTypeArray = null; + ErrAppendMethodParentSym(meth, pctx, out replacementTypeArray); + if (meth.IsConstructor()) + { + // Use the name of the parent class instead of the name "". + ErrAppendName(meth.getClass().name); + } + else if (meth.IsDestructor()) + { + // Use the name of the parent class instead of the name "Finalize". + ErrAppendChar('~'); + ErrAppendName(meth.getClass().name); + } + else if (meth.isConversionOperator()) + { + // implicit/explicit + ErrAppendString(meth.isImplicit() ? "implicit" : "explicit"); + ErrAppendString(" operator "); + + // destination type name + ErrAppendType(meth.RetType, pctx); + } + else if (meth.isOperator) + { + // handle user defined operators + // map from CLS predefined names to "operator " + ErrAppendString("operator "); + + // + // UNDONE: This is kinda slow, but the alternative is to add bits to methsym. + // + string operatorName; + OperatorKind op = Operators.OperatorOfMethodName(GetNameManager(), meth.name); + if (Operators.HasDisplayName(op)) + { + operatorName = Operators.GetDisplayName(op); + } + else + { + // + // either equals or compare + // + if (meth.name == GetNameManager().GetPredefName(PredefinedName.PN_OPEQUALS)) + { + operatorName = "equals"; + } + else + { + Debug.Assert(meth.name == GetNameManager().GetPredefName(PredefinedName.PN_OPCOMPARE)); + operatorName = "compare"; + } + } + ErrAppendString(operatorName); + } + else if (meth.IsExpImpl()) + { + if (meth.errExpImpl != null) + ErrAppendType(meth.errExpImpl, pctx, fArgs); + } + else + { + // regular method + ErrAppendName(meth.name); + } + + if (null == replacementTypeArray) + { + ErrAppendTypeParameters(meth.typeVars, pctx, false); + } + + if (fArgs) + { + // append argument types + ErrAppendChar('('); + + if (!meth.computeCurrentBogusState()) + { + ErrAppendParamList(GetTypeManager().SubstTypeArray(meth.Params, pctx), meth.isVarargs, meth.isParamArray); + } + + ErrAppendChar(')'); + } + } + protected void ErrAppendIndexer(IndexerSymbol indexer, SubstContext pctx) + { + ErrAppendString("this["); + ErrAppendParamList(GetTypeManager().SubstTypeArray(indexer.Params, pctx), false, indexer.isParamArray); + ErrAppendChar(']'); + } + protected void ErrAppendProperty(PropertySymbol prop, SubstContext pctx) + { + ErrAppendParentSym(prop, pctx); + if (prop.IsExpImpl() && prop.swtSlot.Sym != null) + { + SubstContext ctx = new SubstContext(GetTypeManager().SubstType(prop.swtSlot.GetType(), pctx).AsAggregateType()); + ErrAppendSym(prop.swtSlot.Sym, ctx); + } + else if (prop.IsExpImpl()) + { + if (prop.errExpImpl != null) + ErrAppendType(prop.errExpImpl, pctx, false); + if (prop.isIndexer()) + { + ErrAppendChar('.'); + ErrAppendIndexer(prop.AsIndexerSymbol(), pctx); + } + } + else if (prop.isIndexer()) + { + ErrAppendIndexer(prop.AsIndexerSymbol(), pctx); + } + else + { + ErrAppendName(prop.name); + } + } + protected void ErrAppendEvent(EventSymbol @event, SubstContext pctx) + { + } + public void ErrAppendId(MessageID id) + { + string str; + ErrId(out str, id); + ErrAppendString(str); + } + + /* + * Create a fill-in string describing a symbol. + */ + public void ErrAppendSym(Symbol sym, SubstContext pctx) + { + ErrAppendSym(sym, pctx, true); + } + public void ErrAppendSym(Symbol sym, SubstContext pctx, bool fArgs) + { + switch (sym.getKind()) + { + case SYMKIND.SK_NamespaceDeclaration: + // for namespace declarations just convert the namespace + ErrAppendSym(sym.AsNamespaceDeclaration().NameSpace(), null); + break; + + case SYMKIND.SK_GlobalAttributeDeclaration: + ErrAppendName(sym.name); + break; + + case SYMKIND.SK_AggregateDeclaration: + ErrAppendSym(sym.AsAggregateDeclaration().Agg(), pctx); + break; + + case SYMKIND.SK_AggregateSymbol: + { + // Check for a predefined class with a special "nice" name for + // error reported. + string text = PredefinedTypes.GetNiceName(sym.AsAggregateSymbol()); + if (text != null) + { + // Found a nice name. + ErrAppendString(text); + } + else if (sym.AsAggregateSymbol().IsAnonymousType()) + { + ErrAppendId(MessageID.AnonymousType); + break; + } + else + { + ErrAppendParentSym(sym, pctx); + ErrAppendName(sym.name); + ErrAppendTypeParameters(sym.AsAggregateSymbol().GetTypeVars(), pctx, true); + } + break; + } + + case SYMKIND.SK_MethodSymbol: + ErrAppendMethod(sym.AsMethodSymbol(), pctx, fArgs); + break; + + case SYMKIND.SK_PropertySymbol: + ErrAppendProperty(sym.AsPropertySymbol(), pctx); + break; + + case SYMKIND.SK_EventSymbol: + ErrAppendEvent(sym.AsEventSymbol(), pctx); + break; + + case SYMKIND.SK_AssemblyQualifiedNamespaceSymbol: + case SYMKIND.SK_NamespaceSymbol: + if (sym == getBSymmgr().GetRootNS()) + { + ErrAppendId(MessageID.GlobalNamespace); + } + else + { + ErrAppendParentSym(sym, null); + ErrAppendName(sym.name); + } + break; + + case SYMKIND.SK_FieldSymbol: + ErrAppendParentSym(sym, pctx); + ErrAppendName(sym.name); + break; + + case SYMKIND.SK_TypeParameterSymbol: + if (null == sym.name) + { + // It's a standard type variable. + if (sym.AsTypeParameterSymbol().IsMethodTypeParameter()) + ErrAppendChar('!'); + ErrAppendChar('!'); + ErrAppendPrintf("{0}", sym.AsTypeParameterSymbol().GetIndexInTotalParameters()); + } + else + ErrAppendName(sym.name); + break; + + case SYMKIND.SK_LocalVariableSymbol: + case SYMKIND.SK_LabelSymbol: + case SYMKIND.SK_TransparentIdentifierMemberSymbol: + // Generate symbol name. + ErrAppendName(sym.name); + break; + + case SYMKIND.SK_Scope: + case SYMKIND.SK_LambdaScope: + default: + // Shouldn't happen. + Debug.Assert(false, "Bad symbol kind"); + break; + } + } + + public void ErrAppendType(CType pType, SubstContext pCtx) + { + ErrAppendType(pType, pCtx, true); + } + + public void ErrAppendType(CType pType, SubstContext pctx, bool fArgs) + { + if (pctx != null) + { + if (!pctx.FNop()) + { + pType = GetTypeManager().SubstType(pType, pctx); + } + // We shouldn't use the SubstContext again so set it to NULL. + pctx = null; + } + + switch (pType.GetTypeKind()) + { + case TypeKind.TK_AggregateType: + { + AggregateType pAggType = pType.AsAggregateType(); + + // Check for a predefined class with a special "nice" name for + // error reported. + string text = PredefinedTypes.GetNiceName(pAggType.getAggregate()); + if (text != null) + { + // Found a nice name. + ErrAppendString(text); + } + else if (pAggType.getAggregate().IsAnonymousType()) + { + ErrAppendPrintf("AnonymousType#{0}", GetTypeID(pAggType)); + break; + } + else + { + if (pAggType.outerType != null) + { + ErrAppendType(pAggType.outerType, pctx); + ErrAppendChar('.'); + } + else + { + // In a namespace. + ErrAppendParentSym(pAggType.getAggregate(), pctx); + } + ErrAppendName(pAggType.getAggregate().name); + } + ErrAppendTypeParameters(pAggType.GetTypeArgsThis(), pctx, true); + break; + } + + case TypeKind.TK_TypeParameterType: + if (null == pType.GetName()) + { + // It's a standard type variable. + if (pType.AsTypeParameterType().IsMethodTypeParameter()) + { + ErrAppendChar('!'); + } + ErrAppendChar('!'); + ErrAppendPrintf("{0}", pType.AsTypeParameterType().GetIndexInTotalParameters()); + } + else + { + ErrAppendName(pType.GetName()); + } + break; + + case TypeKind.TK_ErrorType: + if (pType.AsErrorType().HasParent()) + { + Debug.Assert(pType.AsErrorType().nameText != null && pType.AsErrorType().typeArgs != null); + ErrAppendParentType(pType, pctx); + ErrAppendName(pType.AsErrorType().nameText); + ErrAppendTypeParameters(pType.AsErrorType().typeArgs, pctx, true); + } + else + { + // Load the string "". + Debug.Assert(null == pType.AsErrorType().typeArgs); + ErrAppendId(MessageID.ERRORSYM); + } + break; + + case TypeKind.TK_NullType: + // Load the string "". + ErrAppendId(MessageID.NULL); + break; + + case TypeKind.TK_OpenTypePlaceholderType: + // Leave blank. + break; + + case TypeKind.TK_BoundLambdaType: + ErrAppendId(MessageID.AnonMethod); + break; + + case TypeKind.TK_UnboundLambdaType: + ErrAppendId(MessageID.Lambda); + break; + + case TypeKind.TK_MethodGroupType: + ErrAppendId(MessageID.MethodGroup); + break; + + case TypeKind.TK_ArgumentListType: + ErrAppendString(TokenFacts.GetText(TokenKind.ArgList)); + break; + + case TypeKind.TK_ArrayType: + { + CType elementType = pType.AsArrayType().GetBaseElementType(); + int rank; + + if (null == elementType) + { + Debug.Assert(false, "No element type"); + break; + } + + ErrAppendType(elementType, pctx); + + for (elementType = pType; + elementType != null && elementType.IsArrayType(); + elementType = elementType.AsArrayType().GetElementType()) + { + rank = elementType.AsArrayType().rank; + + // Add [] with (rank-1) commas inside + ErrAppendChar('['); + +#if ! CSEE + // known rank. + if (rank > 1) + { + ErrAppendChar('*'); + } +#endif + + for (int i = rank; i > 1; --i) + { + ErrAppendChar(','); +#if ! CSEE + + ErrAppendChar('*'); +#endif + + } + + ErrAppendChar(']'); + } + break; + } + + case TypeKind.TK_VoidType: + ErrAppendName(GetNameManager().Lookup(TokenFacts.GetText(TokenKind.Void))); + break; + + case TypeKind.TK_ParameterModifierType: + // add ref or out + ErrAppendString(pType.AsParameterModifierType().isOut ? "out " : "ref "); + + // add base type name + ErrAppendType(pType.AsParameterModifierType().GetParameterType(), pctx); + break; + + case TypeKind.TK_PointerType: + // Generate the base type. + ErrAppendType(pType.AsPointerType().GetReferentType(), pctx); + { + // add the trailing * + ErrAppendChar('*'); + } + break; + + case TypeKind.TK_NullableType: + ErrAppendType(pType.AsNullableType().GetUnderlyingType(), pctx); + ErrAppendChar('?'); + break; + + case TypeKind.TK_NaturalIntegerType: + default: + // Shouldn't happen. + Debug.Assert(false, "Bad type kind"); + break; + } + } + + // Returns true if the argument could be converted to a string. + public bool ErrArgToString(out string psz, ErrArg parg, out bool fUserStrings) + { + fUserStrings = false; + psz = null; + bool result = true; + + switch (parg.eak) + { + case ErrArgKind.Ids: + ErrId(out psz, parg.ids); + break; + case ErrArgKind.SymKind: + ErrSK(out psz, parg.sk); + break; + case ErrArgKind.Type: + BeginString(); + ErrAppendType(parg.pType, null); + EndString(out psz); + fUserStrings = true; + break; + case ErrArgKind.Sym: + BeginString(); + ErrAppendSym(parg.sym, null); + EndString(out psz); + fUserStrings = true; + break; + case ErrArgKind.Name: + if (parg.name == GetNameManager().GetPredefinedName(PredefinedName.PN_INDEXERINTERNAL)) + { + psz = "this"; + } + else + { + psz = parg.name.Text; + } + break; + + case ErrArgKind.Str: + psz = parg.psz; + break; + case ErrArgKind.PredefName: + BeginString(); + ErrAppendName(GetNameManager().GetPredefName(parg.pdn)); + EndString(out psz); + break; + case ErrArgKind.SymWithType: + { + SubstContext ctx = new SubstContext(parg.swtMemo.ats, null); + BeginString(); + ErrAppendSym(parg.swtMemo.sym, ctx, true); + EndString(out psz); + fUserStrings = true; + break; + } + + case ErrArgKind.MethWithInst: + { + SubstContext ctx = new SubstContext(parg.mpwiMemo.ats, parg.mpwiMemo.typeArgs); + BeginString(); + ErrAppendSym(parg.mpwiMemo.sym, ctx, true); + EndString(out psz); + fUserStrings = true; + break; + } + default: + result = false; + break; + } + + return result; + } + protected bool IsDisplayableName(Name name) + { + return name != GetNameManager().GetPredefName(PredefinedName.PN_MISSING); + } + protected void CheckDisplayableName(Name name) + { + if (!IsDisplayableName(name)) + { + fHadUndisplayableStringInError = true; + } + } + + protected NameManager GetNameManager() + { + return m_globalSymbols.GetNameManager(); + } + protected TypeManager GetTypeManager() + { + return m_globalSymbols.GetTypes(); + } + protected BSYMMGR getBSymmgr() + { + return m_globalSymbols.GetGlobalSymbols(); + } + protected int GetTypeID(CType type) + { + return 0; + } + public void ErrId(out string s, MessageID id) + { + s = ErrorFacts.GetMessage(id); + } + } +} diff --git a/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Hosting/Controller.cs b/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Hosting/Controller.cs new file mode 100644 index 000000000..87942166d --- /dev/null +++ b/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Hosting/Controller.cs @@ -0,0 +1,44 @@ +// ==++== +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// ==--== + +namespace Microsoft.CSharp.RuntimeBinder.Errors +{ + //////////////////////////////////////////////////////////////////////////////// + // + // This is the "controller" for compiler objects. The controller is the object + // that exposes/implements ICSCompiler for external consumption. Compiler + // options are configured through this object, and for an actual compilation, + // this object instanciates a LangCompiler, feeds it the appropriate information, + // tells it to compile, and then destroys it. + + internal abstract class CController + { + private CErrorFactory m_errorFactory; + + protected CController() + { + m_errorFactory = new CErrorFactory(); + } + + //////////////////////////////////////////////////////////////////////////////// + // + // This function places a fully-constructed CError object into an error container + // and sends it to the compiler host (this would be the place to batch these guys + // up if we decide to. + // + // Note that if the error can't be put into a container (if, for example, we + // can't create a container) the error is destroyed and the host is notified via + // exception. + + public abstract void SubmitError(CError pError); + + public CErrorFactory GetErrorFactory() + { + return m_errorFactory; + } + } + +} \ No newline at end of file diff --git a/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/BinOpArgInfo.cs b/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/BinOpArgInfo.cs new file mode 100644 index 000000000..152e17144 --- /dev/null +++ b/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/BinOpArgInfo.cs @@ -0,0 +1,86 @@ +// ==++== +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// ==--== + +using System.Diagnostics; +using Microsoft.CSharp.RuntimeBinder.Syntax; + +namespace Microsoft.CSharp.RuntimeBinder.Semantics +{ + internal partial class ExpressionBinder + { + protected class BinOpArgInfo + { + public BinOpArgInfo(EXPR op1, EXPR op2) + { + Debug.Assert(op1 != null); + Debug.Assert(op2 != null); + arg1 = op1; + arg2 = op2; + type1 = arg1.type; + type2 = arg2.type; + typeRaw1 = type1.StripNubs(); + typeRaw2 = type2.StripNubs(); + pt1 = type1.isPredefined() ? type1.getPredefType() : PredefinedType.PT_COUNT; + pt2 = type2.isPredefined() ? type2.getPredefType() : PredefinedType.PT_COUNT; + ptRaw1 = typeRaw1.isPredefined() ? typeRaw1.getPredefType() : PredefinedType.PT_COUNT; + ptRaw2 = typeRaw2.isPredefined() ? typeRaw2.getPredefType() : PredefinedType.PT_COUNT; + } + + public EXPR arg1; + public EXPR arg2; + public PredefinedType pt1; + public PredefinedType pt2; + public PredefinedType ptRaw1; + public PredefinedType ptRaw2; + public CType type1; + public CType type2; + public CType typeRaw1; + public CType typeRaw2; + public BinOpKind binopKind; + public BinOpMask mask; + + public bool ValidForDelegate() + { + return (mask & BinOpMask.Delegate) != 0; + } + + public bool ValidForEnumAndUnderlyingType() + { + return (mask & BinOpMask.EnumUnder) != 0; + } + + public bool ValidForUnderlyingTypeAndEnum() + { + return (mask & BinOpMask.UnderEnum) != 0; + } + + public bool ValidForEnum() + { + return (mask & BinOpMask.Enum) != 0; + } + + public bool ValidForPointer() + { + return (mask & BinOpMask.Ptr) != 0; + } + + public bool ValidForVoidPointer() + { + return (mask & BinOpMask.VoidPtr) != 0; + } + + public bool ValidForPointerAndNumber() + { + return (mask & BinOpMask.PtrNum) != 0; + } + + public bool ValidForNumberAndPointer() + { + return (mask & BinOpMask.NumPtr) != 0; + } + } + } +} diff --git a/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/BinOpKind.cs b/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/BinOpKind.cs new file mode 100644 index 000000000..ef604d07a --- /dev/null +++ b/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/BinOpKind.cs @@ -0,0 +1,48 @@ +// ==++== +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// ==--== + +namespace Microsoft.CSharp.RuntimeBinder.Semantics +{ + internal enum BinOpKind + { + Add, + Sub, + Mul, + Shift, + Equal, + Compare, + Bitwise, + BitXor, + Logical, + Lim + } + internal enum BinOpMask + { + None = 0, + Add = 1 << BinOpKind.Add, + Sub = 1 << BinOpKind.Sub, + Mul = 1 << BinOpKind.Mul, + Shift = 1 << BinOpKind.Shift, + Equal = 1 << BinOpKind.Equal, + Compare = 1 << BinOpKind.Compare, + Bitwise = 1 << BinOpKind.Bitwise, + BitXor = 1 << BinOpKind.BitXor, + Logical = 1 << BinOpKind.Logical, + // The different combinations needed in operators.cs + Integer = Add | Sub | Mul | Equal | Compare | Bitwise | BitXor, + Real = Add | Sub | Mul | Equal | Compare, + BoolNorm = Equal | BitXor, + // These are special ones. + Delegate = Add | Sub | Equal, + Enum = Sub | Equal | Compare | Bitwise | BitXor, + EnumUnder = Add | Sub, + UnderEnum = Add, + Ptr = Sub, + PtrNum = Add | Sub, + NumPtr = Add, + VoidPtr = Equal | Compare, + } +} \ No newline at end of file diff --git a/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/BinOpSig.cs b/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/BinOpSig.cs new file mode 100644 index 000000000..026e68a94 --- /dev/null +++ b/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/BinOpSig.cs @@ -0,0 +1,136 @@ +// ==++== +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// ==--== + +using System.Diagnostics; +using Microsoft.CSharp.RuntimeBinder.Syntax; + +namespace Microsoft.CSharp.RuntimeBinder.Semantics +{ + internal partial class ExpressionBinder + { + protected class BinOpSig + { + public BinOpSig() + { + } + + public BinOpSig(PredefinedType pt1, PredefinedType pt2, + BinOpMask mask, int cbosSkip, PfnBindBinOp pfn, OpSigFlags grfos, BinOpFuncKind fnkind) + { + this.pt1 = pt1; + this.pt2 = pt2; + this.mask = mask; + this.cbosSkip = cbosSkip; + this.pfn = pfn; + this.grfos = grfos; + this.fnkind = fnkind; + } + public PredefinedType pt1; + public PredefinedType pt2; + public BinOpMask mask; + public int cbosSkip; + public PfnBindBinOp pfn; + public OpSigFlags grfos; + public BinOpFuncKind fnkind; + + public bool ConvertOperandsBeforeBinding() + { + return (grfos & OpSigFlags.Convert) != 0; + } + + public bool CanLift() + { + return (grfos & OpSigFlags.CanLift) != 0; + } + + public bool AutoLift() + { + return (grfos & OpSigFlags.AutoLift) != 0; + } + } + + protected class BinOpFullSig : BinOpSig + { + private LiftFlags grflt; + private CType type1; + private CType type2; + + public BinOpFullSig(CType type1, CType type2, PfnBindBinOp pfn, OpSigFlags grfos, + LiftFlags grflt, BinOpFuncKind fnkind) + { + this.pt1 = PredefinedType.PT_UNDEFINEDINDEX; + this.pt2 = PredefinedType.PT_UNDEFINEDINDEX; + this.mask = BinOpMask.None; + this.cbosSkip = 0; + this.pfn = pfn; + this.grfos = grfos; + this.type1 = type1; + this.type2 = type2; + this.grflt = grflt; + this.fnkind = fnkind; + } + + /*************************************************************************************************** + Set the values of the BinOpFullSig from the given BinOpSig. The ExpressionBinder is needed to get + the predefined types. Returns true iff the predef types are found. + ***************************************************************************************************/ + public BinOpFullSig(ExpressionBinder fnc, BinOpSig bos) + { + this.pt1 = bos.pt1; + this.pt2 = bos.pt2; + this.mask = bos.mask; + this.cbosSkip = bos.cbosSkip; + this.pfn = bos.pfn; + this.grfos = bos.grfos; + this.fnkind = bos.fnkind; + + this.type1 = pt1 != PredefinedType.PT_UNDEFINEDINDEX ? fnc.GetOptPDT(pt1) : null; + this.type2 = pt2 != PredefinedType.PT_UNDEFINEDINDEX ? fnc.GetOptPDT(pt2) : null; + this.grflt = LiftFlags.None; + } + + public bool FPreDef() + { + return pt1 != PredefinedType.PT_UNDEFINEDINDEX; + } + + public bool isLifted() + { + if (grflt == LiftFlags.None) + { + return false; + } + + // We can't both convert and lift. + Debug.Assert(((grflt & LiftFlags.Lift1) == 0) || ((grflt & LiftFlags.Convert1) == 0)); + Debug.Assert(((grflt & LiftFlags.Lift2) == 0) || ((grflt & LiftFlags.Convert2) == 0)); + + return true; + } + + public bool ConvertFirst() + { + return (grflt & LiftFlags.Convert1) != 0; + } + + public bool ConvertSecond() + { + return (grflt & LiftFlags.Convert2) != 0; + } + + public CType Type1() + { + return type1; + } + + public CType Type2() + { + return type2; + } + } + } +} + diff --git a/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/Binding/Better.cs b/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/Binding/Better.cs new file mode 100644 index 000000000..03bd13d07 --- /dev/null +++ b/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/Binding/Better.cs @@ -0,0 +1,538 @@ +// ==++== +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// ==--== + +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; +using Microsoft.CSharp.RuntimeBinder.Syntax; + +namespace Microsoft.CSharp.RuntimeBinder.Semantics +{ + internal partial class ExpressionBinder + { + //////////////////////////////////////////////////////////////////////////////// + // This table is used to implement the last set of 'better' conversion rules + // when there are no implicit conversions between T1(down) and T2 (across) + // Use all the simple types plus 1 more for Object + // See CLR section 7.4.1.3 + + static private readonly byte[,] betterConversionTable = + { + // BYTE SHORT INT LONG FLOAT DOUBLE DECIMAL CHAR BOOL SBYTE USHORT UINT ULONG IPTR UIPTR OBJECT + /* BYTE*/ {0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0}, + /* SHORT*/ {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0}, + /* INT*/ {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0}, + /* LONG*/ {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0}, + /* FLOAT*/ {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + /* DOUBLE*/ {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + /* DECIMAL*/{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + /* CHAR*/ {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + /* BOOL*/ {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + /* SBYTE*/ {1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0}, + /* USHORT*/ {0, 2, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0}, + /* UINT*/ {0, 2, 2, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0}, + /* ULONG*/ {0, 2, 2, 2, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0}, + /* IPTR*/ {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + /* UIPTR*/ {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + /* OBJECT*/ {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} + }; + + protected BetterType WhichMethodIsBetterTieBreaker( + CandidateFunctionMember node1, + CandidateFunctionMember node2, + CType pTypeThrough, + ArgInfos args) + { + MethPropWithInst mpwi1 = node1.mpwi; + MethPropWithInst mpwi2 = node2.mpwi; + + // Same signatures. If they have different lifting numbers, the smaller number wins. + // Otherwise, if one is generic and the other isn't then the non-generic wins. + // Otherwise, if one is expanded and the other isn't then the non-expanded wins. + // Otherwise, if one has fewer modopts than the other then it wins. + if (node1.ctypeLift != node2.ctypeLift) + { + return node1.ctypeLift < node2.ctypeLift ? BetterType.Left : BetterType.Right; + } + + // Non-generic wins. + if (mpwi1.TypeArgs.size != 0) + { + if (mpwi2.TypeArgs.size == 0) + { + return BetterType.Right; + } + } + else if (mpwi2.TypeArgs.size != 0) + { + return BetterType.Left; + } + + // Non-expanded wins + if (node1.fExpanded) + { + if (!node2.fExpanded) + { + return BetterType.Right; + } + } + else if (node2.fExpanded) + { + return BetterType.Left; + } + + // See if one's parameter types (un-instantiated) are more specific. + BetterType nT = GetGlobalSymbols().CompareTypes( + RearrangeNamedArguments(mpwi1.MethProp().Params, mpwi1, pTypeThrough, args), + RearrangeNamedArguments(mpwi2.MethProp().Params, mpwi2, pTypeThrough, args)); + if (nT == BetterType.Left || nT == BetterType.Right) + { + return nT; + } + + // Fewer modopts wins. + if (mpwi1.MethProp().modOptCount != mpwi2.MethProp().modOptCount) + { + return mpwi1.MethProp().modOptCount < mpwi2.MethProp().modOptCount ? BetterType.Left : BetterType.Right; + } + + // Bona-fide tie. + return BetterType.Neither; + } + + //////////////////////////////////////////////////////////////////////////////// + + // Find the index of a name on a list. + // There is no failure case; we require that the name actually + // be on the list + + private static int FindName(List names, Name name) + { + int index = names.IndexOf(name); + Debug.Assert(index != -1); + return index; + } + + //////////////////////////////////////////////////////////////////////////////// + // We need to rearange the method parameters so that the type of any specified named argument + // appears in the same place as the named argument. Consider the example below: + // Foo(int x = 4, string y = "", long l = 4) + // Foo(string y = "", string x="", long l = 5) + // and the call site: + // Foo(y:"a") + // After rearanging the parameter types we will have: + // (string, int, long) and (string, string, long) + // By rearanging the arguments as such we make sure that any specified named arguments appear in the same position for both + // methods and we also maintain the relative order of the other parameters (the type long appears after int in the above example) + + private TypeArray RearrangeNamedArguments(TypeArray pta, MethPropWithInst mpwi, + CType pTypeThrough, ArgInfos args) + { + if (!args.fHasExprs) + { + return pta; + } + + #if DEBUG + // We never have a named argument that is in a position in the argument + // list past the end of what would be the formal parameter list. + for (int i = pta.size; i < args.carg ; i++) + { + Debug.Assert(!args.prgexpr[i].isNamedArgumentSpecification()); + } + #endif + + CType type = pTypeThrough != null ? pTypeThrough : mpwi.GetType(); + CType[] typeList = new CType[pta.size]; + MethodOrPropertySymbol methProp = GroupToArgsBinder.FindMostDerivedMethod(GetSymbolLoader(), mpwi.MethProp(), type); + + // We initialize the new type array with the parameters for the method. + for (int iParam = 0; iParam < pta.size; iParam++) + { + typeList[iParam] = pta.Item(iParam); + } + + // We then go over the specified arguments and put the type for any named argument in the right position in the array. + for (int iParam = 0; iParam < args.carg ; iParam++) + { + EXPR arg = args.prgexpr[iParam]; + if (arg.isNamedArgumentSpecification()) + { + // We find the index of the type of the argument in the method parameter list and store that in a temp + int index = FindName(methProp.ParameterNames, arg.asNamedArgumentSpecification().Name); + CType tempType = pta.Item(index); + + // Starting from the current position in the type list up until the location of the type of the optional argument + // We shift types by one: + // before: (int, string, long) + // after: (string, int, long) + // We only touch the types between the current position and the position of the type we need to move + for (int iShift = iParam; iShift < index; iShift++) + { + typeList[iShift + 1] = typeList[iShift]; + } + + typeList[iParam] = tempType; + } + } + + return GetSymbolLoader().getBSymmgr().AllocParams(pta.size, typeList); + } + + //////////////////////////////////////////////////////////////////////////////// + // Determine which method is better for the purposes of overload resolution. + // Better means: as least as good in all params, and better in at least one param. + // Better w/r to a param means is an ordering, from best down: + // 1) same type as argument + // 2) implicit conversion from argument to formal type + // Because of user defined conversion opers this relation is not transitive. + // + // If there is a tie because of identical signatures, the tie may be broken by the + // following rules: + // 1) If one is generic and the other isn't, the non-generic wins. + // 2) Otherwise if one is expanded (params) and the other isn't, the non-expanded wins. + // 3) Otherwise if one has more specific parameter types (at the declaration) it wins: + // This occurs if at least on parameter type is more specific and no parameter type is + // less specific. + //* A type parameter is less specific than a non-type parameter. + //* A constructed type is more specific than another constructed type if at least + // one type argument is more specific and no type argument is less specific than + // the corresponding type args in the other. + // 4) Otherwise if one has more modopts than the other does, the smaller number of modopts wins. + // + // Returns Left if m1 is better, Right if m2 is better, or Neither/Same + + // REFACTOR: Much of this logic is duplicated in WhichTypeIsBetter in conversions.cpp. + // REFACTOR: Refactor this so that it uses the existing logic rather than replicating it. + + protected BetterType WhichMethodIsBetter( + CandidateFunctionMember node1, + CandidateFunctionMember node2, + CType pTypeThrough, + ArgInfos args) + { + MethPropWithInst mpwi1 = node1.mpwi; + MethPropWithInst mpwi2 = node2.mpwi; + + // Substitutions should have already been done on these! + TypeArray pta1 = RearrangeNamedArguments(node1.@params, mpwi1, pTypeThrough, args); + TypeArray pta2 = RearrangeNamedArguments(node2.@params, mpwi2, pTypeThrough, args); + + // If the parameter types for both candidate methods are identical, + // use the tie breaking rules. + + if (pta1 == pta2) + { + return WhichMethodIsBetterTieBreaker(node1, node2, pTypeThrough, args); + } + + // Otherwise, do a parameter-by-parameter comparison: + // + // Given an argument list A with a set of argument expressions {E1, ... En} and + // two applicable function members Mp and Mq with parameter types {P1,... Pn} and + // {Q1, ... Qn}, Mp is defined to be a better function member than Mq if: + //* for each argument the implicit conversion from Ex to Qx is not better than + // the implicit conversion from Ex to Px. + //* for at least one argument, the conversion from Ex to Px is better than the + // conversion from Ex to Qx. + + BetterType betterMethod = BetterType.Neither; + CType type1 = pTypeThrough != null ? pTypeThrough : mpwi1.GetType(); + CType type2 = pTypeThrough != null ? pTypeThrough : mpwi2.GetType(); + MethodOrPropertySymbol methProp1 = ExpressionBinder.GroupToArgsBinder.FindMostDerivedMethod(GetSymbolLoader(), mpwi1.MethProp(), type1); + MethodOrPropertySymbol methProp2 = ExpressionBinder.GroupToArgsBinder.FindMostDerivedMethod(GetSymbolLoader(), mpwi2.MethProp(), type2); + List names1 = methProp1.ParameterNames; + List names2 = methProp2.ParameterNames; + + for (int i = 0; i < args.carg; i++) + { + EXPR arg = args.fHasExprs ? args.prgexpr[i] : null; + CType argType = args.types.Item(i); + CType p1 = pta1.Item(i); + CType p2 = pta2.Item(i); + + // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + // RUNTIME BINDER ONLY CHANGE + // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + // + // We need to consider conversions from the actual runtime type + // since we could have private interfaces that we are converting + + if (arg.RuntimeObjectActualType != null) + { + argType = arg.RuntimeObjectActualType; + } + + // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + // END RUNTIME BINDER ONLY CHANGE + // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + + BetterType betterConversion = WhichConversionIsBetter(arg, argType, p1, p2); + + if (betterMethod == BetterType.Right && betterConversion == BetterType.Left) + { + betterMethod = BetterType.Neither; + break; + } + else if (betterMethod == BetterType.Left && betterConversion == BetterType.Right) + { + betterMethod = BetterType.Neither; + break; + } + else if (betterMethod == BetterType.Neither) + { + if (betterConversion == BetterType.Right || betterConversion == BetterType.Left) + { + betterMethod = betterConversion; + } + } + } + + // We may have different sizes if we had optional parameters. If thats the case, + // the one with fewer parameters wins (ie less optional parameters) unless it is + // expanded. If so, the one with more parameters wins (ie option beats expanded). + if (pta1.size != pta2.size && betterMethod == BetterType.Neither) + { + if (node1.fExpanded && !node2.fExpanded) + { + return BetterType.Right; + } + else if (node2.fExpanded && !node1.fExpanded) + { + return BetterType.Left; + } + + // Here, if both methods needed to use optionals to fill in the signatures, + // then we are ambiguous. Otherwise, take the one that didn't need any + // optionals. + + if (pta1.size == args.carg) + { + return BetterType.Left; + } + else if (pta2.size == args.carg) + { + return BetterType.Right; + } + return BetterType.Neither; + } + + return betterMethod; + } + + protected BetterType WhichConversionIsBetter(EXPR arg, CType argType, + CType p1, CType p2) + { + Debug.Assert(argType != null); + Debug.Assert(p1 != null); + Debug.Assert(p2 != null); + + // 7.4.2.3 Better Conversion From Expression + // + // Given an implicit conversion C1 that converts from an expression E to a type T1 + // and an implicit conversion C2 that converts from an expression E to a type T2, the + // better conversion of the two conversions is determined as follows: + //* if T1 and T2 are the same type, neither conversion is better. + //* If E has a type S and the conversion from S to T1 is better than the conversion from + // S to T2 then C1 is the better conversion. + //* If E has a type S and the conversion from S to T2 is better than the conversion from + // S to T1 then C2 is the better conversion. + //* If E is a lambda expression or anonymous method for which an inferred return type X + // exists and T1 is a delegate type and T2 is a delegate type and T1 and T2 have identical + // parameter lists: + // * If T1 is a delegate of return type Y1 and T2 is a delegate of return type Y2 and the + // conversion from X to Y1 is better than the conversion from X to Y2, then C1 is the + // better return type. + // * If T1 is a delegate of return type Y1 and T2 is a delegate of return type Y2 and the + // conversion from X to Y2 is better than the conversion from X to Y1, then C2 is the + // better return type. + + if (p1 == p2) + { + return BetterType.Same; + } + return WhichConversionIsBetter(argType, p1, p2); + } + + public BetterType WhichConversionIsBetter(CType argType, + CType p1, CType p2) + { + + // 7.4.2.4 Better conversion from type + // + // Given a conversion C1 that converts from a type S to a type T1 and a conversion C2 + // that converts from a type S to a type T2, the better conversion of the two conversions + // is determined as follows: + //* If T1 and T2 are the same type, neither conversion is better + //* If S is T1, C1 is the better conversion. + //* If S is T2, C2 is the better conversion. + //* If an implicit conversion from T1 to T2 exists and no implicit conversion from T2 to + // T1 exists, C1 is the better conversion. + //* If an implicit conversion from T2 to T1 exists and no implicit conversion from T1 to + // T2 exists, C2 is the better conversion. + // + // [Otherwise, see table above for better integral type conversions.] + + if (p1 == p2) + { + return BetterType.Same; + } + + if (argType == p1) + { + return BetterType.Left; + } + + if (argType == p2) + { + return BetterType.Right; + } + + bool a2b = canConvert(p1, p2); + bool b2a = canConvert(p2, p1); + + if (a2b && !b2a) + { + return BetterType.Left; + } + if (b2a && !a2b) + { + return BetterType.Right; + } + + Debug.Assert(b2a == a2b); + + if (p1.isPredefined() && p2.isPredefined() && + p1.getPredefType() <= PredefinedType.PT_OBJECT && p2.getPredefType() <= PredefinedType.PT_OBJECT) + { + int c = betterConversionTable[(int)p1.getPredefType(), (int)p2.getPredefType()]; + if (c == 1) + { + return BetterType.Left; + } + else if (c == 2) + { + return BetterType.Right; + } + } + + return BetterType.Neither; + } + + //////////////////////////////////////////////////////////////////////////////// + // Determine best method for overload resolution. Returns null if no best + // method, in which case two tying methods are returned for error reporting. + + protected CandidateFunctionMember FindBestMethod( + List list, + CType pTypeThrough, + ArgInfos args, + out CandidateFunctionMember methAmbig1, + out CandidateFunctionMember methAmbig2) + { + Debug.Assert(list.Any()); + Debug.Assert(list.First().mpwi != null); + Debug.Assert(list.Count > 0); + + // select the best method: + /* + Effectively, we pick the best item from a set using a non-transitive ranking function + So, pick the first item (candidate) and compare against next (contender), if there is + no next, goto phase 2 + If first is better, move to next contender, if none proceed to phase 2 + If second is better, make the contender the candidate and make the item following + contender into the new contender, if there is none, goto phase 2 + If neither, make contender+1 into candidate and contender+2 into contender, if possible, + otherwise, if contender was last, return null, otherwise if new condidate is last, + goto phase 2 + Phase 2: compare all items before candidate to candidate + If candidate always better, return it, otherwise return null + + */ + // Record two method that are ambiguous for error reporting. + CandidateFunctionMember ambig1 = null; + CandidateFunctionMember ambig2 = null; + bool ambiguous = false; + CandidateFunctionMember candidate = list[0]; + for (int i = 1; i < list.Count; i++) + { + CandidateFunctionMember contender = list[i]; + Debug.Assert(candidate != contender); + + BetterType result = WhichMethodIsBetter(candidate, contender, pTypeThrough, args); + if (result == BetterType.Left) + { + ambiguous = false; + continue; // (meaning m1 is better...) + } + else if (result == BetterType.Right) + { + ambiguous = false; + candidate = contender; + } + else + { + // in case of tie we don't want to bother with the contender who tied... + ambig1 = candidate; + ambig2 = contender; + + i++; + if (i < list.Count) + { + contender = list[i]; + candidate = contender; + } + else + { + ambiguous = true; + } + } + } + if (ambiguous) + goto AMBIG; + + // Now, compare the candidate with items previous to it... + foreach (CandidateFunctionMember contender in list) + { + if (contender == candidate) + { + // We hit our winner, so its good enough... + methAmbig1 = null; + methAmbig2 = null; + return candidate; + } + BetterType result = WhichMethodIsBetter(contender, candidate, pTypeThrough, args); + if (result == BetterType.Right) + { // meaning m2 is better + continue; + } + else if (result == BetterType.Same || result == BetterType.Neither) + { + ambig1 = candidate; + ambig2 = contender; + } + break; + } + + AMBIG: + // an ambig call. Return two of the ambiguous set. + if (ambig1 != null && ambig2 != null) + { + methAmbig1 = ambig1; + methAmbig2 = ambig2; + } + else + { + // For some reason, we have an ambiguity but never had a tie. + // This can easily happen in a circular graph of candidate methods. + methAmbig1 = list.First(); + methAmbig2 = list.Skip(1).First(); + } + + return null; + } + } +} diff --git a/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/Binding/ErrorReporting.cs b/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/Binding/ErrorReporting.cs new file mode 100644 index 000000000..12d47b9e0 --- /dev/null +++ b/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/Binding/ErrorReporting.cs @@ -0,0 +1,154 @@ +// ==++== +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// ==--== + +using System.Diagnostics; +using Microsoft.CSharp.RuntimeBinder.Errors; +using Microsoft.CSharp.RuntimeBinder.Syntax; + +namespace Microsoft.CSharp.RuntimeBinder.Semantics +{ + internal partial class ExpressionBinder + { + private static readonly ErrorCode[] ReadOnlyLocalErrors = + { + ErrorCode.ERR_RefReadonlyLocal, + ErrorCode.ERR_AssgReadonlyLocal, + }; + + protected void ReportLocalError(LocalVariableSymbol local, CheckLvalueKind kind, bool isNested) + { + Debug.Assert(local != null); + + int index = kind == CheckLvalueKind.OutParameter ? 0 : 1; + + Debug.Assert(index != 2 && index != 3); + // There is no way that we can have no cause AND a read-only local nested in a struct with a + // writable field. What would make the local read-only if not one of the causes above? (Const + // locals may not be structs, so we would already have errored out in that scenario.) + + ErrorCode err = ReadOnlyLocalErrors[index]; + + ErrorContext.Error(err, local.name); + } + + private static readonly ErrorCode[] ReadOnlyErrors = + { + ErrorCode.ERR_RefReadonly, + ErrorCode.ERR_AssgReadonly, + ErrorCode.ERR_RefReadonlyStatic, + ErrorCode.ERR_AssgReadonlyStatic, + ErrorCode.ERR_RefReadonly2, + ErrorCode.ERR_AssgReadonly2, + ErrorCode.ERR_RefReadonlyStatic2, + ErrorCode.ERR_AssgReadonlyStatic2 + }; + + protected void ReportReadOnlyError(EXPRFIELD field, CheckLvalueKind kind, bool isNested) + { + Debug.Assert(field != null); + + bool isStatic = field.fwt.Field().isStatic; + + int index = (isNested ? 4 : 0) + (isStatic ? 2 : 0) + (kind == CheckLvalueKind.OutParameter ? 0 : 1); + ErrorCode err = ReadOnlyErrors[index]; + + if (isNested) + { + ErrorContext.Error(err, field.fwt); + } + else + { + ErrorContext.Error(err); + } + } + + // Return true if we actually report a failure. + protected bool TryReportLvalueFailure(EXPR expr, CheckLvalueKind kind) + { + Debug.Assert(expr != null); + + // We have a lvalue failure. Was the reason because this field + // was marked readonly? Give special messages for this case. + + bool isNested = false; // Did we recurse on a field or property to give a better error? + + EXPR walk = expr; + while (true) + { + Debug.Assert(walk != null); + + if (walk.isANYLOCAL_OK()) + { + ReportLocalError(walk.asANYLOCAL().local, kind, isNested); + return true; + } + + EXPR pObject = null; + + if (walk.isPROP()) + { + // We've already reported read-only-property errors. + Debug.Assert(walk.asPROP().mwtSet != null); + pObject = walk.asPROP().GetMemberGroup().GetOptionalObject(); + } + else if (walk.isFIELD()) + { + EXPRFIELD field = walk.asFIELD(); + if (field.fwt.Field().isReadOnly) + { + ReportReadOnlyError(field, kind, isNested); + return true; + } + if (!field.fwt.Field().isStatic) + { + pObject = field.GetOptionalObject(); + } + } + + if (pObject != null && pObject.type.isStructOrEnum()) + { + if (pObject.isCALL() || pObject.isPROP()) + { + // assigning to RHS of method or property getter returning a value-type on the stack or + // passing RHS of method or property getter returning a value-type on the stack, as ref or out + ErrorContext.Error(ErrorCode.ERR_ReturnNotLValue, pObject.GetSymWithType()); + return true; + } + if (pObject.isCAST()) + { + // An unboxing conversion. + // + // In the static compiler, we give the following error here: + // ErrorContext.Error(pObject.GetTree(), ErrorCode.ERR_UnboxNotLValue); + // + // But in the runtime, we allow this - mark that we're doing an + // unbox here, so that we gen the correct expression tree for it. + pObject.flags |= EXPRFLAG.EXF_UNBOXRUNTIME; + return false; + } + } + + // everything else + if (pObject != null && !pObject.isLvalue() && (walk.isFIELD() || (!isNested && walk.isPROP()))) + { + Debug.Assert(pObject.type.isStructOrEnum()); + walk = pObject; + } + else + { + ErrorContext.Error(GetStandardLvalueError(kind)); + return true; + } + isNested = true; + } + } + + public static void ReportTypeArgsNotAllowedError(SymbolLoader symbolLoader, int arity, ErrArgRef argName, ErrArgRef argKind) + { + symbolLoader.ErrorContext.ErrorRef(ErrorCode.ERR_TypeArgsNotAllowed, argName, argKind); + } + } +} diff --git a/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/BindingContextBase.cs b/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/BindingContextBase.cs new file mode 100644 index 000000000..481938607 --- /dev/null +++ b/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/BindingContextBase.cs @@ -0,0 +1,334 @@ +// ==++== +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// ==--== + +using System.Collections.Generic; +using System.Diagnostics; +using Microsoft.CSharp.RuntimeBinder.Errors; + +namespace Microsoft.CSharp.RuntimeBinder.Semantics +{ + // ---------------------------------------------------------------------------- + // This is the interface for the BindingContext, which is + // consumed by the StatementBinder. + // ---------------------------------------------------------------------------- + + internal class OutputContext + { + public LocalVariableSymbol m_pThisPointer; + public MethodSymbol m_pCurrentMethodSymbol; + public bool m_bUnsafeErrorGiven; + }; + + internal enum UNSAFESTATES + { + UNSAFESTATES_Unsafe, + UNSAFESTATES_Safe, + UNSAFESTATES_Unknown, + }; + + internal class BindingContext + { + static public BindingContext CreateInstance( + CSemanticChecker pSemanticChecker, + ExprFactory exprFactory, + OutputContext outputContext, + NameGenerator nameGenerator, + bool bflushLocalVariableTypesForEachStatement, + bool bAllowUnsafeBlocks, + bool bIsOptimizingSwitchAndArrayInit, + bool bShowReachability, + bool bWrapNonExceptionThrows, + bool bInRefactoring, + KAID aidLookupContext + ) + { + return new BindingContext( + pSemanticChecker, + exprFactory, + outputContext, + nameGenerator, + bflushLocalVariableTypesForEachStatement, + bAllowUnsafeBlocks, + bIsOptimizingSwitchAndArrayInit, + bShowReachability, + bWrapNonExceptionThrows, + bInRefactoring, + aidLookupContext); + } + protected BindingContext( + CSemanticChecker pSemanticChecker, + ExprFactory exprFactory, + OutputContext outputContext, + NameGenerator nameGenerator, + bool bflushLocalVariableTypesForEachStatement, + bool bAllowUnsafeBlocks, + bool bIsOptimizingSwitchAndArrayInit, + bool bShowReachability, + bool bWrapNonExceptionThrows, + bool bInRefactoring, + KAID aidLookupContext + ) + { + m_ExprFactory = exprFactory; + m_outputContext = outputContext; + m_pNameGenerator = nameGenerator; + m_pInputFile = null; + m_pParentDecl = null; + m_pContainingAgg = null; + m_pCurrentSwitchType = null; + m_pOriginalConstantField = null; + m_pCurrentFieldSymbol = null; + m_pImplicitlyTypedLocal = null; + m_pOuterScope = null; + m_pFinallyScope = null; + m_pTryScope = null; + m_pCatchScope = null; + m_pCurrentScope = null; + m_pSwitchScope = null; + m_pCurrentBlock = null; + m_UnsafeState = UNSAFESTATES.UNSAFESTATES_Unknown; + m_FinallyNestingCount = 0; + m_bInsideTryOfCatch = false; + m_bInFieldInitializer = false; + m_bInBaseConstructorCall = false; + m_bAllowUnsafeBlocks = bAllowUnsafeBlocks; + m_bIsOptimizingSwitchAndArrayInit = bIsOptimizingSwitchAndArrayInit; + m_bShowReachability = bShowReachability; + m_bWrapNonExceptionThrows = bWrapNonExceptionThrows; + m_bInRefactoring = bInRefactoring; + m_bInAttribute = false; + m_bRespectSemanticsAndReportErrors = true; + m_bflushLocalVariableTypesForEachStatement = bflushLocalVariableTypesForEachStatement; + m_ppamis = null; + m_pamiCurrent = null; + m_pInitType = null; + m_returnErrorSink = null; + + Debug.Assert(pSemanticChecker != null); + this.SemanticChecker = pSemanticChecker; + this.SymbolLoader = SemanticChecker.GetSymbolLoader(); + m_outputContext.m_pThisPointer = null; + m_outputContext.m_pCurrentMethodSymbol = null; + + m_aidExternAliasLookupContext = aidLookupContext; + CheckedNormal = false; + CheckedConstant = false; + } + protected BindingContext(BindingContext parent) + { + m_ExprFactory = parent.m_ExprFactory; + m_outputContext = parent.m_outputContext; + m_pNameGenerator = parent.m_pNameGenerator; + m_pInputFile = parent.m_pInputFile; + m_pParentDecl = parent.m_pParentDecl; + m_pContainingAgg = parent.m_pContainingAgg; + m_pCurrentSwitchType = parent.m_pCurrentSwitchType; + m_pOriginalConstantField = parent.m_pOriginalConstantField; + m_pCurrentFieldSymbol = parent.m_pCurrentFieldSymbol; + m_pImplicitlyTypedLocal = parent.m_pImplicitlyTypedLocal; + m_pOuterScope = parent.m_pOuterScope; + m_pFinallyScope = parent.m_pFinallyScope; + m_pTryScope = parent.m_pTryScope; + m_pCatchScope = parent.m_pCatchScope; + m_pCurrentScope = parent.m_pCurrentScope; + m_pSwitchScope = parent.m_pSwitchScope; + m_pCurrentBlock = parent.m_pCurrentBlock; + m_ppamis = parent.m_ppamis; + m_pamiCurrent = parent.m_pamiCurrent; + m_UnsafeState = parent.m_UnsafeState; + m_FinallyNestingCount = parent.m_FinallyNestingCount; + m_bInsideTryOfCatch = parent.m_bInsideTryOfCatch; + m_bInFieldInitializer = parent.m_bInFieldInitializer; + m_bInBaseConstructorCall = parent.m_bInBaseConstructorCall; + CheckedNormal = parent.CheckedNormal; + CheckedConstant = parent.CheckedConstant; + m_aidExternAliasLookupContext = parent.m_aidExternAliasLookupContext; + + m_bAllowUnsafeBlocks = parent.m_bAllowUnsafeBlocks; + m_bIsOptimizingSwitchAndArrayInit = parent.m_bIsOptimizingSwitchAndArrayInit; + m_bShowReachability = parent.m_bShowReachability; + m_bWrapNonExceptionThrows = parent.m_bWrapNonExceptionThrows; + m_bflushLocalVariableTypesForEachStatement = parent.m_bflushLocalVariableTypesForEachStatement; + m_bInRefactoring = parent.m_bInRefactoring; + m_bInAttribute = parent.m_bInAttribute; + m_bRespectSemanticsAndReportErrors = parent.m_bRespectSemanticsAndReportErrors; + m_pInitType = parent.m_pInitType; + m_returnErrorSink = parent.m_returnErrorSink; + + Debug.Assert(parent.SemanticChecker != null); + this.SemanticChecker = parent.SemanticChecker; + this.SymbolLoader = SemanticChecker.GetSymbolLoader(); + } + + + //the SymbolLoader can be retrieved from m_pSemanticChecker, + //but that is a virtual call that is showing up on the profiler. Retrieve + //the SymbolLoader once at ruction and return a cached copy. + + // PERFORMANCE: Is this cache still necessary? + public SymbolLoader SymbolLoader { get; private set; } + public Declaration m_pParentDecl; + public Declaration ContextForMemberLookup() { return m_pParentDecl; } + + public OutputContext GetOutputContext() + { + return m_outputContext; + } + // Virtual Dispose method - this should only be called by FNCBRECCS's StatePusher. + // It is used to clean up state in the output context for each overridden context. + public virtual void Dispose() + { + } + + // State boolean questions. + + public bool InMethod() + { + return m_outputContext.m_pCurrentMethodSymbol != null; + } + public bool InStaticMethod() + { + return m_outputContext.m_pCurrentMethodSymbol != null && + m_outputContext.m_pCurrentMethodSymbol.isStatic; + } + public bool InConstructor() + { + return m_outputContext.m_pCurrentMethodSymbol != null && + m_outputContext.m_pCurrentMethodSymbol.IsConstructor(); + } + public bool InAnonymousMethod() + { + return null != m_pamiCurrent; + } + public bool InFieldInitializer() + { + return m_bInFieldInitializer; + } + public bool IsThisPointer(EXPR expr) + { + bool localThis = expr.isANYLOCAL() && expr.asANYLOCAL().local == m_outputContext.m_pThisPointer; + bool baseThis = false; + return localThis || baseThis; + } + public bool RespectReadonly() + { + return m_bRespectSemanticsAndReportErrors; + } + public bool IsUnsafeContext() + { + return m_UnsafeState == UNSAFESTATES.UNSAFESTATES_Unsafe; + } + public bool ReportUnsafeErrors() + { + return !m_outputContext.m_bUnsafeErrorGiven && m_bRespectSemanticsAndReportErrors; + } + + public AggregateSymbol ContainingAgg() + { + return m_pContainingAgg; + } + public LocalVariableSymbol GetThisPointer() + { + return m_outputContext.m_pThisPointer; + } + + // Unsafe. + public UNSAFESTATES GetUnsafeState() + { + return m_UnsafeState; + } + + public KAID m_aidExternAliasLookupContext { get; private set; } + + // Members. + + protected ExprFactory m_ExprFactory; + protected OutputContext m_outputContext; + protected NameGenerator m_pNameGenerator; + + // Methods. + + protected InputFile m_pInputFile; + + // symbols. + + // The parent declaration, for various name binding uses. This is either an + // AggregateDeclaration (if parentAgg is non-null), or an NamespaceDeclaration (if parentAgg + // is null). + // Note that parentAgg isn't enough for name binding if partial classes + // are used, because the using clauses in effect may be different and + // unsafe state may be different. + + protected AggregateSymbol m_pContainingAgg; + protected CType m_pCurrentSwitchType; + protected FieldSymbol m_pOriginalConstantField; + protected FieldSymbol m_pCurrentFieldSymbol; + + // If we are in a context where we are binding the right hand side of a declaration + // like var y = (y=5), we need to keep track of what local we are attempting to + // infer the type of, so that we can give an error if that local is used on the + // right hand side. + protected LocalVariableSymbol m_pImplicitlyTypedLocal; + + // Scopes. + + protected Scope m_pOuterScope; + protected Scope m_pFinallyScope; // innermost finally, or pOuterScope if none... + protected Scope m_pTryScope; // innermost try, or pOuterScope if none... + protected Scope m_pCatchScope; // innermose catch, or null if none + protected Scope m_pCurrentScope; // current scope + protected Scope m_pSwitchScope; // innermost switch, or null if none + + protected EXPRBLOCK m_pCurrentBlock; + + // m_ppamis points to the list of child anonymous methods of the current context. + // That is, m_ppamis is where we will add an anonymous method should we find a + // new one while binding. If we are presently binding a nominal method then + // m_ppamis points to the methinfo.pamis. If we are presently binding an + // anonymous method then it points to m_pamiCurrent.pamis. If we are presently + // in a context in which anonymous methods cannot occur (eg, binding an attribute) + // then it is null. + protected List m_ppamis; + // If we are presently binding an anonymous method body then m_pamiCurrent points + // to the anon meth info. If we are binding either a method body or some other + // statement context (eg, binding an attribute, etc) then m_pamiCurrent is null. + protected EXPRBOUNDLAMBDA m_pamiCurrent; + + // Unsafe states. + + protected UNSAFESTATES m_UnsafeState; + + // Variable Counters. + + protected int m_FinallyNestingCount; + + // The rest of the members. + + protected bool m_bInsideTryOfCatch; + protected bool m_bInFieldInitializer; + protected bool m_bInBaseConstructorCall; + protected bool m_bAllowUnsafeBlocks; + protected bool m_bIsOptimizingSwitchAndArrayInit; + protected bool m_bShowReachability; + protected bool m_bWrapNonExceptionThrows; + protected bool m_bInRefactoring; + protected bool m_bInAttribute; + + protected bool m_bflushLocalVariableTypesForEachStatement; + protected bool m_bRespectSemanticsAndReportErrors; // False if we're in the EE. + + protected CType m_pInitType; + + protected IErrorSink m_returnErrorSink; + + public CSemanticChecker SemanticChecker { get; private set; } + + public ExprFactory GetExprFactory() { return m_ExprFactory; } + + public bool CheckedNormal { get; set; } + public bool CheckedConstant { get; set; } + } +} diff --git a/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/BindingContexts.cs b/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/BindingContexts.cs new file mode 100644 index 000000000..4c2d6a9b6 --- /dev/null +++ b/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/BindingContexts.cs @@ -0,0 +1,36 @@ +// ==++== +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// ==--== + +namespace Microsoft.CSharp.RuntimeBinder.Semantics +{ + // ---------------------------------------------------------------------------- + // This file contains classes that create a new statement binding context + // from the current one, but push on some new state. + // ---------------------------------------------------------------------------- + + internal class CheckedContext : BindingContext + { + public static CheckedContext CreateInstance( + BindingContext parentCtx, + bool checkedNormal, + bool checkedConstant) + { + return new CheckedContext(parentCtx, checkedNormal, checkedConstant); + } + + protected CheckedContext( + BindingContext parentCtx, + bool checkedNormal, + bool checkedConstant + ) + : base(parentCtx) + { + CheckedConstant = checkedConstant; + CheckedNormal = checkedNormal; + } + } +} + diff --git a/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/BindingFlag.cs b/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/BindingFlag.cs new file mode 100644 index 000000000..e9ab69db2 --- /dev/null +++ b/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/BindingFlag.cs @@ -0,0 +1,25 @@ +// ==++== +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// ==--== + +namespace Microsoft.CSharp.RuntimeBinder.Semantics +{ + internal enum BindingFlag + { + BIND_RVALUEREQUIRED = 0x0001, // this is a get of expr, not an assignment to expr + BIND_MEMBERSET = 0x0002, // indicates that an lvalue is needed + BIND_FIXEDVALUE = 0x0010, // ok to take address of unfixed + BIND_ARGUMENTS = 0x0020, // this is an argument list to a call... + BIND_BASECALL = 0x0040, // this is a base method or prop call + BIND_USINGVALUE = 0x0080, // local in a using stmt decl + BIND_STMTEXPRONLY = 0x0100, // only allow expressions that are valid in a statement + BIND_TYPEOK = 0x0200, // types are ok to be returned + BIND_MAYBECONFUSEDNEGATIVECAST = 0x0400, // this may be a mistaken negative cast + BIND_METHODNOTOK = 0x0800, // naked methods are not ok to be returned + BIND_DECLNOTOK = 0x1000, // var decls are not ok to be returned + BIND_NOPARAMS = 0x2000, // Do not do params expansion during overload resolution + BIND_SPECULATIVELY = 0x4000, // We're doing a speculative bind. Dont' make any stateful changes that might affect the actual compilation + } +} diff --git a/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/COperators.cs b/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/COperators.cs new file mode 100644 index 000000000..f3dff5ad4 --- /dev/null +++ b/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/COperators.cs @@ -0,0 +1,151 @@ +// ==++== +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// ==--== + +using System.Collections.Generic; +using System.Diagnostics; +using Microsoft.CSharp.RuntimeBinder.Syntax; + +namespace Microsoft.CSharp.RuntimeBinder.Semantics +{ + internal static class Operators + { + private class OPINFO + { + public OPINFO(TokenKind t, PredefinedName pn, ExpressionKind e, int c) + { + iToken = t; + methodName = pn; + expressionKind = e; + } + public TokenKind iToken; + public PredefinedName methodName; + public ExpressionKind expressionKind; + } + + private static readonly Dictionary m_rgOpInfo = new Dictionary() + { +{OperatorKind.OP_NONE, new OPINFO(TokenKind.Unknown , PredefinedName.PN_COUNT , ExpressionKind.EK_COUNT , 0)}, +{OperatorKind.OP_ASSIGN, new OPINFO(TokenKind.Equal , PredefinedName.PN_COUNT , ExpressionKind.EK_COUNT , 2)}, +{OperatorKind.OP_ADDEQ, new OPINFO(TokenKind.PlusEqual , PredefinedName.PN_COUNT , ExpressionKind.EK_MULTIOFFSET + (int)ExpressionKind.EK_ADD , 2)}, +{OperatorKind.OP_SUBEQ, new OPINFO(TokenKind.MinusEqual , PredefinedName.PN_COUNT , ExpressionKind.EK_MULTIOFFSET + (int)ExpressionKind.EK_SUB , 2)}, +{OperatorKind.OP_MULEQ, new OPINFO(TokenKind.SplatEqual , PredefinedName.PN_COUNT , ExpressionKind.EK_MULTIOFFSET + (int)ExpressionKind.EK_MUL , 2)}, +{OperatorKind.OP_DIVEQ, new OPINFO(TokenKind.SlashEqual , PredefinedName.PN_COUNT , ExpressionKind.EK_MULTIOFFSET + (int)ExpressionKind.EK_DIV , 2)}, +{OperatorKind.OP_MODEQ, new OPINFO(TokenKind.PercentEqual , PredefinedName.PN_COUNT , ExpressionKind.EK_MULTIOFFSET + (int)ExpressionKind.EK_MOD , 2)}, +{OperatorKind.OP_ANDEQ, new OPINFO(TokenKind.AndEqual , PredefinedName.PN_COUNT , ExpressionKind.EK_MULTIOFFSET + (int)ExpressionKind.EK_BITAND, 2)}, +{OperatorKind.OP_XOREQ, new OPINFO(TokenKind.HatEqual , PredefinedName.PN_COUNT , ExpressionKind.EK_MULTIOFFSET + (int)ExpressionKind.EK_BITXOR, 2)}, +{OperatorKind.OP_OREQ, new OPINFO(TokenKind.BarEqual , PredefinedName.PN_COUNT , ExpressionKind.EK_MULTIOFFSET + (int)ExpressionKind.EK_BITOR , 2)}, +{OperatorKind.OP_LSHIFTEQ, new OPINFO(TokenKind.LeftShiftEqual , PredefinedName.PN_COUNT , ExpressionKind.EK_MULTIOFFSET + (int)ExpressionKind.EK_LSHIFT, 2)}, +{OperatorKind.OP_RSHIFTEQ, new OPINFO(TokenKind.RightShiftEqual , PredefinedName.PN_COUNT , ExpressionKind.EK_MULTIOFFSET + (int)ExpressionKind.EK_RSHIFT, 2)}, +{OperatorKind.OP_QUESTION, new OPINFO(TokenKind.Question , PredefinedName.PN_COUNT , ExpressionKind.EK_COUNT , 2)}, +{OperatorKind.OP_VALORDEF, new OPINFO(TokenKind.QuestionQuestion , PredefinedName.PN_COUNT , ExpressionKind.EK_COUNT , 2)}, +{OperatorKind.OP_LOGOR, new OPINFO(TokenKind.LogicalOr , PredefinedName.PN_COUNT , ExpressionKind.EK_LOGOR , 2)}, +{OperatorKind.OP_LOGAND, new OPINFO(TokenKind.LogicalAnd , PredefinedName.PN_COUNT , ExpressionKind.EK_LOGAND , 2)}, +{OperatorKind.OP_BITOR, new OPINFO(TokenKind.Bar , PredefinedName.PN_OPBITWISEOR , ExpressionKind.EK_BITOR , 2)}, +{OperatorKind.OP_BITXOR, new OPINFO(TokenKind.Hat , PredefinedName.PN_OPXOR , ExpressionKind.EK_BITXOR , 2)}, +{OperatorKind.OP_BITAND, new OPINFO(TokenKind.Ampersand , PredefinedName.PN_OPBITWISEAND , ExpressionKind.EK_BITAND , 2)}, +{OperatorKind.OP_EQ, new OPINFO(TokenKind.EqualEqual , PredefinedName.PN_OPEQUALITY , ExpressionKind.EK_EQ , 2)}, +{OperatorKind.OP_NEQ, new OPINFO(TokenKind.NotEqual , PredefinedName.PN_OPINEQUALITY , ExpressionKind.EK_NE , 2)}, +{OperatorKind.OP_LT, new OPINFO(TokenKind.LessThan , PredefinedName.PN_OPLESSTHAN , ExpressionKind.EK_LT , 2)}, +{OperatorKind.OP_LE, new OPINFO(TokenKind.LessThanEqual , PredefinedName.PN_OPLESSTHANOREQUAL , ExpressionKind.EK_LE , 2)}, +{OperatorKind.OP_GT, new OPINFO(TokenKind.GreaterThan , PredefinedName.PN_OPGREATERTHAN , ExpressionKind.EK_GT , 2)}, +{OperatorKind.OP_GE, new OPINFO(TokenKind.GreaterThanEqual , PredefinedName.PN_OPGREATERTHANOREQUAL , ExpressionKind.EK_GE , 2)}, +{OperatorKind.OP_IS, new OPINFO(TokenKind.Is , PredefinedName.PN_COUNT , ExpressionKind.EK_COUNT , 2)}, +{OperatorKind.OP_AS, new OPINFO(TokenKind.As , PredefinedName.PN_COUNT , ExpressionKind.EK_COUNT , 2)}, +{OperatorKind.OP_LSHIFT, new OPINFO(TokenKind.LeftShift , PredefinedName.PN_OPLEFTSHIFT , ExpressionKind.EK_LSHIFT , 2)}, +{OperatorKind.OP_RSHIFT, new OPINFO(TokenKind.RightShift , PredefinedName.PN_OPRIGHTSHIFT , ExpressionKind.EK_RSHIFT , 2)}, +{OperatorKind.OP_ADD, new OPINFO(TokenKind.Plus , PredefinedName.PN_OPPLUS , ExpressionKind.EK_ADD , 2)}, +{OperatorKind.OP_SUB, new OPINFO(TokenKind.Minus , PredefinedName.PN_OPMINUS , ExpressionKind.EK_SUB , 2)}, +{OperatorKind.OP_MUL, new OPINFO(TokenKind.Splat , PredefinedName.PN_OPMULTIPLY , ExpressionKind.EK_MUL , 2)}, +{OperatorKind.OP_DIV, new OPINFO(TokenKind.Slash , PredefinedName.PN_OPDIVISION , ExpressionKind.EK_DIV , 2)}, +{OperatorKind.OP_MOD, new OPINFO(TokenKind.Percent , PredefinedName.PN_OPMODULUS , ExpressionKind.EK_MOD , 2)}, +{OperatorKind.OP_NOP, new OPINFO(TokenKind.Unknown , PredefinedName.PN_COUNT , ExpressionKind.EK_COUNT , 1)}, +{OperatorKind.OP_UPLUS, new OPINFO(TokenKind.Plus , PredefinedName.PN_OPUNARYPLUS , ExpressionKind.EK_UPLUS , 1)}, +{OperatorKind.OP_NEG, new OPINFO(TokenKind.Minus , PredefinedName.PN_OPUNARYMINUS , ExpressionKind.EK_NEG , 1)}, +{OperatorKind.OP_BITNOT, new OPINFO(TokenKind.Tilde , PredefinedName.PN_OPCOMPLEMENT , ExpressionKind.EK_BITNOT , 1)}, +{OperatorKind.OP_LOGNOT, new OPINFO(TokenKind.Bang , PredefinedName.PN_OPNEGATION , ExpressionKind.EK_LOGNOT , 1)}, +{OperatorKind.OP_PREINC, new OPINFO(TokenKind.PlusPlus , PredefinedName.PN_OPINCREMENT , ExpressionKind.EK_ADD , 1)}, +{OperatorKind.OP_PREDEC, new OPINFO(TokenKind.MinusMinus , PredefinedName.PN_OPDECREMENT , ExpressionKind.EK_SUB , 1)}, +{OperatorKind.OP_TYPEOF, new OPINFO(TokenKind.TypeOf , PredefinedName.PN_COUNT , ExpressionKind.EK_TYPEOF , 1)}, +{OperatorKind.OP_CHECKED, new OPINFO(TokenKind.Checked , PredefinedName.PN_COUNT , ExpressionKind.EK_COUNT , 1)}, +{OperatorKind.OP_UNCHECKED, new OPINFO(TokenKind.Unchecked , PredefinedName.PN_COUNT , ExpressionKind.EK_COUNT , 1)}, +{OperatorKind.OP_MAKEREFANY, new OPINFO(TokenKind.MakeRef , PredefinedName.PN_COUNT , ExpressionKind.EK_COUNT , 1)}, +{OperatorKind.OP_REFVALUE, new OPINFO(TokenKind.RefValue , PredefinedName.PN_COUNT , ExpressionKind.EK_COUNT , 2)}, +{OperatorKind.OP_REFTYPE, new OPINFO(TokenKind.RefType , PredefinedName.PN_COUNT , ExpressionKind.EK_COUNT , 1)}, +{OperatorKind.OP_ARGS, new OPINFO(TokenKind.ArgList , PredefinedName.PN_COUNT , ExpressionKind.EK_COUNT , 0)}, +{OperatorKind.OP_CAST, new OPINFO(TokenKind.Unknown , PredefinedName.PN_COUNT , ExpressionKind.EK_COUNT , 2)}, +{OperatorKind.OP_INDIR, new OPINFO(TokenKind.Splat , PredefinedName.PN_COUNT , ExpressionKind.EK_COUNT , 1)}, +{OperatorKind.OP_ADDR, new OPINFO(TokenKind.Ampersand , PredefinedName.PN_COUNT , ExpressionKind.EK_COUNT , 1)}, +{OperatorKind.OP_COLON, new OPINFO(TokenKind.Colon , PredefinedName.PN_COUNT , ExpressionKind.EK_COUNT , 2)}, +{OperatorKind.OP_THIS, new OPINFO(TokenKind.This , PredefinedName.PN_COUNT , ExpressionKind.EK_COUNT , 0)}, +{OperatorKind.OP_BASE, new OPINFO(TokenKind.Base , PredefinedName.PN_COUNT , ExpressionKind.EK_COUNT , 0)}, +{OperatorKind.OP_NULL, new OPINFO(TokenKind.Null , PredefinedName.PN_COUNT , ExpressionKind.EK_COUNT , 0)}, +{OperatorKind.OP_TRUE, new OPINFO(TokenKind.True , PredefinedName.PN_OPTRUE , ExpressionKind.EK_COUNT , 1)}, +{OperatorKind.OP_FALSE, new OPINFO(TokenKind.False , PredefinedName.PN_OPFALSE , ExpressionKind.EK_COUNT , 1)}, +{OperatorKind.OP_CALL, new OPINFO(TokenKind.Unknown , PredefinedName.PN_COUNT , ExpressionKind.EK_COUNT , 0)}, +{OperatorKind.OP_DEREF, new OPINFO(TokenKind.Unknown , PredefinedName.PN_COUNT , ExpressionKind.EK_COUNT , 0)}, +{OperatorKind.OP_PAREN, new OPINFO(TokenKind.Unknown , PredefinedName.PN_COUNT , ExpressionKind.EK_COUNT , 0)}, +{OperatorKind.OP_POSTINC, new OPINFO(TokenKind.PlusPlus , PredefinedName.PN_COUNT , ExpressionKind.EK_ADD , 1)}, +{OperatorKind.OP_POSTDEC, new OPINFO(TokenKind.MinusMinus , PredefinedName.PN_COUNT , ExpressionKind.EK_SUB , 1)}, +{OperatorKind.OP_DOT, new OPINFO(TokenKind.Dot , PredefinedName.PN_COUNT , ExpressionKind.EK_COUNT , 2)}, +{OperatorKind.OP_IMPLICIT, new OPINFO(TokenKind.Implicit , PredefinedName.PN_OPIMPLICITMN , ExpressionKind.EK_COUNT , 1)}, +{OperatorKind.OP_EXPLICIT, new OPINFO(TokenKind.Explicit , PredefinedName.PN_OPEXPLICITMN , ExpressionKind.EK_COUNT , 1)}, +{OperatorKind.OP_EQUALS, new OPINFO(TokenKind.Unknown , PredefinedName.PN_OPEQUALS , ExpressionKind.EK_COUNT , 2)}, +{OperatorKind.OP_COMPARE, new OPINFO(TokenKind.Unknown , PredefinedName.PN_OPCOMPARE , ExpressionKind.EK_COUNT , 2)}, +{OperatorKind.OP_DEFAULT, new OPINFO(TokenKind.Unknown , PredefinedName.PN_COUNT , ExpressionKind.EK_COUNT , 0)} + }; + + + private static OPINFO GetInfo(OperatorKind op) + { + //Debug.Assert(IsValid(op)); + return m_rgOpInfo[op]; + } + public static OperatorKind OperatorOfMethodName(NameManager namemgr, Name name) + { + Debug.Assert(name != null); + + for (OperatorKind i = OperatorKind.OP_NONE; i < OperatorKind.OP_LAST; i = (i + 1)) + { + if (HasMethodName(i) && (name == GetMethodName(namemgr, i))) + { + return i; + } + } + + return OperatorKind.OP_NONE; + } + public static bool HasMethodName(OperatorKind op) + { + //Debug.Assert(IsValid(op)); + return GetMethodName(op) != PredefinedName.PN_COUNT; + } + public static PredefinedName GetMethodName(OperatorKind op) + { + //Debug.Assert(IsValid(op)); + return GetInfo(op).methodName; + } + public static Name GetMethodName(NameManager namemgr, OperatorKind op) + { + Debug.Assert(HasMethodName(op)); + return namemgr.GetPredefName(GetMethodName(op)); + } + public static bool HasDisplayName(OperatorKind op) + { + //Debug.Assert(IsValid(op)); + return GetInfo(op).iToken != TokenKind.Unknown; + } + public static string GetDisplayName(OperatorKind op) + { + Debug.Assert(HasDisplayName(op)); + return TokenFacts.GetText(GetInfo(op).iToken); + } + public static ExpressionKind GetExpressionKind(OperatorKind op) + { + //Debug.Assert(IsValid(op)); + return GetInfo(op).expressionKind; + } + } +} diff --git a/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/CandidateFunctionMember.cs b/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/CandidateFunctionMember.cs new file mode 100644 index 000000000..c7ef8a3f5 --- /dev/null +++ b/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/CandidateFunctionMember.cs @@ -0,0 +1,26 @@ +// ==++== +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// ==--== + +namespace Microsoft.CSharp.RuntimeBinder.Semantics +{ + // Used to string together methods in the pool of available methods... + internal class CandidateFunctionMember + { + public CandidateFunctionMember(MethPropWithInst mpwi, TypeArray @params, byte ctypeLift, bool fExpanded) + { + this.mpwi = mpwi; + this.@params = @params; + this.ctypeLift = ctypeLift; + this.fExpanded = fExpanded; + } + public MethPropWithInst mpwi; + // params is the result of type variable substitution on either mpwi.MethProp()->params or + // an expansion of mpwi.MethProp()->params (for a param array). + public TypeArray @params; + public byte ctypeLift; // How many parameter types are lifted (for tie-breaking). + public bool fExpanded; // Whether the params came from expanding mpwi.MethProp()->params. + } +} diff --git a/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/ConstVal.cs b/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/ConstVal.cs new file mode 100644 index 000000000..ec2bc3e97 --- /dev/null +++ b/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/ConstVal.cs @@ -0,0 +1,206 @@ +// ==++== +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// ==--== + +using System; + +namespace Microsoft.CSharp.RuntimeBinder.Semantics +{ + /* + * The kind of allocation used in a constant value. + * Since CONSTVALs don't store a discriminant for the union + * this must be managed by clients. + */ + enum ConstValKind + { + Int, + Double, + Long, + String, + Decimal, + IntPtr, + Float, + Boolean, + Lim + }; + + + internal sealed class CONSTVAL + { + private object value; + + internal CONSTVAL() + : this(null) + { + } + + internal CONSTVAL(object value) + { + this.value = value; + } + + public object objectVal + { + get { return this.value; } + set { this.value = value; } + } + + public bool boolVal + { + get { return SpecialUnbox(this.value); } + set { this.value = SpecialBox(value); } + } + + public sbyte sbyteVal + { + get { return SpecialUnbox(this.value); } + //set { this.value = SpecialBox(value); } + } + + public byte byteVal + { + get { return SpecialUnbox(this.value); } + //set { this.value = SpecialBox(value); } + } + + public short shortVal + { + get { return SpecialUnbox(this.value); } + //set { this.value = SpecialBox(value); } + } + + public ushort ushortVal + { + get { return SpecialUnbox(this.value); } + //set { this.value = SpecialBox(value); } + } + + public int iVal + { + get { return SpecialUnbox(this.value); } + set { this.value = SpecialBox(value); } + } + + public uint uiVal + { + get { return SpecialUnbox(this.value); } + set { this.value = SpecialBox(value); } + } + + public long longVal + { + get { return SpecialUnbox(this.value); } + set { this.value = SpecialBox(value); } + } + + public ulong ulongVal + { + get { return SpecialUnbox(this.value); } + set { this.value = SpecialBox(value); } + } + + public float floatVal + { + get { return SpecialUnbox(this.value); } + set { this.value = SpecialBox(value); } + } + + public double doubleVal + { + get { return SpecialUnbox(this.value); } + set { this.value = SpecialBox(value); } + } + + public decimal decVal + { + get { return SpecialUnbox(this.value); } + set { this.value = SpecialBox(value); } + } + + public char cVal + { + get { return SpecialUnbox(this.value); } + //set { this.value = SpecialBox(value); } + } + + public string strVal + { + get { return SpecialUnbox(this.value); } + set { this.value = SpecialBox(value); } + } + + public bool IsNullRef() + { + return this.value == null; + } + + public bool IsZero(ConstValKind kind) + { + switch (kind) + { + case ConstValKind.Decimal: + return decVal == 0; + case ConstValKind.String: + return false; + default: + return IsDefault(this.value); + } + } + + private T SpecialUnbox(object o) + { + if (IsDefault(o)) + { + return default(T); + } + + return (T)Convert.ChangeType(o, typeof(T), System.Globalization.CultureInfo.InvariantCulture); + } + + private object SpecialBox(T x) + { + return x; + } + + private bool IsDefault(object o) + { + if (o == null) + return true; + + TypeCode code = Type.GetTypeCode(o.GetType()); + switch (code) + { + case TypeCode.Boolean: + return default(bool).Equals(o); + case TypeCode.SByte: + return default(sbyte).Equals(o); + case TypeCode.Byte: + return default(byte).Equals(o); + case TypeCode.Int16: + return default(short).Equals(o); + case TypeCode.UInt16: + return default(ushort).Equals(o); + case TypeCode.Int32: + return default(int).Equals(o); + case TypeCode.UInt32: + return default(uint).Equals(o); + case TypeCode.Int64: + return default(long).Equals(o); + case TypeCode.UInt64: + return default(ulong).Equals(o); + case TypeCode.Single: + return default(float).Equals(o); + case TypeCode.Double: + return default(double).Equals(o); + case TypeCode.Decimal: + return default(decimal).Equals(o); + case TypeCode.Char: + return default(char).Equals(o); + } + + return false; + } + } +} diff --git a/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/ConstValFactory.cs b/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/ConstValFactory.cs new file mode 100644 index 000000000..30b1d1dc0 --- /dev/null +++ b/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/ConstValFactory.cs @@ -0,0 +1,150 @@ +// ==++== +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// ==--== + +namespace Microsoft.CSharp.RuntimeBinder.Semantics +{ + // ConstValFactory owns the construction of CONSTVALs + // This should be the only place in the code which dynamically + // allocates memory for CONSTVALs. + // + // One important client of ConstValFactory is the lexer/parser, + // so ConstValFactory should not take a dependancy on TYPESYMs + // or predefined types. + // + // REFACTOR: Update the Parser to use this class. + // PERFORMANCE: Consider inlining some of these methods. + // CONSIDER: Pointer members of CONSTVAL should point to const values. + // PERFORMANCE: Optimize copying of zero values to not alloc memory. + // PERFORMANCE: Optimize copying of values allocated on same allocator. + // CONSIDER: Consider uniquefying constants based on value. + + internal sealed class ConstValFactory + { + public ConstValFactory() + { } + + public CONSTVAL Copy(ConstValKind kind, CONSTVAL value) + { + return new CONSTVAL(value.objectVal); + } + + public static CONSTVAL GetDefaultValue(ConstValKind kind) + { + CONSTVAL result = new CONSTVAL(); + + switch (kind) + { + case ConstValKind.Int: + result.iVal = 0; + break; + + case ConstValKind.Double: + result.doubleVal = 0; + break; + + case ConstValKind.Long: + result.longVal = 0; + break; + + case ConstValKind.Decimal: + result.decVal = 0; + break; + + case ConstValKind.Float: + result.floatVal = 0; + break; + + case ConstValKind.Boolean: + result.boolVal = false; + break; + } + + return result; + } + + public static CONSTVAL GetNullRef() + { + return new CONSTVAL(); + } + + public static CONSTVAL GetBool(bool value) + { + CONSTVAL result = new CONSTVAL(); + result.boolVal = value; + return result; + } + + public static CONSTVAL GetInt(int value) + { + CONSTVAL result = new CONSTVAL(); ; + result.iVal = value; + return result; + } + + public static CONSTVAL GetUInt(uint value) + { + CONSTVAL result = new CONSTVAL(); + result.uiVal = value; + return result; + } + + public CONSTVAL Create(decimal value) + { + CONSTVAL result = new CONSTVAL(); + result.decVal = value; + return result; + } + + public CONSTVAL Create(string value) + { + CONSTVAL result = new CONSTVAL(); + result.strVal = value; + return result; + } + + public CONSTVAL Create(float value) + { + CONSTVAL result = new CONSTVAL(); + result.floatVal = value; + return result; + } + + public CONSTVAL Create(double value) + { + CONSTVAL result = new CONSTVAL(); + result.doubleVal = value; + return result; + } + + public CONSTVAL Create(long value) + { + CONSTVAL result = new CONSTVAL(); + result.longVal = value; + return result; + } + + public CONSTVAL Create(ulong value) + { + CONSTVAL result = new CONSTVAL(); + result.ulongVal = value; + return result; + } + + internal CONSTVAL Create(bool value) + { + CONSTVAL result = new CONSTVAL(); + result.boolVal = value; + return result; + } + + internal CONSTVAL Create(object p) + { + CONSTVAL result = new CONSTVAL(); + result.objectVal = p; + return result; + } + } +} diff --git a/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/Conversion.cs b/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/Conversion.cs new file mode 100644 index 000000000..f1bfc86a2 --- /dev/null +++ b/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/Conversion.cs @@ -0,0 +1,1859 @@ +// ==++== +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// ==--== + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Globalization; +using Microsoft.CSharp.RuntimeBinder.Errors; +using Microsoft.CSharp.RuntimeBinder.Syntax; + +namespace Microsoft.CSharp.RuntimeBinder.Semantics +{ + internal enum ConvKind + { + Identity = 1, // Identity conversion + Implicit = 2, // Implicit conversion + Explicit = 3, // Explicit conversion + Unknown = 4, // Unknown so call canConvert + None = 5, // None + } + + // Flags for bindImplicitConversion/bindExplicitConversion + internal enum CONVERTTYPE + { + NOUDC = 0x01, // Do not consider user defined conversions. + STANDARD = 0x02, // standard only, but never pass it in, used only to check... + ISEXPLICIT = 0x04, // implicit conversion is really explicit + CHECKOVERFLOW = 0x08, // check overflow (like in a checked context). + FORCECAST = 0x10, // Do not optimize out the cast + STANDARDANDNOUDC = 0x03, // pass this in if you mean standard conversions only + }; + + internal enum BetterType + { + Same = 0, + Left = 1, + Right = 2, + Neither = 3, + } + + internal partial class ExpressionBinder + { + + private delegate bool ConversionFunc( + EXPR pSourceExpr, + CType pSourceType, + EXPRTYPEORNAMESPACE pDestinationTypeExpr, + CType pDestinationTypeForLambdaErrorReporting, + bool needsExprDest, + out EXPR ppDestinationExpr, + CONVERTTYPE flags); + + private static void RoundToFloat(double d, out float f) + { + f = (float)d; + } + private static long I64(long x) { return x; } + private static long I64(ulong x) { return (long)x; } + + private static void RETAILVERIFY(bool b) + { + if (!b) + { + Debug.Assert(false, "panic!"); + throw Error.InternalCompilerError(); + } + } + + // 13.1.2 Implicit numeric conversions + // + // The implicit numeric conversions are: + // + // * From sbyte to short, int, long, float, double, or decimal. + // * From byte to short, ushort, int, uint, long, ulong, float, double, or decimal. + // * From short to int, long, float, double, or decimal. + // * From ushort to int, uint, long, ulong, float, double, or decimal. + // * From int to long, float, double, or decimal. + // * From uint to long, ulong, float, double, or decimal. + // * From long to float, double, or decimal. + // * From ulong to float, double, or decimal. + // * From char to ushort, int, uint, long, ulong, float, double, or decimal. + // * From float to double. + // + // Conversions from int, uint, long or ulong to float and from long or ulong to double can cause a + // loss of precision, but will never cause a loss of magnitude. The other implicit numeric + // conversions never lose any information. + // + // There are no implicit conversions to the char type, so values of the other integral types do not + // automatically convert to the char type. + // + // 13.2.1 Explicit numeric conversions + // + // The explicit numeric conversions are the conversions from a numeric-type to another numeric-type + // for which an implicit numeric conversion (13.1.2) does not already exist: + // + // * From sbyte to byte, ushort, uint, ulong, or char. + // * From byte to sbyte or char. + // * From short to sbyte, byte, ushort, uint, ulong, or char. + // * From ushort to sbyte, byte, short, or char. + // * From int to sbyte, byte, short, ushort, uint, ulong, or char. + // * From uint to sbyte, byte, short, ushort, int, or char. + // * From long to sbyte, byte, short, ushort, int, uint, ulong, or char. + // * From ulong to sbyte, byte, short, ushort, int, uint, long, or char. + // * From char to sbyte, byte, or short. + // * From float to sbyte, byte, short, ushort, int, uint, long, ulong, char, or decimal. + // * From double to sbyte, byte, short, ushort, int, uint, long, ulong, char, float, or decimal. + // * From decimal to sbyte, byte, short, ushort, int, uint, long, ulong, char, float, or double. + + + private const byte ID = (byte)ConvKind.Identity; // 0x01 + private const byte IMP = (byte)ConvKind.Implicit; // 0x02 + private const byte EXP = (byte)ConvKind.Explicit; // 0x03 + private const byte NO = (byte)ConvKind.None; // 0x05 + private const byte CONV_KIND_MASK = 0x0F; + private const byte UDC = 0x40; + private const byte XUD = EXP | UDC; + private const byte IUD = IMP | UDC; + + static private readonly byte[,] simpleTypeConversions = + { +// to: BYTE I2 I4 I8 FLT DBL DEC CHAR BOOL SBYTE U2 U4 U8 +/* from */ +/* BYTE */ { ID , IMP , IMP , IMP , IMP , IMP , IUD, EXP , NO , EXP , IMP , IMP , IMP }, +/* I2 */ { EXP , ID , IMP , IMP , IMP , IMP , IUD, EXP , NO , EXP , EXP , EXP , EXP }, +/* I4 */ { EXP , EXP , ID , IMP , IMP , IMP , IUD, EXP , NO , EXP , EXP , EXP , EXP }, +/* I8 */ { EXP , EXP , EXP , ID , IMP , IMP , IUD, EXP , NO , EXP , EXP , EXP , EXP }, +/* FLT */ { EXP , EXP , EXP , EXP , ID , IMP , XUD, EXP , NO , EXP , EXP , EXP , EXP }, +/* DBL */ { EXP , EXP , EXP , EXP , EXP , ID , XUD, EXP , NO , EXP , EXP , EXP , EXP }, +/* DEC */ { XUD , XUD , XUD , XUD , XUD , XUD , ID , XUD , NO , XUD , XUD , XUD , XUD }, +/* CHAR */ { EXP , EXP , IMP , IMP , IMP , IMP , IUD, ID , NO , EXP , IMP , IMP , IMP }, +/* BOOL */ { NO , NO , NO , NO , NO , NO , NO , NO , ID , NO , NO , NO , NO }, +/*SBYTE */ { EXP , IMP , IMP , IMP , IMP , IMP , IUD, EXP , NO , ID , EXP , EXP , EXP }, +/* U2 */ { EXP , EXP , IMP , IMP , IMP , IMP , IUD, EXP , NO , EXP , ID , IMP , IMP }, +/* U4 */ { EXP , EXP , EXP , IMP , IMP , IMP , IUD, EXP , NO , EXP , EXP , ID , IMP }, +/* U8 */ { EXP , EXP , EXP , EXP , IMP , IMP , IUD, EXP , NO , EXP , EXP , EXP , ID }, + }; + + private const int NUM_SIMPLE_TYPES = (int)PredefinedType.PT_ULONG + 1; + private const int NUM_EXT_TYPES = (int)PredefinedType.PT_OBJECT + 1; + + private static ConvKind GetConvKind(PredefinedType ptSrc, PredefinedType ptDst) + { + if ((int)ptSrc < NUM_SIMPLE_TYPES && (int)ptDst < NUM_SIMPLE_TYPES) + { + return (ConvKind)(simpleTypeConversions[(int)ptSrc, (int)ptDst] & CONV_KIND_MASK); + } + if (ptSrc == ptDst || ptDst == PredefinedType.PT_OBJECT && ptSrc < PredefinedType.PT_COUNT) + { + return ConvKind.Implicit; + } + if (ptSrc == PredefinedType.PT_OBJECT && ptDst < PredefinedType.PT_COUNT) + { + return ConvKind.Explicit; + } + return ConvKind.Unknown; + } + + private static bool isUserDefinedConversion(PredefinedType ptSrc, PredefinedType ptDst) + { + if ((int)ptSrc < NUM_SIMPLE_TYPES && (int)ptDst < NUM_SIMPLE_TYPES) + { + return 0 != (simpleTypeConversions[(int)ptSrc, (int)ptDst] & UDC); + } + return false; + } + + // 14.4.2.3 Better conversion + // + // Given an implicit conversion C1 that converts from a type S to a type T1, and an implicit + // conversion C2 that converts from a type S to a type T2, the better conversion of the two + // conversions is determined as follows: + // + // * If T1 and T2 are the same type, neither conversion is better. + // * If S is T1, C1 is the better conversion. + // * If S is T2, C2 is the better conversion. + // * If an implicit conversion from T1 to T2 exists, and no implicit conversion from T2 to T1 + // exists, C1 is the better conversion. + // * If an implicit conversion from T2 to T1 exists, and no implicit conversion from T1 to T2 + // exists, C2 is the better conversion. + // * If T1 is sbyte and T2 is byte, ushort, uint, or ulong, C1 is the better conversion. + // * If T2 is sbyte and T1 is byte, ushort, uint, or ulong, C2 is the better conversion. + // * If T1 is short and T2 is ushort, uint, or ulong, C1 is the better conversion. + // * If T2 is short and T1 is ushort, uint, or ulong, C2 is the better conversion. + // * If T1 is int and T2 is uint, or ulong, C1 is the better conversion. + // * If T2 is int and T1 is uint, or ulong, C2 is the better conversion. + // * If T1 is long and T2 is ulong, C1 is the better conversion. + // * If T2 is long and T1 is ulong, C2 is the better conversion. + // * Otherwise, neither conversion is better. + // + // If an implicit conversion C1 is defined by these rules to be a better conversion than an + // implicit conversion C2, then it is also the case that C2 is a worse conversion than C1. + + private const byte same = (byte)BetterType.Same; + private const byte left = (byte)BetterType.Left; + private const byte right = (byte)BetterType.Right; + private const byte neither = (byte)BetterType.Neither; + + + static private readonly byte[,] simpleTypeBetter = + { +// BYTE SHORT INT LONG FLOAT DOUBLE DECIMAL CHAR BOOL SBYTE USHORT UINT ULONG IPTR UIPTR OBJECT +/* BYTE */{same ,left ,left ,left ,left ,left ,left ,neither,neither,right ,left ,left ,left ,neither,neither,left }, +/* SHORT */{right ,same ,left ,left ,left ,left ,left ,neither,neither,right ,left ,left ,left ,neither,neither,left }, +/* INT */{right ,right ,same ,left ,left ,left ,left ,right ,neither,right ,right ,left ,left ,neither,neither,left }, +/* LONG */{right ,right ,right ,same ,left ,left ,left ,right ,neither,right ,right ,right ,left ,neither,neither,left }, +/* FLOAT */{right ,right ,right ,right ,same ,left ,neither,right ,neither,right ,right ,right ,right ,neither,neither,left }, +/* DOUBLE */{right ,right ,right ,right ,right ,same ,neither,right ,neither,right ,right ,right ,right ,neither,neither,left }, +/* DECIMAL*/{right ,right ,right ,right ,neither,neither,same ,right ,neither,right ,right ,right ,right ,neither,neither,left }, +/* CHAR */{neither,neither,left ,left ,left ,left ,left ,same ,neither,neither,left ,left ,left ,neither,neither,left }, +/* BOOL */{neither,neither,neither,neither,neither,neither,neither,neither,same ,neither,neither,neither,neither,neither,neither,left }, +/* SBYTE */{left ,left ,left ,left ,left ,left ,left ,neither,neither,same ,left ,left ,left ,neither,neither,left }, +/* USHORT */{right ,right ,left ,left ,left ,left ,left ,right ,neither,right ,same ,left ,left ,neither,neither,left }, +/* UINT */{right ,right ,right ,left ,left ,left ,left ,right ,neither,right ,right ,same ,left ,neither,neither,left }, +/* ULONG */{right ,right ,right ,right ,left ,left ,left ,right ,neither,right ,right ,right ,same ,neither,neither,left }, +/* IPTR */{neither,neither,neither,neither,neither,neither,neither,neither,neither,neither,neither,neither,neither,same ,neither,left }, +/* UIPTR */{neither,neither,neither,neither,neither,neither,neither,neither,neither,neither,neither,neither,neither,neither,same ,left }, +/* OBJECT */{right ,right ,right ,right ,right ,right ,right ,right ,right ,right ,right ,right ,right ,right ,right ,same } + }; +#if DEBUG + static private volatile bool fCheckedBetter = false; + private void CheckBetterTable() + { + if (fCheckedBetter) + { + return; + } + for (int i = 0; i < NUM_EXT_TYPES; i++) + { + Debug.Assert(simpleTypeBetter[i, i] == same); + for (int j = 0; j < i; j++) + { + Debug.Assert(simpleTypeBetter[i, j] != same && simpleTypeBetter[j, i] != same); + Debug.Assert( + (simpleTypeBetter[i, j] == left && simpleTypeBetter[j, i] == right) || + (simpleTypeBetter[i, j] == right && simpleTypeBetter[j, i] == left) || + (simpleTypeBetter[i, j] == neither && simpleTypeBetter[j, i] == neither)); + Debug.Assert( + GetOptPDT((PredefinedType)i) == null || + GetOptPDT((PredefinedType)j) == null || + (!canConvert(GetOptPDT((PredefinedType)i), GetOptPDT((PredefinedType)j), CONVERTTYPE.NOUDC) || simpleTypeBetter[i, j] == left) && + (!canConvert(GetOptPDT((PredefinedType)j), GetOptPDT((PredefinedType)i), CONVERTTYPE.NOUDC) || simpleTypeBetter[j, i] == left)); + } + } + fCheckedBetter = true; + } +#endif // DEBUG + + private BetterType WhichSimpleConversionIsBetter(PredefinedType pt1, PredefinedType pt2) + { +#if DEBUG + CheckBetterTable(); +#endif // DEBUG + RETAILVERIFY((int)pt1 < NUM_EXT_TYPES); + RETAILVERIFY((int)pt2 < NUM_EXT_TYPES); + return (BetterType)simpleTypeBetter[(int)pt1, (int)pt2]; + } + + + + /*************************************************************************************************** + Determined which conversion to a predefined type is better relative to a given type. It is + assumed that the given type is implicitly convertible to both of the predefined types + (possibly via a user defined conversion, method group conversion, etc). + ***************************************************************************************************/ + private BetterType WhichTypeIsBetter(PredefinedType pt1, PredefinedType pt2, CType typeGiven) + { + if (pt1 == pt2) + { + return BetterType.Same; + } + if (typeGiven.isPredefType(pt1)) + { + return BetterType.Left; + } + if (typeGiven.isPredefType(pt2)) + { + return BetterType.Right; + } + if ((int)pt1 <= NUM_EXT_TYPES && (int)pt2 <= NUM_EXT_TYPES) + { + return WhichSimpleConversionIsBetter(pt1, pt2); + } + if (pt2 == PredefinedType.PT_OBJECT && pt1 < PredefinedType.PT_COUNT) + { + return BetterType.Left; + } + if (pt1 == PredefinedType.PT_OBJECT && pt2 < PredefinedType.PT_COUNT) + { + return BetterType.Right; + } + return WhichTypeIsBetter(GetOptPDT(pt1), GetOptPDT(pt2), typeGiven); + } + + + /*************************************************************************************************** + Determined which conversion is better relative to a given type. It is assumed that the given type + (or its associated expression) is implicitly convertible to both of the types (possibly via + a user defined conversion, method group conversion, etc). + ***************************************************************************************************/ + private BetterType WhichTypeIsBetter(CType type1, CType type2, CType typeGiven) + { + Debug.Assert(type1 != null && type2 != null); + if (type1 == type2) + { + return BetterType.Same; + } + if (typeGiven == type1) + { + return BetterType.Left; + } + if (typeGiven == type2) + { + return BetterType.Right; + } + + bool f12 = canConvert(type1, type2); + bool f21 = canConvert(type2, type1); + if (f12 != f21) + { + return f12 ? BetterType.Left : BetterType.Right; + } + + if (!type1.IsNullableType() || !type2.IsNullableType() || + !type1.AsNullableType().UnderlyingType.isPredefined() || + !type2.AsNullableType().UnderlyingType.isPredefined()) + { + return BetterType.Neither; + } + + PredefinedType pt1 = (type1 as NullableType).UnderlyingType.getPredefType(); + PredefinedType pt2 = (type2 as NullableType).UnderlyingType.getPredefType(); + + if ((int)pt1 <= NUM_EXT_TYPES && (int)pt2 <= NUM_EXT_TYPES) + { + return WhichSimpleConversionIsBetter(pt1, pt2); + } + + return BetterType.Neither; + } + + // returns true if an implicit conversion exists from source type to dest type. flags is an optional parameter. + public bool canConvert(CType src, CType dest, CONVERTTYPE flags) + { + EXPRCLASS exprDest = ExprFactory.MakeClass(dest); + return BindImplicitConversion(null, src, exprDest, dest, flags); + } + + public bool canConvert(CType src, CType dest) + { + return canConvert(src, dest, 0); + } + + // returns true if a implicit conversion exists from source expr to dest type. flags is an optional parameter. + public bool canConvert(EXPR expr, CType dest) + { + return canConvert(expr, dest, 0); + } + + public bool canConvert(EXPR expr, CType dest, CONVERTTYPE flags) + { + EXPRCLASS exprDest = ExprFactory.MakeClass(dest); + return BindImplicitConversion(expr, expr.type, exprDest, dest, flags); + } + + // performs an implicit conversion if it's possible. otherwise displays an error. flags is an optional parameter. + + public EXPR mustConvertCore(EXPR expr, EXPRTYPEORNAMESPACE destExpr) + { + return mustConvertCore(expr, destExpr, 0); + } + + public EXPR mustConvertCore(EXPR expr, EXPRTYPEORNAMESPACE destExpr, CONVERTTYPE flags) + { + EXPR exprResult; + CType dest = destExpr.TypeOrNamespace as CType; + + if (BindImplicitConversion(expr, expr.type, destExpr, dest, out exprResult, flags)) + { + // Conversion works. + checkUnsafe(expr.type); // added to the binder so we don't bind to pointer ops + checkUnsafe(dest); // added to the binder so we don't bind to pointer ops + return exprResult; + } + + if (expr.isOK() && !dest.IsErrorType()) + { + // don't report cascading error. + + // For certain situations, try to give a better error. + + FUNDTYPE ftSrc = expr.type.fundType(); + FUNDTYPE ftDest = dest.fundType(); + + if (expr.isCONSTANT_OK() && + expr.type.isSimpleType() && dest.isSimpleType()) + { + if ((ftSrc == FUNDTYPE.FT_I4 && (ftDest <= FUNDTYPE.FT_LASTNONLONG || ftDest == FUNDTYPE.FT_U8)) || + (ftSrc == FUNDTYPE.FT_I8 && ftDest == FUNDTYPE.FT_U8)) + { + // Failed because value was out of range. Report nifty error message. + string value = expr.asCONSTANT().I64Value.ToString(CultureInfo.InvariantCulture); + ErrorContext.Error(ErrorCode.ERR_ConstOutOfRange, value, dest); + exprResult = ExprFactory.CreateCast(0, destExpr, expr); + exprResult.SetError(); + return exprResult; + } + else if (ftSrc == FUNDTYPE.FT_R8 && (0 != (expr.flags & EXPRFLAG.EXF_LITERALCONST)) && + (dest.isPredefType(PredefinedType.PT_FLOAT) || dest.isPredefType(PredefinedType.PT_DECIMAL))) + { + // Tried to assign a literal of type double (the default) to a float or decimal. Suggest use + // of a 'F' or 'M' suffix. + ErrorContext.Error(ErrorCode.ERR_LiteralDoubleCast, dest.isPredefType(PredefinedType.PT_DECIMAL) ? "M" : "F", dest); + exprResult = ExprFactory.CreateCast(0, destExpr, expr); + exprResult.SetError(); + return exprResult; + } + } + + if (expr.type is NullType && dest.fundType() != FUNDTYPE.FT_REF) + { + ErrorContext.Error(dest is TypeParameterType ? ErrorCode.ERR_TypeVarCantBeNull : ErrorCode.ERR_ValueCantBeNull, dest); + } + + else if (expr.isMEMGRP()) + { + BindGrpConversion(expr.asMEMGRP(), dest, true); + } + else if (!TypeManager.TypeContainsAnonymousTypes(dest) && canCast(expr.type, dest, flags)) + { + // can't convert, but explicit exists and can be specified by the user (no anonymous types). + ErrorContext.Error(ErrorCode.ERR_NoImplicitConvCast, new ErrArg(expr.type, ErrArgFlags.Unique), new ErrArg(dest, ErrArgFlags.Unique)); + } + else + { + // Generic "can't convert" error. + ErrorContext.Error(ErrorCode.ERR_NoImplicitConv, new ErrArg(expr.type, ErrArgFlags.Unique), new ErrArg(dest, ErrArgFlags.Unique)); + } + } + exprResult = ExprFactory.CreateCast(0, destExpr, expr); + exprResult.SetError(); + return exprResult; + } + + // performs an implicit conversion if its possible. otherwise returns null. flags is an optional parameter. + // Only call this if you are ALWAYS going to use the returned result (and you're not just going to test and + // possibly throw away the result) + // If the conversion is possible it will modify an Anonymous Method expr thus changing results of + // future conversions. It will also produce possible binding errors for method goups. + + public EXPR tryConvert(EXPR expr, CType dest) + { + return tryConvert(expr, dest, 0); + } + + public EXPR tryConvert(EXPR expr, CType dest, CONVERTTYPE flags) + { + EXPR exprResult; + EXPRCLASS exprDest = ExprFactory.MakeClass(dest); + if (BindImplicitConversion(expr, expr.type, exprDest, dest, out exprResult, flags)) + { + checkUnsafe(expr.type); // added to the binder so we don't bind to pointer ops + checkUnsafe(dest); // added to the binder so we don't bind to pointer ops + // Conversion works. + return exprResult; + } + return null; + } + public EXPR mustConvert(EXPR expr, CType dest) + { + return mustConvert(expr, dest, (CONVERTTYPE)0); + } + public EXPR mustConvert(EXPR expr, CType dest, CONVERTTYPE flags) + { + EXPRCLASS exprClass = ExprFactory.MakeClass(dest); + return mustConvert(expr, exprClass, flags); + } + public EXPR mustConvert(EXPR expr, EXPRTYPEORNAMESPACE dest, CONVERTTYPE flags) + { + return mustConvertCore(expr, dest, flags); + } + +// public bool canCast(EXPR expr, CType dest) +// { +// EXPRCLASS destExpr = GetExprFactory().MakeClass(dest); +// return BindExplicitConversion(expr, expr.type, destExpr, dest, 0); +// } + + // performs an explicit conversion if its possible. otherwise displays an error. + private EXPR mustCastCore(EXPR expr, EXPRTYPEORNAMESPACE destExpr, CONVERTTYPE flags) + { + EXPR exprResult; + + CType dest = destExpr.TypeOrNamespace as CType; + + SemanticChecker.CheckForStaticClass(null, dest, ErrorCode.ERR_ConvertToStaticClass); + if (expr.isOK()) + { + if (BindExplicitConversion(expr, expr.type, destExpr, dest, out exprResult, flags)) + { + // Conversion works. + checkUnsafe(expr.type); // added to the binder so we don't bind to pointer ops + checkUnsafe(dest); // added to the binder so we don't bind to pointer ops + return exprResult; + } + if (dest != null && !(dest is ErrorType)) + { // don't report cascading error. + // For certain situations, try to give a better error. + string value = ""; + EXPR exprConst = expr.GetConst(); + FUNDTYPE expr_type = expr.type.fundType(); + bool simpleConstToSimpleDestination = exprConst != null && expr.type.isSimpleOrEnum() && + dest.isSimpleOrEnum(); + + if (simpleConstToSimpleDestination && expr_type == FUNDTYPE.FT_STRUCT) + { + // We have a constant decimal that is out of range of the destination type. + // In both checked and unchecked contexts we issue an error. No need to recheck conversion in unchecked context. + // Decimal is a SimpleType represented in a FT_STRUCT + ErrorContext.Error(ErrorCode.ERR_ConstOutOfRange, exprConst.asCONSTANT().Val.decVal.ToString(CultureInfo.InvariantCulture), dest); + } + else if (simpleConstToSimpleDestination && Context.CheckedConstant) + { + // check if we failed because we are in checked mode... + bool okNow = canExplicitConversionBeBoundInUncheckedContext(expr, expr.type, destExpr, flags | CONVERTTYPE.NOUDC); + + if (!okNow) + { + CantConvert(expr, dest); + goto CANTCONVERT; + } + + // Failed because value was out of range. Report nifty error message. + if (expr_type <= FUNDTYPE.FT_LASTINTEGRAL) + { + if (expr.type.isUnsigned()) + value = ((ulong)(exprConst.asCONSTANT()).I64Value).ToString(CultureInfo.InvariantCulture); + else + value = ((long)(exprConst.asCONSTANT()).I64Value).ToString(CultureInfo.InvariantCulture); + } + else if (expr_type <= FUNDTYPE.FT_LASTNUMERIC) + { + value = (exprConst.asCONSTANT()).Val.doubleVal.ToString(CultureInfo.InvariantCulture); + } + else + { + // We should have taken care of constant decimal conversion errors + Debug.Assert(expr_type == FUNDTYPE.FT_STRUCT); + Debug.Assert(false, "Error in constant conversion logic!"); + } + ErrorContext.Error(ErrorCode.ERR_ConstOutOfRangeChecked, value, dest); + } + + else if (expr.type is NullType && dest.fundType() != FUNDTYPE.FT_REF) + { + ErrorContext.Error(ErrorCode.ERR_ValueCantBeNull, dest); + } + else if (expr.isMEMGRP()) + { + BindGrpConversion(expr.asMEMGRP(), dest, true); + } + else + { + CantConvert(expr, dest); + } + } + } + CANTCONVERT: + exprResult = ExprFactory.CreateCast(0, destExpr, expr); + exprResult.SetError(); + return exprResult; + } + + private void CantConvert(EXPR expr, CType dest) + { + // Generic "can't convert" error. + // Only report if we dont have an error type. + if (expr.type != null && !(expr.type is ErrorType)) + { + ErrorContext.Error(ErrorCode.ERR_NoExplicitConv, new ErrArg(expr.type, ErrArgFlags.Unique), new ErrArg(dest, ErrArgFlags.Unique)); + } + } + public EXPR mustCast(EXPR expr, CType dest) + { + return mustCast(expr, dest, 0); + } + public EXPR mustCast(EXPR expr, CType dest, CONVERTTYPE flags) + { + EXPRCLASS exprDest = ExprFactory.MakeClass(dest); + return mustCastCore(expr, exprDest, flags); + } + private EXPR mustCastInUncheckedContext(EXPR expr, CType dest, CONVERTTYPE flags) + { + CheckedContext ctx = CheckedContext.CreateInstance(Context, false /*checkedNormal*/, false /*checkedConstant*/); + return (new ExpressionBinder(ctx)).mustCast(expr, dest, flags); + } + + // returns true if an explicit conversion exists from source type to dest type. flags is an optional parameter. + private bool canCast(CType src, CType dest, CONVERTTYPE flags) + { + EXPRCLASS destExpr = ExprFactory.MakeClass(dest); + return BindExplicitConversion(null, src, destExpr, dest, flags); + } + + /*************************************************************************************************** + Convert a method group to a delegate type. + + NOTE: Currently it is not well defined when there is an implicit conversion from a method + group to a delegate type. There are several possibilities. On the two extremes are: + + (1) (Most permissive) When there is at least one applicable method in the method group. + + (2) (Most restrictive) When all of the following are satisified: + * Overload resolution does not produce an error + * The method's parameter types don't require any conversions other than implicit reference + conversions. + * The method's return type is compatible. + * The method's constraints are satisified. + * The method is not conditional. + + For (1), it may be the case that an error is produced whenever the conversion is actually used. + For example, if the result of overload resolution is ambiguous or if the result of overload + resolution is a method with the wrong return result or with unsatisfied constraints. + + For (2), the intent is that if the answer is yes, then an error is never produced. + + Note that (2) is not monotone: adding a method to the method group may cause the answer + to go from yes to no. This has a very odd effect in certain situations: + + Suppose: + * I1 and J1 are interfaces with I1 : J1. + * I2, J2 and K2 are interfaces with I2 : J2, K2. + * Di is a delegate type with signature void Di(Ii). + * A method group named F contains F(D1(I1)) and F(D2(I2)). + * There is another method group named M containing a subset of: + void M(J1) + void M(J2) + void M(K2) + + Under any of the definitions we're considering: + + * If M is { M(J1), M(J2) } then F(M) is an error (ambiguous between F(D1) and F(D2)). + * If M is { M(J1), M(K2) } then F(M) is an error (ambiguous between F(D1) and F(D2)). + * If M is { M(J2), M(K2) } then F(M) is an error (M -> D2 is ambiguous). + + If M is { M(J1), M(J2), M(K2) } what should F(M) be? It seems logical for F(M) to be ambiguous + in this case as well. However, under definition (2), there is no implicit conversion from M + to D2 (since overload resolution is ambiguous). Thus F(M) is unambiguously taken to mean + F(D1) applied to M(J1). Note that the user has just made the situation more ambiguous by having + all three methods in the method group, but we ignore this additional ambiguity and pick a + winner (rather arbitrarily). + + We currently implement (1). The spec needs to be tightened up. + REVIEW : Fix the spec. + ***************************************************************************************************/ + public bool BindGrpConversion(EXPRMEMGRP grp, CType typeDst, bool fReportErrors) + { + EXPRCALL dummy; + return BindGrpConversion(grp, typeDst, false, out dummy, fReportErrors); + } + + public bool BindGrpConversion(EXPRMEMGRP grp, CType typeDst, bool needDest, out EXPRCALL pexprDst, bool fReportErrors) + { + pexprDst = null; + + if (!typeDst.isDelegateType()) + { + if (fReportErrors) + ErrorContext.Error(ErrorCode.ERR_MethGrpToNonDel, grp.name, typeDst); + return false; + } + AggregateType type = typeDst.AsAggregateType(); + MethodSymbol methCtor; + MethodSymbol methInvoke; + methCtor = SymbolLoader.PredefinedMembers.FindDelegateConstructor(type.getAggregate(), fReportErrors); + if (methCtor == null) + return false; + // Now, find the invoke function on the delegate. + methInvoke = SymbolLoader.LookupInvokeMeth(type.getAggregate()); + Debug.Assert(methInvoke != null && methInvoke.isInvoke()); + TypeArray @params = GetTypes().SubstTypeArray(methInvoke.Params, type); + CType typeRet = GetTypes().SubstType(methInvoke.RetType, type); + // Next, verify that the function has a suitable type for the invoke method. + MethPropWithInst mpwiWrap; + MethPropWithInst mpwiAmbig; + MethWithInst mwiWrap; + MethWithInst mwiAmbig; + + if (!BindGrpConversionCore(out mpwiWrap, BindingFlag.BIND_NOPARAMS, grp, ref @params, type, fReportErrors, out mpwiAmbig)) + { + return false; + } + + mwiWrap = new MethWithInst(mpwiWrap); + mwiAmbig = new MethWithInst(mpwiAmbig); + + bool isExtensionMethod = false; + // If the method we have bound to is an extension method and we are using it as an extension and not as a static method + if (methInvoke.Params.Size < @params.Size && mwiWrap.Meth().IsExtension()) + { + isExtensionMethod = true; + TypeArray extParams = GetTypes().SubstTypeArray(mwiWrap.Meth().Params, mwiWrap.GetType()); + // The this parameter must be a reference type. + if (extParams.Item(0).IsTypeParameterType() ? !@params.Item(0).IsRefType() : !extParams.Item(0).IsRefType()) + { + // We should issue a better message here. This is tracked by DevDiv Bugs 142866 + // We were only disallowing value types, hence the error message specific to value types. + // Now we are issuing the same error message for not-known to be reference types, not just value types. + ErrorContext.Error(ErrorCode.ERR_ValueTypeExtDelegate, mwiWrap, extParams.Item(0).IsTypeParameterType() ? @params.Item(0) : extParams.Item(0)); + } + } + + // From here on we should only return true. + if (!fReportErrors && !needDest) + return true; + + // Note: We report errors below even if fReportErrors is false. Note however that we only + // get here if pexprDst is non-null and we'll return true even if we report an error, so this + // is really the only chance we'll get to report the error. + bool fError = (bool)mwiAmbig; + + if (mwiAmbig && !fReportErrors) + { + // Report the ambiguity, since BindGrpConversionCore didn't. + ErrorContext.Error(ErrorCode.ERR_AmbigCall, mwiWrap, mwiAmbig); + } + CType typeRetReal = GetTypes().SubstType(mwiWrap.Meth().RetType, mwiWrap.Ats, mwiWrap.TypeArgs); + if (typeRet != typeRetReal && !CConversions.FImpRefConv(GetSymbolLoader(), typeRetReal, typeRet)) + { + ErrorContext.ErrorRef(ErrorCode.ERR_BadRetType, mwiWrap, typeRetReal); + fError = true; + } + + TypeArray paramsReal = GetTypes().SubstTypeArray(mwiWrap.Meth().Params, mwiWrap.Ats, mwiWrap.TypeArgs); + if (paramsReal != @params) + { + for (int i = 0; i < paramsReal.Size; i++) + { + CType param = @params.Item(i); + CType paramReal = paramsReal.Item(i); + + if (param != paramReal && !CConversions.FImpRefConv(GetSymbolLoader(), param, paramReal)) + { + ErrorContext.ErrorRef(ErrorCode.ERR_MethDelegateMismatch, mwiWrap, typeDst); + fError = true; + break; + } + } + } + + EXPR obj = !isExtensionMethod ? grp.GetOptionalObject() : null; + bool bIsMatchingStatic; + bool constrained; + PostBindMethod(0 != (grp.flags & EXPRFLAG.EXF_BASECALL), ref mwiWrap, obj); + obj = AdjustMemberObject(mwiWrap, obj, out constrained, out bIsMatchingStatic); + if (!bIsMatchingStatic) + { + grp.SetMismatchedStaticBit(); + } + obj = isExtensionMethod ? grp.GetOptionalObject() : obj; + Debug.Assert(mwiWrap.Meth().getKind() == SYMKIND.SK_MethodSymbol); + if (mwiWrap.TypeArgs.Size > 0) + { + // Check method type variable constraints. + TypeBind.CheckMethConstraints(GetSemanticChecker(), GetErrorContext(), mwiWrap); + } + if (mwiWrap.Meth().MethKind() == MethodKindEnum.Latent) + { + ErrorContext.ErrorRef(ErrorCode.ERR_PartialMethodToDelegate, mwiWrap); + } + + if (!needDest) + return true; + + EXPRFUNCPTR funcPtr; + funcPtr = ExprFactory.CreateFunctionPointer(grp.flags & EXPRFLAG.EXF_BASECALL, getVoidType(), null, mwiWrap); + if (!mwiWrap.Meth().isStatic || isExtensionMethod) + { + if (mwiWrap.Meth().getClass().isPredefAgg(PredefinedType.PT_G_OPTIONAL)) + { + ErrorContext.Error(ErrorCode.ERR_DelegateOnNullable, mwiWrap); + } + funcPtr.SetOptionalObject(obj); + if (obj != null && obj.type.fundType() != FUNDTYPE.FT_REF) + { + // Must box the object before creating a delegate to it. + obj = mustConvert(obj, GetReqPDT(PredefinedType.PT_OBJECT)); + } + } + else + { + funcPtr.SetOptionalObject(null); + obj = ExprFactory.CreateNull(); + } + + MethWithInst mwi = new MethWithInst(methCtor, type); + grp.SetOptionalObject(null); + EXPRCALL call = ExprFactory.CreateCall(EXPRFLAG.EXF_NEWOBJCALL | EXPRFLAG.EXF_CANTBENULL, type, ExprFactory.CreateList(obj, funcPtr), grp/*pMemGroup*/, mwi); + + pexprDst = call; + return true; + } + + private bool BindGrpConversionCore(out MethPropWithInst pmpwi, BindingFlag bindFlags, EXPRMEMGRP grp, ref TypeArray args, AggregateType atsDelegate, bool fReportErrors, out MethPropWithInst pmpwiAmbig) + { + bool retval = false; + int carg = args.Size; + + ArgInfos argParam = new ArgInfos(); + argParam.carg = args.Size; + argParam.types = args; + argParam.fHasExprs = false; + GroupToArgsBinder binder = new GroupToArgsBinder(this, bindFlags, grp, argParam, null, false, atsDelegate); + retval = binder.Bind(fReportErrors); + GroupToArgsBinderResult result = binder.GetResultsOfBind(); + pmpwi = result.GetBestResult(); + pmpwiAmbig = result.GetAmbiguousResult(); + return retval; + } + /* + * bindInstanceParamForExtension + * + * This method is called by canConvert for the case of the instance parameter on the extension method + * + */ + private bool canConvertInstanceParamForExtension(EXPR exprSrc, CType typeDest) + { + if (exprSrc == null || exprSrc.type == null) + { + return false; + } + return canConvertInstanceParamForExtension(exprSrc.type, typeDest); + } + + private bool canConvertInstanceParamForExtension(CType typeSrc, CType typeDest) + { + // 26.2.3 Extension method invocations + // + // The following conversions are defined of instance params on Extension methods + // + // * Identity conversions + // * Implicit reference conversions + // * Boxing conversions + + // Always make sure both types are declared. + return CConversions.FIsSameType(typeSrc, typeDest) || + CConversions.FImpRefConv(GetSymbolLoader(), typeSrc, typeDest) || + CConversions.FBoxingConv(GetSymbolLoader(), typeSrc, typeDest); + } + + private bool BindImplicitConversion(EXPR pSourceExpr, CType pSourceType, EXPRTYPEORNAMESPACE pDestinationTypeExpr, CType pDestinationTypeForLambdaErrorReporting, CONVERTTYPE flags) + { + + ImplicitConversion binder = new ImplicitConversion(this, pSourceExpr, pSourceType, pDestinationTypeExpr, false, flags); + return binder.Bind(); + } + private bool BindImplicitConversion(EXPR pSourceExpr, CType pSourceType, EXPRTYPEORNAMESPACE pDestinationTypeExpr, CType pDestinationTypeForLambdaErrorReporting, out EXPR ppDestinationExpr, CONVERTTYPE flags) + { + ImplicitConversion binder = new ImplicitConversion(this, pSourceExpr, pSourceType, pDestinationTypeExpr, true, flags); + bool result = binder.Bind(); + ppDestinationExpr = binder.ExprDest; + return result; + } + + private bool BindImplicitConversion(EXPR pSourceExpr, CType pSourceType, EXPRTYPEORNAMESPACE pDestinationTypeExpr, CType pDestinationTypeForLambdaErrorReporting, bool needsExprDest, out EXPR ppDestinationExpr, CONVERTTYPE flags) + { + ImplicitConversion binder = new ImplicitConversion(this, pSourceExpr, pSourceType, pDestinationTypeExpr, needsExprDest, flags); + bool result = binder.Bind(); + ppDestinationExpr = needsExprDest ? binder.ExprDest : null; + return result; + } + + private bool BindExplicitConversion(EXPR pSourceExpr, CType pSourceType, EXPRTYPEORNAMESPACE pDestinationTypeExpr, CType pDestinationTypeForLambdaErrorReporting, bool needsExprDest, out EXPR ppDestinationExpr, CONVERTTYPE flags) + { + ExplicitConversion binder = new ExplicitConversion(this, pSourceExpr, pSourceType, pDestinationTypeExpr, pDestinationTypeForLambdaErrorReporting, needsExprDest, flags); + bool result = binder.Bind(); + ppDestinationExpr = needsExprDest ? binder.ExprDest : null; + return result; + } + + private bool BindExplicitConversion(EXPR pSourceExpr, CType pSourceType, EXPRTYPEORNAMESPACE pDestinationTypeExpr, CType pDestinationTypeForLambdaErrorReporting, out EXPR ppDestinationExpr, CONVERTTYPE flags) + { + ExplicitConversion binder = new ExplicitConversion(this, pSourceExpr, pSourceType, pDestinationTypeExpr, pDestinationTypeForLambdaErrorReporting, true, flags); + bool result = binder.Bind(); + ppDestinationExpr = binder.ExprDest; + return result; + } + + private bool BindExplicitConversion(EXPR pSourceExpr, CType pSourceType, EXPRTYPEORNAMESPACE pDestinationTypeExpr, CType pDestinationTypeForLambdaErrorReporting, CONVERTTYPE flags) + { + ExplicitConversion binder = new ExplicitConversion(this, pSourceExpr, pSourceType, pDestinationTypeExpr, pDestinationTypeForLambdaErrorReporting, false, flags); + return binder.Bind(); + } + + /*************************************************************************************************** + Binds a user-defined conversion. The parameters to this procedure are the same as + BindImplicitConversion, except the last: implicitOnly - only consider implicit conversions. + + This is a helper routine for BindImplicitConversion and BindExplicitConversion. + + It's non trivial to get this right in the presence of generics. e.g. + + class D { + static implicit operator B (D x) { ... } + } + + class E : D, A> { } + + E x; + List y = x; + + The locals below would have the following values: + + typeList->sym: D, A> + typeCur: E + typeConv = subst(typeList->sym, typeCur) + = subst(D, !0>, ) = D, int> + + retType: B + typeTo = subst(retType, typeConv) + = subst(!0, , int>) = List + params->Item(0): D + typeFrom = subst(params->Item(0), typeConv) + = subst(D, , int>) + = D, int> = typeConv + + REVIEW : Consider caching the results of this. Performing this over and over again + could be a big time waster. + + For lifting over nullable: + * Look in the most base types for the conversions (not in System.Nullable). + * We only lift if both the source type and destination type are nullable and the input + or output of the conversion is not a nullable. + * When we lift we count the number of types (0, 1, 2) that need to be lifted. + A conversion that needs fewer lifts is better than one that requires more (if the lifted + forms have identical signatures). + ***************************************************************************************************/ + private bool bindUserDefinedConversion(EXPR exprSrc, CType typeSrc, CType typeDst, bool needExprDest, out EXPR pexprDst, bool fImplicitOnly) + { + pexprDst = null; + Debug.Assert(exprSrc == null || exprSrc.type == typeSrc); + + // If either type is an interface we should never employ a UD conversion. + if (typeSrc == null || typeDst == null || typeSrc.isInterfaceType() || typeDst.isInterfaceType()) + return false; + CType typeSrcBase = typeSrc.StripNubs(); + CType typeDstBase = typeDst.StripNubs(); + + // Whether we should consider lifted (over nullable) operators. This is + // true exactly when both the source and destination types are nullable. + bool fLiftSrc = typeSrcBase != typeSrc; + bool fLiftDst = typeDstBase != typeDst; + bool fDstHasNull = fLiftDst || typeDst.IsRefType() || typeDst.IsPointerType(); + AggregateType[] rgats = new AggregateType[2]; + int cats = 0; + + // This will be true if it must be the case that either the operator is implicit + // or the from-type of the operator must be the same as the source type. + // This is true when the source type is a type variable. + bool fImplicitOrExactSrc = fImplicitOnly; + + // This flag will be true if we should ignore the IntPtr/UIntPtr -> int/uint conversion + // in favor of the IntPtr/UIntPtr -> long/ulong conversion. + bool fIntPtrOverride2 = false; + + // Get the list of operators from the source. + if (typeSrcBase.IsTypeParameterType()) + { + AggregateType atsBase = typeSrcBase.AsTypeParameterType().GetEffectiveBaseClass(); + if (atsBase != null && atsBase.getAggregate().HasConversion(this.GetSymbolLoader())) + { + rgats[cats++] = atsBase; + } + + // If an implicit conversion exists from the class bound to typeDst, then + // an implicit conversion exists from typeSrc to typeDst. An explicit from + // the class bound to typeDst doesn't buy us anything. + // We can still use an explicit conversion that has this type variable (or + // nullable of it) as its from-type. + fImplicitOrExactSrc = true; + } + else if (typeSrcBase.IsAggregateType() && typeSrcBase.getAggregate().HasConversion(this.GetSymbolLoader())) + { + rgats[cats++] = typeSrcBase.AsAggregateType(); + fIntPtrOverride2 = typeSrcBase.isPredefType(PredefinedType.PT_INTPTR) || typeSrcBase.isPredefType(PredefinedType.PT_UINTPTR); + } + + // Get the list of operators from the destination. + if (typeDstBase.IsTypeParameterType()) + { + // If an explicit conversion exists from typeSrc to the class bound, then + // an explicit conversion exists from typeSrc to typeDst. An implicit is no better + // than an explicit. + AggregateType atsBase; + if (!fImplicitOnly && (atsBase = typeDstBase.AsTypeParameterType().GetEffectiveBaseClass()).getAggregate().HasConversion(this.GetSymbolLoader())) + { + rgats[cats++] = atsBase; + } + } + else if (typeDstBase.IsAggregateType()) + { + if (typeDstBase.getAggregate().HasConversion(this.GetSymbolLoader())) + { + rgats[cats++] = typeDstBase.AsAggregateType(); + } + + if (fIntPtrOverride2 && !typeDstBase.isPredefType(PredefinedType.PT_LONG) && !typeDstBase.isPredefType(PredefinedType.PT_ULONG)) + { + fIntPtrOverride2 = false; + } + } + else + { + fIntPtrOverride2 = false; + } + + // If there are no user defined conversions, we're done. + if (cats == 0) + return false; + + List prguci = new List(); + CType typeBestSrc = null; + CType typeBestDst = null; + bool fBestSrcExact = false; + bool fBestDstExact = false; + int iuciBestSrc = -1; + int iuciBestDst = -1; + + CType typeFrom; + CType typeTo; + + // In the first pass if we find types that are non-comparable, keep one of the types and keep going. + for (int iats = 0; iats < cats; iats++) + { + for (AggregateType atsCur = rgats[iats]; atsCur != null && atsCur.getAggregate().HasConversion(this.GetSymbolLoader()); atsCur = atsCur.GetBaseClass()) + { + AggregateSymbol aggCur = atsCur.getAggregate(); + + // We need to ship with an RTM bug that allows non-standard conversions with these guys. + PredefinedType aggPredefType = aggCur.GetPredefType(); + bool fIntPtrStandard = (aggCur.IsPredefined() && + (aggPredefType == PredefinedType.PT_INTPTR || + aggPredefType == PredefinedType.PT_UINTPTR || + aggPredefType == PredefinedType.PT_DECIMAL)); + + for (MethodSymbol convCur = aggCur.GetFirstUDConversion(); convCur != null; convCur = convCur.ConvNext()) + { + if (convCur.Params.Size != 1) + { + // DDBugs 123512 - If we have a user-defined conversion that + // does not specify the correct number of parameters, we may + // still get here. At this point, we dont want to consider + // the broken conversion, so we simply skip it and move on. + continue; + } + Debug.Assert(convCur.getClass() == aggCur); + + if (fImplicitOnly && !convCur.isImplicit()) + continue; + if (GetSemanticChecker().CheckBogus(convCur)) + continue; + + // Get the substituted src and dst types. + typeFrom = GetTypes().SubstType(convCur.Params.Item(0), atsCur); + typeTo = GetTypes().SubstType(convCur.RetType, atsCur); + + bool fNeedImplicit = fImplicitOnly; + + // If fImplicitOrExactSrc is set then it must be the case that either the conversion + // is implicit or the from-type must be the src type (modulo nullables). + if (fImplicitOrExactSrc && !fNeedImplicit && typeFrom.StripNubs() != typeSrcBase) + { + if (!convCur.isImplicit()) + continue; + fNeedImplicit = true; + } + + { // REVIEW : Can this check be removed? + FUNDTYPE ftFrom; + FUNDTYPE ftTo; + + if ((ftTo = typeTo.fundType()) <= FUNDTYPE.FT_LASTNUMERIC && ftTo > FUNDTYPE.FT_NONE && + (ftFrom = typeFrom.fundType()) <= FUNDTYPE.FT_LASTNUMERIC && ftFrom > FUNDTYPE.FT_NONE) + { + continue; + } + } + + // Ignore the IntPtr/UIntPtr -> int/uint conversion in favor of + // the IntPtr/UIntPtr -> long/ulong conversion. + if (fIntPtrOverride2 && (typeTo.isPredefType(PredefinedType.PT_INT) || typeTo.isPredefType(PredefinedType.PT_UINT))) + continue; + + // Lift the conversion if needed. + if (fLiftSrc && (fDstHasNull || !fNeedImplicit) && typeFrom.IsNonNubValType()) + typeFrom = GetTypes().GetNullable(typeFrom); + if (fLiftDst && typeTo.IsNonNubValType()) + typeTo = GetTypes().GetNullable(typeTo); + + // Check for applicability. + bool fFromImplicit = exprSrc != null ? canConvert(exprSrc, typeFrom, CONVERTTYPE.STANDARDANDNOUDC) : canConvert(typeSrc, typeFrom, CONVERTTYPE.STANDARDANDNOUDC); + if (!fFromImplicit && (fNeedImplicit || + !canConvert(typeFrom, typeSrc, CONVERTTYPE.STANDARDANDNOUDC) && + // We allow IntPtr and UIntPtr to use non-standard explicit casts as long as they don't involve pointer types. + // This is because the framework uses it and RTM allowed it. + (!fIntPtrStandard || typeSrc.IsPointerType() || typeFrom.IsPointerType() || !canCast(typeSrc, typeFrom, CONVERTTYPE.NOUDC)))) + { + continue; + } + bool fToImplicit = canConvert(typeTo, typeDst, CONVERTTYPE.STANDARDANDNOUDC); + if (!fToImplicit && (fNeedImplicit || + !canConvert(typeDst, typeTo, CONVERTTYPE.STANDARDANDNOUDC) && + // We allow IntPtr and UIntPtr to use non-standard explicit casts as long as they don't involve pointer types. + // This is because the framework uses it and RTM allowed it. + (!fIntPtrStandard || typeDst.IsPointerType() || typeTo.IsPointerType() || !canCast(typeTo, typeDst, CONVERTTYPE.NOUDC)))) + { + continue; + } + if (isConvInTable(prguci, convCur, atsCur, fFromImplicit, fToImplicit)) + { + // VSWhidbey 579325: duplicate conversions in the convInfo table cause false ambiguity: + // If a user defined implicit conversion exists in a generic base type, + // it is possible to reach that conversion from both Src and Dst types. In the following + // example, the same implicit conversion is found from both src and dst types. + // + // class A { public static implicit operator B(A a) { return a; } } + // class B : A {} + // class C { void M () { B b = new A(); } } + // + // Note that, this UD implicit conversion is legal. C#20.1.11: + // "If a pre-defined explicit conversion (Section 6.2) exists from type S to type T, + // any user-defined explicit conversions from S to T are ignored. However, + // user-defined implicit conversions from S to T are still considered." + // Also notice that this check is O(n2) in found UD conversions. + continue; + } + + // The conversion is applicable so it affects the best types. + + prguci.Add(new UdConvInfo()); + prguci[prguci.Count - 1].mwt = new MethWithType(); + prguci[prguci.Count - 1].mwt.Set(convCur, atsCur); + prguci[prguci.Count - 1].fSrcImplicit = fFromImplicit; + prguci[prguci.Count - 1].fDstImplicit = fToImplicit; + + if (!fBestSrcExact) + { + if (typeFrom == typeSrc) + { + Debug.Assert((typeBestSrc == null) == (typeBestDst == null)); // If typeBestSrc is null then typeBestDst should be null. + Debug.Assert(fFromImplicit); + typeBestSrc = typeFrom; + iuciBestSrc = prguci.Count - 1; + fBestSrcExact = true; + } + else if (typeBestSrc == null) + { + Debug.Assert(iuciBestSrc == -1); + typeBestSrc = typeFrom; + iuciBestSrc = prguci.Count - 1; + } + else if (typeBestSrc != typeFrom) + { + Debug.Assert(0 <= iuciBestSrc && iuciBestSrc < prguci.Count - 1); + int n = CompareSrcTypesBased(typeBestSrc, prguci[iuciBestSrc].fSrcImplicit, typeFrom, fFromImplicit); + if (n > 0) + { + typeBestSrc = typeFrom; + iuciBestSrc = prguci.Count - 1; + } + } + } + + if (!fBestDstExact) + { + if (typeTo == typeDst) + { + Debug.Assert(fToImplicit); + typeBestDst = typeTo; + iuciBestDst = prguci.Count - 1; + fBestDstExact = true; + } + else if (typeBestDst == null) + { + Debug.Assert(iuciBestDst == -1); + typeBestDst = typeTo; + iuciBestDst = prguci.Count - 1; + } + else if (typeBestDst != typeTo) + { + Debug.Assert(0 <= iuciBestDst && iuciBestDst < prguci.Count - 1); + int n = CompareDstTypesBased(typeBestDst, prguci[iuciBestDst].fDstImplicit, typeTo, fToImplicit); + if (n > 0) + { + typeBestDst = typeTo; + iuciBestDst = prguci.Count - 1; + } + } + } + } + } + } + + Debug.Assert((typeBestSrc == null) == (typeBestDst == null)); + if (typeBestSrc == null) + { + Debug.Assert(iuciBestSrc == -1 && iuciBestDst == -1); + return false; + } + + Debug.Assert(0 <= iuciBestSrc && iuciBestSrc < prguci.Count); + Debug.Assert(0 <= iuciBestDst && iuciBestDst < prguci.Count); + + int ctypeLiftBest = 3; // Bigger than any legal value on purpose. + int iuciBest = -1; + int iuciAmbig = -1; + + // In the second pass, we verify that the types we ended up with are indeed minimal and find the one valid conversion. + for (int iuci = 0; iuci < prguci.Count; iuci++) + { + UdConvInfo uci = prguci[iuci]; + + // Get the substituted src and dst types. + typeFrom = GetTypes().SubstType(uci.mwt.Meth().Params.Item(0), uci.mwt.GetType()); + typeTo = GetTypes().SubstType(uci.mwt.Meth().RetType, uci.mwt.GetType()); + + int ctypeLift = 0; + + // Lift the conversion if needed. + if (fLiftSrc && typeFrom.IsNonNubValType()) + { + typeFrom = GetTypes().GetNullable(typeFrom); + ctypeLift++; + } + if (fLiftDst && typeTo.IsNonNubValType()) + { + typeTo = GetTypes().GetNullable(typeTo); + ctypeLift++; + } + + if (typeFrom == typeBestSrc && typeTo == typeBestDst) + { + // Record the matching conversions. + if (ctypeLiftBest > ctypeLift) + { + // This one is better. + iuciBest = iuci; + iuciAmbig = -1; + ctypeLiftBest = ctypeLift; + continue; + } + + if (ctypeLiftBest < ctypeLift) + { + // Current answer is better. + continue; + } + + // Ambiguous at this lifting level. This only guarantees an error if the + // lifting level is zero. + if (iuciAmbig < 0) + { + iuciAmbig = iuci; + if (ctypeLift == 0) + { + // No point continuing. We have an error. + break; + } + } + continue; + } + + Debug.Assert(typeFrom != typeBestSrc || typeTo != typeBestDst); + + // Verify that the best types are indeed best. Must NOT compare if the best type is exact. + // This is not just an efficiency issue. With nullables there are types that are implicitly + // convertible to each other (eg, int? and int??) and hence not distinguishable by CompareXxxTypesBase. + if (!fBestSrcExact && typeFrom != typeBestSrc) + { + int n = CompareSrcTypesBased(typeBestSrc, prguci[iuciBestSrc].fSrcImplicit, typeFrom, uci.fSrcImplicit); + Debug.Assert(n <= 0); + if (n >= 0) + { + if (!needExprDest) + return true; + iuciBestDst = iuci; + pexprDst = HandleAmbiguity(exprSrc, typeSrc, typeDst, prguci, iuciBestSrc, iuciBestDst); + return true; + } + } + if (!fBestDstExact && typeTo != typeBestDst) + { + int n = CompareDstTypesBased(typeBestDst, prguci[iuciBestDst].fDstImplicit, typeTo, uci.fDstImplicit); + Debug.Assert(n <= 0); + if (n >= 0) + { + if (!needExprDest) + return true; + iuciBestDst = iuci; + pexprDst = HandleAmbiguity(exprSrc, typeSrc, typeDst, prguci, iuciBestSrc, iuciBestDst); + return true; + } + } + } + + if (!needExprDest) + return true; + + if (iuciBest < 0) + { + pexprDst = HandleAmbiguity(exprSrc, typeSrc, typeDst, prguci, iuciBestSrc, iuciBestDst); + return true; + } + if (iuciAmbig >= 0) + { + iuciBestSrc = iuciBest; + iuciBestDst = iuciAmbig; + pexprDst = HandleAmbiguity(exprSrc, typeSrc, typeDst, prguci, iuciBestSrc, iuciBestDst); + return true; + } + + MethWithInst mwiBest = new MethWithInst(prguci[iuciBest].mwt.Meth(), prguci[iuciBest].mwt.GetType(), null); + + Debug.Assert(ctypeLiftBest <= 2); + + typeFrom = GetTypes().SubstType(mwiBest.Meth().Params.Item(0), mwiBest.GetType()); + typeTo = GetTypes().SubstType(mwiBest.Meth().RetType, mwiBest.GetType()); + + EXPR exprDst; + EXPR pTransformedArgument = exprSrc; + + if (ctypeLiftBest > 0 && !typeFrom.IsNullableType() && fDstHasNull) + { + // Create the memgroup. + EXPRMEMGRP pMemGroup = ExprFactory.CreateMemGroup(null, mwiBest); + + // Need to lift over the null. + Debug.Assert(fLiftSrc || fLiftDst); + exprDst = ExprFactory.CreateCall(0, typeDst, exprSrc, pMemGroup, mwiBest); + Debug.Assert(exprDst.isCALL()); + + // We want to bind the unlifted conversion first. + EXPR nonLiftedArg = mustCast(exprSrc, typeFrom); + MarkAsIntermediateConversion(nonLiftedArg); + EXPR nonLiftedResult = BindUDConversionCore(nonLiftedArg, typeFrom, typeTo, typeDst, mwiBest); + EXPRCALL call = exprDst.asCALL(); + + call.castOfNonLiftedResultToLiftedType = mustCast(nonLiftedResult, typeDst); + call.nubLiftKind = NullableCallLiftKind.UserDefinedConversion; + + if (fLiftSrc) + { + // If lifting of the source is required, we need to figure out the intermediate conversion + // from the type of the source to the type of the UD conversion parameter. Note that typeFrom + // is not a nullable type. + EXPR pConversionArgument = null; + if (typeFrom != typeSrcBase) + { + // There is an intermediate conversion. + NullableType pConversionNubSourceType = SymbolLoader.GetTypeManager().GetNullable(typeFrom); + pConversionArgument = mustCast(exprSrc, pConversionNubSourceType); + MarkAsIntermediateConversion(pConversionArgument); + } + else + { + if (typeTo.IsNullableType()) + { + // We need to generate a nullable value access, the conversion will be used without lifting. + pConversionArgument = mustCast(exprSrc, typeFrom); + } + else + { + pConversionArgument = exprSrc; + } + } + Debug.Assert(pConversionArgument != null); + EXPR pConversionCall = ExprFactory.CreateCall(0, typeDst, pConversionArgument, pMemGroup, mwiBest); + Debug.Assert(pConversionCall.isCALL()); + pConversionCall.asCALL().nubLiftKind = NullableCallLiftKind.NotLiftedIntermediateConversion; + call.pConversions = pConversionCall; + } + else + { + EXPR pConversionCall = BindUDConversionCore(nonLiftedArg, typeFrom, typeTo, typeDst, mwiBest); + MarkAsIntermediateConversion(pConversionCall); + call.pConversions = pConversionCall; + } + } + else + { + exprDst = BindUDConversionCore(exprSrc, typeFrom, typeTo, typeDst, mwiBest, out pTransformedArgument); + } + + pexprDst = ExprFactory.CreateUserDefinedConversion(pTransformedArgument, exprDst, mwiBest); + return true; + } + + private EXPR HandleAmbiguity(EXPR exprSrc, CType typeSrc, CType typeDst, List prguci, int iuciBestSrc, int iuciBestDst) + { + EXPR pexprDst; + Debug.Assert(0 <= iuciBestSrc && iuciBestSrc < prguci.Count); + Debug.Assert(0 <= iuciBestDst && iuciBestDst < prguci.Count); + ErrorContext.Error(ErrorCode.ERR_AmbigUDConv, prguci[iuciBestSrc].mwt, prguci[iuciBestDst].mwt, typeSrc, typeDst); + EXPRCLASS exprClass = ExprFactory.MakeClass(typeDst); + pexprDst = ExprFactory.CreateCast(0, exprClass, exprSrc); + pexprDst.SetError(); + return pexprDst; + } + + private void MarkAsIntermediateConversion(EXPR pExpr) + { + Debug.Assert(pExpr != null); + if (pExpr.isCALL()) + { + switch (pExpr.asCALL().nubLiftKind) + { + default: + break; + case NullableCallLiftKind.NotLifted: + pExpr.asCALL().nubLiftKind = NullableCallLiftKind.NotLiftedIntermediateConversion; + break; + case NullableCallLiftKind.NullableConversion: + pExpr.asCALL().nubLiftKind = NullableCallLiftKind.NullableIntermediateConversion; + break; + case NullableCallLiftKind.NullableConversionConstructor: + MarkAsIntermediateConversion(pExpr.asCALL().GetOptionalArguments()); + break; + } + } + else if (pExpr.isUSERDEFINEDCONVERSION()) + { + MarkAsIntermediateConversion(pExpr.asUSERDEFINEDCONVERSION().UserDefinedCall); + } + } + + private EXPR BindUDConversionCore(EXPR pFrom, CType pTypeFrom, CType pTypeTo, CType pTypeDestination, MethWithInst mwiBest) + { + EXPR ppTransformedArgument; + return BindUDConversionCore(pFrom, pTypeFrom, pTypeTo, pTypeDestination, mwiBest, out ppTransformedArgument); + } + + private EXPR BindUDConversionCore(EXPR pFrom, CType pTypeFrom, CType pTypeTo, CType pTypeDestination, MethWithInst mwiBest, out EXPR ppTransformedArgument) + { + EXPRCLASS pClassFrom = ExprFactory.MakeClass(pTypeFrom); + EXPR pTransformedArgument = mustCastCore(pFrom, pClassFrom, CONVERTTYPE.NOUDC); + Debug.Assert(pTransformedArgument != null); + EXPRMEMGRP pMemGroup = ExprFactory.CreateMemGroup(null, mwiBest); + EXPRCALL pCall = ExprFactory.CreateCall(0, pTypeTo, pTransformedArgument, pMemGroup, mwiBest); + EXPRCLASS pDestination = ExprFactory.MakeClass(pTypeDestination); + EXPR pCast = mustCastCore(pCall, pDestination, CONVERTTYPE.NOUDC); + Debug.Assert(pCast != null); + ppTransformedArgument = pTransformedArgument; + return pCast; + } + + /* + * Fold a constant cast. Returns true if the constant could be folded. + */ + private ConstCastResult bindConstantCast(EXPR exprSrc, EXPRTYPEORNAMESPACE exprTypeDest, bool needExprDest, out EXPR pexprDest, bool explicitConversion) + { + pexprDest = null; + Int64 valueInt = 0; + double valueFlt = 0; + CType typeDest = exprTypeDest.TypeOrNamespace.AsType(); + FUNDTYPE ftSrc = exprSrc.type.fundType(); + FUNDTYPE ftDest = typeDest.fundType(); + bool srcIntegral = (ftSrc <= FUNDTYPE.FT_LASTINTEGRAL); + bool srcNumeric = (ftSrc <= FUNDTYPE.FT_LASTNUMERIC); + + EXPRCONSTANT constSrc = exprSrc.GetConst().asCONSTANT(); + Debug.Assert(constSrc != null); + if (ftSrc == FUNDTYPE.FT_STRUCT || ftDest == FUNDTYPE.FT_STRUCT) + { + // Do constant folding involving decimal constants. + EXPR expr = bindDecimalConstCast(exprTypeDest, exprSrc.type, constSrc); + + if (expr == null) + { + if (explicitConversion) + { + return ConstCastResult.CheckFailure; + } + return ConstCastResult.Failure; + } + if (needExprDest) + pexprDest = expr; + return ConstCastResult.Success; + } + + if (explicitConversion && Context.CheckedConstant && !isConstantInRange(constSrc, typeDest, true)) + { + return ConstCastResult.CheckFailure; + } + + if (!needExprDest) + { + return ConstCastResult.Success; + } + + + // Get the source constant value into valueInt or valueFlt. + if (srcIntegral) + { + if (constSrc.type.fundType() == FUNDTYPE.FT_U8) + { + // If we're going from ulong to something, make sure we can fit. + if (ftDest == FUNDTYPE.FT_U8) + { + CONSTVAL cv = GetExprConstants().Create(constSrc.getU64Value()); + pexprDest = ExprFactory.CreateConstant(typeDest, cv); + return ConstCastResult.Success; + } + valueInt = (Int64)(constSrc.getU64Value() & 0xFFFFFFFFFFFFFFFF); + } + else + { + valueInt = constSrc.getI64Value(); + } + } + else if (srcNumeric) + { + valueFlt = constSrc.getVal().doubleVal; + } + else + { + return ConstCastResult.Failure; + } + + // Convert constant to the destination type, truncating if necessary. + // valueInt or valueFlt contains the result of the conversion. + switch (ftDest) + { + case FUNDTYPE.FT_I1: + if (!srcIntegral) + { + valueInt = (Int64)valueFlt; + } + valueInt = (sbyte)(valueInt & 0xFF); + break; + case FUNDTYPE.FT_I2: + if (!srcIntegral) + { + valueInt = (Int64)valueFlt; + } + valueInt = (short)(valueInt & 0xFFFF); + break; + case FUNDTYPE.FT_I4: + if (!srcIntegral) + { + valueInt = (Int64)valueFlt; + } + valueInt = (int)(valueInt & 0xFFFFFFFF); + break; + case FUNDTYPE.FT_I8: + if (!srcIntegral) + { + valueInt = (Int64)valueFlt; + } + break; + case FUNDTYPE.FT_U1: + if (!srcIntegral) + { + valueInt = (Int64)valueFlt; + } + valueInt = (byte)(valueInt & 0xFF); + break; + case FUNDTYPE.FT_U2: + if (!srcIntegral) + { + valueInt = (Int64)valueFlt; + } + valueInt = (ushort)(valueInt & 0xFFFF); + break; + case FUNDTYPE.FT_U4: + if (!srcIntegral) + { + valueInt = (Int64)valueFlt; + } + valueInt = (uint)(valueInt & 0xFFFFFFFF); + break; + case FUNDTYPE.FT_U8: + if (!srcIntegral) + { + valueInt = (long)(ulong)valueFlt; + // code below stolen from jit... + const double two63 = 2147483648.0 * 4294967296.0; + if (valueFlt < two63) + { + valueInt = (Int64)valueFlt; + } + else + { + valueInt = ((Int64)(valueFlt - two63)) + I64(0x8000000000000000); + } + } + break; + case FUNDTYPE.FT_R4: + case FUNDTYPE.FT_R8: + if (srcIntegral) + { + if (ftSrc == FUNDTYPE.FT_U8) + { + valueFlt = (double)(ulong)valueInt; + } + else + { + valueFlt = (double)valueInt; + } + } + if (ftDest == FUNDTYPE.FT_R4) + { + // Force to R4 precision/range. + float f; + RoundToFloat(valueFlt, out f); + valueFlt = f; + } + break; + default: + // We got here because of LAF or Refactoring. We must have had a parser + // error here, because the user is not allowed to have a non-value type + // being cast, but we need to bind for errors anyway. + break; + } + + // Create a new constant with the value in "valueInt" or "valueFlt". + { + CONSTVAL cv = new CONSTVAL(); + if (ftDest == FUNDTYPE.FT_U4) + { + cv.uiVal = (uint)valueInt; + } + else if (ftDest <= FUNDTYPE.FT_LASTNONLONG) + { + cv.iVal = (int)valueInt; + } + else if (ftDest <= FUNDTYPE.FT_LASTINTEGRAL) + { + cv = GetExprConstants().Create(valueInt); + } + else + { + cv = GetExprConstants().Create(valueFlt); + } + EXPRCONSTANT expr = ExprFactory.CreateConstant(typeDest, cv); + pexprDest = expr; + } + return ConstCastResult.Success; + } + + /*************************************************************************************************** + This is a helper method for bindUserDefinedConversion. "Compares" two types relative to a + base type and indicates which is "closer" to base. fImplicit(1|2) specifies whether there is a + standard implicit conversion from base to type(1|2). If fImplicit(1|2) is false there should + be a standard explicit conversion from base to type(1|2). The partial ordering used is as + follows: + + * If exactly one of fImplicit(1|2) is true then the corresponding type is closer. + * Otherwise if there is a standard implicit conversion in neither direction or both directions + then neither is closer. + * Otherwise if both of fImplicit(1|2) are true: + * If there is a standard implicit conversion from type(1|2) to type(2|1) then type(1|2) + is closer. + * Otherwise neither is closer. + * Otherwise both of fImplicit(1|2) are false and: + * If there is a standard implicit conversion from type(1|2) to type(2|1) then type(2|1) + is closer. + * Otherwise neither is closer. + + The return value is -1 if type1 is closer, +1 if type2 is closer and 0 if neither is closer. + ***************************************************************************************************/ + private int CompareSrcTypesBased(CType type1, bool fImplicit1, CType type2, bool fImplicit2) + { + Debug.Assert(type1 != type2); + if (fImplicit1 != fImplicit2) + return fImplicit1 ? -1 : +1; + bool fCon1 = canConvert(type1, type2, CONVERTTYPE.NOUDC); + bool fCon2 = canConvert(type2, type1, CONVERTTYPE.NOUDC); + if (fCon1 == fCon2) + return 0; + return (fImplicit1 == fCon1) ? -1 : +1; + } + + /*************************************************************************************************** + This is a helper method for bindUserDefinedConversion. "Compares" two types relative to a + base type and indicates which is "closer" to base. fImplicit(1|2) specifies whether there is a + standard implicit conversion from type(1|2) to base. If fImplicit(1|2) is false there should + be a standard explicit conversion from type(1|2) to base. The partial ordering used is as + follows: + + * If exactly one of fImplicit(1|2) is true then the corresponding type is closer. + * Otherwise if there is a standard implicit conversion in neither direction or both directions + then neither is closer. + * Otherwise if both of fImplicit(1|2) are true: + * If there is a standard implicit conversion from type(1|2) to type(2|1) then type(2|1) + is closer. + * Otherwise neither is closer. + * Otherwise both of fImplicit(1|2) are false and: + * If there is a standard implicit conversion from type(1|2) to type(2|1) then type(1|2) + is closer. + * Otherwise neither is closer. + + The return value is -1 if type1 is closer, +1 if type2 is closer and 0 if neither is closer. + ***************************************************************************************************/ + private int CompareDstTypesBased(CType type1, bool fImplicit1, CType type2, bool fImplicit2) + { + Debug.Assert(type1 != type2); + if (fImplicit1 != fImplicit2) + return fImplicit1 ? -1 : +1; + bool fCon1 = canConvert(type1, type2, CONVERTTYPE.NOUDC); + bool fCon2 = canConvert(type2, type1, CONVERTTYPE.NOUDC); + if (fCon1 == fCon2) + return 0; + return (fImplicit1 == fCon1) ? +1 : -1; + } + /* + * Bind a constant cast to or from decimal. Return null if cast can't be done. + */ + private EXPR bindDecimalConstCast(EXPRTYPEORNAMESPACE exprDestType, CType srcType, EXPRCONSTANT src) + { + CType destType = exprDestType.TypeOrNamespace.AsType(); + CType typeDecimal = SymbolLoader.GetOptPredefType(PredefinedType.PT_DECIMAL); + CONSTVAL cv = new CONSTVAL(); + + if (typeDecimal == null) + return null; + + if (destType == typeDecimal) + { + // Casting to decimal. + + FUNDTYPE ftSrc = srcType.fundType(); + Decimal result; + + switch (ftSrc) + { + case FUNDTYPE.FT_I1: + case FUNDTYPE.FT_I2: + case FUNDTYPE.FT_I4: + result = Convert.ToDecimal(src.getVal().iVal); + break; + case FUNDTYPE.FT_U1: + case FUNDTYPE.FT_U2: + case FUNDTYPE.FT_U4: + result = Convert.ToDecimal(src.getVal().uiVal); + break; + case FUNDTYPE.FT_R4: + result = Convert.ToDecimal((float)src.getVal().doubleVal); + break; + case FUNDTYPE.FT_R8: + result = Convert.ToDecimal(src.getVal().doubleVal); + break; + case FUNDTYPE.FT_U8: + result = Convert.ToDecimal((ulong)src.getVal().longVal); + break; + case FUNDTYPE.FT_I8: + result = Convert.ToDecimal(src.getVal().longVal); + break; + default: + return null; // Not supported cast. + } + + cv = GetExprConstants().Create(result); + EXPRCONSTANT exprConst = ExprFactory.CreateConstant(typeDecimal, cv); + + return exprConst; + } + + if (srcType == typeDecimal) + { + // Casting from decimal + Decimal decTrunc = 0; + + FUNDTYPE ftDest = destType.fundType(); + try + { + if (ftDest != FUNDTYPE.FT_R4 && ftDest != FUNDTYPE.FT_R8) + { + decTrunc = Decimal.Truncate(src.getVal().decVal); + } + switch (ftDest) + { + case FUNDTYPE.FT_I1: + cv.iVal = Convert.ToSByte(decTrunc); + break; + case FUNDTYPE.FT_U1: + cv.uiVal = Convert.ToByte(decTrunc); + break; + case FUNDTYPE.FT_I2: + cv.iVal = Convert.ToInt16(decTrunc); + break; + case FUNDTYPE.FT_U2: + cv.uiVal = Convert.ToUInt16(decTrunc); + break; + case FUNDTYPE.FT_I4: + cv.iVal = Convert.ToInt32(decTrunc); + break; + case FUNDTYPE.FT_U4: + cv.uiVal = Convert.ToUInt32(decTrunc); + break; + case FUNDTYPE.FT_I8: + cv = GetExprConstants().Create(Convert.ToInt64(decTrunc)); + break; + case FUNDTYPE.FT_U8: + cv = GetExprConstants().Create(Convert.ToUInt64(decTrunc)); + break; + case FUNDTYPE.FT_R4: + cv = GetExprConstants().Create(Convert.ToSingle(src.getVal().decVal)); + break; + case FUNDTYPE.FT_R8: + cv = GetExprConstants().Create(Convert.ToDouble(src.getVal().decVal)); + break; + default: + return null; // Not supported cast. + } + } + catch (OverflowException) + { + return null; + } + EXPRCONSTANT exprConst = ExprFactory.CreateConstant(destType, cv); + // Create the cast that was the original tree for this thing. + return exprConst; + } + return null; + } + + private bool canExplicitConversionBeBoundInUncheckedContext(EXPR exprSrc, CType typeSrc, EXPRTYPEORNAMESPACE typeDest, CONVERTTYPE flags) + { + CheckedContext ctx = CheckedContext.CreateInstance(Context, false /*checkedNormal*/, false /*checkedConstant*/); + Debug.Assert(typeDest != null); + Debug.Assert(typeDest.TypeOrNamespace != null); + return (new ExpressionBinder(ctx)).BindExplicitConversion(exprSrc, typeSrc, typeDest, typeDest.TypeOrNamespace.AsType(), flags); + } + + } + + internal static class ListExtensions + { + public static bool IsEmpty(this List list) + { + return list == null || list.Count == 0; + } + public static T Head(this List list) + { + return list[0]; + } + public static List Tail(this List list) + { + T[] array = new T[list.Count]; + list.CopyTo(array, 0); + List newList = new List(array); + newList.RemoveAt(0); + return newList; + } + + } +} diff --git a/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/Conversions.cs b/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/Conversions.cs new file mode 100644 index 000000000..998aea49e --- /dev/null +++ b/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/Conversions.cs @@ -0,0 +1,348 @@ +// ==++== +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// ==--== + +using System.Diagnostics; +using Microsoft.CSharp.RuntimeBinder.Syntax; + +namespace Microsoft.CSharp.RuntimeBinder.Semantics +{ + // Encapsulates all logic about convertibility between types. + // + // WARNING: These methods do not precisely match the spec. + // WARNING: For example most also return true for identiy conversions, + // WARNING: FExpRefConv includes all Implict and Explicit reference conversions. + + internal static class CConversions + { + // WARNING: These methods do not precisely match the spec. + // WARNING: For example most also return true for identiy conversions, + // WARNING: FExpRefConv includes all Implict and Explicit reference conversions. + + /*************************************************************************************************** + Determine whether there is an implicit reference conversion from typeSrc to typeDst. This is + when the source is a reference type and the destination is a base type of the source. Note + that typeDst.IsRefType() may still return false (when both are type parameters). + ***************************************************************************************************/ + public static bool FImpRefConv(SymbolLoader loader, CType typeSrc, CType typeDst) + { + return typeSrc.IsRefType() && loader.HasIdentityOrImplicitReferenceConversion(typeSrc, typeDst); + } + + /*************************************************************************************************** + Determine whether there is an explicit or implicit reference conversion (or identity conversion) + from typeSrc to typeDst. This is when: + + 13.2.3 Explicit reference conversions + + The explicit reference conversions are: + * From object to any reference-type. + * From any class-type S to any class-type T, provided S is a base class of T. + * From any class-type S to any interface-type T, provided S is not sealed and provided S does not implement T. + * From any interface-type S to any class-type T, provided T is not sealed or provided T implements S. + * From any interface-type S to any interface-type T, provided S is not derived from T. + * From an array-type S with an element type SE to an array-type T with an element type TE, provided all of the following are true: + o S and T differ only in element type. (In other words, S and T have the same number of dimensions.) + o An explicit reference conversion exists from SE to TE. + * From System.Array and the interfaces it implements, to any array-type. + * From System.Delegate and the interfaces it implements, to any delegate-type. + * From a one-dimensional array-type S[] to System.Collections.Generic.IList, System.Collections.Generic.IReadOnlyList and their base interfaces, provided there is an explicit reference conversion from S to T. + * From a generic delegate type S to generic delegate type T, provided all of the follow are true: + o Both types are constructed generic types of the same generic delegate type, D.That is, + S is D and T is D. + o S is not compatible with or identical to T. + o If type parameter Xi is declared to be invariant then Si must be identical to Ti. + o If type parameter Xi is declared to be covariant ("out") then Si must be convertible + to Ti via an identify conversion, implicit reference conversion, or explicit reference conversion. + o If type parameter Xi is declared to be contravariant ("in") then either Si must be identical to Ti, + or Si and Ti must both be reference types. + * From System.Collections.Generic.IList, System.Collections.Generic.IReadOnlyList and their base interfaces to a one-dimensional array-type S[], provided there is an implicit or explicit reference conversion from S[] to System.Collections.Generic.IList or System.Collections.Generic.IReadOnlyList. This is precisely when either S and T are the same type or there is an implicit or explicit reference conversion from S to T. + + For a type-parameter T that is known to be a reference type (25.7), the following explicit reference conversions exist: + * From the effective base class C of T to T and from any base class of C to T. + * From any interface-type to T. + * From T to any interface-type I provided there isnt already an implicit reference conversion from T to I. + * From a type-parameter U to T provided that T depends on U (25.7). [Note: Since T is known to be a reference type, within the scope of T, the run-time type of U will always be a reference type, even if U is not known to be a reference type at compile-time. end note] + + * Both src and dst are reference types and there is a builtin explicit conversion from + src to dst. + * Or src is a reference type and dst is a base type of src (in which case the conversion is + implicit as well). + * Or dst is a reference type and src is a base type of dst. + + The latter two cases can happen with type variables even though the other type variable is not + a reference type. + ***************************************************************************************************/ + public static bool FExpRefConv(SymbolLoader loader, CType typeSrc, CType typeDst) + { + Debug.Assert(typeSrc != null); + Debug.Assert(typeDst != null); + if (typeSrc.IsRefType() && typeDst.IsRefType()) + { + // is there an implicit reference conversion in either direction? + // this handles the bulk of the cases ... + if (loader.HasIdentityOrImplicitReferenceConversion(typeSrc, typeDst) || + loader.HasIdentityOrImplicitReferenceConversion(typeDst, typeSrc)) + { + return true; + } + + // For a type-parameter T that is known to be a reference type (25.7), the following explicit reference conversions exist: + // From any interface-type to T. + // From T to any interface-type I provided there isnt already an implicit reference conversion from T to I. + if (typeSrc.isInterfaceType() && typeDst.IsTypeParameterType()) + { + return true; + } + if (typeSrc.IsTypeParameterType() && typeDst.isInterfaceType()) + { + return true; + } + + // * From any class-type S to any interface-type T, provided S is not sealed + // * From any interface-type S to any class-type T, provided T is not sealed + // * From any interface-type S to any interface-type T, provided S is not derived from T. + if (typeSrc.IsAggregateType() && typeDst.IsAggregateType()) + { + AggregateSymbol aggSrc = typeSrc.AsAggregateType().getAggregate(); + AggregateSymbol aggDest = typeDst.AsAggregateType().getAggregate(); + + if ((aggSrc.IsClass() && !aggSrc.IsSealed() && aggDest.IsInterface()) || + (aggSrc.IsInterface() && aggDest.IsClass() && !aggDest.IsSealed()) || + (aggSrc.IsInterface() && aggDest.IsInterface())) + { + return true; + } + } + + // * From an array-type S with an element type SE to an array-type T with an element type TE, provided all of the following are true: + // o S and T differ only in element type. (In other words, S and T have the same number of dimensions.) + // o An explicit reference conversion exists from SE to TE. + if (typeSrc.IsArrayType() && typeDst.IsArrayType()) + { + return typeSrc.AsArrayType().rank == typeDst.AsArrayType().rank && FExpRefConv(loader, typeSrc.AsArrayType().GetElementType(), typeDst.AsArrayType().GetElementType()); + } + + // * From a one-dimensional array-type S[] to System.Collections.Generic.IList, System.Collections.Generic.IReadOnlyList + // and their base interfaces, provided there is an explicit reference conversion from S to T. + if (typeSrc.IsArrayType()) + { + if (typeSrc.AsArrayType().rank != 1 || + !typeDst.isInterfaceType() || typeDst.AsAggregateType().GetTypeArgsAll().Size != 1) + { + return false; + } + + AggregateSymbol aggIList = loader.GetOptPredefAgg(PredefinedType.PT_G_ILIST); + AggregateSymbol aggIReadOnlyList = loader.GetOptPredefAgg(PredefinedType.PT_G_IREADONLYLIST); + + if ((aggIList == null || + !loader.IsBaseAggregate(aggIList, typeDst.AsAggregateType().getAggregate())) && + (aggIReadOnlyList == null || + !loader.IsBaseAggregate(aggIReadOnlyList, typeDst.AsAggregateType().getAggregate()))) + { + return false; + } + + return FExpRefConv(loader, typeSrc.AsArrayType().GetElementType(), typeDst.AsAggregateType().GetTypeArgsAll().Item(0)); + } + + if (typeDst.IsArrayType() && typeSrc.IsAggregateType()) + { + // * From System.Array and the interfaces it implements, to any array-type. + if (loader.HasIdentityOrImplicitReferenceConversion(loader.GetReqPredefType(PredefinedType.PT_ARRAY), typeSrc)) + { + return true; + } + + // * From System.Collections.Generic.IList, System.Collections.Generic.IReadOnlyList and their base interfaces to a + // one-dimensional array-type S[], provided there is an implicit or explicit reference conversion from S[] to + // System.Collections.Generic.IList or System.Collections.Generic.IReadOnlyList. This is precisely when either S and T + // are the same type or there is an implicit or explicit reference conversion from S to T. + ArrayType arrayDest = typeDst.AsArrayType(); + AggregateType aggtypeSrc = typeSrc.AsAggregateType(); + if (arrayDest.rank != 1 || !typeSrc.isInterfaceType() || + aggtypeSrc.GetTypeArgsAll().Size != 1) + { + return false; + } + + AggregateSymbol aggIList = loader.GetOptPredefAgg(PredefinedType.PT_G_ILIST); + AggregateSymbol aggIReadOnlyList = loader.GetOptPredefAgg(PredefinedType.PT_G_IREADONLYLIST); + + if ((aggIList == null || + !loader.IsBaseAggregate(aggIList, aggtypeSrc.getAggregate())) && + (aggIReadOnlyList == null|| + !loader.IsBaseAggregate(aggIReadOnlyList, aggtypeSrc.getAggregate()))) + { + return false; + } + + CType typeArr = arrayDest.GetElementType(); + CType typeLst = aggtypeSrc.GetTypeArgsAll().Item(0); + + Debug.Assert(!typeArr.IsNeverSameType()); + return typeArr == typeLst || FExpRefConv(loader, typeArr, typeLst); + } + if (HasGenericDelegateExplicitReferenceConversion(loader, typeSrc, typeDst)) + { + return true; + } + } + else if (typeSrc.IsRefType()) + { + // conversion of T . U, where T : class, U + // .. these constraints implies where U : class + return loader.HasIdentityOrImplicitReferenceConversion(typeSrc, typeDst); + } + else if (typeDst.IsRefType()) + { + // conversion of T . U, where U : class, T + // .. these constraints implies where T : class + return loader.HasIdentityOrImplicitReferenceConversion(typeDst, typeSrc); + } + return false; + } + /*************************************************************************************************** + + There exists an explicit conversion ... + * From a generic delegate type S to generic delegate type T, provided all of the follow are true: + o Both types are constructed generic types of the same generic delegate type, D.That is, + S is D and T is D. + o S is not compatible with or identical to T. + o If type parameter Xi is declared to be invariant then Si must be identical to Ti. + o If type parameter Xi is declared to be covariant ("out") then Si must be convertible + to Ti via an identify conversion, implicit reference conversion, or explicit reference conversion. + o If type parameter Xi is declared to be contravariant ("in") then either Si must be identical to Ti, + or Si and Ti must both be reference types. + ***************************************************************************************************/ + public static bool HasGenericDelegateExplicitReferenceConversion(SymbolLoader loader, CType pSource, CType pTarget) + { + if (!pSource.isDelegateType() || + !pTarget.isDelegateType() || + pSource.getAggregate() != pTarget.getAggregate() || + loader.HasIdentityOrImplicitReferenceConversion(pSource, pTarget)) + { + return false; + } + + TypeArray pTypeParams = pSource.getAggregate().GetTypeVarsAll(); + TypeArray pSourceArgs = pSource.AsAggregateType().GetTypeArgsAll(); + TypeArray pTargetArgs = pTarget.AsAggregateType().GetTypeArgsAll(); + + Debug.Assert(pTypeParams.size == pSourceArgs.size); + Debug.Assert(pTypeParams.size == pTargetArgs.size); + + for (int iParam = 0; iParam < pTypeParams.size; ++iParam) + { + CType pSourceArg = pSourceArgs.Item(iParam); + CType pTargetArg = pTargetArgs.Item(iParam); + + // If they're identical then this one is automatically good, so skip it. + // If we have an error type, then we're in some fault tolerance. Let it through. + if (pSourceArg == pTargetArg || pTargetArg.IsErrorType() || pSourceArg.IsErrorType()) + { + continue; + } + TypeParameterType pParam = pTypeParams.Item(iParam).AsTypeParameterType(); + if (pParam.Invariant) + { + return false; + } + + if (pParam.Covariant) + { + if (!FExpRefConv(loader, pSourceArg, pTargetArg)) + { + return false; + } + } + else if (pParam.Contravariant) + { + if (!pSourceArg.IsRefType() || !pTargetArg.IsRefType()) + { + return false; + } + } + } + return true; + } + + /*************************************************************************************************** + 13.1.1 Identity conversion + + An identity conversion converts from any type to the same type. This conversion exists only + such that an entity that already has a required type can be said to be convertible to that type. + + Always returns false if the types are error, anonymous method, or method group + ***************************************************************************************************/ + public static bool FIsSameType(CType typeSrc, CType typeDst) + { + return typeSrc == typeDst && !typeSrc.IsNeverSameType(); + } + /*************************************************************************************************** + Determines whether there is a boxing conversion from typeSrc to typeDst + + 13.1.5 Boxing conversions + + A boxing conversion permits any non-nullable-value-type to be implicitly converted to the type + object or System.ValueType or to any interface-type implemented by the non-nullable-value-type, + and any enum type to be implicitly converted to System.Enum as well. ... An enum can be boxed to + the type System.Enum, since that is the direct base class for all enums (21.4). A struct or enum + can be boxed to the type System.ValueType, since that is the direct base class for all + structs (18.3.2) and a base class for all enums. + + A nullable-type has a boxing conversion to the same set of types to which the nullable-types + underlying type has boxing conversions. + + For a type-parameter T that is not known to be a reference type (25.7), the following conversions + involving T are considered to be boxing conversions at compile-time. At run-time, if T is a value + type, the conversion is executed as a boxing conversion. At run-time, if T is a reference type, + the conversion is executed as an implicit reference conversion or identity conversion. + * From T to its effective base class C, from T to any base class of C, and from T to any + interface implemented by C. [Note: C will be one of the types System.Object, System.ValueType, + or System.Enum (otherwise T would be known to be a reference type and 13.1.4 would apply + instead of this clause). end note] + * From T to an interface-type I in Ts effective interface set and from T to any base + interface of I. + ***************************************************************************************************/ + + public static bool FBoxingConv(SymbolLoader loader, CType typeSrc, CType typeDst) + { + return loader.HasImplicitBoxingConversion(typeSrc, typeDst); + } + + /*************************************************************************************************** + Determines whether there is a wrapping conversion from typeSrc to typeDst + + 13.7 Conversions involving nullable types + + The following terms are used in the subsequent sections: + * The term wrapping denotes the process of packaging a value, of type T, in an instance of type T?. + A value x of type T is wrapped to type T? by evaluating the expression new T?(x). + ***************************************************************************************************/ + public static bool FWrappingConv(CType typeSrc, CType typeDst) + { + return typeDst.IsNullableType() && typeSrc == typeDst.AsNullableType().GetUnderlyingType(); + } + + /*************************************************************************************************** + Determines whether there is a unwrapping conversion from typeSrc to typeDst + + 13.7 Conversions involving nullable types + + The following terms are used in the subsequent sections: + * The term unwrapping denotes the process of obtaining the value, of type T, contained in an + instance of type T?. A value x of type T? is unwrapped to type T by evaluating the expression + x.Value. Attempting to unwrap a null instance causes a System.InvalidOperationException to be + thrown. + + ***************************************************************************************************/ + public static bool FUnwrappingConv(CType typeSrc, CType typeDst) + { + return FWrappingConv(typeDst, typeSrc); + } + } +} diff --git a/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/Declarations/AggregateDeclaration.cs b/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/Declarations/AggregateDeclaration.cs new file mode 100644 index 000000000..8cb92687f --- /dev/null +++ b/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/Declarations/AggregateDeclaration.cs @@ -0,0 +1,40 @@ +// ==++== +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// ==--== + +using System.Reflection; + +namespace Microsoft.CSharp.RuntimeBinder.Semantics +{ + // ---------------------------------------------------------------------------- + // + // AggregateDeclaration + // + // AggregateDeclaration - represents a declaration of a aggregate type. With partial classes, + // an aggregate type might be declared in multiple places. This symbol represents + // on of the declarations. + // + // parent is the containing Declaration. + // ---------------------------------------------------------------------------- + + // Either a ClassNode or a DelegateNode + class AggregateDeclaration : Declaration + { + public AggregateSymbol Agg() + { + return bag.AsAggregateSymbol(); + } + + public new InputFile getInputFile() + { + return null; + } + + public new Assembly GetAssembly() + { + return Agg().AssociatedAssembly; + } + } +} diff --git a/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/Declarations/Declaration.cs b/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/Declarations/Declaration.cs new file mode 100644 index 000000000..4005625d3 --- /dev/null +++ b/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/Declarations/Declaration.cs @@ -0,0 +1,22 @@ +// ==++== +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// ==--== + +namespace Microsoft.CSharp.RuntimeBinder.Semantics +{ + // ---------------------------------------------------------------------------- + // + // Declaration + // + // Base class for NamespaceDeclaration and AggregateDeclaration. Parent is another DECL. + // Children are DECLs. + // ---------------------------------------------------------------------------- + + class Declaration : ParentSymbol + { + public NamespaceOrAggregateSymbol bag; + public Declaration declNext; + } +} diff --git a/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/Declarations/GlobalAttributeDeclaration.cs b/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/Declarations/GlobalAttributeDeclaration.cs new file mode 100644 index 000000000..6ceb2bdfa --- /dev/null +++ b/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/Declarations/GlobalAttributeDeclaration.cs @@ -0,0 +1,12 @@ +// ==++== +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// ==--== + +namespace Microsoft.CSharp.RuntimeBinder.Semantics +{ + class GlobalAttributeDeclaration : Symbol + { + } +} \ No newline at end of file diff --git a/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/Declarations/NamespaceDeclaration.cs b/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/Declarations/NamespaceDeclaration.cs new file mode 100644 index 000000000..7796238d8 --- /dev/null +++ b/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/Declarations/NamespaceDeclaration.cs @@ -0,0 +1,38 @@ +// ==++== +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// ==--== + +namespace Microsoft.CSharp.RuntimeBinder.Semantics +{ + // ---------------------------------------------------------------------------- + // + // NamespaceDeclaration + // + // NamespaceDeclaration - a symbol representing a declaration + // of a namspace in the source. + // + // firstChild/firstChild->nextChild enumerates the + // NSDECLs and AGGDECLs declared within this declaration. + // + // parent is the containing namespace declaration. + // + // Bag() is the namespace corresponding to this declaration. + // + // DeclNext() is the next declaration for the same namespace. + // ---------------------------------------------------------------------------- + + class NamespaceDeclaration : Declaration + { + public NamespaceSymbol Bag() + { + return bag.AsNamespaceSymbol(); + } + + public NamespaceSymbol NameSpace() + { + return bag.AsNamespaceSymbol(); + } + } +} diff --git a/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/EXPRExtensions.cs b/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/EXPRExtensions.cs new file mode 100644 index 000000000..69c120660 --- /dev/null +++ b/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/EXPRExtensions.cs @@ -0,0 +1,218 @@ +// ==++== +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// ==--== + +using System; +using System.Collections.Generic; +using System.Diagnostics; + +namespace Microsoft.CSharp.RuntimeBinder.Semantics +{ + internal static class EXPRExtensions + { + public static EXPR Map(this EXPR expr, ExprFactory factory, Func f) + { + Debug.Assert(f != null); + Debug.Assert(factory != null); + + if (expr == null) + return f(expr); + + EXPR result = null; + EXPR tail = null; + foreach (EXPR item in expr.ToEnumerable()) + { + EXPR mappedItem = f(item); + factory.AppendItemToList(mappedItem, ref result, ref tail); + } + return result; + } + + public static IEnumerable ToEnumerable(this EXPR expr) + { + EXPR exprCur = expr; + while (exprCur != null) + { + if (exprCur.isLIST()) + { + yield return exprCur.asLIST().GetOptionalElement(); + exprCur = exprCur.asLIST().GetOptionalNextListNode(); + } + else + { + yield return exprCur; + yield break; + } + } + } + public static bool isSTMT(this EXPR expr) + { + return (expr == null) ? false : expr.kind < ExpressionKind.EK_StmtLim; + } + public static EXPRSTMT asSTMT(this EXPR expr) + { + Debug.Assert(expr == null || expr.kind < ExpressionKind.EK_StmtLim); + return (EXPRSTMT)expr; + } + public static bool isBIN(this EXPR expr) + { + return (expr == null) ? false : (expr.kind >= ExpressionKind.EK_TypeLim) && + (0 != (expr.flags & EXPRFLAG.EXF_BINOP)); + } + public static bool isUnaryOperator(this EXPR expr) + { + if (expr != null) + { + switch (expr.kind) + { + case ExpressionKind.EK_UNARYOP: + case ExpressionKind.EK_TRUE: + case ExpressionKind.EK_FALSE: + case ExpressionKind.EK_INC: + case ExpressionKind.EK_DEC: + case ExpressionKind.EK_LOGNOT: + case ExpressionKind.EK_NEG: + case ExpressionKind.EK_UPLUS: + case ExpressionKind.EK_BITNOT: + case ExpressionKind.EK_ADDR: + case ExpressionKind.EK_DECIMALNEG: + case ExpressionKind.EK_DECIMALINC: + case ExpressionKind.EK_DECIMALDEC: + return true; + default: + break; + } + } + return false; + } + + public static bool isLvalue(this EXPR expr) + { + return (expr == null) ? false : 0 != (expr.flags & EXPRFLAG.EXF_LVALUE); + } + public static bool isChecked(this EXPR expr) + { + return (expr == null) ? false : 0 != (expr.flags & EXPRFLAG.EXF_CHECKOVERFLOW); + } + public static EXPRBINOP asBIN(this EXPR expr) + { + Debug.Assert(expr == null || 0 != (expr.flags & EXPRFLAG.EXF_BINOP)); + return (EXPRBINOP)expr; + } + public static EXPRUNARYOP asUnaryOperator(this EXPR expr) + { + Debug.Assert(expr == null || expr.isUnaryOperator()); + return (EXPRUNARYOP)expr; + } + public static bool isANYLOCAL(this EXPR expr) + { + return (expr == null) ? false : expr.kind == ExpressionKind.EK_LOCAL || expr.kind == ExpressionKind.EK_THISPOINTER; + } + public static EXPRLOCAL asANYLOCAL(this EXPR expr) + { + Debug.Assert(expr == null || expr.isANYLOCAL()); + return (EXPRLOCAL)expr; + } + public static bool isANYLOCAL_OK(this EXPR expr) + { + return expr.isANYLOCAL() && expr.isOK(); + } + public static bool isNull(this EXPR expr) + { + return expr.isCONSTANT_OK() && (expr.type.fundType() == FUNDTYPE.FT_REF) && expr.asCONSTANT().Val.IsNullRef(); + } + + public static bool isZero(this EXPR expr) + { + return (expr.isCONSTANT_OK()) && (expr.asCONSTANT().isZero()); + } + + public static EXPR GetSeqVal(this EXPR expr) + { + // Scan through EK_SEQUENCE and EK_SEQREV exprs to get the real value. + if (expr == null) + return null; + + EXPR exprVal = expr; + for (; ; ) + { + switch (exprVal.kind) + { + default: + return exprVal; + case ExpressionKind.EK_SEQUENCE: + exprVal = exprVal.asBIN().GetOptionalRightChild(); + break; + case ExpressionKind.EK_SEQREV: + exprVal = exprVal.asBIN().GetOptionalLeftChild(); + break; + } + } + } + + /*************************************************************************************************** + Determine whether this expr has a constant value (EK_CONSTANT or EK_ZEROINIT), possibly with + side effects (via EK_SEQUENCE or EK_SEQREV). Returns NULL if not, or the constant expr if so. + The returned EXPR will always be an EK_CONSTANT or EK_ZEROINIT. + ***************************************************************************************************/ + public static EXPR GetConst(this EXPR expr) + { + EXPR exprVal = expr.GetSeqVal(); + if (null == exprVal || !exprVal.isCONSTANT_OK() && exprVal.kind != ExpressionKind.EK_ZEROINIT) + return null; + return exprVal; + } + + private static void RETAILVERIFY(bool f) + { + if (!f) + Debug.Assert(false, "Panic!"); + } + + public static EXPRRETURN asRETURN(this EXPR expr) { RETAILVERIFY(expr == null || expr.kind == ExpressionKind.EK_RETURN); return (EXPRRETURN)expr; } + public static EXPRBINOP asBINOP(this EXPR expr) { RETAILVERIFY(expr == null || expr.kind == ExpressionKind.EK_BINOP); return (EXPRBINOP)expr; } + public static EXPRLIST asLIST(this EXPR expr) { RETAILVERIFY(expr == null || expr.kind == ExpressionKind.EK_LIST); return (EXPRLIST)expr; } + public static EXPRARRAYINDEX asARRAYINDEX(this EXPR expr) { RETAILVERIFY(expr == null || expr.kind == ExpressionKind.EK_ARRAYINDEX); return (EXPRARRAYINDEX)expr; } + public static EXPRCALL asCALL(this EXPR expr) { RETAILVERIFY(expr == null || expr.kind == ExpressionKind.EK_CALL); return (EXPRCALL)expr; } + public static EXPREVENT asEVENT(this EXPR expr) { RETAILVERIFY(expr == null || expr.kind == ExpressionKind.EK_EVENT); return (EXPREVENT)expr; } + public static EXPRFIELD asFIELD(this EXPR expr) { RETAILVERIFY(expr == null || expr.kind == ExpressionKind.EK_FIELD); return (EXPRFIELD)expr; } + public static EXPRCONSTANT asCONSTANT(this EXPR expr) { RETAILVERIFY(expr == null || expr.kind == ExpressionKind.EK_CONSTANT); return (EXPRCONSTANT)expr; } + public static EXPRFUNCPTR asFUNCPTR(this EXPR expr) { RETAILVERIFY(expr == null || expr.kind == ExpressionKind.EK_FUNCPTR); return (EXPRFUNCPTR)expr; } + public static EXPRPROP asPROP(this EXPR expr) { RETAILVERIFY(expr == null || expr.kind == ExpressionKind.EK_PROP); return (EXPRPROP)expr; } + public static EXPRWRAP asWRAP(this EXPR expr) { RETAILVERIFY(expr == null || expr.kind == ExpressionKind.EK_WRAP); return (EXPRWRAP)expr; } + public static EXPRARRINIT asARRINIT(this EXPR expr) { RETAILVERIFY(expr == null || expr.kind == ExpressionKind.EK_ARRINIT); return (EXPRARRINIT)expr; } + public static EXPRCAST asCAST(this EXPR expr) { RETAILVERIFY(expr == null || expr.kind == ExpressionKind.EK_CAST); return (EXPRCAST)expr; } + public static EXPRUSERDEFINEDCONVERSION asUSERDEFINEDCONVERSION(this EXPR expr) { RETAILVERIFY(expr == null || expr.kind == ExpressionKind.EK_USERDEFINEDCONVERSION); return (EXPRUSERDEFINEDCONVERSION)expr; } + public static EXPRTYPEOF asTYPEOF(this EXPR expr) { RETAILVERIFY(expr == null || expr.kind == ExpressionKind.EK_TYPEOF); return (EXPRTYPEOF)expr; } + public static EXPRZEROINIT asZEROINIT(this EXPR expr) { RETAILVERIFY(expr == null || expr.kind == ExpressionKind.EK_ZEROINIT); return (EXPRZEROINIT)expr; } + public static EXPRUSERLOGOP asUSERLOGOP(this EXPR expr) { RETAILVERIFY(expr == null || expr.kind == ExpressionKind.EK_USERLOGOP); return (EXPRUSERLOGOP)expr; } + public static EXPRMEMGRP asMEMGRP(this EXPR expr) { RETAILVERIFY(expr == null || expr.kind == ExpressionKind.EK_MEMGRP); return (EXPRMEMGRP)expr; } + public static EXPRFIELDINFO asFIELDINFO(this EXPR expr) { RETAILVERIFY(expr == null || expr.kind == ExpressionKind.EK_FIELDINFO); return (EXPRFIELDINFO)expr; } + public static EXPRMETHODINFO asMETHODINFO(this EXPR expr) { RETAILVERIFY(expr == null || expr.kind == ExpressionKind.EK_METHODINFO); return (EXPRMETHODINFO)expr; } + public static EXPRPropertyInfo asPropertyInfo(this EXPR expr) { RETAILVERIFY(expr == null || expr.kind == ExpressionKind.EK_PROPERTYINFO); return (EXPRPropertyInfo)expr; } + public static EXPRNamedArgumentSpecification asNamedArgumentSpecification(this EXPR expr) { RETAILVERIFY(expr == null || expr.kind == ExpressionKind.EK_NamedArgumentSpecification); return (EXPRNamedArgumentSpecification)expr; } + + public static bool isCONSTANT_OK(this EXPR expr) { return (expr == null) ? false : (expr.kind == ExpressionKind.EK_CONSTANT && expr.isOK()); } + public static bool isRETURN(this EXPR expr) { return (expr == null) ? false : (expr.kind == ExpressionKind.EK_RETURN); } + public static bool isLIST(this EXPR expr) { return (expr == null) ? false : (expr.kind == ExpressionKind.EK_LIST); } + public static bool isARRAYINDEX(this EXPR expr) { return (expr == null) ? false : (expr.kind == ExpressionKind.EK_ARRAYINDEX); } + public static bool isCALL(this EXPR expr) { return (expr == null) ? false : (expr.kind == ExpressionKind.EK_CALL); } + public static bool isFIELD(this EXPR expr) { return (expr == null) ? false : (expr.kind == ExpressionKind.EK_FIELD); } + public static bool isCONSTANT(this EXPR expr) { return (expr == null) ? false : (expr.kind == ExpressionKind.EK_CONSTANT); } + public static bool isCLASS(this EXPR expr) { return (expr == null) ? false : (expr.kind == ExpressionKind.EK_CLASS); } + public static bool isPROP(this EXPR expr) { return (expr == null) ? false : (expr.kind == ExpressionKind.EK_PROP); } + public static bool isWRAP(this EXPR expr) { return (expr == null) ? false : (expr.kind == ExpressionKind.EK_WRAP); } + public static bool isARRINIT(this EXPR expr) { return (expr == null) ? false : (expr.kind == ExpressionKind.EK_ARRINIT); } + public static bool isCAST(this EXPR expr) { return (expr == null) ? false : (expr.kind == ExpressionKind.EK_CAST); } + public static bool isUSERDEFINEDCONVERSION(this EXPR expr) { return (expr == null) ? false : (expr.kind == ExpressionKind.EK_USERDEFINEDCONVERSION); } + public static bool isTYPEOF(this EXPR expr) { return (expr == null) ? false : (expr.kind == ExpressionKind.EK_TYPEOF); } + public static bool isZEROINIT(this EXPR expr) { return (expr == null) ? false : (expr.kind == ExpressionKind.EK_ZEROINIT); } + public static bool isMEMGRP(this EXPR expr) { return (expr == null) ? false : (expr.kind == ExpressionKind.EK_MEMGRP); } + public static bool isBOUNDLAMBDA(this EXPR expr) { return (expr == null) ? false : (expr.kind == ExpressionKind.EK_BOUNDLAMBDA); } + public static bool isUNBOUNDLAMBDA(this EXPR expr) { return (expr == null) ? false : (expr.kind == ExpressionKind.EK_UNBOUNDLAMBDA); } + public static bool isMETHODINFO(this EXPR expr) { return (expr == null) ? false : (expr.kind == ExpressionKind.EK_METHODINFO); } + public static bool isNamedArgumentSpecification(this EXPR expr) { return (expr == null) ? false : (expr.kind == ExpressionKind.EK_NamedArgumentSpecification); } + } +} diff --git a/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/EXPRFLAG.cs b/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/EXPRFLAG.cs new file mode 100644 index 000000000..9ef6398ae --- /dev/null +++ b/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/EXPRFLAG.cs @@ -0,0 +1,145 @@ +// ==++== +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// ==--== + +namespace Microsoft.CSharp.RuntimeBinder.Semantics +{ + internal enum EXPRFLAG + { + // These are specific to various node types. + // Order these by value. If you need a new flag, search for the first value that isn't currently valid on your expr kind. + + // 0x1 + EXF_BINOP = 0x1, // On** Many Non Statement Exprs!** This gets its own BIT! + + // 0x2 + EXF_CTOR = 0x2, // Only on EXPRMEMGRP, indicates a constructor. + EXF_NEEDSRET = 0x2, // Only on EXPRBLOCK + EXF_ASLEAVE = 0x2, // Only on EXPRGOTO, EXPRRETURN, means use leave instead of br instruction + EXF_ISFAULT = 0x2, // Only on EXPRTRY, used for fabricated try/fault (no user code) + EXF_HASHTABLESWITCH = 0x2, // Only on EXPRFlatSwitch + EXF_BOX = 0x2, // Only on EXPRCAST, indicates a boxing operation (value type -> object) + EXF_ARRAYCONST = 0x2, // Only on EXPRARRINIT, indicates that we should init using a memory block + EXF_MEMBERSET = 0x2, // Only on EXPRFIELD, indicates that the reference is for set purposes + EXF_OPENTYPE = 0x2, // Only on EXPRTYPEOF. Indicates that the type is an open type. + EXF_LABELREFERENCED = 0x2, // Only on EXPRLABEL. Indicates the label was targeted by a goto. +#if CSEE + EXF_CLASSASSTATICSCHILD = 0x2, // Only on EXPRCLASS, indicates a synthetized child which is parent of statics + EXF_DECLASSG = 0x2, // Only on EK_ASSG +#endif + EXF_GENERATEDQMARK = 0x2, // only on EK_QMARK + + // 0x4 + EXF_INDEXER = 0x4, // Only on EXPRMEMGRP, indicates an indexer. + EXF_GOTOCASE = 0x4, // Only on EXPRGOTO, means goto case or goto default + EXF_REMOVEFINALLY = 0x4, // Only on EXPRTRY, means that the try-finally should be converted to normal code + EXF_UNBOX = 0x4, // Only on EXPRCAST, indicates a unboxing operation (object -> value type) + EXF_ARRAYALLCONST = 0x4, // Only on EXPRARRINIT, indicated that all elems are constant (must also have ARRAYCONST set) + EXF_CTORPREAMBLE = 0x4, // Only on EXPRBLOCK, indicates that the block is the preamble of a constructor - contains field inits and base ctor call + EXF_USERLABEL = 0x4, // Only on EXPRLABEL, indicates that this is a source-code label, not a compiler-generated label + + // 0x8 + EXF_OPERATOR = 0x8, // Only on EXPRMEMGRP, indicates an operator. + EXF_ISPOSTOP = 0x8, // Only on EXPRMULTI, indicates ++ + EXF_FINALLYBLOCKED = 0x8, // Only on EXPRTRY, EXPRGOTO, EXPRRETURN, means that FINALLY block end is unreachable + EXF_REFCHECK = 0x8, // Only on EXPRCAST, indicates an reference checked cast is required + EXF_WRAPASTEMP = 0x8, // Only on EXPRWRAP, indicates that this wrap represents an actual local +#if CSEE + EXF_ADDROFREF = 0x8, // Only on EXPRBINOP, with kind == EK_ADDR, indicates*f where f is of a class type +#endif + + // 0x10 + EXF_LITERALCONST = 0x10, // Only on EXPRCONSTANT, means this was not a folded constant + EXF_BADGOTO = 0x10, // Only on EXPRGOTO, indicates an unrealizable goto + EXF_RETURNISYIELD = 0x10, // Only on EXPRRETURN, means this is really a yield, and flow continues + EXF_ISFINALLY = 0x10, // Only on EXPRTRY + EXF_NEWOBJCALL = 0x10, // Only on EXPRCALL and EXPRMEMGRP, to indicate new <...>(...) + EXF_INDEXEXPR = 0x10, // Only on EXPRCAST, indicates a special cast for array indexing + EXF_REPLACEWRAP = 0x10, // Only on EXPRWRAP, it means the wrap should be replaced with its expr (during rewriting) +#if CSEE + EXF_ADDROFREFLOC = 0x10, // Only on EXPRBINOP, with kind == EK_ADDR, indicates &f where f is of a class type +#endif + + // 0x20 + EXF_UNREALIZEDGOTO = 0x20, // Only on EXPRGOTO, means target unknown + EXF_CONSTRAINED = 0x20, // Only on EXPRCALL, EXPRPROP, indicates a call through a method or prop on a type variable or value type + EXF_FORCE_BOX = 0x20, // Only on EXPRCAST, GENERICS: indicates a "forcing" boxing operation (if type parameter boxed then nop, i.e. object -> object, else value type -> object) + EXF_SIMPLENAME = 0x20, // Only on EXPRMEMGRP, We're binding a dynamic simple name. +#if CSEE + EXF_BUCKETSINDEX = 0x20, // Only on EXPRBINOP, with kind == EK_INDEX, indicates Everett view of hashtable bucket +#endif + + // 0x40 + EXF_ASFINALLYLEAVE = 0x40, // Only on EXPRGOTO, EXPRRETURN, means leave through a finally, ASLEAVE must also be set + EXF_BASECALL = 0x40, // Only on EXPRCALL, EXPRFNCPTR, EXPRPROP, EXPREVENT, and EXPRMEMGRP, indicates a "base.XXXX" call + EXF_FORCE_UNBOX = 0x40, // Only on EXPRCAST, GENERICS: indicates a "forcing" unboxing operation (if type parameter boxed then castclass, i.e. object -> object, else object -> value type) + EXF_ADDRNOCONV = 0x40, // Only on EXPRBINOP, with kind == EK_ADDR, indicates that a conv.u should NOT be emitted. + + // 0x80 + EXF_GOTONOTBLOCKED = 0x80, // Only on EXPRGOTO, means the goto is known to not pass through a finally which does not terminate + EXF_DELEGATE = 0x80, // Only on EXPRMEMGRP, indicates an implicit invocation of a delegate: d() vs d.Invoke(). + EXF_STATIC_CAST = 0x80, // Only on EXPRCAST, indicates a static cast is required. We implement with stloc, ldloc to a temp of the correct type. + + // 0x100 + EXF_USERCALLABLE = 0x100, // Only on EXPRMEMGRP, indicates a user callable member group. + EXF_UNBOXRUNTIME = 0x100, // Only on EXPRCAST, indicates that the runtime binder should unbox this. + + // 0x200 + EXF_NEWSTRUCTASSG = 0x200, // Only on EXPRCALL, indicates that this is a constructor call which assigns to object +#if CSEE + EXF_OBJDECL = 0x200, // Only on EXPRCAST, indicates a cast of object or itf declared var to actual type +#endif + EXF_GENERATEDSTMT = 0x200, // Only on statement exprs. Indicates that the statement is compiler generated + // so we shouldn't report things like "unreachable code" on it. + + // 0x400 + EXF_IMPLICITSTRUCTASSG = 0x400, // Only on EXPRCALL, indicates that this an implicit struct assg call +#if CSEE + EXF_BASECAST = 0x400, // Only on EXPRCAST, indicates a cast from declared type to its base type +#endif + EXF_MARKING = 0x400, // Only on statement exprs. Indicates that we're currently marking + // its children for reachability (it's up the stack). + + //*** The following are usable on multiple node types.*** + // 0x000800 and above + + EXF_UNREACHABLEBEGIN = 0x000800, // indicates an unreachable statement + EXF_UNREACHABLEEND = 0x001000, // indicates the end of the statement is unreachable + EXF_USEORIGDEBUGINFO = 0x002000, // Only set on EXPRDEBUGNOOP, but tested generally. Indicates foreach node should not be overridden to in token + EXF_LASTBRACEDEBUGINFO = 0x004000, // indicates override tree to set debuginfo on last brace + EXF_NODEBUGINFO = 0x008000, // indicates no debug info for this statement + EXF_IMPLICITTHIS = 0x010000, // indicates a compiler provided this pointer (in the EE, when doing autoexp, this can be anything) + EXF_CANTBENULL = 0x020000, // indicate this expression can't ever be null (e.g., "this"). + EXF_CHECKOVERFLOW = 0x040000, // indicates that operation should be checked for overflow +#if CSEE + EXF_THREWEXCEPTION = 0x080000, // indicates the the expr originally threw an exception +#endif + EXF_PUSH_OP_FIRST = 0x100000, // On any expr, indicates that the first operand must be placed on the stack before + // anything else - this is needed for multi-ops involving string concat. + EXF_ASSGOP = 0x200000, // On any non stmt exprs, indicates assignment node... + EXF_LVALUE = 0x400000, // On any exprs. An lvalue - whether it's legal to assign. + + // THIS IS THE HIGHEST FLAG: + + // Indicates that the expression came from a LocalVariableSymbol, FieldSymbol, or PropertySymbol whose type has the same name so + // it's OK to use the type instead of the element if using the element would generate an error. + EXF_SAMENAMETYPE = 0x800000, + + EXF_MASK_ANY = EXF_UNREACHABLEBEGIN | EXF_UNREACHABLEEND | + EXF_USEORIGDEBUGINFO | EXF_LASTBRACEDEBUGINFO | EXF_NODEBUGINFO | + EXF_IMPLICITTHIS | EXF_CANTBENULL | EXF_CHECKOVERFLOW | + EXF_PUSH_OP_FIRST | EXF_ASSGOP | EXF_LVALUE | EXF_SAMENAMETYPE +#if CSEE + | EXF_THREWEXCEPTION +#endif +, + + // Used to mask the cast flags off an EXPRCAST. + EXF_CAST_ALL = EXF_BOX | EXF_UNBOX | EXF_REFCHECK | EXF_INDEXEXPR | EXF_FORCE_BOX | EXF_FORCE_UNBOX | EXF_STATIC_CAST +#if CSEE + | EXF_MAYBE_BOX | EXF_OBJDECL | EXF_BASECAST +#endif + } +} diff --git a/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/ExplicitConversion.cs b/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/ExplicitConversion.cs new file mode 100644 index 000000000..9a3dabcf3 --- /dev/null +++ b/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/ExplicitConversion.cs @@ -0,0 +1,871 @@ +// ==++== +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// ==--== + +using System.Diagnostics; +using Microsoft.CSharp.RuntimeBinder.Syntax; + +namespace Microsoft.CSharp.RuntimeBinder.Semantics +{ + internal partial class ExpressionBinder + { + + // ---------------------------------------------------------------------------- + // BindExplicitConversion + // ---------------------------------------------------------------------------- + + private class ExplicitConversion + { + private ExpressionBinder binder; + private EXPR exprSrc; + private CType typeSrc; + private CType typeDest; + private EXPRTYPEORNAMESPACE exprTypeDest; + + // This is for lambda error reporting. The reason we have this is because we + // store errors for lambda conversions, and then we dont bind the conversion + // again to report errors. Consider the following case: + // + // int? x = () => null; + // + // When we try to convert the lambda to the nullable type int?, we first + // attempt the conversion to int. If that fails, then we know there is no + // conversion to int?, since int is a predef type. We then look for UserDefined + // conversions, and fail. When we report the errors, we ask the lambda for its + // conversion errors. But since we attempted its conversion to int and not int?, + // we report the wrong error. This field is to keep track of the right type + // to report the error on, so that when the lambda conversion fails, it reports + // errors on the correct type. + + private CType m_pDestinationTypeForLambdaErrorReporting; + private EXPR exprDest; + private bool needsExprDest; + private CONVERTTYPE flags; + + // ---------------------------------------------------------------------------- + // BindExplicitConversion + // ---------------------------------------------------------------------------- + + public ExplicitConversion(ExpressionBinder binder, EXPR exprSrc, CType typeSrc, EXPRTYPEORNAMESPACE typeDest, CType pDestinationTypeForLambdaErrorReporting, bool needsExprDest, CONVERTTYPE flags) + { + this.binder = binder; + this.exprSrc = exprSrc; + this.typeSrc = typeSrc; + this.typeDest = typeDest.TypeOrNamespace.AsType(); + this.m_pDestinationTypeForLambdaErrorReporting = pDestinationTypeForLambdaErrorReporting; + this.exprTypeDest = typeDest; + this.needsExprDest = needsExprDest; + this.flags = flags; + this.exprDest = null; + } + public EXPR ExprDest { get { return exprDest; } } + /* + * BindExplicitConversion + * + * This is a complex routine with complex parameter. Generally, this should + * be called through one of the helper methods that insulates you + * from the complexity of the interface. This routine handles all the logic + * associated with explicit conversions. + * + * Note that this function calls BindImplicitConversion first, so the main + * logic is only concerned with conversions that can be made explicitly, but + * not implicitly. + */ + public bool Bind() + { + // To test for a standard conversion, call canConvert(exprSrc, typeDest, STANDARDANDCONVERTTYPE.NOUDC) and + // canConvert(typeDest, typeSrc, STANDARDANDCONVERTTYPE.NOUDC). + Debug.Assert((flags & CONVERTTYPE.STANDARD) == 0); + + // 13.2 Explicit conversions + // + // The following conversions are classified as explicit conversions: + // + // * All implicit conversions + // * Explicit numeric conversions + // * Explicit enumeration conversions + // * Explicit reference conversions + // * Explicit interface conversions + // * Unboxing conversions + // * Explicit type parameter conversions + // * User-defined explicit conversions + // * Explicit nullable conversions + // * Lifted user-defined explicit conversions + // + // Explicit conversions can occur in cast expressions (14.6.6). + // + // The explicit conversions that are not implicit conversions are conversions that cannot be + // proven always to succeed, conversions that are known possibly to lose information, and + // conversions across domains of types sufficiently different to merit explicit notation. + + // The set of explicit conversions includes all implicit conversions. + + // Don't try user-defined conversions now because we'll try them again later. + if (binder.BindImplicitConversion(exprSrc, typeSrc, exprTypeDest, m_pDestinationTypeForLambdaErrorReporting, needsExprDest, out exprDest, flags | CONVERTTYPE.ISEXPLICIT)) + { + return true; + } + + if (typeSrc == null || typeDest == null || typeSrc.IsErrorType() || + typeDest.IsErrorType() || typeDest.IsNeverSameType()) + { + return false; + } + + if (typeDest.IsNullableType()) + { + // This is handled completely by BindImplicitConversion. + return false; + } + + if (typeSrc.IsNullableType()) + { + return bindExplicitConversionFromNub(); + } + + if (bindExplicitConversionFromArrayToIList()) + { + return true; + } + + // if we were casting an integral constant to another constant type, + // then, if the constant were in range, then the above call would have succeeded. + + // But it failed, and so we know that the constant is not in range + + switch (typeDest.GetTypeKind()) + { + default: + VSFAIL("Bad type kind"); + return false; + case TypeKind.TK_VoidType: + return false; // Can't convert to a method group or anon method. + case TypeKind.TK_NullType: + return false; // Can never convert TO the null type. + case TypeKind.TK_TypeParameterType: + if (bindExplicitConversionToTypeVar()) + { + return true; + } + break; + case TypeKind.TK_ArrayType: + if (bindExplicitConversionToArray(typeDest.AsArrayType())) + { + return true; + } + break; + case TypeKind.TK_PointerType: + if (bindExplicitConversionToPointer()) + { + return true; + } + break; + case TypeKind.TK_AggregateType: + { + AggCastResult result = bindExplicitConversionToAggregate(typeDest.AsAggregateType()); + + if (result == AggCastResult.Success) + { + return true; + } + if (result == AggCastResult.Abort) + { + return false; + } + break; + } + } + + // No built-in conversion was found. Maybe a user-defined conversion? + if (0 == (flags & CONVERTTYPE.NOUDC)) + { + return binder.bindUserDefinedConversion(exprSrc, typeSrc, typeDest, needsExprDest, out exprDest, false); + } + return false; + } + + private bool bindExplicitConversionFromNub() + { + Debug.Assert(typeSrc != null); + Debug.Assert(typeDest != null); + + // If S and T are value types and there is a builtin conversion from S => T then there is an + // explicit conversion from S? => T that throws on null. + if (typeDest.IsValType() && binder.BindExplicitConversion(null, typeSrc.StripNubs(), exprTypeDest, m_pDestinationTypeForLambdaErrorReporting, flags | CONVERTTYPE.NOUDC)) + { + if (needsExprDest) + { + EXPR valueSrc = exprSrc; + // UNDONE: This is a holdover from the days when you could have nullable of nullable. + // UNDONE: Can we remove this loop? + while (valueSrc.type.IsNullableType()) + { + valueSrc = binder.BindNubValue(valueSrc); + } + Debug.Assert(valueSrc.type == typeSrc.StripNubs()); + if (!binder.BindExplicitConversion(valueSrc, valueSrc.type, exprTypeDest, m_pDestinationTypeForLambdaErrorReporting, needsExprDest, out exprDest, flags | CONVERTTYPE.NOUDC)) + { + VSFAIL("BindExplicitConversion failed unexpectedly"); + return false; + } + if (exprDest.kind == ExpressionKind.EK_USERDEFINEDCONVERSION) + { + exprDest.asUSERDEFINEDCONVERSION().Argument = exprSrc; + } + } + return true; + } + + if ((flags & CONVERTTYPE.NOUDC) == 0) + { + return binder.bindUserDefinedConversion(exprSrc, typeSrc, typeDest, needsExprDest, out exprDest, false); + } + return false; + } + + private bool bindExplicitConversionFromArrayToIList() + { + // 13.2.2 + // + // The explicit reference conversions are: + // + // * From a one-dimensional array-type S[] to System.Collections.Generic.IList, System.Collections.Generic.IReadOnlyList and + // their base interfaces, provided there is an explicit reference conversion from S to T. + + Debug.Assert(typeSrc != null); + Debug.Assert(typeDest != null); + + if (!typeSrc.IsArrayType() || typeSrc.AsArrayType().rank != 1 || + !typeDest.isInterfaceType() || typeDest.AsAggregateType().GetTypeArgsAll().Size != 1) + { + return false; + } + + AggregateSymbol aggIList = GetSymbolLoader().GetOptPredefAgg(PredefinedType.PT_G_ILIST); + AggregateSymbol aggIReadOnlyList = GetSymbolLoader().GetOptPredefAgg(PredefinedType.PT_G_IREADONLYLIST); + + if ((aggIList == null || + !GetSymbolLoader().IsBaseAggregate(aggIList, typeDest.AsAggregateType().getAggregate())) && + (aggIReadOnlyList == null || + !GetSymbolLoader().IsBaseAggregate(aggIReadOnlyList, typeDest.AsAggregateType().getAggregate()))) + { + return false; + } + + CType typeArr = typeSrc.AsArrayType().GetElementType(); + CType typeLst = typeDest.AsAggregateType().GetTypeArgsAll().Item(0); + + if (!CConversions.FExpRefConv(GetSymbolLoader(), typeArr, typeLst)) + { + return false; + } + + if (needsExprDest) + binder.bindSimpleCast(exprSrc, exprTypeDest, out exprDest, EXPRFLAG.EXF_REFCHECK); + return true; + } + + private bool bindExplicitConversionToTypeVar() + { + + // 13.2.3 Explicit reference conversions + // + // For a type-parameter T that is known to be a reference type (25.7), the following + // explicit reference conversions exist: + // + // * From the effective base class C of T to T and from any base class of C to T. + // * From any interface-type to T. + // * From a type-parameter U to T provided that T depends on U (25.7). + + Debug.Assert(typeSrc != null); + Debug.Assert(typeDest != null); + + // NOTE: for the flags, we have to use EXPRFLAG.EXF_FORCE_UNBOX (not EXPRFLAG.EXF_REFCHECK) even when + // we know that the type is a reference type. The verifier expects all code for + // type parameters to behave as if the type parameter is a value type. + // The jitter should be smart about it.... + if (typeSrc.isInterfaceType() || binder.canConvert(typeDest, typeSrc, CONVERTTYPE.NOUDC)) + { + if (!needsExprDest) + { + return true; + } + + // There is an explicit, possibly unboxing, conversion from Object or any interface to + // a type variable. This will involve a type check and possibly an unbox. + // There is an explicit conversion from non-interface X to the type var iff there is an + // implicit conversion from the type var to X. + if (typeSrc.IsTypeParameterType()) + { + // Need to box first before unboxing. + EXPR exprT; + EXPRCLASS exprObj = GetExprFactory().MakeClass(binder.GetReqPDT(PredefinedType.PT_OBJECT)); + binder.bindSimpleCast(exprSrc, exprObj, out exprT, EXPRFLAG.EXF_FORCE_BOX); + exprSrc = exprT; + } + if (needsExprDest) + binder.bindSimpleCast(exprSrc, exprTypeDest, out exprDest, EXPRFLAG.EXF_FORCE_UNBOX); + return true; + } + return false; + } + + private bool bindExplicitConversionFromIListToArray(ArrayType arrayDest) + { + // 13.2.2 + // + // The explicit reference conversions are: + // + // * From System.Collections.Generic.IList, System.Collections.Generic.IReadOnlyList and their base interfaces + // to a one-dimensional array-type S[], provided there is an implicit or explicit reference conversion from + // S[] to System.Collections.Generic.IList or System.Collections.Generic.IReadOnlyList. This is precisely when either S and T + // are the same type or there is an implicit or explicit reference conversion from S to T. + + if (arrayDest.rank != 1 || !typeSrc.isInterfaceType() || + typeSrc.AsAggregateType().GetTypeArgsAll().Size != 1) + { + return false; + } + + AggregateSymbol aggIList = GetSymbolLoader().GetOptPredefAgg(PredefinedType.PT_G_ILIST); + AggregateSymbol aggIReadOnlyList = GetSymbolLoader().GetOptPredefAgg(PredefinedType.PT_G_IREADONLYLIST); + + if ((aggIList == null || + !GetSymbolLoader().IsBaseAggregate(aggIList, typeSrc.AsAggregateType().getAggregate())) && + (aggIReadOnlyList == null || + !GetSymbolLoader().IsBaseAggregate(aggIReadOnlyList, typeSrc.AsAggregateType().getAggregate()))) + { + return false; + } + + CType typeArr = arrayDest.GetElementType(); + CType typeLst = typeSrc.AsAggregateType().GetTypeArgsAll().Item(0); + + Debug.Assert(!typeArr.IsNeverSameType()); + if (typeArr != typeLst && !CConversions.FExpRefConv(GetSymbolLoader(), typeArr, typeLst)) + { + return false; + } + if (needsExprDest) + binder.bindSimpleCast(exprSrc, exprTypeDest, out exprDest, EXPRFLAG.EXF_REFCHECK); + return true; + } + + private bool bindExplicitConversionFromArrayToArray(ArrayType arraySrc, ArrayType arrayDest) + { + // 13.2.2 + // + // The explicit reference conversions are: + // + // * From an array-type S with an element type SE to an array-type T with an element type + // TE, provided all of the following are true: + // + // * S and T differ only in element type. (In other words, S and T have the same number + // of dimensions.) + // + // * An explicit reference conversion exists from SE to TE. + + if (arraySrc.rank != arrayDest.rank) + { + return false; // Ranks do not match. + } + + if (CConversions.FExpRefConv(GetSymbolLoader(), arraySrc.GetElementType(), arrayDest.GetElementType())) + { + if (needsExprDest) + binder.bindSimpleCast(exprSrc, exprTypeDest, out exprDest, EXPRFLAG.EXF_REFCHECK); + return true; + } + + return false; + } + + private bool bindExplicitConversionToArray(ArrayType arrayDest) + { + Debug.Assert(typeSrc != null); + Debug.Assert(arrayDest != null); + + if (typeSrc.IsArrayType()) + { + return bindExplicitConversionFromArrayToArray(typeSrc.AsArrayType(), arrayDest); + } + + if (bindExplicitConversionFromIListToArray(arrayDest)) + { + return true; + } + + // 13.2.2 + // + // The explicit reference conversions are: + // + // * From System.Array and the interfaces it implements, to any array-type. + + if (binder.canConvert(binder.GetReqPDT(PredefinedType.PT_ARRAY), typeSrc, CONVERTTYPE.NOUDC)) + { + if (needsExprDest) + binder.bindSimpleCast(exprSrc, exprTypeDest, out exprDest, EXPRFLAG.EXF_REFCHECK); + return true; + } + return false; + } + + private bool bindExplicitConversionToPointer() + { + + // 27.4 Pointer conversions + // + // in an unsafe context, the set of available explicit conversions (13.2) is extended to + // include the following explicit pointer conversions: + // + // * From any pointer-type to any other pointer-type. + // * From sbyte, byte, short, ushort, int, uint, long, or ulong to any pointer-type. + + if (typeSrc.IsPointerType() || typeSrc.fundType() <= FUNDTYPE.FT_LASTINTEGRAL && typeSrc.isNumericType()) + { + if (needsExprDest) + binder.bindSimpleCast(exprSrc, exprTypeDest, out exprDest); + return true; + } + return false; + } + + // 13.2.2 Explicit enumeration conversions + // + // The explicit enumeration conversions are: + // + // * From sbyte, byte, short, ushort, int, uint, long, ulong, char, float, double, or + // decimal to any enum-type. + // + // * From any enum-type to sbyte, byte, short, ushort, int, uint, long, ulong, char, + // float, double, or decimal. + // + // * From any enum-type to any other enum-type. + // + // * An explicit enumeration conversion between two types is processed by treating any + // participating enum-type as the underlying type of that enum-type, and then performing + // an implicit or explicit numeric conversion between the resulting types. + + private AggCastResult bindExplicitConversionFromEnumToAggregate(AggregateType aggTypeDest) + { + Debug.Assert(typeSrc != null); + Debug.Assert(aggTypeDest != null); + + if (!typeSrc.isEnumType()) + { + return AggCastResult.Failure; + } + + AggregateSymbol aggDest = aggTypeDest.getAggregate(); + if (aggDest.isPredefAgg(PredefinedType.PT_DECIMAL)) + { + return bindExplicitConversionFromEnumToDecimal(aggTypeDest); + } + + + if (!aggDest.getThisType().isNumericType() && + !aggDest.IsEnum() && + !(aggDest.IsPredefined() && aggDest.GetPredefType() == PredefinedType.PT_CHAR)) + { + return AggCastResult.Failure; + } + + if (exprSrc.GetConst() != null) + { + ConstCastResult result = binder.bindConstantCast(exprSrc, exprTypeDest, needsExprDest, out exprDest, true); + if (result == ConstCastResult.Success) + { + return AggCastResult.Success; + } + else if (result == ConstCastResult.CheckFailure) + { + return AggCastResult.Abort; + } + } + + if (needsExprDest) + binder.bindSimpleCast(exprSrc, exprTypeDest, out exprDest); + return AggCastResult.Success; + } + + private AggCastResult bindExplicitConversionFromDecimalToEnum(AggregateType aggTypeDest) + { + Debug.Assert(typeSrc != null); + Debug.Assert(typeSrc.isPredefType(PredefinedType.PT_DECIMAL)); + + // There is an explicit conversion from decimal to all integral types. + if (exprSrc.GetConst() != null) + { + // Fold the constant cast if possible. + ConstCastResult result = binder.bindConstantCast(exprSrc, exprTypeDest, needsExprDest, out exprDest, true); + if (result == ConstCastResult.Success) + { + return AggCastResult.Success; // else, don't fold and use a regular cast, below. + } + if (result == ConstCastResult.CheckFailure && 0 == (flags & CONVERTTYPE.CHECKOVERFLOW)) + { + return AggCastResult.Abort; + } + } + + // All casts from decimal to integer types are bound as user-defined conversions. + + bool bIsConversionOK = true; + if (needsExprDest) + { + // According the language, this is a standard conversion, but it is implemented + // through a user-defined conversion. Because it's a standard conversion, we don't + // test the CONVERTTYPE.NOUDC flag here. + CType underlyingType = aggTypeDest.underlyingType(); + bIsConversionOK = binder.bindUserDefinedConversion(exprSrc, typeSrc, underlyingType, needsExprDest, out exprDest, false); + + if (bIsConversionOK) + { + // upcast to the Enum type + binder.bindSimpleCast(exprDest, exprTypeDest, out exprDest); + } + } + return bIsConversionOK ? AggCastResult.Success : AggCastResult.Failure; + } + + private AggCastResult bindExplicitConversionFromEnumToDecimal(AggregateType aggTypeDest) + { + Debug.Assert(typeSrc != null); + Debug.Assert(aggTypeDest != null); + Debug.Assert(aggTypeDest.isPredefType(PredefinedType.PT_DECIMAL)); + + AggregateType underlyingType = typeSrc.underlyingType().AsAggregateType(); + + // Need to first cast the source expr to its underlying type. + + EXPR exprCast; + + if (exprSrc == null) + { + exprCast = null; + } + else + { + EXPRCLASS underlyingExpr = GetExprFactory().MakeClass(underlyingType); + binder.bindSimpleCast(exprSrc, underlyingExpr, out exprCast); + } + + // There is always an implicit conversion from any integral type to decimal. + + if (exprCast.GetConst() != null) + { + // Fold the constant cast if possible. + ConstCastResult result = binder.bindConstantCast(exprCast, exprTypeDest, needsExprDest, out exprDest, true); + if (result == ConstCastResult.Success) + { + return AggCastResult.Success; // else, don't fold and use a regular cast, below. + } + if (result == ConstCastResult.CheckFailure && 0 == (flags & CONVERTTYPE.CHECKOVERFLOW)) + { + return AggCastResult.Abort; + } + } + + // Conversions from integral types to decimal are always bound as a user-defined conversion. + + if (needsExprDest) + { + // According the language, this is a standard conversion, but it is implemented + // through a user-defined conversion. Because it's a standard conversion, we don't + // test the CONVERTTYPE.NOUDC flag here. + + bool ok = binder.bindUserDefinedConversion(exprCast, underlyingType, aggTypeDest, needsExprDest, out exprDest, false); + Debug.Assert(ok); + } + + return AggCastResult.Success; + } + + private AggCastResult bindExplicitConversionToEnum(AggregateType aggTypeDest) + { + Debug.Assert(typeSrc != null); + Debug.Assert(aggTypeDest != null); + + AggregateSymbol aggDest = aggTypeDest.getAggregate(); + if (!aggDest.IsEnum()) + { + return AggCastResult.Failure; + } + + if (typeSrc.isPredefType(PredefinedType.PT_DECIMAL)) + { + return bindExplicitConversionFromDecimalToEnum(aggTypeDest); + } + + if (typeSrc.isNumericType() || (typeSrc.isPredefined() && typeSrc.getPredefType() == PredefinedType.PT_CHAR)) + { + // Transform constant to constant. + if (exprSrc.GetConst() != null) + { + ConstCastResult result = binder.bindConstantCast(exprSrc, exprTypeDest, needsExprDest, out exprDest, true); + if (result == ConstCastResult.Success) + { + return AggCastResult.Success; + } + if (result == ConstCastResult.CheckFailure) + { + return AggCastResult.Abort; + } + } + if (needsExprDest) + binder.bindSimpleCast(exprSrc, exprTypeDest, out exprDest); + return AggCastResult.Success; + } + else if (typeSrc.isPredefined() && + (typeSrc.isPredefType(PredefinedType.PT_OBJECT) || typeSrc.isPredefType(PredefinedType.PT_VALUE) || typeSrc.isPredefType(PredefinedType.PT_ENUM))) + { + if (needsExprDest) + binder.bindSimpleCast(exprSrc, exprTypeDest, out exprDest, EXPRFLAG.EXF_UNBOX); + return AggCastResult.Success; + } + return AggCastResult.Failure; + } + + private AggCastResult bindExplicitConversionBetweenSimpleTypes(AggregateType aggTypeDest) + { + + // 13.2.1 + // + // Because the explicit conversions include all implicit and explicit numeric conversions, + // it is always possible to convert from any numeric-type to any other numeric-type using + // a cast expression (14.6.6). + + Debug.Assert(typeSrc != null); + Debug.Assert(aggTypeDest != null); + + if (!typeSrc.isSimpleType() || !aggTypeDest.isSimpleType()) + { + return AggCastResult.Failure; + } + + AggregateSymbol aggDest = aggTypeDest.getAggregate(); + + Debug.Assert(typeSrc.isPredefined() && aggDest.IsPredefined()); + + PredefinedType ptSrc = typeSrc.getPredefType(); + PredefinedType ptDest = aggDest.GetPredefType(); + + Debug.Assert((int)ptSrc < NUM_SIMPLE_TYPES && (int)ptDest < NUM_SIMPLE_TYPES); + + ConvKind convertKind = GetConvKind(ptSrc, ptDest); + // Identity and implicit conversions should already have been handled. + Debug.Assert(convertKind != ConvKind.Implicit); + Debug.Assert(convertKind != ConvKind.Identity); + + if (convertKind != ConvKind.Explicit) + { + return AggCastResult.Failure; + } + + if (exprSrc.GetConst() != null) + { + // Fold the constant cast if possible. + ConstCastResult result = binder.bindConstantCast(exprSrc, exprTypeDest, needsExprDest, out exprDest, true); + if (result == ConstCastResult.Success) + { + return AggCastResult.Success; // else, don't fold and use a regular cast, below. + } + if (result == ConstCastResult.CheckFailure && 0 == (flags & CONVERTTYPE.CHECKOVERFLOW)) + { + return AggCastResult.Abort; + } + } + + bool bConversionOk = true; + if (needsExprDest) + { + // Explicit conversions involving decimals are bound as user-defined conversions. + if (isUserDefinedConversion(ptSrc, ptDest)) + { + // According the language, this is a standard conversion, but it is implemented + // through a user-defined conversion. Because it's a standard conversion, we don't + // test the CONVERTTYPE.NOUDC flag here. + bConversionOk = binder.bindUserDefinedConversion(exprSrc, typeSrc, aggTypeDest, needsExprDest, out exprDest, false); + } + else + { + binder.bindSimpleCast(exprSrc, exprTypeDest, out exprDest, (flags & CONVERTTYPE.CHECKOVERFLOW) != 0 ? EXPRFLAG.EXF_CHECKOVERFLOW : 0); + } + } + return bConversionOk ? AggCastResult.Success : AggCastResult.Failure; + } + + private AggCastResult bindExplicitConversionBetweenAggregates(AggregateType aggTypeDest) + { + + // 13.2.3 + // + // The explicit reference conversions are: + // + // * From object to any reference-type. + // * From any class-type S to any class-type T, provided S is a base class of T. + // * From any class-type S to any interface-type T, provided S is not sealed and + // provided S does not implement T. + // * From any interface-type S to any class-type T, provided T is not sealed or provided + // T implements S. + // * From any interface-type S to any interface-type T, provided S is not derived from T. + + Debug.Assert(typeSrc != null); + Debug.Assert(aggTypeDest != null); + + if (!typeSrc.IsAggregateType()) + { + return AggCastResult.Failure; + } + + AggregateSymbol aggSrc = typeSrc.AsAggregateType().getAggregate(); + AggregateSymbol aggDest = aggTypeDest.getAggregate(); + + if (GetSymbolLoader().HasBaseConversion(aggTypeDest, typeSrc.AsAggregateType())) + { + if (needsExprDest) + { + if (aggDest.IsValueType() && aggSrc.getThisType().fundType() == FUNDTYPE.FT_REF) + { + binder.bindSimpleCast(exprSrc, exprTypeDest, out exprDest, EXPRFLAG.EXF_UNBOX); + } + else + { + binder.bindSimpleCast(exprSrc, exprTypeDest, out exprDest, EXPRFLAG.EXF_REFCHECK | (exprSrc != null ? (exprSrc.flags & EXPRFLAG.EXF_CANTBENULL) : 0)); + } + } + return AggCastResult.Success; + } + + if ((aggSrc.IsClass() && !aggSrc.IsSealed() && aggDest.IsInterface()) || + (aggSrc.IsInterface() && aggDest.IsClass() && !aggDest.IsSealed()) || + (aggSrc.IsInterface() && aggDest.IsInterface()) || + CConversions.HasGenericDelegateExplicitReferenceConversion(GetSymbolLoader(), typeSrc, aggTypeDest)) + { + if (needsExprDest) + binder.bindSimpleCast(exprSrc, exprTypeDest, out exprDest, EXPRFLAG.EXF_REFCHECK | (exprSrc != null ? (exprSrc.flags & EXPRFLAG.EXF_CANTBENULL) : 0)); + return AggCastResult.Success; + } + return AggCastResult.Failure; + } + + private AggCastResult bindExplicitConversionFromPointerToInt(AggregateType aggTypeDest) + { + + // 27.4 Pointer conversions + // in an unsafe context, the set of available explicit conversions (13.2) is extended to include + // the following explicit pointer conversions: + // + // * From any pointer-type to sbyte, byte, short, ushort, int, uint, long, or ulong. + + if (!typeSrc.IsPointerType() || aggTypeDest.fundType() > FUNDTYPE.FT_LASTINTEGRAL || !aggTypeDest.isNumericType()) + { + return AggCastResult.Failure; + } + if (needsExprDest) + binder.bindSimpleCast(exprSrc, exprTypeDest, out exprDest); + return AggCastResult.Success; + } + + private AggCastResult bindExplicitConversionFromTypeVarToAggregate(AggregateType aggTypeDest) + { + + // 13.2.3 Explicit reference conversions + // + // For a type-parameter T that is known to be a reference type (25.7), the following + // explicit reference conversions exist: + // + // * From T to any interface-type I provided there isn't already an implicit reference + // conversion from T to I. + + if (!typeSrc.IsTypeParameterType()) + { + return AggCastResult.Failure; + } +#if ! CSEE + if (aggTypeDest.getAggregate().IsInterface()) +#else + if ((exprSrc != null && !exprSrc.eeValue.substType.IsNullableType()) || aggTypeDest.getAggregate().IsInterface()) +#endif + { + // Explicit conversion of type variables to interfaces. + if (needsExprDest) + binder.bindSimpleCast(exprSrc, exprTypeDest, out exprDest, EXPRFLAG.EXF_FORCE_BOX | EXPRFLAG.EXF_REFCHECK); + return AggCastResult.Success; + } + return AggCastResult.Failure; + } + + private AggCastResult bindExplicitConversionToAggregate(AggregateType aggTypeDest) + { + Debug.Assert(typeSrc != null); + Debug.Assert(aggTypeDest != null); + + // TypeReference and ArgIterator can't be boxed (or converted to anything else) + if (typeSrc.isSpecialByRefType()) + { + return AggCastResult.Abort; + } + + AggCastResult result; + + result = bindExplicitConversionFromEnumToAggregate(aggTypeDest); + if (result != AggCastResult.Failure) + { + return result; + } + + result = bindExplicitConversionToEnum(aggTypeDest); + if (result != AggCastResult.Failure) + { + return result; + } + + result = bindExplicitConversionBetweenSimpleTypes(aggTypeDest); + if (result != AggCastResult.Failure) + { + return result; + } + + result = bindExplicitConversionBetweenAggregates(aggTypeDest); + if (result != AggCastResult.Failure) + { + return result; + } + + result = bindExplicitConversionFromPointerToInt(aggTypeDest); + if (result != AggCastResult.Failure) + { + return result; + } + + if (typeSrc.IsVoidType()) + { + // No conversion is allowed to or from a void type (user defined or otherwise) + // This is most likely the result of a failed anonymous method or member group conversion + return AggCastResult.Abort; + } + + result = bindExplicitConversionFromTypeVarToAggregate(aggTypeDest); + if (result != AggCastResult.Failure) + { + return result; + } + + return AggCastResult.Failure; + } + + private SymbolLoader GetSymbolLoader() + { + return binder.GetSymbolLoader(); + } + + private ExprFactory GetExprFactory() + { + return binder.GetExprFactory(); + } + } + } +} diff --git a/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/ExprFactory.cs b/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/ExprFactory.cs new file mode 100644 index 000000000..e4f07d6a7 --- /dev/null +++ b/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/ExprFactory.cs @@ -0,0 +1,961 @@ +// ==++== +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// ==--== + +using System; +using System.Diagnostics; +using Microsoft.CSharp.RuntimeBinder.Syntax; + +namespace Microsoft.CSharp.RuntimeBinder.Semantics +{ + internal sealed class ExprFactory + { + private GlobalSymbolContext m_globalSymbolContext; + private ConstValFactory m_constants; + + public ExprFactory(GlobalSymbolContext globalSymbolContext) + { + Debug.Assert(globalSymbolContext != null); + m_globalSymbolContext = globalSymbolContext; + m_constants = new ConstValFactory(); + } + public ConstValFactory GetExprConstants() + { + return m_constants; + } + private TypeManager GetTypes() + { + return m_globalSymbolContext.GetTypes(); + } + private BSYMMGR GetGlobalSymbols() + { + return m_globalSymbolContext.GetGlobalSymbols(); + } + + public EXPRCALL CreateCall(EXPRFLAG nFlags, CType pType, EXPR pOptionalArguments, EXPRMEMGRP pMemberGroup, MethWithInst MWI) + { + Debug.Assert(0 == (nFlags & + ~( + EXPRFLAG.EXF_NEWOBJCALL | EXPRFLAG.EXF_CONSTRAINED | EXPRFLAG.EXF_BASECALL | + EXPRFLAG.EXF_NEWSTRUCTASSG | + EXPRFLAG.EXF_IMPLICITSTRUCTASSG | EXPRFLAG.EXF_MASK_ANY + ) + )); + + EXPRCALL rval = new EXPRCALL(); + rval.kind = ExpressionKind.EK_CALL; + rval.type = pType; + rval.flags = nFlags; + rval.SetOptionalArguments(pOptionalArguments); + rval.SetMemberGroup(pMemberGroup); + rval.nubLiftKind = NullableCallLiftKind.NotLifted; + rval.castOfNonLiftedResultToLiftedType = null; + + rval.mwi = MWI; + Debug.Assert(rval != null); + return (rval); + } + + public EXPRFIELD CreateField(EXPRFLAG nFlags, CType pType, EXPR pOptionalObject, uint nOffset, FieldWithType FWT, EXPR pOptionalLHS) + { + Debug.Assert(0 == (nFlags & ~(EXPRFLAG.EXF_MEMBERSET | EXPRFLAG.EXF_MASK_ANY))); + EXPRFIELD rval = new EXPRFIELD(); + rval.kind = ExpressionKind.EK_FIELD; + rval.type = pType; + rval.flags = nFlags; + rval.SetOptionalObject(pOptionalObject); + if (FWT != null) + { + rval.fwt = FWT; + } + Debug.Assert(rval != null); + return (rval); + } + + public EXPRFUNCPTR CreateFunctionPointer(EXPRFLAG nFlags, CType pType, EXPR pObject, MethWithInst MWI) + { + Debug.Assert(0 == (nFlags & ~(EXPRFLAG.EXF_BASECALL))); + EXPRFUNCPTR rval = new EXPRFUNCPTR(); + rval.kind = ExpressionKind.EK_FUNCPTR; + rval.type = pType; + rval.flags = nFlags; + rval.OptionalObject = pObject; + rval.mwi = new MethWithInst(MWI); + Debug.Assert(rval != null); + return (rval); + } + + public EXPRARRINIT CreateArrayInit(EXPRFLAG nFlags, CType pType, EXPR pOptionalArguments, EXPR pOptionalArgumentDimensions, int[] pDimSizes) + { + Debug.Assert(0 == (nFlags & + ~(EXPRFLAG.EXF_MASK_ANY | EXPRFLAG.EXF_ARRAYCONST | EXPRFLAG.EXF_ARRAYALLCONST))); + EXPRARRINIT rval = new EXPRARRINIT(); + rval.kind = ExpressionKind.EK_ARRINIT; + rval.type = pType; + rval.SetOptionalArguments(pOptionalArguments); + rval.SetOptionalArgumentDimensions(pOptionalArgumentDimensions); + rval.dimSizes = pDimSizes; + rval.dimSize = pDimSizes != null ? pDimSizes.Length : 0; + Debug.Assert(rval != null); + return (rval); + } + + public EXPRPROP CreateProperty(CType pType, EXPR pOptionalObject) + { + MethPropWithInst mwi = new MethPropWithInst(); + EXPRMEMGRP pMemGroup = CreateMemGroup(pOptionalObject, mwi); + return CreateProperty(pType, null, null, pMemGroup, null, null, null); + } + + public EXPRPROP CreateProperty(CType pType, EXPR pOptionalObjectThrough, EXPR pOptionalArguments, EXPRMEMGRP pMemberGroup, PropWithType pwtSlot, MethWithType mwtGet, MethWithType mwtSet) + { + EXPRPROP rval = new EXPRPROP(); + rval.kind = ExpressionKind.EK_PROP; + rval.type = pType; + rval.flags = 0; + rval.SetOptionalObjectThrough(pOptionalObjectThrough); + rval.SetOptionalArguments(pOptionalArguments); + rval.SetMemberGroup(pMemberGroup); + + if (pwtSlot != null) + { + rval.pwtSlot = pwtSlot; + } + if (mwtSet != null) + { + rval.mwtSet = mwtSet; + } + Debug.Assert(rval != null); + return (rval); + } + + public EXPREVENT CreateEvent(CType pType, EXPR pOptionalObject, EventWithType EWT) + { + EXPREVENT rval = new EXPREVENT(); + rval.kind = ExpressionKind.EK_EVENT; + rval.type = pType; + rval.flags = 0; + rval.OptionalObject = pOptionalObject; + if (EWT != null) + { + rval.ewt = EWT; + } + Debug.Assert(rval != null); + return (rval); + } + + public EXPRMEMGRP CreateMemGroup(EXPRFLAG nFlags, Name pName, TypeArray pTypeArgs, SYMKIND symKind, CType pTypePar, MethodOrPropertySymbol pMPS, EXPR pObject, CMemberLookupResults memberLookupResults) + { + Debug.Assert(0 == (nFlags & ~( + EXPRFLAG.EXF_CTOR | EXPRFLAG.EXF_INDEXER | EXPRFLAG.EXF_OPERATOR | EXPRFLAG.EXF_NEWOBJCALL | + EXPRFLAG.EXF_BASECALL | EXPRFLAG.EXF_DELEGATE | EXPRFLAG.EXF_USERCALLABLE | EXPRFLAG.EXF_MASK_ANY + ) + )); + EXPRMEMGRP rval = new EXPRMEMGRP(); + rval.kind = ExpressionKind.EK_MEMGRP; + rval.type = GetTypes().GetMethGrpType(); + rval.flags = nFlags; + rval.name = pName; + rval.typeArgs = pTypeArgs; + rval.sk = symKind; + rval.SetParentType(pTypePar); + rval.SetOptionalObject(pObject); + rval.SetMemberLookupResults(memberLookupResults); + rval.SetOptionalLHS(null); + if (rval.typeArgs == null) + { + rval.typeArgs = BSYMMGR.EmptyTypeArray(); + } + Debug.Assert(rval != null); + return (rval); + } + + public EXPRMEMGRP CreateMemGroup( + EXPR pObject, + MethPropWithInst mwi) + { + Name pName = mwi.Sym != null ? mwi.Sym.name : null; + MethodOrPropertySymbol methProp = mwi.MethProp(); + + CType pType = mwi.GetType(); + if (pType == null) + { + pType = GetTypes().GetErrorSym(); + } + + return CreateMemGroup(0, pName, mwi.TypeArgs, methProp != null ? methProp.getKind() : SYMKIND.SK_MethodSymbol, mwi.GetType(), methProp, pObject, new CMemberLookupResults(GetGlobalSymbols().AllocParams(1, new CType[] { pType }), pName)); + } + + public EXPRUSERDEFINEDCONVERSION CreateUserDefinedConversion(EXPR arg, EXPR call, MethWithInst mwi) + { + Debug.Assert(arg != null); + Debug.Assert(call != null); + EXPRUSERDEFINEDCONVERSION rval = new EXPRUSERDEFINEDCONVERSION(); + rval.kind = ExpressionKind.EK_USERDEFINEDCONVERSION; + rval.type = call.type; + rval.flags = 0; + rval.Argument = arg; + rval.UserDefinedCall = call; + rval.UserDefinedCallMethod = mwi; + if (call.HasError()) + { + rval.SetError(); + } + Debug.Assert(rval != null); + return rval; + } + + public EXPRCAST CreateCast(EXPRFLAG nFlags, CType pType, EXPR pArg) + { + return CreateCast(nFlags, CreateClass(pType, null, null), pArg); + } + + public EXPRCAST CreateCast(EXPRFLAG nFlags, EXPRTYPEORNAMESPACE pType, EXPR pArg) + { + Debug.Assert(pArg != null); + Debug.Assert(pType != null); + Debug.Assert(0 == (nFlags & ~(EXPRFLAG.EXF_CAST_ALL | EXPRFLAG.EXF_MASK_ANY))); + EXPRCAST rval = new EXPRCAST(); + rval.type = pType.TypeOrNamespace as CType; + rval.kind = ExpressionKind.EK_CAST; + rval.Argument = pArg; + rval.flags = nFlags; + rval.DestinationType = pType; + Debug.Assert(rval != null); + return (rval); + } + + public EXPRRETURN CreateReturn(EXPRFLAG nFlags, Scope pCurrentScope, EXPR pOptionalObject) + { + return CreateReturn(nFlags, pCurrentScope, pOptionalObject, pOptionalObject); + } + + public EXPRRETURN CreateReturn(EXPRFLAG nFlags, Scope pCurrentScope, EXPR pOptionalObject, EXPR pOptionalOriginalObject) + { + Debug.Assert(0 == (nFlags & + ~(EXPRFLAG.EXF_ASLEAVE | EXPRFLAG.EXF_FINALLYBLOCKED | EXPRFLAG.EXF_RETURNISYIELD | + EXPRFLAG.EXF_ASFINALLYLEAVE | EXPRFLAG.EXF_GENERATEDSTMT | EXPRFLAG.EXF_MARKING | + EXPRFLAG.EXF_MASK_ANY + ) + )); + EXPRRETURN rval = new EXPRRETURN(); + rval.kind = ExpressionKind.EK_RETURN; + rval.type = null; + rval.flags = nFlags; + rval.SetOptionalObject(pOptionalObject); + Debug.Assert(rval != null); + return (rval); + } + + public EXPRLOCAL CreateLocal(EXPRFLAG nFlags, LocalVariableSymbol pLocal) + { + Debug.Assert(0 == (nFlags & ~(EXPRFLAG.EXF_MASK_ANY))); + + CType type = null; + if (pLocal != null) + { + type = pLocal.GetType(); + } + + EXPRLOCAL rval = new EXPRLOCAL(); + rval.kind = ExpressionKind.EK_LOCAL; + rval.type = type; + rval.flags = nFlags; + rval.local = pLocal; + Debug.Assert(rval != null); + return (rval); + } + + public EXPRTHISPOINTER CreateThis(LocalVariableSymbol pLocal, bool fImplicit) + { + Debug.Assert(pLocal == null || pLocal.isThis); + + CType type = null; + if (pLocal != null) + { + type = pLocal.GetType(); + } + + EXPRFLAG flags = EXPRFLAG.EXF_CANTBENULL; + if (fImplicit) + { + flags |= EXPRFLAG.EXF_IMPLICITTHIS; + } + if (type != null && type.isStructType()) + { + flags |= EXPRFLAG.EXF_LVALUE; + } + + EXPRTHISPOINTER rval = new EXPRTHISPOINTER(); + rval.kind = ExpressionKind.EK_THISPOINTER; + rval.type = type; + rval.flags = flags; + rval.local = pLocal; + Debug.Assert(rval != null); + return (rval); + } + + // UNDONE: Rename to CreateBoundAnonymousFunction + public EXPRBOUNDLAMBDA CreateAnonymousMethod(AggregateType delegateType) + { + Debug.Assert(delegateType == null || delegateType.isDelegateType()); + EXPRBOUNDLAMBDA rval = new EXPRBOUNDLAMBDA(); + rval.kind = ExpressionKind.EK_BOUNDLAMBDA; + rval.type = delegateType; + rval.flags = 0; + Debug.Assert(rval != null); + return (rval); + } + + // UNDONE: Rename to CreateUnboundAnonymousFunction + public EXPRUNBOUNDLAMBDA CreateLambda() + { + CType type = GetTypes().GetAnonMethType(); + + + EXPRUNBOUNDLAMBDA rval = new EXPRUNBOUNDLAMBDA(); + rval.kind = ExpressionKind.EK_UNBOUNDLAMBDA; + rval.type = type; + rval.flags = 0; + Debug.Assert(rval != null); + return (rval); + } + + public EXPRHOISTEDLOCALEXPR CreateHoistedLocalInExpression(EXPRLOCAL localToHoist) + { + Debug.Assert(localToHoist != null); + EXPRHOISTEDLOCALEXPR rval = new EXPRHOISTEDLOCALEXPR(); + rval.kind = ExpressionKind.EK_HOISTEDLOCALEXPR; + rval.type = GetTypes().GetOptPredefAgg(PredefinedType.PT_EXPRESSION).getThisType(); + rval.flags = 0; + return rval; + } + + public EXPRMETHODINFO CreateMethodInfo(MethPropWithInst mwi) + { + return CreateMethodInfo(mwi.Meth(), mwi.GetType(), mwi.TypeArgs); + } + + public EXPRMETHODINFO CreateMethodInfo(MethodSymbol method, AggregateType methodType, TypeArray methodParameters) + { + Debug.Assert(method != null); + Debug.Assert(methodType != null); + EXPRMETHODINFO methodInfo = new EXPRMETHODINFO(); + CType type; + if (method.IsConstructor()) + { + type = GetTypes().GetOptPredefAgg(PredefinedType.PT_CONSTRUCTORINFO).getThisType(); + } + else + { + type = GetTypes().GetOptPredefAgg(PredefinedType.PT_METHODINFO).getThisType(); + } + + methodInfo.kind = ExpressionKind.EK_METHODINFO; + methodInfo.type = type; + methodInfo.flags = 0; + methodInfo.Method = new MethWithInst(method, methodType, methodParameters); + return methodInfo; + } + + public EXPRPropertyInfo CreatePropertyInfo(PropertySymbol prop, AggregateType propertyType) + { + Debug.Assert(prop != null); + Debug.Assert(propertyType != null); + EXPRPropertyInfo propInfo = new EXPRPropertyInfo(); + + propInfo.kind = ExpressionKind.EK_PROPERTYINFO; + propInfo.type = GetTypes().GetOptPredefAgg(PredefinedType.PT_PROPERTYINFO).getThisType(); + propInfo.flags = 0; + propInfo.Property = new PropWithType(prop, propertyType); + + return propInfo; + } + + public EXPRFIELDINFO CreateFieldInfo(FieldSymbol field, AggregateType fieldType) + { + Debug.Assert(field != null); + Debug.Assert(fieldType != null); + EXPRFIELDINFO rval = new EXPRFIELDINFO(); + rval.kind = ExpressionKind.EK_FIELDINFO; + rval.type = GetTypes().GetOptPredefAgg(PredefinedType.PT_FIELDINFO).getThisType(); ; + rval.flags = 0; + rval.Init(field, fieldType); + return rval; + } + + public EXPRTYPEOF CreateTypeOf(EXPRTYPEORNAMESPACE pSourceType) + { + EXPRTYPEOF rval = new EXPRTYPEOF(); + rval.kind = ExpressionKind.EK_TYPEOF; + rval.type = GetTypes().GetReqPredefAgg(PredefinedType.PT_TYPE).getThisType(); + rval.flags = EXPRFLAG.EXF_CANTBENULL; + rval.SetSourceType(pSourceType); + Debug.Assert(rval != null); + return (rval); + } + public EXPRTYPEOF CreateTypeOf(CType pSourceType) + { + return CreateTypeOf(MakeClass(pSourceType)); + } + + public EXPRUSERLOGOP CreateUserLogOp(CType pType, EXPR pCallTF, EXPRCALL pCallOp) + { + Debug.Assert(pCallTF != null); + Debug.Assert(pCallOp != null); + Debug.Assert(pCallOp.GetOptionalArguments() != null); + Debug.Assert(pCallOp.GetOptionalArguments().isLIST()); + Debug.Assert(pCallOp.GetOptionalArguments().asLIST().GetOptionalElement() != null); + EXPRUSERLOGOP rval = new EXPRUSERLOGOP(); + EXPR leftChild = pCallOp.GetOptionalArguments().asLIST().GetOptionalElement(); + Debug.Assert(leftChild != null); + if (leftChild.isWRAP()) + { + // In the EE case, we don't create WRAPEXPRs. + leftChild = leftChild.asWRAP().GetOptionalExpression(); + Debug.Assert(leftChild != null); + } + rval.kind = ExpressionKind.EK_USERLOGOP; + rval.type = pType; + rval.flags = EXPRFLAG.EXF_ASSGOP; + rval.TrueFalseCall = pCallTF; + rval.OperatorCall = pCallOp; + rval.FirstOperandToExamine = leftChild; + Debug.Assert(rval != null); + return (rval); + } + + public EXPRUSERLOGOP CreateUserLogOpError(CType pType, EXPR pCallTF, EXPRCALL pCallOp) + { + EXPRUSERLOGOP rval = CreateUserLogOp(pType, pCallTF, pCallOp); + rval.SetError(); + return rval; + } + + public EXPRCONCAT CreateConcat(EXPR op1, EXPR op2) + { + Debug.Assert(op1 != null && op1.type != null); + Debug.Assert(op2 != null && op2.type != null); + Debug.Assert(op1.type.isPredefType(PredefinedType.PT_STRING) || op2.type.isPredefType(PredefinedType.PT_STRING)); + + CType type = op1.type; + if (!type.isPredefType(PredefinedType.PT_STRING)) + { + type = op2.type; + } + + Debug.Assert(type.isPredefType(PredefinedType.PT_STRING)); + + EXPRCONCAT rval = new EXPRCONCAT(); + rval.kind = ExpressionKind.EK_CONCAT; + rval.type = type; + rval.flags = 0; + rval.SetFirstArgument(op1); + rval.SetSecondArgument(op2); + Debug.Assert(rval != null); + return (rval); + } + + public EXPRCONSTANT CreateStringConstant(string str) + { + return CreateConstant(GetTypes().GetReqPredefAgg(PredefinedType.PT_STRING).getThisType(), m_constants.Create(str)); + } + + public EXPRMULTIGET CreateMultiGet(EXPRFLAG nFlags, CType pType, EXPRMULTI pOptionalMulti) + { + Debug.Assert(0 == (nFlags & ~(EXPRFLAG.EXF_MASK_ANY))); + EXPRMULTIGET rval = new EXPRMULTIGET(); + + rval.kind = ExpressionKind.EK_MULTIGET; + rval.type = pType; + rval.flags = nFlags; + rval.SetOptionalMulti(pOptionalMulti); + Debug.Assert(rval != null); + return (rval); + } + + public EXPRMULTI CreateMulti(EXPRFLAG nFlags, CType pType, EXPR pLeft, EXPR pOp) + { + Debug.Assert(pLeft != null); + Debug.Assert(pOp != null); + EXPRMULTI rval = new EXPRMULTI(); + + rval.kind = ExpressionKind.EK_MULTI; + rval.type = pType; + rval.flags = nFlags; + rval.SetLeft(pLeft); + rval.SetOperator(pOp); + Debug.Assert(rval != null); + return (rval); + } + + //////////////////////////////////////////////////////////////////////////////// + // + // Precondition: + // + // pType - Non-null + // + // This returns a null for reference types and an EXPRZEROINIT for all others. + + public EXPR CreateZeroInit(CType pType) + { + EXPRCLASS exprClass = MakeClass(pType); + return CreateZeroInit(exprClass); + } + + public EXPR CreateZeroInit(EXPRTYPEORNAMESPACE pTypeExpr) + { + return CreateZeroInit(pTypeExpr, null, false); + } + + private EXPR CreateZeroInit(EXPRTYPEORNAMESPACE pTypeExpr, EXPR pOptionalOriginalConstructorCall, bool isConstructor) + { + Debug.Assert(pTypeExpr != null); + CType pType = pTypeExpr.TypeOrNamespace.AsType(); + bool bIsError = false; + + if (pType.isEnumType()) + { + // For enum types, we create a constant that has the default value + // as an object pointer. + ConstValFactory factory = new ConstValFactory(); + EXPRCONSTANT expr = CreateConstant(pType, factory.Create(Activator.CreateInstance(pType.AssociatedSystemType))); + return expr; + } + + switch (pType.fundType()) + { + default: + bIsError = true; + break; + + case FUNDTYPE.FT_PTR: + { + CType nullType = GetTypes().GetNullType(); + + // UNDONE: I think this if is always false ... + if (nullType.fundType() == pType.fundType()) + { + // Create a constant here. + + EXPRCONSTANT expr = CreateConstant(pType, ConstValFactory.GetDefaultValue(ConstValKind.IntPtr)); + return (expr); + } + + // Just allocate a new node and fill it in. + + EXPRCAST cast = CreateCast(0, pTypeExpr, CreateNull()); // UNDONE: should pTree be passed in here? + return (cast); + } + + case FUNDTYPE.FT_REF: + case FUNDTYPE.FT_I1: + case FUNDTYPE.FT_U1: + case FUNDTYPE.FT_I2: + case FUNDTYPE.FT_U2: + case FUNDTYPE.FT_I4: + case FUNDTYPE.FT_U4: + case FUNDTYPE.FT_I8: + case FUNDTYPE.FT_U8: + case FUNDTYPE.FT_R4: + case FUNDTYPE.FT_R8: + { + EXPRCONSTANT expr = CreateConstant(pType, ConstValFactory.GetDefaultValue(pType.constValKind())); + EXPRCONSTANT exprInOriginal = CreateConstant(pType, ConstValFactory.GetDefaultValue(pType.constValKind())); + exprInOriginal.SetOptionalConstructorCall(pOptionalOriginalConstructorCall); + return expr; + // UNDONE: Check other bogus casts + } + case FUNDTYPE.FT_STRUCT: + if (pType.isPredefType(PredefinedType.PT_DECIMAL)) + { + EXPRCONSTANT expr = CreateConstant(pType, ConstValFactory.GetDefaultValue(pType.constValKind())); + EXPRCONSTANT exprOriginal = CreateConstant(pType, ConstValFactory.GetDefaultValue(pType.constValKind())); + exprOriginal.SetOptionalConstructorCall(pOptionalOriginalConstructorCall); + return expr; + } + break; + + case FUNDTYPE.FT_VAR: + break; + } + + EXPRZEROINIT rval = new EXPRZEROINIT(); + rval.kind = ExpressionKind.EK_ZEROINIT; + rval.type = pType; + rval.flags = 0; + rval.OptionalConstructorCall = pOptionalOriginalConstructorCall; + rval.IsConstructor = isConstructor; + + if (bIsError) + { + rval.SetError(); + } + + Debug.Assert(rval != null); + return (rval); + } + + public EXPRCONSTANT CreateConstant(CType pType, CONSTVAL constVal) + { + return CreateConstant(pType, constVal, null); + } + + public EXPRCONSTANT CreateConstant(CType pType, CONSTVAL constVal, EXPR pOriginal) + { + EXPRCONSTANT rval = CreateConstant(pType); + rval.setVal(constVal); + Debug.Assert(rval != null); + return (rval); + } + + public EXPRCONSTANT CreateConstant(CType pType) + { + EXPRCONSTANT rval = new EXPRCONSTANT(); + rval.kind = ExpressionKind.EK_CONSTANT; + rval.type = pType; + rval.flags = 0; + return rval; + } + + public EXPRCONSTANT CreateIntegerConstant(int x) + { + return CreateConstant(GetTypes().GetReqPredefAgg(PredefinedType.PT_INT).getThisType(), ConstValFactory.GetInt(x)); + } + public EXPRCONSTANT CreateBoolConstant(bool b) + { + return CreateConstant(GetTypes().GetReqPredefAgg(PredefinedType.PT_BOOL).getThisType(), ConstValFactory.GetBool(b)); + } + public EXPRBLOCK CreateBlock(EXPRBLOCK pOptionalCurrentBlock, EXPRSTMT pOptionalStatements, Scope pOptionalScope) + { + EXPRBLOCK rval = new EXPRBLOCK(); + rval.kind = ExpressionKind.EK_BLOCK; + rval.type = null; + rval.flags = 0; + rval.SetOptionalStatements(pOptionalStatements); + rval.OptionalScopeSymbol = pOptionalScope; + Debug.Assert(rval != null); + return (rval); + } + + public EXPRQUESTIONMARK CreateQuestionMark(EXPR pTestExpression, EXPRBINOP pConsequence) + { + Debug.Assert(pTestExpression != null); + Debug.Assert(pConsequence != null); + + CType pType = pConsequence.type; + if (pType == null) + { + Debug.Assert(pConsequence.GetOptionalLeftChild() != null); + pType = pConsequence.GetOptionalLeftChild().type; + Debug.Assert(pType != null); + } + EXPRQUESTIONMARK pResult = new EXPRQUESTIONMARK(); + pResult.kind = ExpressionKind.EK_QUESTIONMARK; + pResult.type = pType; + pResult.flags = 0; + pResult.SetTestExpression(pTestExpression); + pResult.SetConsequence(pConsequence); + Debug.Assert(pResult != null); + return pResult; + } + + public EXPRARRAYINDEX CreateArrayIndex(EXPR pArray, EXPR pIndex) + { + CType pType = pArray.type; + + if (pType != null && pType.IsArrayType()) + { + pType = pType.AsArrayType().GetElementType(); + } + else if (pType == null) + { + pType = GetTypes().GetReqPredefAgg(PredefinedType.PT_INT).getThisType(); + } + EXPRARRAYINDEX pResult = new EXPRARRAYINDEX(); + pResult.kind = ExpressionKind.EK_ARRAYINDEX; + pResult.type = pType; + pResult.flags = 0; + pResult.SetArray(pArray); + pResult.SetIndex(pIndex); + return pResult; + } + + public EXPRARRAYLENGTH CreateArrayLength(EXPR pArray) + { + EXPRARRAYLENGTH pResult = new EXPRARRAYLENGTH(); + pResult.kind = ExpressionKind.EK_ARRAYLENGTH; + pResult.type = GetTypes().GetReqPredefAgg(PredefinedType.PT_INT).getThisType(); + pResult.flags = 0; + pResult.SetArray(pArray); + return pResult; + } + + public EXPRBINOP CreateBinop(ExpressionKind exprKind, CType pType, EXPR p1, EXPR p2) + { + //Debug.Assert(exprKind.isBinaryOperator()); + EXPRBINOP rval = new EXPRBINOP(); + rval.kind = exprKind; + rval.type = pType; + rval.flags = EXPRFLAG.EXF_BINOP; + rval.SetOptionalLeftChild(p1); + rval.SetOptionalRightChild(p2); + rval.isLifted = false; + rval.SetOptionalUserDefinedCall(null); + rval.SetUserDefinedCallMethod(null); + Debug.Assert(rval != null); + return (rval); + } + + public EXPRUNARYOP CreateUnaryOp(ExpressionKind exprKind, CType pType, EXPR pOperand) + { + Debug.Assert(exprKind.isUnaryOperator()); + Debug.Assert(pOperand != null); + EXPRUNARYOP rval = new EXPRUNARYOP(); + rval.kind = exprKind; + rval.type = pType; + rval.flags = 0; + rval.Child = pOperand; + rval.OptionalUserDefinedCall = null; + rval.UserDefinedCallMethod = null; + Debug.Assert(rval != null); + return (rval); + } + + public EXPR CreateOperator(ExpressionKind exprKind, CType pType, EXPR pArg1, EXPR pOptionalArg2) + { + Debug.Assert(pArg1 != null); + EXPR rval = null; + if (exprKind.isUnaryOperator()) + { + Debug.Assert(pOptionalArg2 == null); + rval = CreateUnaryOp(exprKind, pType, pArg1); + } + else + rval = CreateBinop(exprKind, pType, pArg1, pOptionalArg2); + Debug.Assert(rval != null); + return rval; + } + + + public EXPRBINOP CreateUserDefinedBinop(ExpressionKind exprKind, CType pType, EXPR p1, EXPR p2, EXPR call, MethPropWithInst pmpwi) + { + Debug.Assert(p1 != null); + Debug.Assert(p2 != null); + Debug.Assert(call != null); + EXPRBINOP rval = new EXPRBINOP(); + rval.kind = exprKind; + rval.type = pType; + rval.flags = EXPRFLAG.EXF_BINOP; + rval.SetOptionalLeftChild(p1); + rval.SetOptionalRightChild(p2); + // The call may be lifted, but we do not mark the outer binop as lifted. + rval.isLifted = false; + rval.SetOptionalUserDefinedCall(call); + rval.SetUserDefinedCallMethod(pmpwi); + if (call.HasError()) + { + rval.SetError(); + } + Debug.Assert(rval != null); + return (rval); + } + + public EXPRUNARYOP CreateUserDefinedUnaryOperator(ExpressionKind exprKind, CType pType, EXPR pOperand, EXPR call, MethPropWithInst pmpwi) + { + Debug.Assert(pType != null); + Debug.Assert(pOperand != null); + Debug.Assert(call != null); + Debug.Assert(pmpwi != null); + EXPRUNARYOP rval = new EXPRUNARYOP(); + rval.kind = exprKind; + rval.type = pType; + rval.flags = 0; + rval.Child = pOperand; + // The call may be lifted, but we do not mark the outer binop as lifted. + rval.OptionalUserDefinedCall = call; + rval.UserDefinedCallMethod = pmpwi; + if (call.HasError()) + { + rval.SetError(); + } + Debug.Assert(rval != null); + return (rval); + } + + public EXPRUNARYOP CreateNeg(EXPRFLAG nFlags, EXPR pOperand) + { + Debug.Assert(pOperand != null); + EXPRUNARYOP pUnaryOp = CreateUnaryOp(ExpressionKind.EK_NEG, pOperand.type, pOperand); + pUnaryOp.flags |= nFlags; + return pUnaryOp; + } + + //////////////////////////////////////////////////////////////////////////////// + // Create a node that evaluates the first, evaluates the second, results in the second. + + public EXPRBINOP CreateSequence(EXPR p1, EXPR p2) + { + Debug.Assert(p1 != null); + Debug.Assert(p2 != null); + return CreateBinop(ExpressionKind.EK_SEQUENCE, p2.type, p1, p2); + } + + //////////////////////////////////////////////////////////////////////////////// + // Create a node that evaluates the first, evaluates the second, results in the first. + + public EXPRBINOP CreateReverseSequence(EXPR p1, EXPR p2) + { + Debug.Assert(p1 != null); + Debug.Assert(p2 != null); + return CreateBinop(ExpressionKind.EK_SEQREV, p1.type, p1, p2); + } + + public EXPRASSIGNMENT CreateAssignment(EXPR pLHS, EXPR pRHS) + { + EXPRASSIGNMENT pAssignment = new EXPRASSIGNMENT(); + pAssignment.kind = ExpressionKind.EK_ASSIGNMENT; + pAssignment.type = pLHS.type; + pAssignment.flags = EXPRFLAG.EXF_ASSGOP; + pAssignment.SetLHS(pLHS); + pAssignment.SetRHS(pRHS); + return pAssignment; + } + + //////////////////////////////////////////////////////////////////////////////// + + public EXPRNamedArgumentSpecification CreateNamedArgumentSpecification(Name pName, EXPR pValue) + { + EXPRNamedArgumentSpecification pResult = new EXPRNamedArgumentSpecification(); + + pResult.kind = ExpressionKind.EK_NamedArgumentSpecification; + pResult.type = pValue.type; + pResult.flags = 0; + pResult.Value = pValue; + pResult.Name = pName; + + return pResult; + } + + + public EXPRWRAP CreateWrap( + Scope pCurrentScope, + EXPR pOptionalExpression + ) + { + EXPRWRAP rval = new EXPRWRAP(); + rval.kind = ExpressionKind.EK_WRAP; + rval.type = null; + rval.flags = 0; + rval.SetOptionalExpression(pOptionalExpression); + if (pOptionalExpression != null) + { + rval.setType(pOptionalExpression.type); + } + rval.flags |= EXPRFLAG.EXF_LVALUE; + + Debug.Assert(rval != null); + return (rval); + } + public EXPRWRAP CreateWrapNoAutoFree(Scope pCurrentScope, EXPR pOptionalWrap) + { + EXPRWRAP rval = CreateWrap(pCurrentScope, pOptionalWrap); + return rval; + } + public EXPRBINOP CreateSave(EXPRWRAP wrap) + { + Debug.Assert(wrap != null); + EXPRBINOP expr = CreateBinop(ExpressionKind.EK_SAVE, wrap.type, wrap.GetOptionalExpression(), wrap); + expr.setAssignment(); + return expr; + } + + public EXPR CreateNull() + { + return CreateConstant(GetTypes().GetNullType(), ConstValFactory.GetNullRef()); + } + + public void AppendItemToList( + EXPR newItem, + ref EXPR first, + ref EXPR last + ) + { + // UNDONE: This is craziness. Refactor expression lists so that they are List. + if (newItem == null) + { + // Nothing changes. + return; + } + if (first == null) + { + Debug.Assert(last == first); + first = newItem; + last = newItem; + return; + } + if (first.kind != ExpressionKind.EK_LIST) + { + Debug.Assert(last == first); + first = CreateList(first, newItem); + last = first; + return; + } + Debug.Assert(last.kind == ExpressionKind.EK_LIST); + Debug.Assert(last.asLIST().OptionalNextListNode != null); + Debug.Assert(last.asLIST().OptionalNextListNode.kind != ExpressionKind.EK_LIST); + last.asLIST().OptionalNextListNode = CreateList(last.asLIST().OptionalNextListNode, newItem); + last = last.asLIST().OptionalNextListNode; + } + + public EXPRLIST CreateList(EXPR op1, EXPR op2) + { + EXPRLIST rval = new EXPRLIST(); + rval.kind = ExpressionKind.EK_LIST; + rval.type = null; + rval.flags = 0; + rval.SetOptionalElement(op1); + rval.SetOptionalNextListNode(op2); + Debug.Assert(rval != null); + return (rval); + } + public EXPRLIST CreateList(EXPR op1, EXPR op2, EXPR op3) + { + return CreateList(op1, CreateList(op2, op3)); + } + public EXPRLIST CreateList(EXPR op1, EXPR op2, EXPR op3, EXPR op4) + { + return CreateList(op1, CreateList(op2, CreateList(op3, op4))); + } + public EXPRTYPEARGUMENTS CreateTypeArguments(TypeArray pTypeArray, EXPR pOptionalElements) + { + Debug.Assert(pTypeArray != null); + EXPRTYPEARGUMENTS rval = new EXPRTYPEARGUMENTS(); + rval.kind = ExpressionKind.EK_TYPEARGUMENTS; + rval.type = null; + rval.flags = 0; + rval.SetOptionalElements(pOptionalElements); + return rval; + } + + public EXPRCLASS CreateClass(CType pType, EXPR pOptionalLHS, EXPRTYPEARGUMENTS pOptionalTypeArguments) + { + Debug.Assert(pType != null); + EXPRCLASS rval = new EXPRCLASS(); + rval.kind = ExpressionKind.EK_CLASS; + rval.type = pType; + rval.TypeOrNamespace = pType; + Debug.Assert(rval != null); + return (rval); + } + + public EXPRCLASS MakeClass(CType pType) + { + Debug.Assert(pType != null); + return CreateClass(pType, null/* LHS */, null/* type arguments */); + } + } +} + diff --git a/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/ExpressionBinder.cs b/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/ExpressionBinder.cs new file mode 100644 index 000000000..d7b30b7eb --- /dev/null +++ b/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/ExpressionBinder.cs @@ -0,0 +1,2661 @@ +// ==++== +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// ==--== + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using Microsoft.CSharp.RuntimeBinder.Errors; +using Microsoft.CSharp.RuntimeBinder.Syntax; + +namespace Microsoft.CSharp.RuntimeBinder.Semantics +{ + // Used by bindUserDefinedConversion + internal class UdConvInfo + { + public MethWithType mwt; + public bool fSrcImplicit; + public bool fDstImplicit; + } + + ////////////////////////////////////////////////////////////////////////////////////////////// + // Small wrapper for passing around argument information for the various BindGrpTo methods + // It is used because most things only need the type, but in the case of METHGRPs and ANONMETHs + // the expr is also needed to determine if a conversion is possible + internal class ArgInfos + { + public int carg; + public TypeArray types; + public bool fHasExprs; + public List prgexpr; + } + + internal enum BodyType + { + NormalBlock, + StatementExpression, + ReturnedExpression + } + + + internal enum ConstCastResult + { + Success, // Constant can be cast to type + Failure, // Constant cannot be cast to type + CheckFailure // Constant cannot be cast to type because of overflow in checked context + // (Note that this only happens when the conversion is explicit; implicit + // conversions never overflow, that's why they're implicit.) + } + + internal enum AggCastResult + { + Success, // We found a conversion, stop looking + Failure, // This conversion doesn't work, keep looking + Abort // No possible conversion can work, stop looking + } + + internal enum UnaryOperatorSignatureFindResult + { + Match, + Continue, + Return + } + + + internal enum UnaOpKind + { + Plus, + Minus, + Tilde, + Bang, + IncDec, + Lim + } + + internal enum UnaOpMask + { + None = 0, + Plus = 1 << UnaOpKind.Plus, + Minus = 1 << UnaOpKind.Minus, + Tilde = 1 << UnaOpKind.Tilde, + Bang = 1 << UnaOpKind.Bang, + IncDec = 1 << UnaOpKind.IncDec, + // The different combinations needed in operators.cs + Signed = Plus | Minus | Tilde, + Unsigned = Plus | Tilde, + Real = Plus | Minus, + Bool = Bang, + } + + internal enum OpSigFlags + { + None = 0, + Convert = 0x01, // Convert the operands before calling the bind method + CanLift = 0x02, // Operator has a lifted form + AutoLift = 0x04, // Standard nullable lifting + // The different combinations needed in operators.cs + Value = Convert | CanLift | AutoLift, + Reference = Convert, + BoolBit = Convert | CanLift, + } + + internal enum LiftFlags + { + None = 0, + Lift1 = 0x01, + Lift2 = 0x02, + Convert1 = 0x04, + Convert2 = 0x08, + } + + internal enum CheckLvalueKind + { + Assignment, + OutParameter, + Increment, + } + + internal enum BinOpFuncKind + { + BoolBinOp, + BoolBitwiseOp, + DecBinOp, + DelBinOp, + EnumBinOp, + IntBinOp, + PtrBinOp, + PtrCmpOp, + RealBinOp, + RefCmpOp, + ShiftOp, + StrBinOp, + StrCmpOp, + None + } + + internal enum UnaOpFuncKind + { + BoolUnaOp, + DecUnaOp, + EnumUnaOp, + IntUnaOp, + RealUnaOp, + LiftedIncOpCore, + None + } + + internal partial class ExpressionBinder + { + // ExpressionBinder - General Rules + // + // Express the Contract + // + // Use assertions and naming guidelines to express the contract for methods. + // The most common issue is whether an argument may be null or not. If an + // argument may not be null, then the method must ASSERT that before any other + // code. If an argument may be null then the name of the argument should + // include 'Optional'. The exception to this rule is the input parse tree + // parameter. If the parse tree may be null, then the method name should + // include an 'Opt' suffix. For example bindArgumentList should really be + // named bindArgumentListOpt. Abbreviations should be avoided, but the 'Opt' + // suffix gets an exception because it is used consistently in the language + // spec. + // + // + // Error Tolerant + // + // Do not rely on the input parse tree being complete. Erroneous code may + // result in parse trees with required children missing, or with unexpected + // structure. Find out what the invariants are for the parse tree being + // consumed and code defensively. + // + // Similarly, the result of binding children nodes may not be 'OK'. The child + // node may have contained some semantic errors and the binding code in the + // parent must cope gracefully with the result. For example, an EXPRMEMGRP may + // contain no members. + // + // + // Error Recovery + // + // Always attempt to bind children nodes even if errors have already been + // detected in other children. Always build a new node representing a 'best + // guess' at the semantics of the parse tree. Never discard the results of + // binding a child node even if the binding has errors. Since a new node is + // always produced there should always be a place to add bindings with errors + // to the result. + // + // This ensures that full semantic information for all nodes is produced - + // that the expression binder always produces a 'best guess' for every + // expression in source. + // + // + // Error Reporting + // + // If a child expression has an error, then no new error's for the parent + // should be reported, unless there is no way that the new error was caused + // by any child errors. If child nodes don't have any errors, and the new + // node does have an error, then at least one error must be reported. These + // rules ensure that an error is always reported for erroneous code, and that + // only the most meaningful error is reported from a set of cascading errors. + // + // + // Map Back To the Source + // + // When constructing new expression nodes, attach the appropriate parse tree + // node. The attached parse tree is used for: + // - error location reporting + // - debug sequence points + // - stepping + // - local variable scopes + // - finding the most meaningful expression for a parse tree node + // + // + // Meaning not Implementation + // + // The expression trees resulting from the initial binding pass should + // represent the semantics of the input source code. There should be a + // direct mapping between the newly constructed expression and the input + // parse tree. + // + // The Whidbey codebase had the habit of producing expressions in the initial + // binding which were closer in representation to the generated IL than the + // input source code. In Orcas all transformations which may lose semantic + // information about the source must be done after the initial expression + // binding phase. + // + // Special Cases + // + // - Constant folding - Constant folding is a semantic losing + // transformation which must be performed to complete expression + // analysis. When creating a folded constant, create an expression + // node representing the unfolded expression, then pass this as a + // child expression of a new constant expression. + // - Color Color - The new Type or Instance expression covers this case. + // It is produced from bindSimpleName. + // - Method Group - Method groups should be preserved in expression trees. + // This includes as children of Call expressions, and delegate construction + // nodes. + // - Type Binding - This is a big one. Whenever a type is bound in an + // expression, the binding of the component parts of the type must be + // preserved. This includes every identifier in a dotted type or + // namespace name, as well as the type binding information for the + // type arguments of constructed types. The semantic information for + // intermediate type binding results is represented by an + // EXPRTYPEORNAMESPACE. Types can be bound in several places in + // expressions: + // - Sizeof + // - Typeof + // - New + // - Is/As + // - Cast + // - Type Arguments supplied to generic method calls. + // - Left hand side of a dot operator. + // - Parameter types in anonymous methods and lambdas. + // - Local Variables + // + // + // Want to eventually Have's + // + // Only build the new node once all children have been built. + // Factory should require all children as arguments. + // Factory method sets the "Do Children have Errors?" bit - not done manually. + // Once constructed Expression trees are not mutated - doesn't work easily for statements unfortunately. + + protected delegate EXPR PfnBindBinOp(ExpressionKind ek, EXPRFLAG flags, EXPR op1, EXPR op2); + protected delegate EXPR PfnBindUnaOp(ExpressionKind ek, EXPRFLAG flags, EXPR op); + + protected BindingContext Context; + public BindingContext GetContext() { return Context; } + protected CNullable m_nullable; + + private static void VSFAIL(string s) + { + Debug.Assert(false, s); + } + + public ExpressionBinder(BindingContext context) + { + Context = context; + m_nullable = new CNullable(GetSymbolLoader(), GetErrorContext(), GetExprFactory()); + g_binopSignatures = new BinOpSig[] + { + new BinOpSig (PredefinedType.PT_INT, PredefinedType.PT_INT, BinOpMask.Integer, 8, BindIntBinOp, OpSigFlags.Value, BinOpFuncKind.IntBinOp ), + new BinOpSig (PredefinedType.PT_UINT, PredefinedType.PT_UINT, BinOpMask.Integer, 7, BindIntBinOp, OpSigFlags.Value, BinOpFuncKind.IntBinOp ), + new BinOpSig (PredefinedType.PT_LONG, PredefinedType.PT_LONG, BinOpMask.Integer, 6, BindIntBinOp, OpSigFlags.Value, BinOpFuncKind.IntBinOp ), + new BinOpSig (PredefinedType.PT_ULONG, PredefinedType.PT_ULONG, BinOpMask.Integer, 5, BindIntBinOp, OpSigFlags.Value, BinOpFuncKind.IntBinOp ), + /* ERROR */ new BinOpSig (PredefinedType.PT_ULONG, PredefinedType.PT_LONG, BinOpMask.Integer, 4, null, OpSigFlags.Value, BinOpFuncKind.None ), + /* ERROR */ new BinOpSig (PredefinedType.PT_LONG, PredefinedType.PT_ULONG, BinOpMask.Integer, 3, null, OpSigFlags.Value, BinOpFuncKind.None ), + new BinOpSig (PredefinedType.PT_FLOAT, PredefinedType.PT_FLOAT, BinOpMask.Real, 1, BindRealBinOp, OpSigFlags.Value, BinOpFuncKind.RealBinOp ), + new BinOpSig (PredefinedType.PT_DOUBLE, PredefinedType.PT_DOUBLE, BinOpMask.Real, 0, BindRealBinOp, OpSigFlags.Value, BinOpFuncKind.RealBinOp ), + new BinOpSig (PredefinedType.PT_DECIMAL, PredefinedType.PT_DECIMAL, BinOpMask.Real, 0, BindDecBinOp, OpSigFlags.Value, BinOpFuncKind.DecBinOp ), + new BinOpSig (PredefinedType.PT_STRING, PredefinedType.PT_STRING, BinOpMask.Equal, 0, BindStrCmpOp, OpSigFlags.Reference, BinOpFuncKind.StrCmpOp ), + new BinOpSig (PredefinedType.PT_STRING, PredefinedType.PT_STRING, BinOpMask.Add, 2, BindStrBinOp, OpSigFlags.Reference, BinOpFuncKind.StrBinOp ), + new BinOpSig (PredefinedType.PT_STRING, PredefinedType.PT_OBJECT, BinOpMask.Add, 1, BindStrBinOp, OpSigFlags.Reference, BinOpFuncKind.StrBinOp ), + new BinOpSig (PredefinedType.PT_OBJECT, PredefinedType.PT_STRING, BinOpMask.Add, 0, BindStrBinOp, OpSigFlags.Reference, BinOpFuncKind.StrBinOp ), + new BinOpSig (PredefinedType.PT_INT, PredefinedType.PT_INT, BinOpMask.Shift, 3, BindShiftOp, OpSigFlags.Value, BinOpFuncKind.ShiftOp ), + new BinOpSig (PredefinedType.PT_UINT, PredefinedType.PT_INT, BinOpMask.Shift, 2, BindShiftOp, OpSigFlags.Value, BinOpFuncKind.ShiftOp ), + new BinOpSig (PredefinedType.PT_LONG, PredefinedType.PT_INT, BinOpMask.Shift, 1, BindShiftOp, OpSigFlags.Value, BinOpFuncKind.ShiftOp ), + new BinOpSig (PredefinedType.PT_ULONG, PredefinedType.PT_INT, BinOpMask.Shift, 0, BindShiftOp, OpSigFlags.Value, BinOpFuncKind.ShiftOp ), + new BinOpSig (PredefinedType.PT_BOOL, PredefinedType.PT_BOOL, BinOpMask.BoolNorm, 0, BindBoolBinOp, OpSigFlags.Value, BinOpFuncKind.BoolBinOp ), + // Make boolean logical operators liftable so that they dont give funny short circuiting semantics. + // This is for DDBugs 677075. + new BinOpSig (PredefinedType.PT_BOOL, PredefinedType.PT_BOOL, BinOpMask.Logical, 0, BindBoolBinOp, OpSigFlags.BoolBit, BinOpFuncKind.BoolBinOp ), + new BinOpSig (PredefinedType.PT_BOOL, PredefinedType.PT_BOOL, BinOpMask.Bitwise, 0, BindLiftedBoolBitwiseOp, OpSigFlags.BoolBit, BinOpFuncKind.BoolBitwiseOp ), + }; + g_rguos = new UnaOpSig[] + { + new UnaOpSig( PredefinedType.PT_INT, UnaOpMask.Signed, 7, BindIntUnaOp, UnaOpFuncKind.IntUnaOp ), + new UnaOpSig( PredefinedType.PT_UINT, UnaOpMask.Unsigned, 6, BindIntUnaOp, UnaOpFuncKind.IntUnaOp ), + new UnaOpSig( PredefinedType.PT_LONG, UnaOpMask.Signed, 5, BindIntUnaOp, UnaOpFuncKind.IntUnaOp ), + new UnaOpSig( PredefinedType.PT_ULONG, UnaOpMask.Unsigned, 4, BindIntUnaOp, UnaOpFuncKind.IntUnaOp ), + /* ERROR */ new UnaOpSig( PredefinedType.PT_ULONG, UnaOpMask.Minus, 3, null, UnaOpFuncKind.None ), + new UnaOpSig( PredefinedType.PT_FLOAT, UnaOpMask.Real, 1, BindRealUnaOp, UnaOpFuncKind.RealUnaOp ), + new UnaOpSig( PredefinedType.PT_DOUBLE, UnaOpMask.Real, 0, BindRealUnaOp, UnaOpFuncKind.RealUnaOp ), + new UnaOpSig( PredefinedType.PT_DECIMAL, UnaOpMask.Real, 0, BindDecUnaOp, UnaOpFuncKind.DecUnaOp ), + new UnaOpSig( PredefinedType.PT_BOOL, UnaOpMask.Bool, 0, BindBoolUnaOp, UnaOpFuncKind.BoolUnaOp ), + new UnaOpSig( PredefinedType.PT_INT, UnaOpMask.IncDec, 6, null, UnaOpFuncKind.None ), + new UnaOpSig( PredefinedType.PT_UINT, UnaOpMask.IncDec, 5, null, UnaOpFuncKind.None ), + new UnaOpSig( PredefinedType.PT_LONG, UnaOpMask.IncDec, 4, null, UnaOpFuncKind.None ), + new UnaOpSig( PredefinedType.PT_ULONG, UnaOpMask.IncDec, 3, null, UnaOpFuncKind.None ), + new UnaOpSig( PredefinedType.PT_FLOAT, UnaOpMask.IncDec, 1, null, UnaOpFuncKind.None ), + new UnaOpSig( PredefinedType.PT_DOUBLE, UnaOpMask.IncDec, 0, null, UnaOpFuncKind.None ), + new UnaOpSig( PredefinedType.PT_DECIMAL, UnaOpMask.IncDec, 0, null, UnaOpFuncKind.None ), + }; + } + protected SymbolLoader GetSymbolLoader() { return SymbolLoader; } + protected SymbolLoader SymbolLoader + { + get + { + return Context.SymbolLoader; + } + } + protected CSemanticChecker SemanticChecker + { + get + { + return Context.SemanticChecker; + } + } + public CSemanticChecker GetSemanticChecker() { return SemanticChecker; } + + // UNDONE: move this to the binding context + private ErrorHandling ErrorContext + { + get + { + return SymbolLoader.ErrorContext; + } + } + private ErrorHandling GetErrorContext() { return ErrorContext; } + protected BSYMMGR GetGlobalSymbols() + { + return GetSymbolLoader().getBSymmgr(); + } + protected TypeManager GetTypes() { return TypeManager; } + protected TypeManager TypeManager { get { return SymbolLoader.TypeManager; } } + private ExprFactory GetExprFactory() { return ExprFactory; } + private ExprFactory ExprFactory { get { return Context.GetExprFactory(); } } + private ConstValFactory GetExprConstants() + { + return GetExprFactory().GetExprConstants(); + } + protected AggregateType GetReqPDT(PredefinedType pt) + { + return GetReqPDT(pt, GetSymbolLoader()); + } + protected static AggregateType GetReqPDT(PredefinedType pt, SymbolLoader symbolLoader) + { + Debug.Assert(pt != PredefinedType.PT_VOID); // use getVoidType() + return symbolLoader.GetReqPredefType(pt, true); + } + protected AggregateType GetOptPDT(PredefinedType pt) + { + return GetOptPDT(pt, true); + } + protected AggregateType GetOptPDT(PredefinedType pt, bool WarnIfNotFound) + { + Debug.Assert(pt != PredefinedType.PT_VOID); // use getVoidType() + if (WarnIfNotFound) + { + return GetSymbolLoader().GetOptPredefTypeErr(pt, true); + } + else + { + return GetSymbolLoader().GetOptPredefType(pt, true); + } + } + protected CType VoidType { get { return GetSymbolLoader().GetTypeManager().GetVoid(); } } + protected CType getVoidType() { return VoidType; } + + public EXPR GenerateAssignmentConversion(EXPR op1, EXPR op2, bool allowExplicit) + { + if (allowExplicit) + { + return mustCastCore(op2, GetExprFactory().MakeClass(op1.type), 0); + } + else + { + return mustConvertCore(op2, GetExprFactory().MakeClass(op1.type)); + } + } + + //////////////////////////////////////////////////////////////////////////////// + // Bind the simple assignment operator =. + + public EXPR bindAssignment(EXPR op1, EXPR op2, bool allowExplicit) + { + bool fOp2NotAddrOp = false; + bool fOp2WasCast = false; + + if (!op1.isANYLOCAL_OK()) + { + if (!checkLvalue(op1, CheckLvalueKind.Assignment)) + { + EXPR rval = GetExprFactory().CreateAssignment(op1, op2); + rval.SetError(); + return rval; + } + } + else + { + if (op2.type.IsArrayType()) + { + return BindPtrToArray(op1.asANYLOCAL(), op2); + } + if (op2.type == GetReqPDT(PredefinedType.PT_STRING)) + { + op2 = bindPtrToString(op2); + } + else if (op2.kind == ExpressionKind.EK_ADDR) + { + op2.flags |= EXPRFLAG.EXF_ADDRNOCONV; + } + else if (op2.isOK()) + { + fOp2NotAddrOp = true; + fOp2WasCast = (op2.isCAST()); + } + // REVIEW : GENERICS?? + } + + op2 = GenerateAssignmentConversion(op1, op2, allowExplicit); + if (op2.isOK() && fOp2NotAddrOp) + { + // Only report these errors if the convert succeeded + if (fOp2WasCast) + { + ErrorContext.Error(ErrorCode.ERR_BadCastInFixed); + } + else + { + ErrorContext.Error(ErrorCode.ERR_FixedNotNeeded); + } + } + return GenerateOptimizedAssignment(op1, op2); + } + + internal EXPR BindArrayIndexCore(BindingFlag bindFlags, EXPR pOp1, EXPR pOp2) + { + EXPR pExpr; + bool bIsError = false; + if (!pOp1.isOK() || !pOp2.isOK()) + { + bIsError = true; + } + + CType pIntType = GetReqPDT(PredefinedType.PT_INT); + + // Array indexing must occur on an array type. + if (!pOp1.type.IsArrayType()) + { + Debug.Assert(!pOp1.type.IsPointerType()); + pExpr = bindIndexer(pOp1, pOp2, bindFlags); + if (bIsError) + { + pExpr.SetError(); + } + return pExpr; + } + ArrayType pArrayType = pOp1.type.AsArrayType(); + checkUnsafe(pArrayType.GetElementType()); // added to the binder so we don't bind to pointer ops + // Check the rank of the array against the number of indices provided, and + // convert the indexes to ints + + CType pDestType = chooseArrayIndexType(pOp2); + + if (null == pDestType) + { + // using int as the type will allow us to give a better error... + pDestType = pIntType; + } + + int rank = pArrayType.rank; + int cIndices = 0; + + EXPR transformedIndices = pOp2.Map(GetExprFactory(), + (EXPR x) => + { + cIndices++; + EXPR pTemp = mustConvert(x, pDestType); + if (pDestType == pIntType) + return pTemp; + EXPRFLAG flag; +#if CSEE + flag = 0; +#else + flag = EXPRFLAG.EXF_INDEXEXPR; +#endif + EXPRCLASS exprType = GetExprFactory().MakeClass(pDestType); + return GetExprFactory().CreateCast(flag, exprType, pTemp); + }); + + if (cIndices != rank) + { + ErrorContext.Error(ErrorCode.ERR_BadIndexCount, rank); + pExpr = GetExprFactory().CreateArrayIndex(pOp1, transformedIndices); + pExpr.SetError(); + return pExpr; + } + + // Allocate a new expression, the type is the element type of the array. + // Array index operations are always lvalues. + pExpr = GetExprFactory().CreateArrayIndex(pOp1, transformedIndices); + pExpr.flags |= EXPRFLAG.EXF_LVALUE | EXPRFLAG.EXF_ASSGOP; + + if (bIsError) + { + pExpr.SetError(); + } + + return pExpr; + } + + //////////////////////////////////////////////////////////////////////////////// + // REFACTOR: This code should be moved to !CSEE. We need to sort out binding of fixed var + // decls first however. + + protected EXPRUNARYOP bindPtrToString(EXPR @string) + { + CType typeRet = GetTypes().GetPointer(GetReqPDT(PredefinedType.PT_CHAR)); + EXPRUNARYOP rval; + + rval = GetExprFactory().CreateUnaryOp(ExpressionKind.EK_ADDR, typeRet, @string); + + return rval; + } + + //////////////////////////////////////////////////////////////////////////////// + // REFACTOR: This code should be moved to !CSEE. We need to sort out binding of fixed var + // decls first however. + + protected EXPRQUESTIONMARK BindPtrToArray(EXPRLOCAL exprLoc, EXPR array) + { + CType typeElem = array.type.AsArrayType().GetElementType(); + CType typePtrElem = GetTypes().GetPointer(typeElem); + + // element must be unmanaged... + if (GetSymbolLoader().isManagedType(typeElem)) + { + ErrorContext.Error(ErrorCode.ERR_ManagedAddr, typeElem); + } + + SetExternalRef(typeElem); + + EXPR test = null; + // we need to wrap the array so we can effectively generate something like this: + // (((temp = array) != null && temp.Length > 0) ? loc = temp[0] : loc = null) + // NOTE: The assignment needs to be inside the ExpressionKind.EK_QUESTIONMARK. See Whidbey bug #397859. + // We can't do loc = (... ? ... : ...) since the CLR type of temp[0] is a managed + // pointer and null is a UIntPtr - which confuses the JIT. We can't just convert + // temp[0] to UIntPtr with a conv.u instruction because then if a GC occurs between + // the time of the cast and the assignment to the local, we're toast. + EXPRWRAP wrapArray = WrapShortLivedExpression(array).asWRAP(); + EXPR save = GetExprFactory().CreateSave(wrapArray); + EXPR nullTest = GetExprFactory().CreateBinop(ExpressionKind.EK_NE, GetReqPDT(PredefinedType.PT_BOOL), save, GetExprFactory().CreateConstant(wrapArray.type, ConstValFactory.GetInt(0))); + EXPR lenTest; + + if (array.type.AsArrayType().rank == 1) + { + EXPR len = GetExprFactory().CreateArrayLength(wrapArray); + lenTest = GetExprFactory().CreateBinop(ExpressionKind.EK_NE, GetReqPDT(PredefinedType.PT_BOOL), len, GetExprFactory().CreateConstant(GetReqPDT(PredefinedType.PT_INT), ConstValFactory.GetInt(0))); + } + else + { + EXPRCALL call = BindPredefMethToArgs(PREDEFMETH.PM_ARRAY_GETLENGTH, wrapArray, null, null, null); + lenTest = GetExprFactory().CreateBinop(ExpressionKind.EK_NE, GetReqPDT(PredefinedType.PT_BOOL), call, GetExprFactory().CreateConstant(GetReqPDT(PredefinedType.PT_INT), ConstValFactory.GetInt(0))); + } + + test = GetExprFactory().CreateBinop(ExpressionKind.EK_LOGAND, GetReqPDT(PredefinedType.PT_BOOL), nullTest, lenTest); + + EXPR list = null; + EXPR pList = list; + EXPR pLastList = null; + for (int cc = 0; cc < array.type.AsArrayType().rank; cc++) + { + GetExprFactory().AppendItemToList(GetExprFactory().CreateConstant(GetReqPDT(PredefinedType.PT_INT), ConstValFactory.GetInt(0)), ref pList, ref pLastList); + } + Debug.Assert(list != null); + + EXPR exprAddr = GetExprFactory().CreateUnaryOp(ExpressionKind.EK_ADDR, typePtrElem, GetExprFactory().CreateArrayIndex(wrapArray, list)); + exprAddr.flags |= EXPRFLAG.EXF_ADDRNOCONV; + exprAddr = mustConvert(exprAddr, exprLoc.type, CONVERTTYPE.NOUDC); + exprAddr = GetExprFactory().CreateAssignment(exprLoc, exprAddr); + exprAddr.flags |= EXPRFLAG.EXF_ASSGOP; + exprAddr = GetExprFactory().CreateBinop(ExpressionKind.EK_SEQREV, exprLoc.type, exprAddr, WrapShortLivedExpression(wrapArray)); // free the temp + + EXPR exprnull = GetExprFactory().CreateZeroInit(exprLoc.type); + exprnull = GetExprFactory().CreateAssignment(exprLoc, exprnull); + exprnull.flags |= EXPRFLAG.EXF_ASSGOP; + + EXPRBINOP exprRes = GetExprFactory().CreateBinop(ExpressionKind.EK_BINOP, exprAddr.type, exprAddr, exprnull); + return GetExprFactory().CreateQuestionMark(test, exprRes); + } + + + protected EXPR bindIndexer(EXPR pObject, EXPR args, BindingFlag bindFlags) + { + Name pName; + CType type = pObject.type; + + if (!type.IsAggregateType() && !type.IsTypeParameterType()) + { + ErrorContext.Error(ErrorCode.ERR_BadIndexLHS, type); + MethWithInst mwi = new MethWithInst(null, null); + EXPRMEMGRP pMemGroup = GetExprFactory().CreateMemGroup(pObject, mwi); + EXPRCALL rval = GetExprFactory().CreateCall(0, type, args, pMemGroup, null); + rval.SetError(); + return rval; + } + + pName = GetSymbolLoader().GetNameManager().GetPredefName(PredefinedName.PN_INDEXERINTERNAL); + + MemberLookup mem = new MemberLookup(); + if (!mem.Lookup(GetSemanticChecker(), type, pObject, ContextForMemberLookup(), pName, 0, + (bindFlags & BindingFlag.BIND_BASECALL) != 0 ? (MemLookFlags.BaseCall | MemLookFlags.Indexer) : MemLookFlags.Indexer)) + { + mem.ReportErrors(); + type = GetTypes().GetErrorSym(); + Symbol pSymbol = null; + + if (mem.SwtInaccessible().Sym != null) + { + Debug.Assert(mem.SwtInaccessible().Sym.IsMethodOrPropertySymbol()); + type = mem.SwtInaccessible().MethProp().RetType; + pSymbol = mem.SwtInaccessible().Sym; + } + + EXPRMEMGRP memgrp = null; + + if (pSymbol != null) + { + memgrp = GetExprFactory().CreateMemGroup((EXPRFLAG)mem.GetFlags(), // UNDONE: Gross cast here. + pName, BSYMMGR.EmptyTypeArray(), pSymbol.getKind(), mem.GetSourceType(), null/*pMPS*/, mem.GetObject(), mem.GetResults()); + memgrp.SetInaccessibleBit(); + } + else + { + MethWithInst mwi = new MethWithInst(null, null); + memgrp = GetExprFactory().CreateMemGroup(mem.GetObject(), mwi); + } + + EXPRCALL rval = GetExprFactory().CreateCall(0, type, args, memgrp, null); + rval.SetError(); + return rval; + } + + Debug.Assert(mem.SymFirst().IsPropertySymbol() && mem.SymFirst().AsPropertySymbol().isIndexer()); + + EXPRMEMGRP grp = GetExprFactory().CreateMemGroup((EXPRFLAG)mem.GetFlags(), // UNDONE: Gross cast + pName, BSYMMGR.EmptyTypeArray(), mem.SymFirst().getKind(), mem.GetSourceType(), null/*pMPS*/, mem.GetObject(), mem.GetResults()); + + EXPR pResult = BindMethodGroupToArguments(bindFlags, grp, args); + Debug.Assert(pResult.HasObject()); + if (pResult.getObject() == null) + { + // We must be in an error scenario where the object was not allowed. + // This can happen if the user tries to access the indexer off the + // type and not an instance or if the incorrect type/number of arguments + // were passed for binding. + pResult.SetObject(pObject); + pResult.SetError(); + } + return pResult; + } + + //////////////////////////////////////////////////////////////////////////////// + // Create a cast node with the given expression flags. + public void bindSimpleCast(EXPR exprSrc, EXPRTYPEORNAMESPACE typeDest, out EXPR pexprDest) + { + bindSimpleCast(exprSrc, typeDest, out pexprDest, 0); + } + public void bindSimpleCast(EXPR exprSrc, EXPRTYPEORNAMESPACE exprTypeDest, out EXPR pexprDest, EXPRFLAG exprFlags) + { + Debug.Assert(exprTypeDest != null); + Debug.Assert(exprTypeDest.TypeOrNamespace != null); + Debug.Assert(exprTypeDest.TypeOrNamespace.IsType()); + CType typeDest = exprTypeDest.TypeOrNamespace.AsType(); + pexprDest = null; + // If the source is a constant, and cast is really simple (no change in fundemental + // type, no flags), then create a new constant node with the new type instead of + // creating a cast node. This allows compile-time constants to be easily recognized. + EXPR exprConst = exprSrc.GetConst(); + + // Make the cast expr anyway, and if we find that we have a constant, then set the cast expr + // as the original tree for the constant. Otherwise, return the cast expr. + + EXPRCAST exprCast = GetExprFactory().CreateCast(exprFlags, exprTypeDest, exprSrc); + if (Context.CheckedNormal) + { + exprCast.flags |= EXPRFLAG.EXF_CHECKOVERFLOW; + } + + // Check if we have a compile time constant. If we do, create a constant for it and set the + // original tree to the cast. + + if (exprConst != null && exprFlags == 0 && + exprSrc.type.fundType() == typeDest.fundType() && + (!exprSrc.type.isPredefType(PredefinedType.PT_STRING) || exprConst.asCONSTANT().getVal().IsNullRef())) + { + EXPRCONSTANT expr = GetExprFactory().CreateConstant(typeDest, exprConst.asCONSTANT().getVal()); + pexprDest = expr; + return; + } + + pexprDest = exprCast; + Debug.Assert(exprCast.GetArgument() != null); + return; + } + + //////////////////////////////////////////////////////////////////////////////// + // Binds a call to a method, return type is an error or an EXPRCALL. + // + // tree - ParseTree for error messages + // pObject - pObject to call method on + // pmwi - Meth to bind to. This will be morphed when we remap to an override. + // args - arguments + // exprFlags - Flags to put on the generated expr + + internal EXPRCALL BindToMethod(MethWithInst mwi, EXPR pArguments, EXPRMEMGRP pMemGroup, MemLookFlags flags) + { + Debug.Assert(mwi.Sym != null && mwi.Sym.IsMethodSymbol() && (!mwi.Meth().isOverride || mwi.Meth().isHideByName)); + Debug.Assert(pMemGroup != null); + + bool fConstrained; + bool bIsMatchingStatic; + EXPR pObject = pMemGroup.GetOptionalObject(); + CType callingObjectType = pObject != null ? pObject.type : null; + PostBindMethod((flags & MemLookFlags.BaseCall) != 0, ref mwi, pObject); + pObject = AdjustMemberObject(mwi, pObject, out fConstrained, out bIsMatchingStatic); + pMemGroup.SetOptionalObject(pObject); + + CType pReturnType = null; + if ((flags & (MemLookFlags.Ctor | MemLookFlags.NewObj)) == (MemLookFlags.Ctor | MemLookFlags.NewObj)) + { + pReturnType = mwi.Ats; + } + else + { + pReturnType = GetTypes().SubstType(mwi.Meth().RetType, mwi.GetType(), mwi.TypeArgs); + } + + EXPRCALL pResult = GetExprFactory().CreateCall(0, pReturnType, pArguments, pMemGroup, mwi); + if (!bIsMatchingStatic) + { + pResult.SetMismatchedStaticBit(); + } + + if (!pResult.isOK()) + { + return pResult; + } + + // Set the return type and flags for constructors. + if ((flags & MemLookFlags.Ctor) != 0) + { + if ((flags & MemLookFlags.NewObj) != 0) + { + pResult.flags |= EXPRFLAG.EXF_NEWOBJCALL | EXPRFLAG.EXF_CANTBENULL; + } + else + { + Debug.Assert(pResult.type == getVoidType()); + } + } + + if ((flags & MemLookFlags.BaseCall) != 0) + { + pResult.flags |= EXPRFLAG.EXF_BASECALL; + } + else if (fConstrained && pObject != null) + { + // Use the constrained prefix. + pResult.flags |= EXPRFLAG.EXF_CONSTRAINED; + } + + verifyMethodArgs(pResult, callingObjectType); + + return pResult; + } + + //////////////////////////////////////////////////////////////////////////////// + // Construct the EXPR node which corresponds to a field expression + // for a given field and pObject pointer. + + internal EXPR BindToField(EXPR pObject, FieldWithType fwt, BindingFlag bindFlags) + { + return BindToField(pObject, fwt, bindFlags, null/*OptionalLHS*/); + } + + //////////////////////////////////////////////////////////////////////////////// + + internal EXPR BindToField(EXPR pOptionalObject, FieldWithType fwt, BindingFlag bindFlags, EXPR pOptionalLHS) + { + Debug.Assert(fwt.GetType() != null && fwt.Field().getClass() == fwt.GetType().getAggregate()); + + CType pFieldType = GetTypes().SubstType(fwt.Field().GetType(), fwt.GetType()); + if (pOptionalObject != null && !pOptionalObject.isOK()) + { + EXPRFIELD pField = GetExprFactory().CreateField(0, pFieldType, pOptionalObject, 0, fwt, pOptionalLHS); + pField.SetError(); + return pField; + } + + EXPR pOriginalObject = pOptionalObject; + bool bIsMatchingStatic; + bool pfConstrained; + pOptionalObject = AdjustMemberObject(fwt, pOptionalObject, out pfConstrained, out bIsMatchingStatic); + + checkUnsafe(pFieldType); // added to the binder so we don't bind to pointer ops + + EXPRFIELD pResult; + { + bool isLValue = false; + if ((pOptionalObject != null && pOptionalObject.type.IsPointerType()) || objectIsLvalue(pOptionalObject)) + { + isLValue = true; + } + // Exception: a readonly field is not an lvalue unless we're in the constructor/static constructor appropriate + // for the field. + if (RespectReadonly() && fwt.Field().isReadOnly) + { + if (ContainingAgg() == null || + !InMethod() || !InConstructor() || + fwt.Field().getClass() != ContainingAgg() || + InStaticMethod() != fwt.Field().isStatic || + (pOptionalObject != null && !isThisPointer(pOptionalObject)) || + InAnonymousMethod()) + { + isLValue = false; + } + } + + pResult = GetExprFactory().CreateField(isLValue ? EXPRFLAG.EXF_LVALUE : 0, pFieldType, pOptionalObject, 0, fwt, pOptionalLHS); + if (!bIsMatchingStatic) + { + pResult.SetMismatchedStaticBit(); + } + + if (pFieldType.IsErrorType()) + { + pResult.SetError(); + } + Debug.Assert(BindingFlag.BIND_MEMBERSET == (BindingFlag)EXPRFLAG.EXF_MEMBERSET); + pResult.flags |= (EXPRFLAG)(bindFlags & BindingFlag.BIND_MEMBERSET); + } + + // If this field is the backing field of a WindowsRuntime event then we need to bind to its + // invocationlist property which is a delegate containing all the handlers. + if (pResult.isFIELD() && + fwt.Field().isEvent && + fwt.Field().getEvent(GetSymbolLoader()) != null && + fwt.Field().getEvent(GetSymbolLoader()).IsWindowsRuntimeEvent) + { + CType fieldType = fwt.Field().GetType(); + if (fieldType.IsAggregateType()) + { + // Access event backing field (EventRegistrationTokenTable) using + // EventRegistrationTokenTable.GetOrCreateEventRegistrationTokenTable() + // to ensure non-null + pResult.setType(GetTypes().GetParameterModifier(pResult.type, false)); + + Name getOrCreateMethodName = GetSymbolLoader().GetNameManager().GetPredefName(PredefinedName.PN_GETORCREATEEVENTREGISTRATIONTOKENTABLE); + GetSymbolLoader().RuntimeBinderSymbolTable.PopulateSymbolTableWithName(getOrCreateMethodName.Text, null, fieldType.AssociatedSystemType); + MethodSymbol getOrCreateMethod = GetSymbolLoader().LookupAggMember(getOrCreateMethodName, fieldType.getAggregate(), symbmask_t.MASK_MethodSymbol).AsMethodSymbol(); + + MethPropWithInst getOrCreatempwi = new MethPropWithInst(getOrCreateMethod, fieldType.AsAggregateType()); + EXPRMEMGRP getOrCreateGrp = GetExprFactory().CreateMemGroup(null, getOrCreatempwi); + + EXPR getOrCreateCall = BindToMethod(new MethWithInst(getOrCreatempwi), + pResult, + getOrCreateGrp, + (MemLookFlags)MemLookFlags.None); + + AggregateSymbol fieldTypeSymbol = fieldType.AsAggregateType().GetOwningAggregate(); + Name invocationListName = GetSymbolLoader().GetNameManager().GetPredefName(PredefinedName.PN_INVOCATIONLIST); + + // InvocationList might not be populated in the symbol table as no one would have called it. + GetSymbolLoader().RuntimeBinderSymbolTable.PopulateSymbolTableWithName(invocationListName.Text, null, fieldType.AssociatedSystemType); + PropertySymbol invocationList = GetSymbolLoader().LookupAggMember( + invocationListName, + fieldTypeSymbol, + symbmask_t.MASK_PropertySymbol).AsPropertySymbol(); + + MethPropWithInst mpwi = new MethPropWithInst(invocationList, fieldType.AsAggregateType()); + EXPRMEMGRP memGroup = GetExprFactory().CreateMemGroup(getOrCreateCall, mpwi); + + PropWithType pwt = new PropWithType(invocationList, fieldType.AsAggregateType()); + EXPR propertyExpr = BindToProperty(getOrCreateCall, pwt, bindFlags, null, null, memGroup); + return propertyExpr; + } + } + + return pResult; + } + + //////////////////////////////////////////////////////////////////////////////// + + internal EXPR BindToProperty(EXPR pObject, PropWithType pwt, BindingFlag bindFlags, EXPR args, AggregateType pOtherType, EXPRMEMGRP pMemGroup) + { + Debug.Assert(pwt.Sym != null && + pwt.Sym.IsPropertySymbol() && + pwt.GetType() != null && + pwt.Prop().getClass() == pwt.GetType().getAggregate()); + Debug.Assert(pwt.Prop().Params.size == 0 || pwt.Prop().isIndexer()); + Debug.Assert(pOtherType == null || + !pwt.Prop().isIndexer() && + pOtherType.getAggregate() == pwt.Prop().RetType.getAggregate()); + + bool fConstrained; + MethWithType mwtGet; + MethWithType mwtSet; + EXPR pObjectThrough = null; + + // We keep track of the type of the pObject which we're doing the call through so that we can report + // protection access errors later, either below when binding the get, or later when checking that + // the setter is actually an lvalue. If we're actually doing a base.prop call then we do not + // need to ensure that the left side of the dot is an instance of the derived class, otherwise + // we save it away for later. + if (0 == (bindFlags & BindingFlag.BIND_BASECALL)) + { + pObjectThrough = pObject; + } + + bool bIsMatchingStatic; + PostBindProperty((bindFlags & BindingFlag.BIND_BASECALL) != 0, pwt, pObject, out mwtGet, out mwtSet); + + if (mwtGet && + (!mwtSet || + mwtSet.GetType() == mwtGet.GetType() || + GetSymbolLoader().HasBaseConversion(mwtGet.GetType(), mwtSet.GetType()) + ) + ) + { + pObject = AdjustMemberObject(mwtGet, pObject, out fConstrained, out bIsMatchingStatic); + } + else if (mwtSet) + { + pObject = AdjustMemberObject(mwtSet, pObject, out fConstrained, out bIsMatchingStatic); + } + else + { + pObject = AdjustMemberObject(pwt, pObject, out fConstrained, out bIsMatchingStatic); + } + pMemGroup.SetOptionalObject(pObject); + + CType pReturnType = GetTypes().SubstType(pwt.Prop().RetType, pwt.GetType()); + Debug.Assert(pOtherType == pReturnType || pOtherType == null); + + if (pObject != null && !pObject.isOK()) + { + EXPRPROP pResult = GetExprFactory().CreateProperty(pReturnType, pObjectThrough, args, pMemGroup, pwt, null, null); + if (!bIsMatchingStatic) + { + pResult.SetMismatchedStaticBit(); + } + pResult.SetError(); + return pResult; + } + + // if we are doing a get on this thing, and there is no get, and + // most imporantly, we are not leaving the arguments to be bound by the array index + // then error... + if ((bindFlags & BindingFlag.BIND_RVALUEREQUIRED) != 0) + { + if (!mwtGet) + { + if (pOtherType != null) + { + return GetExprFactory().MakeClass(pOtherType); + } + ErrorContext.ErrorRef(ErrorCode.ERR_PropertyLacksGet, pwt); + } + else if (((bindFlags & BindingFlag.BIND_BASECALL) != 0) && mwtGet.Meth().isAbstract) + { + // if the get exists, but is abstract, forbid the call as well... + if (pOtherType != null) + { + return GetExprFactory().MakeClass(pOtherType); + } + ErrorContext.Error(ErrorCode.ERR_AbstractBaseCall, pwt); + } + else + { + CType type = null; + if (pObjectThrough != null) + { + type = pObjectThrough.type; + } + + ACCESSERROR error = SemanticChecker.CheckAccess2(mwtGet.Meth(), mwtGet.GetType(), ContextForMemberLookup(), type); + if (error != ACCESSERROR.ACCESSERROR_NOERROR) + { + // if the get exists, but is not accessible, give an error. + if (pOtherType != null) + { + return GetExprFactory().MakeClass(pOtherType); + } + + if (error == ACCESSERROR.ACCESSERROR_NOACCESSTHRU) + { + ErrorContext.Error(ErrorCode.ERR_BadProtectedAccess, pwt, type, ContextForMemberLookup()); + } + else + { + ErrorContext.ErrorRef(ErrorCode.ERR_InaccessibleGetter, pwt); + } + } + } + } + + EXPRPROP result = GetExprFactory().CreateProperty(pReturnType, pObjectThrough, args, pMemGroup, pwt, mwtGet, mwtSet); + if (!bIsMatchingStatic) + { + result.SetMismatchedStaticBit(); + } + + Debug.Assert(EXPRFLAG.EXF_BASECALL == (EXPRFLAG)BindingFlag.BIND_BASECALL); + if ((EXPRFLAG.EXF_BASECALL & (EXPRFLAG)bindFlags) != 0) + { + result.flags |= EXPRFLAG.EXF_BASECALL; + } + else if (fConstrained && pObject != null) + { + // Use the constrained prefix. + result.flags |= EXPRFLAG.EXF_CONSTRAINED; + } + + if (result.GetOptionalArguments() != null) + { + verifyMethodArgs(result, pObjectThrough != null ? pObjectThrough.type : null); + } + + // REVIEW Shouldn't this check whether methSet is accessible before + // setting EXPRFLAG.EXF_LVALUE? + if (mwtSet && objectIsLvalue(result.GetMemberGroup().GetOptionalObject())) + { + result.flags |= EXPRFLAG.EXF_LVALUE; + } + if (pOtherType != null) + { + result.flags |= EXPRFLAG.EXF_SAMENAMETYPE; + } + + return result; + } + + //////////////////////////////////////////////////////////////////////////////// + // BADLY PLACED CODE: Move this method to operators.cpp + + internal EXPR bindUDUnop(ExpressionKind ek, EXPR arg) + { + Name pName = ekName(ek); + Debug.Assert(pName != null); + + CType typeSrc = arg.type; + + LAgain: + switch (typeSrc.GetTypeKind()) + { + case TypeKind.TK_NullableType: + typeSrc = typeSrc.StripNubs(); + goto LAgain; + case TypeKind.TK_TypeParameterType: + typeSrc = typeSrc.AsTypeParameterType().GetEffectiveBaseClass(); + goto LAgain; + case TypeKind.TK_AggregateType: + if (!typeSrc.isClassType() && !typeSrc.isStructType() || typeSrc.AsAggregateType().getAggregate().IsSkipUDOps()) + return null; + break; + default: + return null; + } + + ArgInfos info = new ArgInfos(); + + info.carg = 1; + FillInArgInfoFromArgList(info, arg); + + List methFirstList = new List(); + MethodSymbol methCur = null; + AggregateType atsCur = typeSrc.AsAggregateType(); + + for (; ; ) + { + // Find the next operator. + methCur = (methCur == null) ? + GetSymbolLoader().LookupAggMember(pName, atsCur.getAggregate(), symbmask_t.MASK_MethodSymbol).AsMethodSymbol() : + GetSymbolLoader().LookupNextSym(methCur, atsCur.getAggregate(), symbmask_t.MASK_MethodSymbol).AsMethodSymbol(); + + if (methCur == null) + { + // Find the next type. + // If we've found some applicable methods in a class then we don't need to look any further. + if (!methFirstList.IsEmpty()) + break; + atsCur = atsCur.GetBaseClass(); + if (atsCur == null) + break; + continue; + } + + // Only look at operators with 1 args. + if (!methCur.isOperator || methCur.Params.size != 1) + continue; + Debug.Assert(methCur.typeVars.size == 0); + + TypeArray paramsCur = GetTypes().SubstTypeArray(methCur.Params, atsCur); + CType typeParam = paramsCur.Item(0); + NullableType nubParam; + + if (canConvert(arg, typeParam)) + { + methFirstList.Add(new CandidateFunctionMember( + new MethPropWithInst(methCur, atsCur, BSYMMGR.EmptyTypeArray()), + paramsCur, + 0, + false)); + } + else if (GetSymbolLoader().FCanLift() && typeParam.IsNonNubValType() && + GetTypes().SubstType(methCur.RetType, atsCur).IsNonNubValType() && + canConvert(arg, nubParam = GetTypes().GetNullable(typeParam))) + { + methFirstList.Add(new CandidateFunctionMember( + new MethPropWithInst(methCur, atsCur, BSYMMGR.EmptyTypeArray()), + GetGlobalSymbols().AllocParams(1, new CType[] { nubParam }), + 1, + false)); + } + } + + if (methFirstList.IsEmpty()) + return null; + + CandidateFunctionMember pmethAmbig1; + CandidateFunctionMember pmethAmbig2; + CandidateFunctionMember pmethBest = FindBestMethod(methFirstList, null, info, out pmethAmbig1, out pmethAmbig2); + + if (pmethBest == null) + { + // No winner, so its an ambigous call... + ErrorContext.Error(ErrorCode.ERR_AmbigCall, pmethAmbig1.mpwi, pmethAmbig2.mpwi); + + EXPRMEMGRP pMemGroup = GetExprFactory().CreateMemGroup(null, pmethAmbig1.mpwi); + EXPRCALL rval = GetExprFactory().CreateCall(0, null, arg, pMemGroup, null); + rval.SetError(); + return rval; + } + + if (SemanticChecker.CheckBogus(pmethBest.mpwi.Meth())) + { + ErrorContext.ErrorRef(ErrorCode.ERR_BindToBogus, pmethBest.mpwi); + + EXPRMEMGRP pMemGroup = GetExprFactory().CreateMemGroup(null, pmethBest.mpwi); + EXPRCALL rval = GetExprFactory().CreateCall(0, null, arg, pMemGroup, null); + rval.SetError(); + return rval; + } + + EXPRCALL call; + + if (pmethBest.ctypeLift != 0) + { + call = BindLiftedUDUnop(arg, pmethBest.@params.Item(0), pmethBest.mpwi); + } + else + { + call = BindUDUnopCall(arg, pmethBest.@params.Item(0), pmethBest.mpwi); + } + + return GetExprFactory().CreateUserDefinedUnaryOperator(ek, call.type, arg, call, pmethBest.mpwi); + } + + //////////////////////////////////////////////////////////////////////////////// + // BADLY PLACED CODE: Move this method to operators.cpp + + EXPRCALL BindLiftedUDUnop(EXPR arg, CType typeArg, MethPropWithInst mpwi) + { + CType typeRaw = typeArg.StripNubs(); + if (!arg.type.IsNullableType() || !canConvert(arg.type.StripNubs(), typeRaw, CONVERTTYPE.NOUDC)) + { + // Convert then lift. + arg = mustConvert(arg, typeArg); + } + Debug.Assert(arg.type.IsNullableType()); + + CType typeRet = GetTypes().SubstType(mpwi.Meth().RetType, mpwi.GetType()); + if (!typeRet.IsNullableType()) + { + typeRet = GetTypes().GetNullable(typeRet); + } + + // First bind the non-lifted version for errors. + EXPR nonLiftedArg = mustCast(arg, typeRaw); + EXPRCALL nonLiftedResult = BindUDUnopCall(nonLiftedArg, typeRaw, mpwi); + + EXPRMEMGRP pMemGroup = GetExprFactory().CreateMemGroup(null, mpwi); + EXPRCALL call = GetExprFactory().CreateCall(0, typeRet, arg, pMemGroup, null); + call.mwi = new MethWithInst(mpwi); + call.castOfNonLiftedResultToLiftedType = mustCast(nonLiftedResult, typeRet, 0); + call.nubLiftKind = NullableCallLiftKind.Operator; + return call; + } + + //////////////////////////////////////////////////////////////////////////////// + // BADLY PLACED CODE: Move this method to operators.cpp + + EXPRCALL BindUDUnopCall(EXPR arg, CType typeArg, MethPropWithInst mpwi) + { + CType typeRet = GetTypes().SubstType(mpwi.Meth().RetType, mpwi.GetType()); + checkUnsafe(typeRet); // added to the binder so we don't bind to pointer ops + EXPRMEMGRP pMemGroup = GetExprFactory().CreateMemGroup(null, mpwi); + EXPRCALL call = GetExprFactory().CreateCall(0, typeRet, mustConvert(arg, typeArg), pMemGroup, null); + call.mwi = new MethWithInst(mpwi); + verifyMethodArgs(call, mpwi.GetType()); + return call; + } + + //////////////////////////////////////////////////////////////////////////////// + // Given a method group or indexer group, bind it to the arguments for an + // invocation. This method can change the arguments to bind with Extension + // Methods + private bool BindMethodGroupToArgumentsCore(out GroupToArgsBinderResult pResults, BindingFlag bindFlags, EXPRMEMGRP grp, ref EXPR args, int carg, bool bindingCollectionAdd, bool bHasNamedArgumentSpecifiers) + { + bool retval = false; + ArgInfos pargInfo; + ArgInfos pOriginalArgInfo; + int exprCount = carg; + + pargInfo = new ArgInfos(); + pargInfo.carg = carg; + FillInArgInfoFromArgList(pargInfo, args); + + pOriginalArgInfo = new ArgInfos(); + pOriginalArgInfo.carg = carg; + FillInArgInfoFromArgList(pOriginalArgInfo, args); + + GroupToArgsBinder binder = new GroupToArgsBinder(this, bindFlags, grp, pargInfo, pOriginalArgInfo, bHasNamedArgumentSpecifiers, null/*atsDelegate*/); + if (bindingCollectionAdd) + { + retval = binder.BindCollectionAddArgs(); + } + else + { + retval = binder.Bind(true /*ReportErrors*/); + } + + pResults = binder.GetResultsOfBind(); + return retval; + } + + //////////////////////////////////////////////////////////////////////////////// + // Given a method group or indexer group, bind it to the arguments for an + // invocation. + internal EXPR BindMethodGroupToArguments(BindingFlag bindFlags, EXPRMEMGRP grp, EXPR args) + { + Debug.Assert(grp.sk == SYMKIND.SK_MethodSymbol || grp.sk == SYMKIND.SK_PropertySymbol && ((grp.flags & EXPRFLAG.EXF_INDEXER) != 0)); + + // Count the args. + bool argTypeErrors; + int carg = CountArguments(args, out argTypeErrors); + // We need to store the object because BindMethodGroupToArgumentsCore will + // null it out in the case of an extension method, which is then consumed + // by BindToMethod. After that, we want to set the object back. + EXPR pObject = grp.GetOptionalObject(); + + // If we weren't given a pName, then we couldn't bind the method pName, so we should + // just bail out of here. + + if (grp.name == null) + { + EXPRCALL rval = GetExprFactory().CreateCall(0, GetTypes().GetErrorSym(), args, grp, null); + rval.SetError(); + return rval; + } + + // If we have named arguments specified, make sure we have them all appearing after + // fixed arguments. + bool bSeenNamed = false; + if (!VerifyNamedArgumentsAfterFixed(args, out bSeenNamed)) + { + EXPRCALL rval = GetExprFactory().CreateCall(0, GetTypes().GetErrorSym(), args, grp, null); + rval.SetError(); + return rval; + } + + GroupToArgsBinderResult result; + if (!BindMethodGroupToArgumentsCore(out result, bindFlags, grp, ref args, carg, false, bSeenNamed)) + { + Debug.Assert(false, "Why didn't BindMethodGroupToArgumentsCore throw an error?"); + return null; + } + + EXPR exprRes; + MethPropWithInst mpwiBest = result.GetBestResult(); + + if (grp.sk == SYMKIND.SK_PropertySymbol) + { + Debug.Assert((grp.flags & EXPRFLAG.EXF_INDEXER) != 0); + //PropWithType pwt = new PropWithType(mpwiBest.Prop(), mpwiBest.GetType()); + + exprRes = BindToProperty(grp.GetOptionalObject(), new PropWithType(mpwiBest), (bindFlags | (BindingFlag)(grp.flags & EXPRFLAG.EXF_BASECALL)), args, null/*typeOther*/, grp); + } + else + { + exprRes = BindToMethod(new MethWithInst(mpwiBest), args, grp, (MemLookFlags)grp.flags); + } + return exprRes; + } + + ///////////////////////////////////////////////////////////////////////////////// + + private bool VerifyNamedArgumentsAfterFixed(EXPR args, out bool seenNamed) + { + EXPR list = args; + seenNamed = false; + while (list != null) + { + EXPR arg; + if (list.isLIST()) + { + arg = list.asLIST().GetOptionalElement(); + list = list.asLIST().GetOptionalNextListNode(); + } + else + { + arg = list; + list = null; + } + + Debug.Assert(arg != null); + if (arg.isNamedArgumentSpecification()) + { + seenNamed = true; + } + else + { + if (seenNamed) + { + GetErrorContext().Error(ErrorCode.ERR_NamedArgumentSpecificationBeforeFixedArgument); + return false; + } + } + } + return true; + } + + //////////////////////////////////////////////////////////////////////////////// + // This finds a method and binds it to the args provided. + + internal EXPRCALL BindPredefMethToArgs(PREDEFMETH predefMethod, EXPR obj, EXPR args, TypeArray clsTypeArgs, TypeArray methTypeArgs) + { + MethodSymbol methSym = GetSymbolLoader().getPredefinedMembers().GetMethod(predefMethod); + if (methSym == null) + { + MethWithInst mwi = new MethWithInst(null, null); + EXPRMEMGRP pMemGroup = GetExprFactory().CreateMemGroup(obj, mwi); + EXPRCALL rval = GetExprFactory().CreateCall(0, null, args, pMemGroup, null); + rval.SetError(); + return rval; + } + + AggregateSymbol agg = methSym.getClass(); + if (clsTypeArgs == null) + { + clsTypeArgs = BSYMMGR.EmptyTypeArray(); + } + AggregateType aggType = GetTypes().GetAggregate(agg, clsTypeArgs); + + MethPropWithInst mpwiBest = new MethPropWithInst(methSym, aggType, methTypeArgs); + EXPRMEMGRP memgroup = GetExprFactory().CreateMemGroup(obj, mpwiBest); + + EXPRCALL exprRes = BindToMethod(new MethWithInst(mpwiBest), args, memgroup, (MemLookFlags)MemLookFlags.None); + + return exprRes; + } + //////////////////////////////////////////////////////////////////////////////// + // Report a bad operator types error to the user. + protected EXPR BadOperatorTypesError(ExpressionKind ek, EXPR pOperand1, EXPR pOperand2) + { + return BadOperatorTypesError(ek, pOperand1, pOperand2, null); + } + + protected EXPR BadOperatorTypesError(ExpressionKind ek, EXPR pOperand1, EXPR pOperand2, CType pTypeErr) + { + // This is a hack, but we need to store the operation somewhere... the first argument's as + // good a place as any. + string strOp = pOperand1.errorString; + + pOperand1 = UnwrapExpression(pOperand1); + + if (pOperand1 != null) + { + if (pOperand2 != null) + { + pOperand2 = UnwrapExpression(pOperand2); + if (pOperand1.type != null && + !pOperand1.type.IsErrorType() && + pOperand2.type != null && + !pOperand2.type.IsErrorType()) + { + ErrorContext.Error(ErrorCode.ERR_BadBinaryOps, strOp, pOperand1.type, pOperand2.type); + } + } + else if (pOperand1.type != null && !pOperand1.type.IsErrorType()) + { + ErrorContext.Error(ErrorCode.ERR_BadUnaryOp, strOp, pOperand1.type); + } + } + + if (pTypeErr == null) + { + pTypeErr = GetReqPDT(PredefinedType.PT_OBJECT); + } + + EXPR rval = GetExprFactory().CreateOperator(ek, pTypeErr, pOperand1, pOperand2); + rval.SetError(); + return rval; + } + + + protected EXPR UnwrapExpression(EXPR pExpression) + { + EXPR pExpr = pExpression; + while (pExpr != null && pExpr.isWRAP() && pExpr.asWRAP().GetOptionalExpression() != null) + { + pExpr = pExpr.asWRAP().GetOptionalExpression(); + } + + return pExpr; + } + + private static ErrorCode GetStandardLvalueError(CheckLvalueKind kind) + { + switch (kind) + { + default: + VSFAIL("bad kind"); + return ErrorCode.ERR_AssgLvalueExpected; + case CheckLvalueKind.Assignment: + return ErrorCode.ERR_AssgLvalueExpected; + case CheckLvalueKind.OutParameter: + return ErrorCode.ERR_RefLvalueExpected; + case CheckLvalueKind.Increment: + return ErrorCode.ERR_IncrementLvalueExpected; + } + } + + protected void CheckLvalueProp(EXPRPROP prop) + { + Debug.Assert(prop != null); + Debug.Assert(prop.isLvalue()); + + // We have an lvalue property. Give an error if this is an abstract property + // or an inaccessible property. + + if (prop.isBaseCall() && prop.mwtSet.Meth().isAbstract) + { + ErrorContext.Error(ErrorCode.ERR_AbstractBaseCall, prop.mwtSet); + } + else + { + CType type = null; + if (prop.GetOptionalObjectThrough() != null) + { + type = prop.GetOptionalObjectThrough().type; + } + + CheckPropertyAccess(prop.mwtSet, prop.pwtSlot, type); + } + } + + protected bool CheckPropertyAccess(MethWithType mwt, PropWithType pwtSlot, CType type) + { + ACCESSERROR error = SemanticChecker.CheckAccess2(mwt.Meth(), mwt.GetType(), ContextForMemberLookup(), type); + if (error == ACCESSERROR.ACCESSERROR_NOACCESSTHRU) + { + ErrorContext.Error(ErrorCode.ERR_BadProtectedAccess, pwtSlot, type, ContextForMemberLookup()); + return false; + } + else if (error == ACCESSERROR.ACCESSERROR_NOACCESS) + { + ErrorContext.Error(mwt.Meth().isSetAccessor() ? ErrorCode.ERR_InaccessibleSetter : ErrorCode.ERR_InaccessibleGetter, pwtSlot); + return false; + } + return true; + } + + //////////////////////////////////////////////////////////////////////////////// + // A false return means not to process the expr any further - it's totally out + // of place. For example - a method group or an anonymous method. + internal bool checkLvalue(EXPR expr, CheckLvalueKind kind) + { + + if (!expr.isOK()) + return false; + if (expr.isLvalue()) + { + if (expr.isPROP()) + { + CheckLvalueProp(expr.asPROP()); + } + markFieldAssigned(expr); + return true; + } + + switch (expr.kind) + { + case ExpressionKind.EK_PROP: + if (kind == CheckLvalueKind.OutParameter) + { + // passing a property as ref or out + ErrorContext.Error(ErrorCode.ERR_RefProperty); + return true; + } + if (!expr.asPROP().mwtSet) + { + // Assigning to a property without a setter. + // See VSWhidbey bug 524600: If we have + // bool? b = true; (bool)b = false; + // then this is realized immediately as + // b.Value = false; + // and no ExpressionKind.EK_CAST is generated. We'd rather not give a "you're writing + // to a read-only property" error in the case where the property access + // is not explicit in the source code. Fortunately in this case the + // cast is still hanging around in the parse tree, so we can look for it. + + // POSSIBLE ERROR: It would be nice to also give this error for other situations + // POSSIBLE ERROR: in which the user is attempting to assign to a value, such as + // POSSIBLE ERROR: an explicit (bool)b.Value = false; + // POSSIBLE ERROR: Unfortunately we cannot use this trick in that situation because + // POSSIBLE ERROR: we've already discarded the OperatorKind.OP_CAST node. (This is an SyntaxKind.Dot). + + // SPEC VIOLATION: More generally: + // SPEC VIOLATION: The spec states that the result of any cast is a value, not a + // SPEC VIOLATION: variable. Unfortunately we do not correctly implement this + // SPEC VIOLATION: and we probably should not start implementing it because this + // SPEC VIOLATION: would be a breaking change. We currently discard "no op" casts + // SPEC VIOLATION: very aggressively rather than generating an ExpressionKind.EK_CAST node. + + // REFACTOR: Ultimately it would be nice to change the binder so that + // REFACTOR: casts were not discarded so aggressively, exceptions to the + // REFACTOR: spec were more clearly represented in the code, and realization + // REFACTOR: of nullables was moved into a later pass. + + ErrorContext.Error(ErrorCode.ERR_AssgReadonlyProp, expr.asPROP().pwtSlot); + return true; + } + break; + + case ExpressionKind.EK_ARRAYLENGTH: + if (kind == CheckLvalueKind.OutParameter) + { + // passing a property as ref or out + ErrorContext.Error(ErrorCode.ERR_RefProperty); + } + else + { + // Special case, the length property of an array + ErrorContext.Error(ErrorCode.ERR_AssgReadonlyProp, GetSymbolLoader().getPredefinedMembers().GetProperty(PREDEFPROP.PP_ARRAY_LENGTH)); + } + return true; + + case ExpressionKind.EK_BOUNDLAMBDA: + case ExpressionKind.EK_UNBOUNDLAMBDA: + case ExpressionKind.EK_CONSTANT: + ErrorContext.Error(GetStandardLvalueError(kind)); + return false; + case ExpressionKind.EK_MEMGRP: + { + ErrorCode err = (kind == CheckLvalueKind.OutParameter) ? ErrorCode.ERR_RefReadonlyLocalCause : ErrorCode.ERR_AssgReadonlyLocalCause; + ErrorContext.Error(err, expr.asMEMGRP().name, new ErrArgIds(MessageID.MethodGroup)); + return false; + } + default: + break; + } + + return !TryReportLvalueFailure(expr, kind); + } + + internal void PostBindMethod(bool fBaseCall, ref MethWithInst pMWI, EXPR pObject) + { + MethWithInst mwiOrig = pMWI; + + // If it is virtual, find a remap of the method to something more specific. This + // may alter where the method is found. + if (pObject != null && (fBaseCall || pObject.type.isSimpleType() || pObject.type.isSpecialByRefType())) + { + RemapToOverride(GetSymbolLoader(), pMWI, pObject.type); + } + + if (fBaseCall && pMWI.Meth().isAbstract) + { + ErrorContext.Error(ErrorCode.ERR_AbstractBaseCall, pMWI); + } + + if (pMWI.Meth().RetType != null) + { + checkUnsafe(pMWI.Meth().RetType); + bool fCheckParams = false; + + if (pMWI.Meth().isExternal) + { + fCheckParams = true; + SetExternalRef(pMWI.Meth().RetType); + } + + // We need to check unsafe on the parameters as well, since we cannot check in conversion. + TypeArray pParams = pMWI.Meth().Params; + + for (int i = 0; i < pParams.size; i++) + { + // This is an optimization: don't call this in the vast majority of cases + CType type = pParams.Item(i); + + if (type.isUnsafe()) + { + checkUnsafe(type); + } + if (fCheckParams && type.IsParameterModifierType()) + { + SetExternalRef(type); + } + } + } + } + + protected void PostBindProperty(bool fBaseCall, PropWithType pwt, EXPR pObject, out MethWithType pmwtGet, out MethWithType pmwtSet) + { + pmwtGet = new MethWithType(); + pmwtSet = new MethWithType(); + // Get the accessors. + if (pwt.Prop().methGet != null) + { + pmwtGet.Set(pwt.Prop().methGet, pwt.GetType()); + } + else + { + pmwtGet.Clear(); + } + + if (pwt.Prop().methSet != null) + { + pmwtSet.Set(pwt.Prop().methSet, pwt.GetType()); + } + else + { + pmwtSet.Clear(); + } + + // If it is virtual, find a remap of the method to something more specific. This + // may alter where the accessors are found. + if (fBaseCall && pObject != null) + { + if (pmwtGet) + { + RemapToOverride(GetSymbolLoader(), pmwtGet, pObject.type); + } + if (pmwtSet) + { + RemapToOverride(GetSymbolLoader(), pmwtSet, pObject.type); + } + } + + if (pwt.Prop().RetType != null) + { + checkUnsafe(pwt.Prop().RetType); + } + } + + private EXPR AdjustMemberObject(SymWithType swt, EXPR pObject, out bool pfConstrained, out bool pIsMatchingStatic) + { + // Assert that the type is present and is an instantiation of the member's parent. + Debug.Assert(swt.GetType() != null && swt.GetType().getAggregate() == swt.Sym.parent.AsAggregateSymbol()); + bool bIsMatchingStatic = IsMatchingStatic(swt, pObject); + pIsMatchingStatic = bIsMatchingStatic; + pfConstrained = false; + + bool isStatic = swt.Sym.isStatic; + + // If our static doesn't match, bail out of here. + if (!bIsMatchingStatic) + { + if (isStatic) + { + // If we have a mismatched static, a static method, and the binding flag + // that tells us we're binding simple names, then insert a type here instead. + if ((pObject.flags & EXPRFLAG.EXF_SIMPLENAME) != 0) + { + // We've made the static match now. + pIsMatchingStatic = true; + return null; + } + else + { + ErrorContext.ErrorRef(ErrorCode.ERR_ObjectProhibited, swt); + return null; + } + } + else + { + ErrorContext.ErrorRef(ErrorCode.ERR_ObjectRequired, swt); + return pObject; + } + } + + // At this point, all errors for static invocations have been reported, and + // the object has been nulled out. So return out of here. + if (isStatic) + { + return null; + } + + // If we're in a constructor, then bail. + if (swt.Sym.IsMethodSymbol() && swt.Meth().IsConstructor()) + { + return pObject; + } + + if (pObject == null) + { + if (InFieldInitializer() && !InStaticMethod() && ContainingAgg() == swt.Sym.parent) + { + ErrorContext.ErrorRef(ErrorCode.ERR_FieldInitRefNonstatic, swt); // give better error message for common mistake See VS7:119218 + } + else if (InAnonymousMethod() && !InStaticMethod() && ContainingAgg() == swt.Sym.parent && ContainingAgg().IsStruct()) + { + ErrorContext.Error(ErrorCode.ERR_ThisStructNotInAnonMeth); + } + else + { + return null; + } + + // For fields or structs, make a this pointer for us to use. + + EXPRTHISPOINTER thisExpr = GetExprFactory().CreateThis(Context.GetThisPointer(), true); + thisExpr.SetMismatchedStaticBit(); + if (thisExpr.type == null) + { + thisExpr.setType(GetTypes().GetErrorSym()); + } + return thisExpr; + } + + CType typeObj = pObject.type; + CType typeTmp; + + if (typeObj.IsNullableType() && (typeTmp = typeObj.AsNullableType().GetAts(GetErrorContext())) != null && typeTmp != swt.GetType()) + { + typeObj = typeTmp; + } + + if (typeObj.IsTypeParameterType() || typeObj.IsAggregateType()) + { + AggregateSymbol aggCalled = null; + aggCalled = swt.Sym.parent.AsAggregateSymbol(); + Debug.Assert(swt.GetType().getAggregate() == aggCalled); + + // If we're invoking code on a struct-valued field, mark the struct as assigned (to + // avoid warning CS0649) - see Whidbey bug #434291. + if (pObject.isFIELD() && !pObject.asFIELD().fwt.Field().isAssigned && !swt.Sym.IsFieldSymbol() && + typeObj.isStructType() && !typeObj.isPredefined()) + { + pObject.asFIELD().fwt.Field().isAssigned = true; + } + + if (pfConstrained && + (typeObj.IsTypeParameterType() || + typeObj.isStructType() && swt.GetType().IsRefType() && swt.Sym.IsVirtual())) + { + // For calls on type parameters or virtual calls on struct types (not enums), + // use the constrained prefix. + pfConstrained = true; + } + + EXPR objNew = tryConvert(pObject, swt.GetType(), CONVERTTYPE.NOUDC); + + // This check ensures that we do not bind to methods in an outerclass + // which are visible, but whose this pointer is of an incorrect type... + // ... also handles case of calling an pObject method on a RefAny value. + // WE don't give a great message for this, but it'll do. + if (objNew == null) + { + if (!pObject.type.isSpecialByRefType()) + { + ErrorContext.Error(ErrorCode.ERR_WrongNestedThis, swt.GetType(), pObject.type); + } + else + { + ErrorContext.Error(ErrorCode.ERR_NoImplicitConv, pObject.type, swt.GetType()); + } + } + pObject = objNew; + } + + return pObject; + } + ///////////////////////////////////////////////////////////////////////////////// + + bool IsMatchingStatic(SymWithType swt, EXPR pObject) + { + Symbol pSym = swt.Sym; + + // Instance constructors are always ok, static constructors are never ok. + if (pSym.IsMethodSymbol() && pSym.AsMethodSymbol().IsConstructor()) + { + return !pSym.AsMethodSymbol().isStatic; + } + + bool isStatic = swt.Sym.isStatic; + + if (isStatic) + { + // If we're static and we dont have an object, or we have an implicit this, + // then we're ok. The reason implicit this is ok is because if the user is + // just typing something like: + // + // Equals( + // + // then the implicit this can bind to statics. + + if (pObject == null || ((pObject.flags & EXPRFLAG.EXF_IMPLICITTHIS) != 0)) + { + return true; + } + + if ((pObject.flags & EXPRFLAG.EXF_SAMENAMETYPE) == 0) + { + return false; + } + } + else if (pObject == null) + { + // We're not static, and we dont have an object. This is ok in certain scenarios: + bool bNonStaticField = InFieldInitializer() && !InStaticMethod() && ContainingAgg() == swt.Sym.parent; + bool bAnonymousMethod = InAnonymousMethod() && !InStaticMethod() && ContainingAgg() == swt.Sym.parent && ContainingAgg().IsStruct(); + + if (!bNonStaticField && !bAnonymousMethod) + { + return false; + } + } + return true; + } + + //////////////////////////////////////////////////////////////////////////////// + // this determines whether the expression as an pObject of a prop or field is an + // lvalue + + bool objectIsLvalue(EXPR pObject) + { + return ( + pObject == null || // statics are always lvalues + + isThisPointer(pObject) || // the this pointer's fields or props are lvalues + + (((pObject.flags & EXPRFLAG.EXF_LVALUE) != 0) && (pObject.kind != ExpressionKind.EK_PROP)) || + // things marked as lvalues have props/fields which are lvalues, with one exception: props of structs + // do not have fields/structs as lvalues + + !pObject.type.isStructOrEnum() + // non-struct types are lvalues (such as non-struct method returns) + ); + } + //////////////////////////////////////////////////////////////////////////////// + // For a base call we need to remap from the virtual to the specific override + // to invoke. This is also used to map a virtual on pObject (like ToString) to + // the specific override when the pObject is a simple type (int, bool, char, + // etc). In these cases it is safe to assume that any override won't later be + // removed.... We start searching from "typeObj" up the superclass hierarchy + // until we find a method with an exact signature match. + + public static void RemapToOverride(SymbolLoader symbolLoader, SymWithType pswt, CType typeObj) + { + // For a property/indexer we remap the accessors, not the property/indexer. + // Since every event has both accessors we remap the event instead of the accessors. + Debug.Assert(pswt && (pswt.Sym.IsMethodSymbol() || pswt.Sym.IsEventSymbol() || pswt.Sym.IsMethodOrPropertySymbol())); + Debug.Assert(typeObj != null); + + // Don't remap static or interface methods. + if (typeObj.IsNullableType()) + { + typeObj = typeObj.AsNullableType().GetAts(symbolLoader.GetErrorContext()); + if (typeObj == null) + { + VSFAIL("Why did GetAts return null?"); + return; + } + } + + // Don't remap non-virtual members + if (!typeObj.IsAggregateType() || typeObj.isInterfaceType() || !pswt.Sym.IsVirtual()) + { + return; + } + + symbmask_t mask = pswt.Sym.mask(); + + AggregateType atsObj = typeObj.AsAggregateType(); + + // Search for an override version of the method. + // REVIEW : this isn't going to work for calling arrays or Enum methods + while (atsObj != null && atsObj.getAggregate() != pswt.Sym.parent) + { + for (Symbol symT = symbolLoader.LookupAggMember(pswt.Sym.name, atsObj.getAggregate(), mask); + symT != null; + symT = symbolLoader.LookupNextSym(symT, atsObj.getAggregate(), mask)) + { + if (symT.IsOverride() && (symT.SymBaseVirtual() == pswt.Sym || symT.SymBaseVirtual() == pswt.Sym.SymBaseVirtual())) + { + pswt.Set(symT, atsObj); + return; + } + } + atsObj = atsObj.GetBaseClass(); + } + } + + protected void verifyMethodArgs(EXPR call, CType callingObjectType) + { + Debug.Assert(call.isCALL() || call.isPROP()); + + EXPR argsPtr = call.getArgs(); + SymWithType swt = call.GetSymWithType(); + MethodOrPropertySymbol mp = swt.Sym.AsMethodOrPropertySymbol(); + TypeArray pTypeArgs = call.isCALL() ? call.asCALL().mwi.TypeArgs : null; + EXPR newArgs; + AdjustCallArgumentsForParams(callingObjectType, swt.GetType(), mp, pTypeArgs, argsPtr, out newArgs); + call.setArgs(newArgs); + } + + protected void AdjustCallArgumentsForParams(CType callingObjectType, CType type, MethodOrPropertySymbol mp, TypeArray pTypeArgs, EXPR argsPtr, out EXPR newArgs) + { + Debug.Assert(mp != null); + Debug.Assert(mp.Params != null); + newArgs = null; + EXPR newArgsTail = null; + + MethodOrPropertySymbol mostDerivedMethod = ExpressionBinder.GroupToArgsBinder.FindMostDerivedMethod(GetSymbolLoader(), mp, callingObjectType); + + int paramCount = mp.Params.size; + TypeArray @params = mp.Params; + int iDst = 0; + bool markTypeFromExternCall = mp.IsFMETHSYM() && mp.AsFMETHSYM().isExternal; + int argCount = ExpressionIterator.Count(argsPtr); + + if (mp.IsFMETHSYM() && mp.AsFMETHSYM().isVarargs) + { + paramCount--; // we don't care about the vararg sentinel + } + + bool bDontFixParamArray = false; + + EXPR indir = null; + ExpressionIterator it = new ExpressionIterator(argsPtr); + + if (argsPtr == null) + { + if (mp.isParamArray) + goto FIXUPPARAMLIST; + return; + } + for (; !it.AtEnd(); it.MoveNext()) + { + indir = it.Current(); + // this will splice the optional arguments into the list + + if (indir.type.IsParameterModifierType()) + { + if (paramCount != 0) + paramCount--; + if (markTypeFromExternCall) + SetExternalRef(indir.type); + GetExprFactory().AppendItemToList(indir, ref newArgs, ref newArgsTail); + } + else if (paramCount != 0) + { + if (paramCount == 1 && mp.isParamArray && argCount > mp.Params.size) + { + // we arrived at the last formal, and we have more than one actual, so + // we need to put the rest in an array... + goto FIXUPPARAMLIST; + } + + EXPR argument = indir; + EXPR rval; + if (argument.isNamedArgumentSpecification()) + { + int index = 0; + // If we're named, look for the type of the matching name. + foreach (Name i in mostDerivedMethod.ParameterNames) + { + if (i == argument.asNamedArgumentSpecification().Name) + { + break; + } + index++; + } + Debug.Assert(index != mp.Params.size); + CType substDestType = GetTypes().SubstType(@params.Item(index), type, pTypeArgs); + + // If we cant convert the argument and we're the param array argument, then deal with it. + if (!canConvert(argument.asNamedArgumentSpecification().Value, substDestType) && + mp.isParamArray && index == mp.Params.size - 1) + { + // We have a param array, but we're not at the end yet. This will happen + // with named arguments when the user specifies a name for the param array, + // and its not an actual array. + // + // For example: + // void Foo(int y, params int[] x); + // ... + // Foo(x:1, y:1); + CType arrayType = GetTypes().SubstType(mp.Params.Item(mp.Params.size - 1), type, pTypeArgs); + CType elemType = arrayType.AsArrayType().GetElementType(); + + // Use an EK_ARRINIT even in the empty case so empty param arrays in attributes work. + EXPRARRINIT arrayInit = GetExprFactory().CreateArrayInit(0, arrayType, null, null, null); + arrayInit.GeneratedForParamArray = true; + arrayInit.dimSizes = new int[] { arrayInit.dimSize }; + arrayInit.dimSize = 1; + arrayInit.SetOptionalArguments(argument.asNamedArgumentSpecification().Value); + + argument.asNamedArgumentSpecification().Value = arrayInit; + bDontFixParamArray = true; + } + else + { + // Otherwise, force the conversion and get errors if needed. + argument.asNamedArgumentSpecification().Value = tryConvert( + argument.asNamedArgumentSpecification().Value, + substDestType); + } + rval = argument; + } + else + { + CType substDestType = GetTypes().SubstType(@params.Item(iDst), type, pTypeArgs); + rval = tryConvert(indir, substDestType); + } + + if (rval == null) + { + // the last arg failed to fix up, so it must fixup into the array element + // if we have a param array (we will be passing a 1 element array...) + if (mp.isParamArray && paramCount == 1 && argCount >= mp.Params.size) + { + goto FIXUPPARAMLIST; + } + else + { + // This is either the error case that the args are of the wrong type, + // or that we have some optional arguments being used. Either way, + // we wont need to expand the param array. + return; + } + } + Debug.Assert(rval != null); + indir = rval; + GetExprFactory().AppendItemToList(rval, ref newArgs, ref newArgsTail); + paramCount--; + } + // note that destype might not be valid if we are in varargs, but then we won't ever use it... + iDst++; + + if (paramCount != 0 && mp.isParamArray && iDst == argCount) + { + // we run out of actuals, but we still have formals, so this is an empty array being passed + // into the last param... + indir = null; + it.MoveNext(); + goto FIXUPPARAMLIST; + } + } + + return; + + FIXUPPARAMLIST: + if (bDontFixParamArray) + { + // We've already fixed the param array for named arguments. + return; + } + + // we need to create an array and put it as the last arg... + CType substitutedArrayType = GetTypes().SubstType(mp.Params.Item(mp.Params.size - 1), type, pTypeArgs); + if (!substitutedArrayType.IsArrayType() || substitutedArrayType.AsArrayType().rank != 1) + { + // Invalid type for params array parameter. Happens in LAF scenarios, e.g. + // + // void Foo(int i, params int ar = null) { } + // ... + // Foo(1); + return; + } + + CType elementType = substitutedArrayType.AsArrayType().GetElementType(); + + // Use an EK_ARRINIT even in the empty case so empty param arrays in attributes work. + EXPRARRINIT exprArrayInit = GetExprFactory().CreateArrayInit(0, substitutedArrayType, null, null, null); + exprArrayInit.GeneratedForParamArray = true; + exprArrayInit.dimSizes = new int[] { exprArrayInit.dimSize }; + + if (it.AtEnd()) + { + exprArrayInit.dimSize = 0; + exprArrayInit.dimSizes[0] = 0; + exprArrayInit.SetOptionalArguments(null); + if (argsPtr == null) + { + argsPtr = exprArrayInit; + } + else + { + argsPtr = GetExprFactory().CreateList(argsPtr, exprArrayInit); + } + GetExprFactory().AppendItemToList(exprArrayInit, ref newArgs, ref newArgsTail); + } + else + { + // Go through the list - for each argument, do the conversion and append it to the new list. + EXPR newList = null; + EXPR newListTail = null; + int count = 0; + + for (; !it.AtEnd(); it.MoveNext()) + { + EXPR expr = it.Current(); + count++; + + if (expr.isNamedArgumentSpecification()) + { + expr.asNamedArgumentSpecification().Value = tryConvert( + expr.asNamedArgumentSpecification().Value, elementType); + } + else + { + expr = tryConvert(expr, elementType); + } + GetExprFactory().AppendItemToList(expr, ref newList, ref newListTail); + } + + exprArrayInit.dimSize = count; + exprArrayInit.dimSizes[0] = count; + exprArrayInit.SetOptionalArguments(newList); + GetExprFactory().AppendItemToList(exprArrayInit, ref newArgs, ref newArgsTail); + } + } + + //////////////////////////////////////////////////////////////////////////////// + // Sets the isAssigned bit + + protected void markFieldAssigned(EXPR expr) + { + if (expr.isFIELD() && 0 != (expr.flags & EXPRFLAG.EXF_LVALUE)) + { + EXPRFIELD field; + + do + { + field = expr.asFIELD(); + field.fwt.Field().isAssigned = true; + expr = field.GetOptionalObject(); + } + while (field.fwt.Field().getClass().IsStruct() && !field.fwt.Field().isStatic && expr != null && expr.isFIELD()); + } + } + + protected void SetExternalRef(CType type) + { + AggregateSymbol agg = type.GetNakedAgg(); + if (null == agg || agg.HasExternReference()) + return; + + agg.SetHasExternReference(true); + foreach (Symbol sym in agg.Children()) + { + if (sym.IsFieldSymbol()) + SetExternalRef(sym.AsFieldSymbol().GetType()); + } + } + + + private static readonly PredefinedType[] rgptIntOp = + { + PredefinedType.PT_INT, + PredefinedType.PT_UINT, + PredefinedType.PT_LONG, + PredefinedType.PT_ULONG + }; + + + + internal CType chooseArrayIndexType(EXPR args) + { + // first, select the allowable types + for (int ipt = 0; ipt < rgptIntOp.Length; ipt++) + { + CType type = GetReqPDT(rgptIntOp[ipt]); + foreach (EXPR arg in args.ToEnumerable()) + { + if (!canConvert(arg, type)) + { + goto NEXTI; + } + } + return type; + NEXTI: + ; + } + return null; + } + + internal void FillInArgInfoFromArgList(ArgInfos argInfo, EXPR args) + { + CType[] prgtype = new CType[argInfo.carg]; + argInfo.fHasExprs = true; + argInfo.prgexpr = new List(); + + int iarg = 0; + for (EXPR list = args; list != null; iarg++) + { + EXPR arg; + if (list.isLIST()) + { + arg = list.asLIST().GetOptionalElement(); + list = list.asLIST().GetOptionalNextListNode(); + } + else + { + arg = list; + list = null; + } + + Debug.Assert(arg != null); + + if (arg.type != null) + { + prgtype[iarg] = (CType)arg.type; + } + else + { + prgtype[iarg] = GetTypes().GetErrorSym(); + } + argInfo.prgexpr.Add(arg); + } + Debug.Assert(iarg <= argInfo.carg); + argInfo.types = GetGlobalSymbols().AllocParams(iarg, prgtype); + } + + protected bool TryGetExpandedParams(TypeArray @params, int count, out TypeArray ppExpandedParams) + { + CType[] prgtype; + if (count < @params.size - 1) + { + // The user has specified less arguments than our parameters, but we still + // need to return our set of types without the param array. This is in the + // case that all the parameters are optional. + prgtype = new CType[@params.size - 1]; + @params.CopyItems(0, @params.size - 1, prgtype); + ppExpandedParams = GetGlobalSymbols().AllocParams(@params.size - 1, prgtype); + return true; + } + + prgtype = new CType[count]; + @params.CopyItems(0, @params.size - 1, prgtype); + + CType type = @params.Item(@params.size - 1); + CType elementType = null; + + if (!type.IsArrayType()) + { + ppExpandedParams = null; + // If we dont have an array sym, we dont have expanded parameters. + return false; + } + + // At this point, we have an array sym. + elementType = type.AsArrayType().GetElementType(); + + for (int itype = @params.size - 1; itype < count; itype++) + { + prgtype[itype] = elementType; + } + + ppExpandedParams = GetGlobalSymbols().AllocParams(prgtype); + + return true; + } + + // Is the method/property callable. Not if it's an override or not user-callable. + public static bool IsMethPropCallable(MethodOrPropertySymbol sym, bool requireUC) + { + // The hide-by-pName option for binding other languages takes precedence over general + // rules of not binding to overrides. + return (!sym.isOverride || sym.isHideByName) && (!requireUC || sym.isUserCallable()); + } + + private bool isConvInTable(List convTable, MethodSymbol meth, AggregateType ats, bool fSrc, bool fDst) + { + foreach (UdConvInfo conv in convTable) + { + if (conv.mwt.Meth() == meth && + conv.mwt.GetType() == ats && + conv.fSrcImplicit == fSrc && + conv.fDstImplicit == fDst) + { + return true; + } + } + return false; + } + + //////////////////////////////////////////////////////////////////////////////// + // Check to see if an integral constant is within range of a integral + // destination type. + public static bool isConstantInRange(EXPRCONSTANT exprSrc, CType typeDest) + { + return isConstantInRange(exprSrc, typeDest, false); + } + + public static bool isConstantInRange(EXPRCONSTANT exprSrc, CType typeDest, bool realsOk) + { + + FUNDTYPE ftSrc = exprSrc.type.fundType(); + FUNDTYPE ftDest = typeDest.fundType(); + + if (ftSrc > FUNDTYPE.FT_LASTINTEGRAL || ftDest > FUNDTYPE.FT_LASTINTEGRAL) + { + if (!realsOk) + { + return false; + } + else if (ftSrc > FUNDTYPE.FT_LASTNUMERIC || ftDest > FUNDTYPE.FT_LASTNUMERIC) + { + return false; + } + } + + // if converting to a float type, this always suceeds... + if (ftDest > FUNDTYPE.FT_LASTINTEGRAL) + { + return true; + } + + // if converting from float to an integral type, we need to check whether it fits + if (ftSrc > FUNDTYPE.FT_LASTINTEGRAL) + { + double dvalue = (exprSrc.asCONSTANT().getVal().doubleVal); + + switch (ftDest) + { + case FUNDTYPE.FT_I1: + if (dvalue > -0x81 && dvalue < 0x80) + return true; + break; + case FUNDTYPE.FT_I2: + if (dvalue > -0x8001 && dvalue < 0x8000) + return true; + break; + case FUNDTYPE.FT_I4: + if (dvalue > I64(-0x80000001) && dvalue < I64(0x80000000)) + return true; + break; + case FUNDTYPE.FT_I8: + // 0x7FFFFFFFFFFFFFFFFFFF is rounded to 0x800000000000000000000 in 64 bit double precision + // floating point representation. The conversion back to ulong is not possible. + if (dvalue >= -9223372036854775808.0 && dvalue < 9223372036854775808.0) + { + return true; + } + break; + case FUNDTYPE.FT_U1: + if (dvalue > -1 && dvalue < 0x100) + return true; + break; + case FUNDTYPE.FT_U2: + if (dvalue > -1 && dvalue < 0x10000) + return true; + break; + case FUNDTYPE.FT_U4: + if (dvalue > -1 && dvalue < I64(0x100000000)) + return true; + break; + case FUNDTYPE.FT_U8: + // 0xFFFFFFFFFFFFFFFFFFFF is rounded to 0x100000000000000000000 in 64 bit double precision + // floating point representation. The conversion back to ulong is not possible. + if (dvalue > -1.0 && dvalue < 18446744073709551616.0) + { + return true; + } + break; + default: + break; + } + return false; + } + + // U8 src is unsigned, so deal with values > MAX_LONG here. + if (ftSrc == FUNDTYPE.FT_U8) + { + ulong value = exprSrc.asCONSTANT().getU64Value(); + + switch (ftDest) + { + case FUNDTYPE.FT_I1: + if (value <= (ulong)SByte.MaxValue) + return true; + break; + case FUNDTYPE.FT_I2: + if (value <= (ulong)Int16.MaxValue) + return true; + break; + case FUNDTYPE.FT_I4: + if (value <= Int32.MaxValue) + return true; + break; + case FUNDTYPE.FT_I8: + if (value <= Int64.MaxValue) + return true; + break; + case FUNDTYPE.FT_U1: + if (value <= Byte.MaxValue) + return true; + break; + case FUNDTYPE.FT_U2: + if (value <= UInt16.MaxValue) + return true; + break; + case FUNDTYPE.FT_U4: + if (value <= UInt32.MaxValue) + return true; + break; + case FUNDTYPE.FT_U8: + return true; + default: + break; + } + } + else + { + long value = exprSrc.asCONSTANT().getI64Value(); + + switch (ftDest) + { + case FUNDTYPE.FT_I1: + if (value >= -128 && value <= 127) + return true; + break; + case FUNDTYPE.FT_I2: + if (value >= -0x8000 && value <= 0x7fff) + return true; + break; + case FUNDTYPE.FT_I4: + if (value >= I64(-0x80000000) && value <= I64(0x7fffffff)) + return true; + break; + case FUNDTYPE.FT_I8: + return true; + case FUNDTYPE.FT_U1: + if (value >= 0 && value <= 0xff) + return true; + break; + case FUNDTYPE.FT_U2: + if (value >= 0 && value <= 0xffff) + return true; + break; + case FUNDTYPE.FT_U4: + if (value >= 0 && value <= I64(0xffffffff)) + return true; + break; + case FUNDTYPE.FT_U8: + if (value >= 0) + return true; + break; + default: + break; + } + } + return false; + } + + readonly static private PredefinedName[] EK2NAME = + { + PredefinedName.PN_OPEQUALS, + PredefinedName.PN_OPCOMPARE, + PredefinedName.PN_OPTRUE, + PredefinedName.PN_OPFALSE, + PredefinedName.PN_OPINCREMENT, + PredefinedName.PN_OPDECREMENT, + PredefinedName.PN_OPNEGATION, + PredefinedName.PN_OPEQUALITY, + PredefinedName.PN_OPINEQUALITY, + PredefinedName.PN_OPLESSTHAN, + PredefinedName.PN_OPLESSTHANOREQUAL, + PredefinedName.PN_OPGREATERTHAN, + PredefinedName.PN_OPGREATERTHANOREQUAL, + PredefinedName.PN_OPPLUS, + PredefinedName.PN_OPMINUS, + PredefinedName.PN_OPMULTIPLY, + PredefinedName.PN_OPDIVISION, + PredefinedName.PN_OPMODULUS, + PredefinedName.PN_OPUNARYMINUS, + PredefinedName.PN_OPUNARYPLUS, + PredefinedName.PN_OPBITWISEAND, + PredefinedName.PN_OPBITWISEOR, + PredefinedName.PN_OPXOR, + PredefinedName.PN_OPCOMPLEMENT, + PredefinedName.PN_OPLEFTSHIFT, + PredefinedName.PN_OPRIGHTSHIFT, + }; + + protected Name ekName(ExpressionKind ek) + { + Debug.Assert(ek >= ExpressionKind.EK_FIRSTOP && (ek - ExpressionKind.EK_FIRSTOP) < (int)EK2NAME.Length); + return GetSymbolLoader().GetNameManager().GetPredefName(EK2NAME[ek - ExpressionKind.EK_FIRSTOP]); + } + + public void checkUnsafe(CType type) + { + checkUnsafe(type, ErrorCode.ERR_UnsafeNeeded, null); + } + public void checkUnsafe(CType type, ErrorCode errCode, ErrArg pArg) + { + Debug.Assert((errCode != ErrorCode.ERR_SizeofUnsafe) || pArg != null); + if (type == null || type.isUnsafe()) + { + if (!isUnsafeContext() && ReportUnsafeErrors()) + { + if (pArg != null) + ErrorContext.Error(errCode, pArg); + else + ErrorContext.Error(errCode); + } + RecordUnsafeUsage(); + } + } + protected bool InMethod() + { + return Context.InMethod(); + } + protected bool InStaticMethod() + { + return Context.InStaticMethod(); + } + protected bool InConstructor() + { + return Context.InConstructor(); + } + protected bool InAnonymousMethod() + { + return Context.InAnonymousMethod(); + } + protected bool InFieldInitializer() + { + return Context.InFieldInitializer(); + } + + //////////////////////////////////////////////////////////////////////////////// + private Declaration ContextForMemberLookup() + { + return Context.ContextForMemberLookup(); + } + + protected AggregateSymbol ContainingAgg() + { + return Context.ContainingAgg(); + } + protected bool isThisPointer(EXPR expr) + { + return Context.IsThisPointer(expr); + } + protected bool RespectReadonly() + { + return Context.RespectReadonly(); + } + protected bool isUnsafeContext() + { + return Context.IsUnsafeContext(); + } + protected bool ReportUnsafeErrors() + { + return Context.ReportUnsafeErrors(); + } + protected virtual void RecordUnsafeUsage() + { + RecordUnsafeUsage(Context); + } + protected virtual EXPR WrapShortLivedExpression(EXPR expr) + { + return GetExprFactory().CreateWrap(null, expr); + } + + virtual protected EXPR GenerateOptimizedAssignment(EXPR op1, EXPR op2) + { + return GetExprFactory().CreateAssignment(op1, op2); + } + + public static void RecordUnsafeUsage(BindingContext context) + { + if (!(context.GetUnsafeState() == UNSAFESTATES.UNSAFESTATES_Unsafe) && + !context.GetOutputContext().m_bUnsafeErrorGiven) + { + context.GetOutputContext().m_bUnsafeErrorGiven = true; + } + } + + internal static int CountArguments(EXPR args, out bool typeErrors) + { + int carg = 0; + typeErrors = false; + for (EXPR list = args; list != null; carg++) + { + EXPR arg; + + if (list.isLIST()) + { + arg = list.asLIST().GetOptionalElement(); + list = list.asLIST().GetOptionalNextListNode(); + } + else + { + arg = list; + list = null; + } + + Debug.Assert(arg != null); + + if (arg.type == null || arg.type.IsErrorType()) + { + typeErrors = true; + } + } + return carg; + } + } +} + diff --git a/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/ExpressionKind.cs b/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/ExpressionKind.cs new file mode 100644 index 000000000..2aaf134bb --- /dev/null +++ b/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/ExpressionKind.cs @@ -0,0 +1,207 @@ +// ==++== +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// ==--== + +namespace Microsoft.CSharp.RuntimeBinder.Semantics +{ + internal enum ExpressionKind + { + EK_BLOCK, + //EK_STMTAS, + EK_RETURN, + //EK_DECL, + //EK_LABEL, + //EK_PLACEHOLDERLABEL, + //EK_GOTO, + //EK_GOTOIF, + //EK_FlatSwitch, + //EK_SWITCHLABEL, + //EK_ENDSWITCHLABEL, + //EK_TRY, + //EK_HANDLER, + //EK_THROW, + EK_NOOP, + //EK_DEBUGNOOP, + + //EK_For, + //EK_Foreach, + //EK_Using, + //EK_Switch, + //EK_SwitchSection, + //EK_SwitchSectionLabel, + //EK_Lock, + + //EK_Attribute, + // Now expressions. Keep BINOP first! + EK_BINOP, + EK_UNARYOP, + EK_ASSIGNMENT, + EK_LIST, + EK_QUESTIONMARK, + //EK_MAKEREFANY, + //EK_TYPEREFANY, + EK_ARRAYINDEX, + EK_ARRAYLENGTH, + EK_ARGUMENTHANDLE, + EK_CALL, + EK_EVENT, + EK_FIELD, + EK_LOCAL, + //EK_BASE, + EK_THISPOINTER, + EK_CONSTANT, + //EK_TYPEORSIMPLENAME, + // The following exprs are used to represent the results of typebinding. + // Look in exprnodes.h for a more detailed description. + EK_TYPEARGUMENTS, + EK_TYPEORNAMESPACE, + //EK_TYPEORNAMESPACEERROR, + //EK_ARRAYTYPE, + //EK_POINTERTYPE, + //EK_NULLABLETYPE, + EK_CLASS, + //EK_NSPACE, + EK_ALIAS, + // End type exprs. + //EK_ERROR, + EK_FUNCPTR, + EK_PROP, + EK_MULTI, + EK_MULTIGET, + //EK_STTMP, + //EK_LDTMP, + //EK_FREETMP, + EK_WRAP, + EK_CONCAT, + EK_ARRINIT, + //EK_ARRAYCREATION, + EK_CAST, + //EK_EXPLICITCAST, + EK_USERDEFINEDCONVERSION, + //EK_ARGLIST, + //EK_NEWTYVAR, + EK_TYPEOF, + //EK_SIZEOF, + EK_ZEROINIT, + EK_USERLOGOP, + EK_MEMGRP, + EK_BOUNDLAMBDA, + EK_UNBOUNDLAMBDA, + //EK_LAMBDAPARAMETER, + EK_HOISTEDLOCALEXPR, + EK_FIELDINFO, + EK_METHODINFO, + EK_PROPERTYINFO, + //EK_DBLQMARK, + //EK_VALUERA, + //EK_INITIALIZER, + //EK_INITASSIGN, + //EK_LOCALLOC, + //EK_IS, + //EK_AS, + //EK_DELEGATECREATION, + //EK_COLLECTIONELEMENT, + //EK_METHODBODY, + EK_NamedArgumentSpecification, + + /*************************************************************************************************** + Ones below here are not used to create actual expr types, only EK_ values. +***************************************************************************************************/ + EK_COUNT, + EK_EQUALS, // this is only used as a parameter, no actual exprs are constructed with it + EK_FIRSTOP = EK_EQUALS, + EK_COMPARE, // this is only used as a parameter, no actual exprs are constructed with it + EK_TRUE, + EK_FALSE, + EK_INC, + EK_DEC, + EK_LOGNOT, + // keep EK_EQ to EK_GE in the same sequence (ILGENREC::genCondBranch) + EK_EQ, + EK_RELATIONAL_MIN = EK_EQ, + EK_NE, + EK_LT, + EK_LE, + EK_GT, + EK_GE, + EK_RELATIONAL_MAX = EK_GE, + // keep EK_ADD to EK_RSHIFT in the same sequence (ILGENREC::genBinopExpr) + EK_ADD, + EK_ARITH_MIN = EK_ADD, + EK_SUB, + EK_MUL, + EK_DIV, + EK_MOD, + EK_NEG, + EK_UPLUS, + EK_ARITH_MAX = EK_UPLUS, + EK_BITAND, + EK_BIT_MIN = EK_BITAND, + EK_BITOR, + EK_BITXOR, + EK_BITNOT, + EK_BIT_MAX = EK_BITNOT, + EK_LSHIFT, + EK_RSHIFT, + // keep EK_ADD to EK_RSHIFT in the same sequence (ILGENREC::genBinopExpr) + EK_LOGAND, + EK_LOGOR, + EK_SEQUENCE, // p1 is side effects, p2 is values + EK_SEQREV, // p1 is values, p2 is side effects + EK_SAVE, // p1 is expr, p2 is wrap to be saved into... + EK_SWAP, + EK_INDIR, + EK_ADDR, + // Next we have the predefined operator kinds. We have one EXPRKINDDEF for each of these. + // So for example, we will have an EK_STRINGCOMPARISON, and an EK_DELEGATEADDITION etc. + EK_STRINGEQ, + EK_STRINGNE, + EK_DELEGATEEQ, + EK_DELEGATENE, + EK_DELEGATEADD, + EK_DELEGATESUB, + EK_DECIMALNEG, + EK_DECIMALINC, + EK_DECIMALDEC, +#if EERANGE + EK_RANGE, +#endif + EK_MULTIOFFSET, // This has to be last!!! To deal /w multiops we add this to the op to obtain the ek in the op table + // Statements are all before expressions and the first expression is EK_BINOP + EK_ExprMin = EK_BINOP, + EK_StmtLim = EK_ExprMin, + // EK types starting with EK_COUNT do not have associated EXPR structures, + // and are all binary operators. + EK_TypeLim = EK_COUNT, + } + + internal static class ExpressionKindExtensions + { + public static bool isRelational(this ExpressionKind kind) + { + return ExpressionKind.EK_RELATIONAL_MIN <= kind && kind <= ExpressionKind.EK_RELATIONAL_MAX; + } + public static bool isUnaryOperator(this ExpressionKind kind) + { + switch (kind) + { + case ExpressionKind.EK_TRUE: + case ExpressionKind.EK_FALSE: + case ExpressionKind.EK_INC: + case ExpressionKind.EK_DEC: + case ExpressionKind.EK_LOGNOT: + case ExpressionKind.EK_NEG: + case ExpressionKind.EK_UPLUS: + case ExpressionKind.EK_BITNOT: + case ExpressionKind.EK_ADDR: + case ExpressionKind.EK_DECIMALNEG: + case ExpressionKind.EK_DECIMALINC: + case ExpressionKind.EK_DECIMALDEC: + return true; + } + return false; + } + } +} diff --git a/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/FileRecord.cs b/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/FileRecord.cs new file mode 100644 index 000000000..7a9b0301f --- /dev/null +++ b/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/FileRecord.cs @@ -0,0 +1,15 @@ +// ==++== +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// ==--== + +using System.Diagnostics; +using Microsoft.CSharp.RuntimeBinder.Syntax; + +namespace Microsoft.CSharp.RuntimeBinder.Semantics +{ + class FileRecord + { + } +} diff --git a/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/FundamentalTypes.cs b/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/FundamentalTypes.cs new file mode 100644 index 000000000..e7578ab02 --- /dev/null +++ b/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/FundamentalTypes.cs @@ -0,0 +1,31 @@ +// ==++== +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// ==--== + +namespace Microsoft.CSharp.RuntimeBinder.Semantics +{ + internal enum FUNDTYPE + { + FT_NONE, // No fundemental type + FT_I1, + FT_I2, + FT_I4, + FT_U1, + FT_U2, + FT_U4, + FT_LASTNONLONG = FT_U4, // Last one that fits in a int. + FT_I8, + FT_U8, // integral types + FT_LASTINTEGRAL = FT_U8, + FT_R4, + FT_R8, // floating types + FT_LASTNUMERIC = FT_R8, + FT_REF, // reference type + FT_STRUCT, // structure type + FT_PTR, // pointer to unmanaged memory + FT_VAR, // polymorphic, unbounded, not yet committed + FT_COUNT // number of enumerators. + } +} diff --git a/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/GlobalSymbolContext.cs b/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/GlobalSymbolContext.cs new file mode 100644 index 000000000..d245f05a7 --- /dev/null +++ b/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/GlobalSymbolContext.cs @@ -0,0 +1,49 @@ +// ==++== +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// ==--== + +using Microsoft.CSharp.RuntimeBinder.Syntax; + +namespace Microsoft.CSharp.RuntimeBinder.Semantics +{ + /***************************************************************************** + A GlobalSymbolContext represents the global symbol tables for a compilation. + This includes symbols, types, declarations. + *****************************************************************************/ + + internal class GlobalSymbolContext + { + private PredefinedTypes m_predefTypes; + private NameManager m_nameManager; + + public GlobalSymbolContext(NameManager namemgr) + { + TypeManager = new TypeManager(); + GlobalSymbols = new BSYMMGR(namemgr, TypeManager); + m_predefTypes = new PredefinedTypes(GlobalSymbols); + TypeManager.Init(GlobalSymbols, m_predefTypes); + GlobalSymbols.Init(); + + m_nameManager = namemgr; + } + + public TypeManager TypeManager { get; private set; } + public TypeManager GetTypes() { return TypeManager; } + public BSYMMGR GlobalSymbols { get; private set; } + public BSYMMGR GetGlobalSymbols() { return GlobalSymbols; } + public NameManager GetNameManager() { return m_nameManager; } + public PredefinedTypes GetPredefTypes() { return m_predefTypes; } + + public SymFactory GetGlobalSymbolFactory() + { + return GetGlobalSymbols().GetSymFactory(); + } + + public MiscSymFactory GetGlobalMiscSymFactory() + { + return GetGlobalSymbols().GetMiscSymFactory(); + } + } +} diff --git a/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/GroupToArgsBinder.cs b/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/GroupToArgsBinder.cs new file mode 100644 index 000000000..2d074ad9d --- /dev/null +++ b/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/GroupToArgsBinder.cs @@ -0,0 +1,1575 @@ +// ==++== +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// ==--== + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using Microsoft.CSharp.RuntimeBinder.Errors; +using Microsoft.CSharp.RuntimeBinder.Syntax; + +namespace Microsoft.CSharp.RuntimeBinder.Semantics +{ + // ---------------------------------------------------------------------------- + // This class takes an EXPRMEMGRP and a set of arguments and binds the arguments + // to the best applicable method in the group. + // ---------------------------------------------------------------------------- + + internal partial class ExpressionBinder + { + internal class GroupToArgsBinder + { + private enum Result + { + Success, + Failure_SearchForExpanded, + Failure_NoSearchForExpanded + } + + private ExpressionBinder m_pExprBinder; + private bool m_fCandidatesUnsupported; + private BindingFlag m_fBindFlags; + private EXPRMEMGRP m_pGroup; + private ArgInfos m_pArguments; + private ArgInfos m_pOriginalArguments; + private bool m_bHasNamedArguments; + private AggregateType m_pDelegate; + private AggregateType m_pCurrentType; + private MethodOrPropertySymbol m_pCurrentSym; + private TypeArray m_pCurrentTypeArgs; + private TypeArray m_pCurrentParameters; + private TypeArray m_pBestParameters; + private int m_nArgBest; + // Keep track of the first 20 or so syms with the wrong arg count. + private SymWithType[] m_swtWrongCount = new SymWithType[20]; + private int m_nWrongCount; + private bool m_bIterateToEndOfNsList; // we have found an appliacable extension method only itereate to + // end of current namespaces extension method list + private bool m_bBindingCollectionAddArgs; // Report parameter modifiers as error + private GroupToArgsBinderResult m_results; + private List m_methList; + private MethPropWithInst m_mpwiParamTypeConstraints; + private MethPropWithInst m_mpwiBogus; + private MethPropWithInst m_mpwiCantInferInstArg; + private MethWithType m_mwtBadArity; + private Name m_pInvalidSpecifiedName; + private Name m_pNameUsedInPositionalArgument; + private Name m_pDuplicateSpecifiedName; + // When we find a type with an interface, then we want to mark all other interfaces that it + // implements as being hidden. We also want to mark object as being hidden. So stick them + // all in this list, and then for subsequent types, if they're in this list, then we + // ignore them. + private List m_HiddenTypes; + private bool m_bArgumentsChangedForNamedOrOptionalArguments; + + public GroupToArgsBinder(ExpressionBinder exprBinder, BindingFlag bindFlags, EXPRMEMGRP grp, ArgInfos args, ArgInfos originalArgs, bool bHasNamedArguments, AggregateType atsDelegate) + { + Debug.Assert(grp != null); + Debug.Assert(exprBinder != null); + Debug.Assert(args != null); + + m_pExprBinder = exprBinder; + m_fCandidatesUnsupported = false; + m_fBindFlags = bindFlags; + m_pGroup = grp; + m_pArguments = args; + m_pOriginalArguments = originalArgs; + m_bHasNamedArguments = bHasNamedArguments; + m_pDelegate = atsDelegate; + m_pCurrentType = null; + m_pCurrentSym = null; + m_pCurrentTypeArgs = null; + m_pCurrentParameters = null; + m_pBestParameters = null; + m_nArgBest = -1; + m_nWrongCount = 0; + m_bIterateToEndOfNsList = false; + m_bBindingCollectionAddArgs = false; + m_results = new GroupToArgsBinderResult(); + m_methList = new List(); + m_mpwiParamTypeConstraints = new MethPropWithInst(); + m_mpwiBogus = new MethPropWithInst(); + m_mpwiCantInferInstArg = new MethPropWithInst(); + m_mwtBadArity = new MethWithType(); + m_HiddenTypes = new List(); + } + + // ---------------------------------------------------------------------------- + // This method does the actual binding. + // ---------------------------------------------------------------------------- + + public bool Bind(bool bReportErrors) + { + Debug.Assert(m_pGroup.sk == SYMKIND.SK_MethodSymbol || m_pGroup.sk == SYMKIND.SK_PropertySymbol && 0 != (m_pGroup.flags & EXPRFLAG.EXF_INDEXER)); + + // We need the EXPRs for error reporting for non-delegates + Debug.Assert(m_pDelegate != null || m_pArguments.fHasExprs); + + LookForCandidates(); + if (!GetResultOfBind(bReportErrors)) + { + if (bReportErrors) + { + ReportErrorsOnFailure(); + } + return false; + } + return true; + } + + public GroupToArgsBinderResult GetResultsOfBind() + { + return m_results; + } + + public bool BindCollectionAddArgs() + { + m_bBindingCollectionAddArgs = true; + return Bind(true /* bReportErrors */); + } + private SymbolLoader GetSymbolLoader() + { + return m_pExprBinder.GetSymbolLoader(); + } + private CSemanticChecker GetSemanticChecker() + { + return m_pExprBinder.GetSemanticChecker(); + } + private ErrorHandling GetErrorContext() + { + return m_pExprBinder.GetErrorContext(); + } + public static CType GetTypeQualifier(EXPRMEMGRP pGroup) + { + Debug.Assert(pGroup != null); + + CType rval = null; + + if (0 != (pGroup.flags & EXPRFLAG.EXF_BASECALL)) + { + rval = null; + } + else if (0 != (pGroup.flags & EXPRFLAG.EXF_CTOR)) + { + rval = pGroup.GetParentType(); + } + else if (pGroup.GetOptionalObject() != null) + { + rval = pGroup.GetOptionalObject().type; + } + else + { + rval = null; + } + return rval; + } + + private void LookForCandidates() + { + bool fExpanded = false; + bool bSearchForExpanded = true; + int cswtMaxWrongCount = m_swtWrongCount.Length; + bool allCandidatesUnsupported = true; + bool lookedAtCandidates = false; + + // Calculate the mask based on the type of the sym we've found so far. This + // is to ensure that if we found a propsym (or methsym, or whatever) the + // iterator will only return propsyms (or methsyms, or whatever) + symbmask_t mask = (symbmask_t)(1 << (int)m_pGroup.sk); + + CType pTypeThrough = m_pGroup.GetOptionalObject() != null ? m_pGroup.GetOptionalObject().type : null; + CMemberLookupResults.CMethodIterator iterator = m_pGroup.GetMemberLookupResults().GetMethodIterator(GetSemanticChecker(), GetSymbolLoader(), pTypeThrough, GetTypeQualifier(m_pGroup), m_pExprBinder.ContextForMemberLookup(), true, // AllowBogusAndInaccessible + false, m_pGroup.typeArgs.size, m_pGroup.flags, mask); + while (true) + { + bool bFoundExpanded; + Result currentTypeArgsResult; + + bFoundExpanded = false; + if (bSearchForExpanded && !fExpanded) + { + bFoundExpanded = fExpanded = ConstructExpandedParameters(); + } + + // Get the next sym to search for. + if (!bFoundExpanded) + { + fExpanded = false; + + if (!GetNextSym(iterator)) + { + break; + } + + // Get the parameters. + m_pCurrentParameters = m_pCurrentSym.Params; + bSearchForExpanded = true; + } + + if (m_bArgumentsChangedForNamedOrOptionalArguments) + { + // If we changed them last time, then we need to reset them. + m_bArgumentsChangedForNamedOrOptionalArguments = false; + CopyArgInfos(m_pOriginalArguments, m_pArguments); + } + + // If we have named arguments, reorder them for this method. + if (m_pArguments.fHasExprs) + { + // If we dont have EXPRs, its because we're doing a method group conversion. + // In those scenarios, we never want to add named arguments or optional arguments. + if (m_bHasNamedArguments) + { + if (!ReOrderArgsForNamedArguments()) + { + continue; + } + } + else if (HasOptionalParameters()) + { + if (!AddArgumentsForOptionalParameters()) + { + continue; + } + } + } + + if (!bFoundExpanded) + { + lookedAtCandidates = true; + allCandidatesUnsupported &= m_pCurrentSym.getBogus(); + + // If we have the wrong number of arguments and still have room in our cache of 20 (: this needs + // to get fixed... why 20?), then store it in our cache and go to the next sym. + if (m_pCurrentParameters.size != m_pArguments.carg) + { + if (m_nWrongCount < cswtMaxWrongCount && + (!m_pCurrentSym.isParamArray || m_pArguments.carg < m_pCurrentParameters.size - 1)) + { + m_swtWrongCount[m_nWrongCount++] = new SymWithType(m_pCurrentSym, m_pCurrentType); + } + bSearchForExpanded = true; + continue; + } + } + + // If we cant use the current symbol, then we've filtered it, so get the next one. + + if (!iterator.CanUseCurrentSymbol()) + { + continue; + } + + // Get the current type args. + currentTypeArgsResult = DetermineCurrentTypeArgs(); + if (currentTypeArgsResult != Result.Success) + { + bSearchForExpanded = (currentTypeArgsResult == Result.Failure_SearchForExpanded); + continue; + } + + // Check access. + bool fCanAccess = !iterator.IsCurrentSymbolInaccessible(); + if (!fCanAccess && (!m_methList.IsEmpty() || m_results.GetInaccessibleResult())) + { + // We'll never use this one for error reporting anyway, so just skip it. + bSearchForExpanded = false; + continue; + } + + // Check bogus. + bool fBogus = fCanAccess && iterator.IsCurrentSymbolBogus(); + if (fBogus && (!m_methList.IsEmpty() || m_results.GetInaccessibleResult() || m_mpwiBogus)) + { + // We'll never use this one for error reporting anyway, so just skip it. + bSearchForExpanded = false; + continue; + } + + // Check convertibility of arguments. + if (!ArgumentsAreConvertible()) + { + bSearchForExpanded = true; + continue; + } + + // We know we have the right number of arguments and they are all convertible. + if (!fCanAccess) + { + // In case we never get an accessible method, this will allow us to give + // a better error... + Debug.Assert(!m_results.GetInaccessibleResult()); + m_results.GetInaccessibleResult().Set(m_pCurrentSym, m_pCurrentType, m_pCurrentTypeArgs); + } + else if (fBogus) + { + // In case we never get a good method, this will allow us to give + // a better error... + Debug.Assert(!m_mpwiBogus); + m_mpwiBogus.Set(m_pCurrentSym, m_pCurrentType, m_pCurrentTypeArgs); + } + else + { + // This is a plausible method / property to call. + // Link it in at the end of the list. + m_methList.Add(new CandidateFunctionMember( + new MethPropWithInst(m_pCurrentSym, m_pCurrentType, m_pCurrentTypeArgs), + m_pCurrentParameters, + 0, + fExpanded)); + + // When we find a method, we check if the type has interfaces. If so, mark the other interfaces + // as hidden, and object as well. + + if (m_pCurrentType.isInterfaceType()) + { + TypeArray ifaces = m_pCurrentType.GetIfacesAll(); + for (int i = 0; i < ifaces.size; i++) + { + AggregateType type = ifaces.Item(i).AsAggregateType(); + + Debug.Assert(type.isInterfaceType()); + m_HiddenTypes.Add(type); + } + + // Mark object. + AggregateType typeObject = GetSymbolLoader().GetReqPredefType(PredefinedType.PT_OBJECT, true); + m_HiddenTypes.Add(typeObject); + } + } + + // Don't look at the expanded form. + bSearchForExpanded = false; + } + m_fCandidatesUnsupported = allCandidatesUnsupported && lookedAtCandidates; + + // Restore the arguments to their original state if we changed them for named/optional arguments. + // ILGen will take care of putting the real arguments in there. + if (m_bArgumentsChangedForNamedOrOptionalArguments) + { + // If we changed them last time, then we need to reset them. + CopyArgInfos(m_pOriginalArguments, m_pArguments); + } + } + + private void CopyArgInfos(ArgInfos src, ArgInfos dst) + { + dst.carg = src.carg; + dst.types = src.types; + dst.fHasExprs = src.fHasExprs; + + dst.prgexpr.Clear(); + for (int i = 0; i < src.prgexpr.Count; i++) + { + dst.prgexpr.Add(src.prgexpr[i]); + } + } + + private bool GetResultOfBind(bool bReportErrors) + { + // We looked at all the evidence, and we come to render the verdict: + CandidateFunctionMember pmethBest; + + if (!m_methList.IsEmpty()) + { + if (m_methList.Count == 1) + { + // We found the single best method to call. + pmethBest = m_methList.Head(); + } + else + { + // We have some ambiguities, lets sort them out. + CandidateFunctionMember pAmbig1 = null; + CandidateFunctionMember pAmbig2 = null; + + CType pTypeThrough = m_pGroup.GetOptionalObject() != null ? m_pGroup.GetOptionalObject().type : null; + pmethBest = m_pExprBinder.FindBestMethod(m_methList, pTypeThrough, m_pArguments, out pAmbig1, out pAmbig2); + + if (null == pmethBest) + { + // Arbitrarily use the first one, but make sure to report errors or give the ambiguous one + // back to the caller. + pmethBest = pAmbig1; + m_results.AmbiguousResult = pAmbig2.mpwi; + + if (bReportErrors) + { + if (pAmbig1.@params != pAmbig2.@params || + pAmbig1.mpwi.MethProp().Params.size != pAmbig2.mpwi.MethProp().Params.size || + pAmbig1.mpwi.TypeArgs != pAmbig2.mpwi.TypeArgs || + pAmbig1.mpwi.GetType() != pAmbig2.mpwi.GetType() || + pAmbig1.mpwi.MethProp().Params == pAmbig2.mpwi.MethProp().Params) + { + GetErrorContext().Error(ErrorCode.ERR_AmbigCall, pAmbig1.mpwi, pAmbig2.mpwi); + } + else + { + // The two signatures are identical so don't use the type args in the error message. + GetErrorContext().Error(ErrorCode.ERR_AmbigCall, pAmbig1.mpwi.MethProp(), pAmbig2.mpwi.MethProp()); + } + } + } + } + + // This is the "success" exit path. + Debug.Assert(pmethBest != null); + m_results.BestResult = pmethBest.mpwi; + + // Record our best match in the memgroup as well. This is temporary. + + if (bReportErrors) + { + ReportErrorsOnSuccess(); + } + return true; + } + + return false; + } + + ///////////////////////////////////////////////////////////////////////////////// + // This method returns true if we're able to match arguments to their names. + // If we either have too many arguments, or we cannot match their names, then + // we return false. + // + // Note that if we have not enough arguments, we still return true as long as + // we can find matching parameters for each named arguments, and all parameters + // that do not have a matching argument are optional parameters. + + private bool ReOrderArgsForNamedArguments() + { + // First we need to find the method that we're actually trying to call. + MethodOrPropertySymbol methprop = FindMostDerivedMethod(m_pCurrentSym, m_pGroup.GetOptionalObject()); + if (methprop == null) + { + return false; + } + + int numParameters = m_pCurrentParameters.size; + + // If we have no parameters, or fewer parameters than we have arguments, bail. + if (numParameters == 0 || numParameters < m_pArguments.carg) + { + return false; + } + + // Make sure all the names we specified are in the list and we dont have duplicates. + if (!NamedArgumentNamesAppearInParameterList(methprop)) + { + return false; + } + + m_bArgumentsChangedForNamedOrOptionalArguments = ReOrderArgsForNamedArguments( + methprop, + m_pCurrentParameters, + m_pCurrentType, + m_pGroup, + m_pArguments, + m_pExprBinder.GetTypes(), + m_pExprBinder.GetExprFactory(), + GetSymbolLoader()); + return m_bArgumentsChangedForNamedOrOptionalArguments; + } + + internal static bool ReOrderArgsForNamedArguments( + MethodOrPropertySymbol methprop, + TypeArray pCurrentParameters, + AggregateType pCurrentType, + EXPRMEMGRP pGroup, + ArgInfos pArguments, + TypeManager typeManager, + ExprFactory exprFactory, + SymbolLoader symbolLoader) + { + // We use the param count from pCurrentParameters because they may have been resized + // for param arrays. + int numParameters = pCurrentParameters.size; + + EXPR[] pExprArguments = new EXPR[numParameters]; + + // Now go through the parameters. First set all positional arguments in the new argument + // set, then for the remainder, look for a named argument with a matching name. + int index = 0; + EXPR paramArrayArgument = null; + TypeArray @params = typeManager.SubstTypeArray( + pCurrentParameters, + pCurrentType, + pGroup.typeArgs); + foreach (Name name in methprop.ParameterNames) + { + // This can happen if we had expanded our param array to size 0. + if (index >= pCurrentParameters.size) + { + break; + } + + // If: + // (1) we have a param array method + // (2) we're on the last arg + // (3) the thing we have is an array init thats generated for param array + // then let us through. + if (methprop.isParamArray && + index < pArguments.carg && + pArguments.prgexpr[index].isARRINIT() && pArguments.prgexpr[index].asARRINIT().GeneratedForParamArray) + { + paramArrayArgument = pArguments.prgexpr[index]; + } + + // Positional. + if (index < pArguments.carg && + !pArguments.prgexpr[index].isNamedArgumentSpecification() && + !(pArguments.prgexpr[index].isARRINIT() && pArguments.prgexpr[index].asARRINIT().GeneratedForParamArray)) + { + pExprArguments[index] = pArguments.prgexpr[index++]; + continue; + } + + // Look for names. + EXPR pNewArg = FindArgumentWithName(pArguments, name); + if (pNewArg == null) + { + if (methprop.IsParameterOptional(index)) + { + pNewArg = GenerateOptionalArgument(symbolLoader, exprFactory, methprop, @params.Item(index), index); + } + else if (paramArrayArgument != null && index == methprop.Params.Count - 1) + { + // If we have a param array argument and we're on the last one, then use it. + pNewArg = paramArrayArgument; + } + else + { + // No name and no default value. + return false; + } + } + pExprArguments[index++] = pNewArg; + } + + // Here we've found all the arguments, or have default values for them. + CType[] prgTypes = new CType[pCurrentParameters.size]; + for (int i = 0; i < numParameters; i++) + { + if (i < pArguments.prgexpr.Count) + { + pArguments.prgexpr[i] = pExprArguments[i]; + } + else + { + pArguments.prgexpr.Add(pExprArguments[i]); + } + prgTypes[i] = pArguments.prgexpr[i].type; + } + pArguments.carg = pCurrentParameters.size; + pArguments.types = symbolLoader.getBSymmgr().AllocParams(pCurrentParameters.size, prgTypes); + return true; + } + + ///////////////////////////////////////////////////////////////////////////////// + + private static EXPR GenerateOptionalArgument( + SymbolLoader symbolLoader, + ExprFactory exprFactory, + MethodOrPropertySymbol methprop, + CType type, + int index) + { + CType pParamType = type; + CType pRawParamType = type.IsNullableType() ? type.AsNullableType().GetUnderlyingType() : type; + + EXPR optionalArgument = null; + if (methprop.HasDefaultParameterValue(index)) + { + CType pConstValType = methprop.GetDefaultParameterValueConstValType(index); + CONSTVAL cv = methprop.GetDefaultParameterValue(index); + + if (pConstValType.isPredefType(PredefinedType.PT_DATETIME) && + (pRawParamType.isPredefType(PredefinedType.PT_DATETIME) || pRawParamType.isPredefType(PredefinedType.PT_OBJECT) || pRawParamType.isPredefType(PredefinedType.PT_VALUE))) + { + // This is the specific case where we want to create a DateTime + // but the constval that stores it is a long. + + AggregateType dateTimeType = symbolLoader.GetReqPredefType(PredefinedType.PT_DATETIME); + optionalArgument = exprFactory.CreateConstant(dateTimeType, new CONSTVAL(DateTime.FromBinary(cv.longVal))); + } + else if (pConstValType.isSimpleOrEnumOrString()) + { + // In this case, the constval is a simple type (all the numerics, including + // decimal), or an enum or a string. This covers all the substantial values, + // and everything else that can be encoded is just null or default(something). + + // For enum parameters, we create a constant of the enum type. For everything + // else, we create the appropriate constant. + + if (pRawParamType.isEnumType() && pConstValType == pRawParamType.underlyingType()) + { + optionalArgument = exprFactory.CreateConstant(pRawParamType, cv); + } + else + { + optionalArgument = exprFactory.CreateConstant(pConstValType, cv); + } + } + else if ((pParamType.IsRefType() || pParamType.IsNullableType()) && cv.IsNullRef()) + { + // We have an "= null" default value with a reference type or a nullable type. + + optionalArgument = exprFactory.CreateNull(); + } + else + { + // We have a default value that is encoded as a nullref, and that nullref is + // interpreted as default(something). For instance, the pParamType could be + // a type parameter type or a non-simple value type. + + optionalArgument = exprFactory.CreateZeroInit(pParamType); + } + } + else + { + // There was no default parameter specified, so generally use default(T), + // except for some cases when the parameter type in metatdata is object. + + if (pParamType.isPredefType(PredefinedType.PT_OBJECT)) + { + if (methprop.MarshalAsObject(index)) + { + // For [opt] parameters of type object, if we have marshal(iunknown), + // marshal(idispatch), or marshal(interface), then we emit a null. + + optionalArgument = exprFactory.CreateNull(); + } +#if !SILVERLIGHT + else if (methprop.IsDispatchConstantParameter(index) + || methprop.IsUnknownConstantParameter(index)) + { + // Otherwise, if we have an [IUnknownConstant] or [IDispatchConstant], + // then we emit the appropriate wrapper type constructed with a null + + if (methprop.IsUnknownConstantParameter(index)) + { + AggregateType unknownWrapperType = symbolLoader.GetOptPredefType(PredefinedType.PT_UNKNOWNWRAPPER); + optionalArgument = exprFactory.CreateConstant(unknownWrapperType, new CONSTVAL(new System.Runtime.InteropServices.UnknownWrapper(null))); + } + else + { + AggregateType dispatchWrapperType = symbolLoader.GetOptPredefType(PredefinedType.PT_DISPATCHWRAPPER); + optionalArgument = exprFactory.CreateConstant(dispatchWrapperType, new CONSTVAL(new System.Runtime.InteropServices.DispatchWrapper(null))); + } + } +#endif + else + { + // Otherwise, we generate Type.Missing + + AggregateSymbol agg = symbolLoader.GetOptPredefAgg(PredefinedType.PT_MISSING); + Name name = symbolLoader.GetNameManager().GetPredefinedName(PredefinedName.PN_CAP_VALUE); + FieldSymbol field = symbolLoader.LookupAggMember(name, agg, symbmask_t.MASK_FieldSymbol).AsFieldSymbol(); + FieldWithType fwt = new FieldWithType(field, agg.getThisType()); + EXPRFIELD exprField = exprFactory.CreateField(0, agg.getThisType(), null, 0, fwt, null); + + if (agg.getThisType() != type) + { + optionalArgument = exprFactory.CreateCast(0, type, exprField); + } + else + { + optionalArgument = exprField; + } + } + } + else + { + // Every type aside from object that doesn't have a default value gets + // its default value. + + optionalArgument = exprFactory.CreateZeroInit(pParamType); + } + } + + Debug.Assert(optionalArgument != null); + optionalArgument.IsOptionalArgument = true; + return optionalArgument; + } + + ///////////////////////////////////////////////////////////////////////////////// + + private MethodOrPropertySymbol FindMostDerivedMethod( + MethodOrPropertySymbol pMethProp, + EXPR pObject) + { + return FindMostDerivedMethod(GetSymbolLoader(), pMethProp, pObject != null ? pObject.type : null); + } + + ///////////////////////////////////////////////////////////////////////////////// + + public static MethodOrPropertySymbol FindMostDerivedMethod( + SymbolLoader symbolLoader, + MethodOrPropertySymbol pMethProp, + CType pType) + { + MethodSymbol method; + bool bIsIndexer = false; + + if (pMethProp.IsMethodSymbol()) + { + method = pMethProp.AsMethodSymbol(); + } + else + { + PropertySymbol prop = pMethProp.AsPropertySymbol(); + method = prop.methGet != null ? prop.methGet : prop.methSet; + if (method == null) + { + return null; + } + bIsIndexer = prop.isIndexer(); + } + + if (!method.isVirtual) + { + return method; + } + + if (pType == null) + { + // This must be a static call. + return method; + } + + // Now get the slot method. + if (method.swtSlot != null && method.swtSlot.Meth() != null) + { + method = method.swtSlot.Meth(); + } + + if (!pType.IsAggregateType()) + { + // Not something that can have overrides anyway. + return method; + } + + for (AggregateSymbol pAggregate = pType.AsAggregateType().GetOwningAggregate(); + pAggregate != null && pAggregate.GetBaseAgg() != null; + pAggregate = pAggregate.GetBaseAgg()) + { + for (MethodOrPropertySymbol meth = symbolLoader.LookupAggMember(method.name, pAggregate, symbmask_t.MASK_MethodSymbol | symbmask_t.MASK_PropertySymbol).AsMethodOrPropertySymbol(); + meth != null; + meth = symbolLoader.LookupNextSym(meth, pAggregate, symbmask_t.MASK_MethodSymbol | symbmask_t.MASK_PropertySymbol).AsMethodOrPropertySymbol()) + { + if (!meth.isOverride) + { + continue; + } + if (meth.swtSlot.Sym != null && meth.swtSlot.Sym == method) + { + if (bIsIndexer) + { + Debug.Assert(meth.IsMethodSymbol()); + return meth.AsMethodSymbol().getProperty(); + } + else + { + return meth; + } + } + } + } + + // If we get here, it means we can have two cases: one is that we have + // a delegate. This is because the delegate invoke method is virtual and is + // an override, but we wont have the slots set up correctly, and will + // not find the base type in the inheritance hierarchy. The second is that + // we're calling off of the base itself. + Debug.Assert(method.parent.IsAggregateSymbol()); + return method; + } + + + ///////////////////////////////////////////////////////////////////////////////// + + private bool HasOptionalParameters() + { + MethodOrPropertySymbol methprop = FindMostDerivedMethod(m_pCurrentSym, m_pGroup.GetOptionalObject()); + return methprop != null ? methprop.HasOptionalParameters() : false; + } + + ///////////////////////////////////////////////////////////////////////////////// + // Returns true if we can either add enough optional parameters to make the + // argument list match, or if we dont need to at all. + + private bool AddArgumentsForOptionalParameters() + { + if (m_pCurrentParameters.size <= m_pArguments.carg) + { + // If we have enough arguments, or too many, no need to add any optionals here. + return true; + } + + // First we need to find the method that we're actually trying to call. + MethodOrPropertySymbol methprop = FindMostDerivedMethod(m_pCurrentSym, m_pGroup.GetOptionalObject()); + if (methprop == null) + { + return false; + } + + // If we're here, we know we're not in a named argument case. As such, we can + // just generate defaults for every missing argument. + int i = m_pArguments.carg; + int index = 0; + TypeArray @params = m_pExprBinder.GetTypes().SubstTypeArray( + m_pCurrentParameters, + m_pCurrentType, + m_pGroup.typeArgs); + EXPR[] pArguments = new EXPR[m_pCurrentParameters.size - i]; + for (; i < @params.size; i++, index++) + { + if (!methprop.IsParameterOptional(i)) + { + // We dont have an optional here, but we need to fill it in. + return false; + } + + pArguments[index] = GenerateOptionalArgument(GetSymbolLoader(), m_pExprBinder.GetExprFactory(), methprop, @params.Item(i), i); + } + + // Success. Lets copy them in now. + for (int n = 0; n < index; n++) + { + m_pArguments.prgexpr.Add(pArguments[n]); + } + CType[] prgTypes = new CType[@params.size]; + for (int n = 0; n < @params.size; n++) + { + prgTypes[n] = m_pArguments.prgexpr[n].type; + } + m_pArguments.types = GetSymbolLoader().getBSymmgr().AllocParams(@params.size, prgTypes); + m_pArguments.carg = @params.size; + m_bArgumentsChangedForNamedOrOptionalArguments = true; + return true; + } + + ///////////////////////////////////////////////////////////////////////////////// + + private static EXPR FindArgumentWithName(ArgInfos pArguments, Name pName) + { + for (int i = 0; i < pArguments.carg; i++) + { + if (pArguments.prgexpr[i].isNamedArgumentSpecification() && + pArguments.prgexpr[i].asNamedArgumentSpecification().Name == pName) + { + return pArguments.prgexpr[i]; + } + } + return null; + } + + ///////////////////////////////////////////////////////////////////////////////// + + private bool NamedArgumentNamesAppearInParameterList( + MethodOrPropertySymbol methprop) + { + // Keep track of the current position in the parameter list so that we can check + // containment from this point onwards as well as complete containment. This is + // for error reporting. The user cannot specify a named argument for a parameter + // that has a fixed argument value. + List currentPosition = methprop.ParameterNames; + HashSet names = new HashSet(); + for (int i = 0; i < m_pArguments.carg; i++) + { + if (!m_pArguments.prgexpr[i].isNamedArgumentSpecification()) + { + if (!currentPosition.IsEmpty()) + { + currentPosition = currentPosition.Tail(); + } + continue; + } + + Name name = m_pArguments.prgexpr[i].asNamedArgumentSpecification().Name; + if (!methprop.ParameterNames.Contains(name)) + { + if (m_pInvalidSpecifiedName == null) + { + m_pInvalidSpecifiedName = name; + } + return false; + } + else if (!currentPosition.Contains(name)) + { + if (m_pNameUsedInPositionalArgument == null) + { + m_pNameUsedInPositionalArgument = name; + } + return false; + } + if (names.Contains(name)) + { + if (m_pDuplicateSpecifiedName == null) + { + m_pDuplicateSpecifiedName = name; + } + return false; + } + names.Add(name); + } + return true; + } + + // This method returns true if we have another sym to consider. + // If we've found a match in the current type, and have no more syms to consider in this type, then we + // return false. + private bool GetNextSym(CMemberLookupResults.CMethodIterator iterator) + { + if (!iterator.MoveNext(m_methList.IsEmpty(), m_bIterateToEndOfNsList)) + { + return false; + } + m_pCurrentSym = iterator.GetCurrentSymbol(); + AggregateType type = iterator.GetCurrentType(); + + // If our current type is null, this is our first iteration, so set the type. + // If our current type is not null, and we've got a new type now, and we've already matched + // a symbol, then bail out. + + if (m_pCurrentType != type && + m_pCurrentType != null && + !m_methList.IsEmpty() && + !m_methList.Head().mpwi.GetType().isInterfaceType() && + (!m_methList.Head().mpwi.Sym.IsMethodSymbol() || !m_methList.Head().mpwi.Meth().IsExtension())) + { + return false; + } + else if (m_pCurrentType != type && + m_pCurrentType != null && + !m_methList.IsEmpty() && + !m_methList.Head().mpwi.GetType().isInterfaceType() && + m_methList.Head().mpwi.Sym.IsMethodSymbol() && + m_methList.Head().mpwi.Meth().IsExtension()) + { + // we have found a applicable method that is an extension now we must move to the end of the NS list before quiting + if (m_pGroup.GetOptionalObject() != null) + { + // if we find this while looking for static methods we should ignore it + m_bIterateToEndOfNsList = true; + } + } + + m_pCurrentType = type; + + // We have a new type. If this type is hidden, we need another type. + + while (m_HiddenTypes.Contains(m_pCurrentType)) + { + // Move through this type and get the next one. + for (; iterator.GetCurrentType() == m_pCurrentType; iterator.MoveNext(m_methList.IsEmpty(), m_bIterateToEndOfNsList)) ; + m_pCurrentSym = iterator.GetCurrentSymbol(); + m_pCurrentType = iterator.GetCurrentType(); + + if (iterator.AtEnd()) + { + return false; + } + } + return true; + } + + private bool ConstructExpandedParameters() + { + // Deal with params. + if (m_pCurrentSym == null || m_pArguments == null || m_pCurrentParameters == null) + { + return false; + } + if (0 != (m_fBindFlags & BindingFlag.BIND_NOPARAMS)) + { + return false; + } + if (!m_pCurrentSym.isParamArray) + { + return false; + } + + // Count the number of optionals in the method. If there are enough optionals + // and actual arguments, then proceed. + { + int numOptionals = 0; + for (int i = m_pArguments.carg; i < m_pCurrentSym.Params.size; i++) + { + if (m_pCurrentSym.IsParameterOptional(i)) + { + numOptionals++; + } + } + if (m_pArguments.carg + numOptionals < m_pCurrentParameters.size - 1) + { + return false; + } + } + + Debug.Assert(m_methList.IsEmpty() || m_methList.Head().mpwi.MethProp() != m_pCurrentSym); + // Construct the expanded params. + return m_pExprBinder.TryGetExpandedParams(m_pCurrentSym.Params, m_pArguments.carg, out m_pCurrentParameters); + } + + private Result DetermineCurrentTypeArgs() + { + TypeArray typeArgs = m_pGroup.typeArgs; + + // Get the type args. + if (m_pCurrentSym.IsMethodSymbol() && m_pCurrentSym.AsMethodSymbol().typeVars.size != typeArgs.size) + { + MethodSymbol methSym = m_pCurrentSym.AsMethodSymbol(); + // Can't infer if some type args are specified. + if (typeArgs.size > 0) + { + if (!m_mwtBadArity) + { + m_mwtBadArity.Set(methSym, m_pCurrentType); + } + return Result.Failure_NoSearchForExpanded; + } + Debug.Assert(methSym.typeVars.size > 0); + + // Try to infer. If we have an errorsym in the type arguments, we know we cant infer, + // but we want to attempt it anyway. We'll mark this as "cant infer" so that we can + // report the appropriate error, but we'll continue inferring, since we want + // error sym to go to any type. + + // UNDONE: Do we know that m_pCurrentType is always the parent of the method symbol? + + bool inferenceSucceeded; + + inferenceSucceeded = MethodTypeInferrer.Infer( + m_pExprBinder, GetSymbolLoader(), + methSym, m_pCurrentType.GetTypeArgsAll(), m_pCurrentParameters, + m_pArguments, out m_pCurrentTypeArgs); + + if (!inferenceSucceeded) + { + if (m_results.IsBetterUninferrableResult(m_pCurrentTypeArgs)) + { + TypeArray pTypeVars = methSym.typeVars; + if (pTypeVars != null && m_pCurrentTypeArgs != null && pTypeVars.size == m_pCurrentTypeArgs.size) + { + m_mpwiCantInferInstArg.Set(m_pCurrentSym.AsMethodSymbol(), m_pCurrentType, m_pCurrentTypeArgs); + } + else + { + m_mpwiCantInferInstArg.Set(m_pCurrentSym.AsMethodSymbol(), m_pCurrentType, pTypeVars); + } + } + return Result.Failure_SearchForExpanded; + } + } + else + { + m_pCurrentTypeArgs = typeArgs; + } + return Result.Success; + } + + private bool ArgumentsAreConvertible() + { + bool containsErrorSym = false; + bool bIsInstanceParameterConvertible = false; + if (m_pArguments.carg != 0) + { + UpdateArguments(); + for (int ivar = 0; ivar < m_pArguments.carg; ivar++) + { + CType var = m_pCurrentParameters.Item(ivar); + bool constraintErrors = !TypeBind.CheckConstraints(GetSemanticChecker(), GetErrorContext(), var, CheckConstraintsFlags.NoErrors); + if (constraintErrors && !DoesTypeArgumentsContainErrorSym(var)) + { + m_mpwiParamTypeConstraints.Set(m_pCurrentSym, m_pCurrentType, m_pCurrentTypeArgs); + return false; + } + } + + for (int ivar = 0; ivar < m_pArguments.carg; ivar++) + { + CType var = m_pCurrentParameters.Item(ivar); + containsErrorSym |= DoesTypeArgumentsContainErrorSym(var); + bool fresult; + + if (m_pArguments.fHasExprs) + { + EXPR pArgument = m_pArguments.prgexpr[ivar]; + + // If we have a named argument, strip it to do the conversion. + if (pArgument.isNamedArgumentSpecification()) + { + pArgument = pArgument.asNamedArgumentSpecification().Value; + } + + fresult = m_pExprBinder.canConvert(pArgument, var); + } + else + { + fresult = m_pExprBinder.canConvert(m_pArguments.types.Item(ivar), var); + } + + // Mark this as a legitimate error if we didn't have any error syms. + if (!fresult && !containsErrorSym) + { + if (ivar > m_nArgBest) + { + m_nArgBest = ivar; + + // If we already have best method for instance methods don't overwrite with extensions + if (!m_results.GetBestResult()) + { + m_results.GetBestResult().Set(m_pCurrentSym, m_pCurrentType, m_pCurrentTypeArgs); + m_pBestParameters = m_pCurrentParameters; + } + } + else if (ivar == m_nArgBest && m_pArguments.types.Item(ivar) != var) + { + // this is to eliminate the paranoid case of types that are equal but can't convert + // (think ErrorType != ErrorType) + // See if they just differ in out / ref. + CType argStripped = m_pArguments.types.Item(ivar).IsParameterModifierType() ? + m_pArguments.types.Item(ivar).AsParameterModifierType().GetParameterType() : m_pArguments.types.Item(ivar); + CType varStripped = var.IsParameterModifierType() ? var.AsParameterModifierType().GetParameterType() : var; + + if (argStripped == varStripped) + { + // If we already have best method for instance methods don't overwrite with extensions + if (!m_results.GetBestResult()) + { + m_results.GetBestResult().Set(m_pCurrentSym, m_pCurrentType, m_pCurrentTypeArgs); + m_pBestParameters = m_pCurrentParameters; + } + } + } + + if (m_pCurrentSym.IsMethodSymbol()) + { + // Do not store the result if we have an extension method and the instance + // parameter isn't convertible. + + if (!m_pCurrentSym.AsMethodSymbol().IsExtension() || bIsInstanceParameterConvertible) + { + m_results.AddInconvertibleResult( + m_pCurrentSym.AsMethodSymbol(), + m_pCurrentType, + m_pCurrentTypeArgs); + } + } + return false; + } + } + } + + if (containsErrorSym) + { + if (m_results.IsBetterUninferrableResult(m_pCurrentTypeArgs) && m_pCurrentSym.IsMethodSymbol()) + { + // If we're an instance method or we're an extension that has an inferrable instance argument, + // then mark us down. Note that the extension may not need to infer type args, + // so check if we have any type variables at all to begin with. + if (!m_pCurrentSym.AsMethodSymbol().IsExtension() || + m_pCurrentSym.AsMethodSymbol().typeVars.size == 0 || + MethodTypeInferrer.CanObjectOfExtensionBeInferred( + m_pExprBinder, + GetSymbolLoader(), + m_pCurrentSym.AsMethodSymbol(), + m_pCurrentType.GetTypeArgsAll(), + m_pCurrentSym.AsMethodSymbol().Params, + m_pArguments)) + { + m_results.GetUninferrableResult().Set( + m_pCurrentSym.AsMethodSymbol(), + m_pCurrentType, + m_pCurrentTypeArgs); + } + } + } + else + { + if (m_pCurrentSym.IsMethodSymbol()) + { + // Do not store the result if we have an extension method and the instance + // parameter isn't convertible. + + if (!m_pCurrentSym.AsMethodSymbol().IsExtension() || bIsInstanceParameterConvertible) + { + m_results.AddInconvertibleResult( + m_pCurrentSym.AsMethodSymbol(), + m_pCurrentType, + m_pCurrentTypeArgs); + } + } + } + return !containsErrorSym; + } + + private void UpdateArguments() + { + // Parameter types might have changed as a result of + // method type inference. + + m_pCurrentParameters = m_pExprBinder.GetTypes().SubstTypeArray( + m_pCurrentParameters, m_pCurrentType, m_pCurrentTypeArgs); + + // It is also possible that an optional argument has changed its value + // as a result of method type inference. For example, when inferring + // from Foo(10) to Foo(T t1, T t2 = default(T)), the fabricated + // argument list starts off as being (10, default(T)). After type + // inference has successfully inferred T as int, it needs to be + // transformed into (10, default(int)) before applicability checking + // notices that default(T) is not assignable to int. + + if (m_pArguments.prgexpr == null || m_pArguments.prgexpr.Count == 0) + { + return; + } + + MethodOrPropertySymbol pMethod = null; + for(int iParam = 0 ; iParam < m_pCurrentParameters.size; ++iParam) + { + EXPR pArgument = m_pArguments.prgexpr[iParam]; + if (!pArgument.IsOptionalArgument) + { + continue; + } + CType pType = m_pCurrentParameters.Item(iParam); + + if (pType == pArgument.type) + { + continue; + } + + // Argument has changed its type because of method type inference. Recompute it. + if (pMethod == null) + { + pMethod = FindMostDerivedMethod(m_pCurrentSym, m_pGroup.GetOptionalObject()); + Debug.Assert(pMethod != null); + } + Debug.Assert(pMethod.IsParameterOptional(iParam)); + EXPR pArgumentNew = GenerateOptionalArgument(GetSymbolLoader(), m_pExprBinder.GetExprFactory(), pMethod, m_pCurrentParameters[iParam], iParam); + m_pArguments.prgexpr[iParam] = pArgumentNew; + } + } + + private bool DoesTypeArgumentsContainErrorSym(CType var) + { + if (!var.IsAggregateType()) + { + return false; + } + + TypeArray typeVars = var.AsAggregateType().GetTypeArgsAll(); + for (int i = 0; i < typeVars.size; i++) + { + CType type = typeVars.Item(i); + if (type.IsErrorType()) + { + return true; + } + else if (type.IsAggregateType()) + { + // If we have an agg type sym, check if its type args have errors. + if (DoesTypeArgumentsContainErrorSym(type)) + { + return true; + } + } + } + return false; + } + + // ---------------------------------------------------------------------------- + + private void ReportErrorsOnSuccess() + { + // used for Methods and Indexers + Debug.Assert(m_pGroup.sk == SYMKIND.SK_MethodSymbol || m_pGroup.sk == SYMKIND.SK_PropertySymbol && 0 != (m_pGroup.flags & EXPRFLAG.EXF_INDEXER)); + Debug.Assert(m_pGroup.typeArgs.size == 0 || m_pGroup.sk == SYMKIND.SK_MethodSymbol); + + // if this is a binding to finalize on object, then complain: + if (m_results.GetBestResult().MethProp().name == GetSymbolLoader().GetNameManager().GetPredefName(PredefinedName.PN_DTOR) && + m_results.GetBestResult().MethProp().getClass().isPredefAgg(PredefinedType.PT_OBJECT)) + { + if (0 != (m_pGroup.flags & EXPRFLAG.EXF_BASECALL)) + { + GetErrorContext().Error(ErrorCode.ERR_CallingBaseFinalizeDeprecated); + } + else + { + GetErrorContext().Error(ErrorCode.ERR_CallingFinalizeDepracated); + } + } + + Debug.Assert(0 == (m_pGroup.flags & EXPRFLAG.EXF_USERCALLABLE) || m_results.GetBestResult().MethProp().isUserCallable()); + + if (m_pGroup.sk == SYMKIND.SK_MethodSymbol) + { + Debug.Assert(m_results.GetBestResult().MethProp().IsMethodSymbol()); + + if (m_results.GetBestResult().TypeArgs.size > 0) + { + // Check method type variable constraints. + TypeBind.CheckMethConstraints(GetSemanticChecker(), GetErrorContext(), new MethWithInst(m_results.GetBestResult())); + } + } + } + + private void ReportErrorsOnFailure() + { + // First and foremost, report if the user specified a name more than once. + if (m_pDuplicateSpecifiedName != null) + { + GetErrorContext().Error(ErrorCode.ERR_DuplicateNamedArgument, m_pDuplicateSpecifiedName); + return; + } + + Debug.Assert(m_methList.IsEmpty()); + // Report inaccessible. + if (m_results.GetInaccessibleResult()) + { + // We might have called this, but it is inaccesable... + GetSemanticChecker().ReportAccessError(m_results.GetInaccessibleResult(), m_pExprBinder.ContextForMemberLookup(), GetTypeQualifier(m_pGroup)); + return; + } + + // Report bogus. + if (m_mpwiBogus) + { + // We might have called this, but it is bogus... + GetErrorContext().ErrorRef(ErrorCode.ERR_BindToBogus, m_mpwiBogus); + return; + } + + bool bUseDelegateErrors = false; + Name nameErr = m_pGroup.name; + + // Check for an invoke. + if (m_pGroup.GetOptionalObject() != null && + m_pGroup.GetOptionalObject().type != null && + m_pGroup.GetOptionalObject().type.isDelegateType() && + m_pGroup.name == GetSymbolLoader().GetNameManager().GetPredefName(PredefinedName.PN_INVOKE)) + { + Debug.Assert(!m_results.GetBestResult() || m_results.GetBestResult().MethProp().getClass().IsDelegate()); + Debug.Assert(!m_results.GetBestResult() || m_results.GetBestResult().GetType().getAggregate().IsDelegate()); + bUseDelegateErrors = true; + nameErr = m_pGroup.GetOptionalObject().type.getAggregate().name; + } + + if (m_results.GetBestResult()) + { + // If we had some invalid arguments for best matching. + ReportErrorsForBestMatching(bUseDelegateErrors, nameErr); + } + else if (m_results.GetUninferrableResult() || m_mpwiCantInferInstArg) + { + if (!m_results.GetUninferrableResult()) + { + //copy the extension method for which instacne argument type inference failed + m_results.GetUninferrableResult().Set(m_mpwiCantInferInstArg.Sym.AsMethodSymbol(), m_mpwiCantInferInstArg.GetType(), m_mpwiCantInferInstArg.TypeArgs); + } + Debug.Assert(m_results.GetUninferrableResult().Sym.IsMethodSymbol()); + + MethodSymbol sym = m_results.GetUninferrableResult().Meth(); + TypeArray pCurrentParameters = sym.Params; + // if we tried to bind to an extensionmethod and the instance argument Type Inference failed then the method does not exist + // on the type at all. this is treated as a lookup error + CType type = null; + if (m_pGroup.GetOptionalObject() != null) + { + type = m_pGroup.GetOptionalObject().type; + } + else if (m_pGroup.GetOptionalLHS() != null) + { + type = m_pGroup.GetOptionalLHS().type; + } + + MethWithType mwtCantInfer = new MethWithType(); + mwtCantInfer.Set(m_results.GetUninferrableResult().Meth(), m_results.GetUninferrableResult().GetType()); + GetErrorContext().Error(ErrorCode.ERR_CantInferMethTypeArgs, mwtCantInfer); + } + else if (m_mwtBadArity) + { + int cvar = m_mwtBadArity.Meth().typeVars.size; + GetErrorContext().ErrorRef(cvar > 0 ? ErrorCode.ERR_BadArity : ErrorCode.ERR_HasNoTypeVars, m_mwtBadArity, new ErrArgSymKind(m_mwtBadArity.Meth()), m_pArguments.carg); + } + else if (m_mpwiParamTypeConstraints) + { + // This will always report an error + TypeBind.CheckMethConstraints(GetSemanticChecker(), GetErrorContext(), new MethWithInst(m_mpwiParamTypeConstraints)); + } + else if (m_pInvalidSpecifiedName != null) + { + // Give a better message for delegate invoke. + if (m_pGroup.GetOptionalObject() != null && + m_pGroup.GetOptionalObject().type.IsAggregateType() && + m_pGroup.GetOptionalObject().type.AsAggregateType().GetOwningAggregate().IsDelegate()) + { + GetErrorContext().Error(ErrorCode.ERR_BadNamedArgumentForDelegateInvoke, m_pGroup.GetOptionalObject().type.AsAggregateType().GetOwningAggregate().name, m_pInvalidSpecifiedName); + } + else + { + GetErrorContext().Error(ErrorCode.ERR_BadNamedArgument, m_pGroup.name, m_pInvalidSpecifiedName); + } + } + else if (m_pNameUsedInPositionalArgument != null) + { + GetErrorContext().Error(ErrorCode.ERR_NamedArgumentUsedInPositional, m_pNameUsedInPositionalArgument); + } + else + { + CParameterizedError error; + + if (m_pDelegate != null) + { + GetErrorContext().MakeError(out error, ErrorCode.ERR_MethDelegateMismatch, nameErr, m_pDelegate); + GetErrorContext().AddRelatedTypeLoc(error, m_pDelegate); + } + else + { + // The number of arguments must be wrong. + + if (m_fCandidatesUnsupported) + { + GetErrorContext().MakeError(out error, ErrorCode.ERR_BindToBogus, nameErr); + } + else if (bUseDelegateErrors) + { + Debug.Assert(0 == (m_pGroup.flags & EXPRFLAG.EXF_CTOR)); + GetErrorContext().MakeError(out error, ErrorCode.ERR_BadDelArgCount, nameErr, m_pArguments.carg); + } + else + { + if (0 != (m_pGroup.flags & EXPRFLAG.EXF_CTOR)) + { + Debug.Assert(!m_pGroup.GetParentType().IsTypeParameterType()); + GetErrorContext().MakeError(out error, ErrorCode.ERR_BadCtorArgCount, m_pGroup.GetParentType(), m_pArguments.carg); + } + else + { + GetErrorContext().MakeError(out error, ErrorCode.ERR_BadArgCount, nameErr, m_pArguments.carg); + } + } + } + + // Report possible matches (same name and is accesible). We stored these in m_swtWrongCount. + for (int i = 0; i < m_nWrongCount; i++) + { + if (GetSemanticChecker().CheckAccess( + m_swtWrongCount[i].Sym, + m_swtWrongCount[i].GetType(), + m_pExprBinder.ContextForMemberLookup(), + GetTypeQualifier(m_pGroup))) + { + GetErrorContext().AddRelatedSymLoc(error, m_swtWrongCount[i].Sym); + } + } + GetErrorContext().SubmitError(error); + } + } + private void ReportErrorsForBestMatching(bool bUseDelegateErrors, Name nameErr) + { + // Best matching overloaded method 'name' had some invalid arguments. + if (m_pDelegate != null) + { + GetErrorContext().ErrorRef(ErrorCode.ERR_MethDelegateMismatch, nameErr, m_pDelegate, m_results.GetBestResult()); + return; + } + + if (m_bBindingCollectionAddArgs) + { + if (ReportErrorsForCollectionAdd()) + { + return; + } + } + + if (bUseDelegateErrors) + { + // Point to the Delegate, not the Invoke method + GetErrorContext().Error(ErrorCode.ERR_BadDelArgTypes, m_results.GetBestResult().GetType()); + } + else + { + if (m_results.GetBestResult().Sym.IsMethodSymbol() && m_results.GetBestResult().Sym.AsMethodSymbol().IsExtension() && m_pGroup.GetOptionalObject() != null) + { + GetErrorContext().Error(ErrorCode.ERR_BadExtensionArgTypes, m_pGroup.GetOptionalObject().type, m_pGroup.name, m_results.GetBestResult().Sym); + } + else if (m_bBindingCollectionAddArgs) + { + GetErrorContext().Error(ErrorCode.ERR_BadArgTypesForCollectionAdd, m_results.GetBestResult()); + } + else + { + GetErrorContext().Error(ErrorCode.ERR_BadArgTypes, m_results.GetBestResult()); + } + } + + // Argument X: cannot convert type 'Y' to type 'Z' + for (int ivar = 0; ivar < m_pArguments.carg; ivar++) + { + CType var = m_pBestParameters.Item(ivar); + + if (!m_pExprBinder.canConvert(m_pArguments.prgexpr[ivar], var)) + { + // See if they just differ in out / ref. + CType argStripped = m_pArguments.types.Item(ivar).IsParameterModifierType() ? + m_pArguments.types.Item(ivar).AsParameterModifierType().GetParameterType() : m_pArguments.types.Item(ivar); + CType varStripped = var.IsParameterModifierType() ? var.AsParameterModifierType().GetParameterType() : var; + if (argStripped == varStripped) + { + if (varStripped != var) + { + // The argument is wrong in ref / out-ness. + GetErrorContext().Error(ErrorCode.ERR_BadArgRef, ivar + 1, (var.IsParameterModifierType() && var.AsParameterModifierType().isOut) ? "out" : "ref"); + } + else + { + CType argument = m_pArguments.types.Item(ivar); + + // the argument is decorated, but doesn't needs a 'ref' or 'out' + GetErrorContext().Error(ErrorCode.ERR_BadArgExtraRef, ivar + 1, (argument.IsParameterModifierType() && argument.AsParameterModifierType().isOut) ? "out" : "ref"); + } + } + else + { + // if we tried to bind to an extensionmethod and the instance argument conversion failed then the method does not exist + // on the type at all. + Symbol sym = m_results.GetBestResult().Sym; + if (ivar == 0 && sym.IsMethodSymbol() && sym.AsMethodSymbol().IsExtension() && m_pGroup.GetOptionalObject() != null && + !m_pExprBinder.canConvertInstanceParamForExtension(m_pGroup.GetOptionalObject(), sym.AsMethodSymbol().Params.Item(0))) + { + if (!m_pGroup.GetOptionalObject().type.getBogus()) + { + GetErrorContext().Error(ErrorCode.ERR_BadInstanceArgType, m_pGroup.GetOptionalObject().type, var); + } + } + else + { + GetErrorContext().Error(ErrorCode.ERR_BadArgType, ivar + 1, new ErrArg(m_pArguments.types.Item(ivar), ErrArgFlags.Unique), new ErrArg(var, ErrArgFlags.Unique)); + } + } + } + } + } + + private bool ReportErrorsForCollectionAdd() + { + for (int ivar = 0; ivar < m_pArguments.carg; ivar++) + { + CType var = m_pBestParameters.Item(ivar); + if (var.IsParameterModifierType()) + { + GetErrorContext().ErrorRef(ErrorCode.ERR_InitializerAddHasParamModifiers, m_results.GetBestResult()); + return true; + } + } + return false; + } + } + } +} diff --git a/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/GroupToArgsBinderResult.cs b/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/GroupToArgsBinderResult.cs new file mode 100644 index 000000000..9c7c43e85 --- /dev/null +++ b/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/GroupToArgsBinderResult.cs @@ -0,0 +1,112 @@ +// ==++== +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// ==--== + +using System.Collections.Generic; + +namespace Microsoft.CSharp.RuntimeBinder.Semantics +{ + internal partial class ExpressionBinder + { + + // ---------------------------------------------------------------------------- + // This class takes an EXPRMEMGRP and a set of arguments and binds the arguments + // to the best applicable method in the group. + // ---------------------------------------------------------------------------- + + internal class GroupToArgsBinderResult + { + public MethPropWithInst BestResult; + public MethPropWithInst GetBestResult() { return BestResult; } + public MethPropWithInst AmbiguousResult; + public MethPropWithInst GetAmbiguousResult() { return AmbiguousResult; } + public MethPropWithInst InaccessibleResult; + public MethPropWithInst GetInaccessibleResult() { return InaccessibleResult; } + public MethPropWithInst UninferrableResult; + public MethPropWithInst GetUninferrableResult() { return UninferrableResult; } + public MethPropWithInst InconvertibleResult; + public GroupToArgsBinderResult() + { + BestResult = new MethPropWithInst(); + AmbiguousResult = new MethPropWithInst(); + InaccessibleResult = new MethPropWithInst(); + UninferrableResult = new MethPropWithInst(); + InconvertibleResult = new MethPropWithInst(); + m_inconvertibleResults = new List(); + } + + private List m_inconvertibleResults; + + ///////////////////////////////////////////////////////////////////////////////// + + public void AddInconvertibleResult( + MethodSymbol method, + AggregateType currentType, + TypeArray currentTypeArgs) + { + if (InconvertibleResult.Sym == null) + { + // This is the first one, so set it for error reporting usage. + InconvertibleResult.Set(method, currentType, currentTypeArgs); + } + m_inconvertibleResults.Add(new MethPropWithInst(method, currentType, currentTypeArgs)); + } + + ///////////////////////////////////////////////////////////////////////////////// + + private static int NumberOfErrorTypes(TypeArray pTypeArgs) + { + int nCount = 0; + for (int i = 0; i < pTypeArgs.Size; i++) + { + if (pTypeArgs.Item(i).IsErrorType()) + { + nCount++; + } + } + return nCount; + } + + private static bool IsBetterThanCurrent(TypeArray pTypeArgs1, TypeArray pTypeArgs2) + { + int leftErrors = NumberOfErrorTypes(pTypeArgs1); + int rightErrors = NumberOfErrorTypes(pTypeArgs2); + + if (leftErrors == rightErrors) + { + int max = pTypeArgs1.Size > pTypeArgs2.Size ? pTypeArgs2.Size : pTypeArgs1.Size; + + // If we dont have a winner yet, go through each element's type args. + for (int i = 0; i < max; i++) + { + if (pTypeArgs1.Item(i).IsAggregateType()) + { + leftErrors += NumberOfErrorTypes(pTypeArgs1.Item(i).AsAggregateType().GetTypeArgsAll()); + } + if (pTypeArgs2.Item(i).IsAggregateType()) + { + rightErrors += NumberOfErrorTypes(pTypeArgs2.Item(i).AsAggregateType().GetTypeArgsAll()); + } + } + } + return rightErrors < leftErrors; + } + + public bool IsBetterUninferrableResult(TypeArray pTypeArguments) + { + if (UninferrableResult.Sym == null) + { + // If we dont even have a result, then its definitely better. + return true; + } + if (pTypeArguments == null) + { + return false; + } + return IsBetterThanCurrent(UninferrableResult.TypeArgs, pTypeArguments); + } + } + } +} diff --git a/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/ImplicitConversion.cs b/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/ImplicitConversion.cs new file mode 100644 index 000000000..b5884be00 --- /dev/null +++ b/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/ImplicitConversion.cs @@ -0,0 +1,911 @@ +// ==++== +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// ==--== + +using System.Diagnostics; +using Microsoft.CSharp.RuntimeBinder.Errors; +using Microsoft.CSharp.RuntimeBinder.Syntax; + +namespace Microsoft.CSharp.RuntimeBinder.Semantics +{ + internal partial class ExpressionBinder + { + + // ---------------------------------------------------------------------------- + // BindImplicitConversion + // ---------------------------------------------------------------------------- + + private class ImplicitConversion + { + public ImplicitConversion(ExpressionBinder binder, EXPR exprSrc, CType typeSrc, EXPRTYPEORNAMESPACE typeDest, bool needsExprDest, CONVERTTYPE flags) + { + this.binder = binder; + this.exprSrc = exprSrc; + this.typeSrc = typeSrc; + this.typeDest = typeDest.TypeOrNamespace.AsType(); + this.exprTypeDest = typeDest; + this.needsExprDest = needsExprDest; + this.flags = flags; + this.exprDest = null; + } + public EXPR ExprDest { get { return exprDest; } } + private EXPR exprDest; + private ExpressionBinder binder; + private EXPR exprSrc; + private CType typeSrc; + private CType typeDest; + private EXPRTYPEORNAMESPACE exprTypeDest; + private bool needsExprDest; + private CONVERTTYPE flags; + + /* + * BindImplicitConversion + * + * This is a complex routine with complex parameters. Generally, this should + * be called through one of the helper methods that insulates you + * from the complexity of the interface. This routine handles all the logic + * associated with implicit conversions. + * + * exprSrc - the expression being converted. Can be null if only type conversion + * info is being supplied. + * typeSrc - type of the source + * typeDest - type of the destination + * exprDest - returns an expression of the src converted to the dest. If null, we + * only care about whether the conversion can be attempted, not the + * expression tree. + * flags - flags possibly customizing the conversions allowed. E.g., can suppress + * user-defined conversions. + * + * returns true if the conversion can be made, false if not. + */ + public bool Bind() + { + // 13.1 Implicit conversions + // + // The following conversions are classified as implicit conversions: + // + // * Identity conversions + // * Implicit numeric conversions + // * Implicit enumeration conversions + // * Implicit reference conversions + // * Boxing conversions + // * Implicit type parameter conversions + // * Implicit constant expression conversions + // * User-defined implicit conversions + // * Implicit conversions from an anonymous method expression to a compatible delegate type + // * Implicit conversion from a method group to a compatible delegate type + // * Conversions from the null type (11.2.7) to any nullable type + // * Implicit nullable conversions + // * Lifted user-defined implicit conversions + // + // Implicit conversions can occur in a variety of situations, including function member invocations + // (14.4.3), cast expressions (14.6.6), and assignments (14.14). + + // Can't convert to or from the error type. + if (typeSrc == null || typeDest == null || typeDest.IsNeverSameType()) + { + return false; + } + + Debug.Assert(typeSrc != null && typeDest != null); // types must be supplied. + Debug.Assert(exprSrc == null || typeSrc == exprSrc.type); // type of source should be correct if source supplied + Debug.Assert(!needsExprDest || exprSrc != null); // need source expr to create dest expr + + switch (typeDest.GetTypeKind()) + { + case TypeKind.TK_ErrorType: + Debug.Assert(typeDest.AsErrorType().HasTypeParent() || typeDest.AsErrorType().HasNSParent()); + if (typeSrc != typeDest) + { + return false; + } + if (needsExprDest) + { + exprDest = exprSrc; + } + return true; + case TypeKind.TK_NullType: + // Can only convert to the null type if src is null. + if (!typeSrc.IsNullType()) + { + return false; + } + if (needsExprDest) + { + exprDest = exprSrc; + } + return true; + case TypeKind.TK_MethodGroupType: + VSFAIL("Something is wrong with Type.IsNeverSameType()"); + return false; + case TypeKind.TK_NaturalIntegerType: + case TypeKind.TK_ArgumentListType: + return typeSrc == typeDest; + case TypeKind.TK_VoidType: + return false; + default: + break; + } + + if (typeSrc.IsErrorType()) + { + Debug.Assert(!typeDest.IsErrorType()); + return false; + } + + // 13.1.1 Identity conversion + // + // An identity conversion converts from any type to the same type. This conversion exists only + // such that an entity that already has a required type can be said to be convertible to that type. + + if (typeSrc == typeDest && + ((flags & CONVERTTYPE.ISEXPLICIT) == 0 || (!typeSrc.isPredefType(PredefinedType.PT_FLOAT) && !typeSrc.isPredefType(PredefinedType.PT_DOUBLE)))) + { + if (needsExprDest) + { + exprDest = exprSrc; + } + return true; + } + + if (typeDest.IsNullableType()) + { + return BindNubConversion(typeDest.AsNullableType()); + } + + if (typeSrc.IsNullableType()) + { + return bindImplicitConversionFromNullable(typeSrc.AsNullableType()); + } + + if ((flags & CONVERTTYPE.ISEXPLICIT) != 0) + { + flags |= CONVERTTYPE.NOUDC; + } + + // Get the fundamental types of destination. + FUNDTYPE ftDest = typeDest.fundType(); + Debug.Assert(ftDest != FUNDTYPE.FT_NONE || typeDest.IsParameterModifierType()); + + switch (typeSrc.GetTypeKind()) + { + default: + VSFAIL("Bad type symbol kind"); + break; + case TypeKind.TK_MethodGroupType: + if (exprSrc.isMEMGRP()) + { + EXPRCALL outExpr; + bool retVal = binder.BindGrpConversion(exprSrc.asMEMGRP(), typeDest, needsExprDest, out outExpr, false); + exprDest = outExpr; + return retVal; + } + return false; + case TypeKind.TK_VoidType: + case TypeKind.TK_ErrorType: + case TypeKind.TK_ParameterModifierType: + case TypeKind.TK_ArgumentListType: + return false; + case TypeKind.TK_NullType: + if (bindImplicitConversionFromNull()) + { + return true; + } + // If not, try user defined implicit conversions. + break; + case TypeKind.TK_ArrayType: + if (bindImplicitConversionFromArray()) + { + return true; + } + // If not, try user defined implicit conversions. + break; + case TypeKind.TK_PointerType: + if (bindImplicitConversionFromPointer()) + { + return true; + } + // If not, try user defined implicit conversions. + break; + case TypeKind.TK_TypeParameterType: + if (bindImplicitConversionFromTypeVar(typeSrc.AsTypeParameterType())) + { + return true; + } + // If not, try user defined implicit conversions. + break; + case TypeKind.TK_AggregateType: + // TypeReference and ArgIterator can't be boxed (or converted to anything else) + if (typeSrc.isSpecialByRefType()) + { + return false; + } + if (bindImplicitConversionFromAgg(typeSrc.AsAggregateType())) + { + return true; + } + // If not, try user defined implicit conversions. + break; + } + + // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + // RUNTIME BINDER ONLY CHANGE + // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + // + // Every incoming dynamic operand should be implicitly convertible + // to any type that it is an instance of. + + if (exprSrc != null + && exprSrc.RuntimeObject != null + && typeDest.AssociatedSystemType.IsInstanceOfType(exprSrc.RuntimeObject) + && binder.GetSemanticChecker().CheckTypeAccess(typeDest, binder.Context.ContextForMemberLookup())) + { + if (needsExprDest) + { + binder.bindSimpleCast(exprSrc, exprTypeDest, out exprDest, exprSrc.flags & EXPRFLAG.EXF_CANTBENULL); + } + return true; + } + + // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + // END RUNTIME BINDER ONLY CHANGE + // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + + // 13.1.8 User-defined implicit conversions + // + // A user-defined implicit conversion consists of an optional standard implicit conversion, + // followed by execution of a user-defined implicit conversion operator, followed by another + // optional standard implicit conversion. The exact rules for evaluating user-defined + // conversions are described in 13.4.3. + + if (0 == (flags & CONVERTTYPE.NOUDC)) + { + return binder.bindUserDefinedConversion(exprSrc, typeSrc, typeDest, needsExprDest, out exprDest, true); + } + + // No conversion was found. + + return false; + } + + + /*************************************************************************************************** + Called by BindImplicitConversion when the destination type is Nullable. The following + conversions are handled by this method: + + * For S in { object, ValueType, interfaces implemented by underlying type} there is an explicit + unboxing conversion S => T? + * System.Enum => T? there is an unboxing conversion if T is an enum type + * null => T? implemented as default(T?) + + * Implicit T?* => T?+ implemented by either wrapping or calling GetValueOrDefault the + appropriate number of times. + * If imp/exp S => T then imp/exp S => T?+ implemented by converting to T then wrapping the + appropriate number of times. + * If imp/exp S => T then imp/exp S?+ => T?+ implemented by calling GetValueOrDefault (m-1) times + then calling HasValue, producing a null if it returns false, otherwise calling Value, + converting to T then wrapping the appropriate number of times. + + The 3 rules above can be summarized with the following recursive rules: + + * If imp/exp S => T? then imp/exp S? => T? implemented as + qs.HasValue ? (T?)(qs.Value) : default(T?) + * If imp/exp S => T then imp/exp S => T? implemented as new T?((T)s) + + This method also handles calling bindUserDefinedConverion. This method does NOT handle + the following conversions: + + * Implicit boxing conversion from S? to { object, ValueType, Enum, ifaces implemented by S }. (Handled by BindImplicitConversion.) + * If imp/exp S => T then explicit S?+ => T implemented by calling Value the appropriate number + of times. (Handled by BindExplicitConversion.) + + The recursive equivalent is: + + * If imp/exp S => T and T is not nullable then explicit S? => T implemented as qs.Value + + Some nullable conversion are NOT standard conversions. In particular, if S => T is implicit + then S? => T is not standard. Similarly if S => T is not implicit then S => T? is not standard. + ***************************************************************************************************/ + private bool BindNubConversion(NullableType nubDst) + { + // This code assumes that STANDARD and ISEXPLICIT are never both set. + // bindUserDefinedConversion should ensure this! + Debug.Assert(0 != (~flags & (CONVERTTYPE.STANDARD | CONVERTTYPE.ISEXPLICIT))); + Debug.Assert(exprSrc == null || exprSrc.type == typeSrc); + Debug.Assert(!needsExprDest || exprSrc != null); + Debug.Assert(typeSrc != nubDst); // BindImplicitConversion should have taken care of this already. + AggregateType atsDst = nubDst.GetAts(GetErrorContext()); + if (atsDst == null) + return false; + + // Check for the unboxing conversion. This takes precedence over the wrapping conversions. + if (GetSymbolLoader().HasBaseConversion(nubDst.GetUnderlyingType(), typeSrc) && !CConversions.FWrappingConv(typeSrc, nubDst)) + { + // These should be different! Fix the caller if typeSrc is an AggregateType of Nullable. + Debug.Assert(atsDst != typeSrc); + + // typeSrc is a base type of the destination nullable type so there is an explicit + // unboxing conversion. + if (0 == (flags & CONVERTTYPE.ISEXPLICIT)) + { + return false; + } + + if (needsExprDest) + { + binder.bindSimpleCast(exprSrc, exprTypeDest, out exprDest, EXPRFLAG.EXF_UNBOX); + } + return true; + } + + int cnubDst; + int cnubSrc; + CType typeDstBase = nubDst.StripNubs(out cnubDst); + EXPRCLASS exprTypeDstBase = GetExprFactory().MakeClass(typeDstBase); + CType typeSrcBase = typeSrc.StripNubs(out cnubSrc); + + ConversionFunc pfn = (flags & CONVERTTYPE.ISEXPLICIT) != 0 ? + (ConversionFunc)binder.BindExplicitConversion : + (ConversionFunc)binder.BindImplicitConversion; + + if (cnubSrc == 0) + { + Debug.Assert(typeSrc == typeSrcBase); + + // The null type can be implicitly converted to T? as the default value. + if (typeSrc.IsNullType()) + { + // If we have the constant null, generate it as a default value of T?. If we have + // some crazy expression which has been determined to be always null, like (null??null) + // keep it in its expression form and transform it in the nullable rewrite pass. + if (needsExprDest) + { + if (exprSrc.isCONSTANT_OK()) + { + exprDest = GetExprFactory().CreateZeroInit(nubDst); + } + else + { + exprDest = GetExprFactory().CreateCast(0x00, typeDest, exprSrc); + } + } + return true; + } + + EXPR exprTmp = exprSrc; + + // If there is an implicit/explicit S => T then there is an implicit/explicit S => T? + if (typeSrc == typeDstBase || pfn(exprSrc, typeSrc, exprTypeDstBase, nubDst, needsExprDest, out exprTmp, flags | CONVERTTYPE.NOUDC)) + { + if (needsExprDest) + { + // UNDONE: This is a premature realization of the nullable conversion as + // UNDONE: a constructor. Rather than flagging this, can we simply emit it + // UNDONE: as a cast node and have the operator rewrite pass turn it into + // UNDONE: a constructor call? + EXPRUSERDEFINEDCONVERSION exprUDC = exprTmp.kind == ExpressionKind.EK_USERDEFINEDCONVERSION ? exprTmp.asUSERDEFINEDCONVERSION() : null; + if (exprUDC != null) + { + exprTmp = exprUDC.UserDefinedCall; + } + + // This logic is left over from the days when T?? was legal. However there are error/LAF cases that necessitates the loop. + // typeSrc is not nullable so just wrap the required number of times. For legal code (cnubDst <= 0). + + for (int i = 0; i < cnubDst; i++) + { + exprTmp = binder.BindNubNew(exprTmp); + exprTmp.asCALL().nubLiftKind = NullableCallLiftKind.NullableConversionConstructor; + } + if (exprUDC != null) + { + exprUDC.UserDefinedCall = exprTmp; + exprUDC.setType((CType)exprTmp.type); + exprTmp = exprUDC; + } + Debug.Assert(exprTmp.type == nubDst); + exprDest = exprTmp; + } + return true; + } + + // No builtin conversion. Maybe there is a user defined conversion.... + return 0 == (flags & CONVERTTYPE.NOUDC) && binder.bindUserDefinedConversion(exprSrc, typeSrc, nubDst, needsExprDest, out exprDest, 0 == (flags & CONVERTTYPE.ISEXPLICIT)); + } + + // Both are Nullable so there is only a conversion if there is a conversion between the base types. + // That is, if there is an implicit/explicit S => T then there is an implicit/explicit S?+ => T?+. + if (typeSrcBase != typeDstBase && !pfn(null, typeSrcBase, exprTypeDstBase, nubDst, false, out exprDest, flags | CONVERTTYPE.NOUDC)) + { + // No builtin conversion. Maybe there is a user defined conversion.... + return 0 == (flags & CONVERTTYPE.NOUDC) && binder.bindUserDefinedConversion(exprSrc, typeSrc, nubDst, needsExprDest, out exprDest, 0 == (flags & CONVERTTYPE.ISEXPLICIT)); + } + + if (needsExprDest) + { + MethWithInst mwi = new MethWithInst(null, null); + EXPRMEMGRP pMemGroup = GetExprFactory().CreateMemGroup(null, mwi); + EXPRCALL exprDst = GetExprFactory().CreateCall(0, nubDst, exprSrc, pMemGroup, null); + + // Here we want to first check whether or not the conversions work on the base types. + + EXPR arg1 = binder.mustCast(exprSrc, typeSrcBase); + EXPRCLASS arg2 = GetExprFactory().MakeClass(typeDstBase); + + bool convertible; + if (0 != (flags & CONVERTTYPE.ISEXPLICIT)) + { + convertible = binder.BindExplicitConversion(arg1, arg1.type, arg2, typeDstBase, out arg1, flags | CONVERTTYPE.NOUDC); + } + else + { + convertible = binder.BindImplicitConversion(arg1, arg1.type, arg2, typeDstBase, out arg1, flags | CONVERTTYPE.NOUDC); + } + if (!convertible) + { + VSFAIL("bind(Im|Ex)plicitConversion failed unexpectedly"); + return false; + } + + exprDst.castOfNonLiftedResultToLiftedType = binder.mustCast(arg1, nubDst, 0); + exprDst.nubLiftKind = NullableCallLiftKind.NullableConversion; + exprDst.pConversions = exprDst.castOfNonLiftedResultToLiftedType; + exprDest = exprDst; + } + + return true; + } + + private bool bindImplicitConversionFromNull() + { + // null type can be implicitly converted to any reference type or pointer type or type + // variable with reference-type constraint. + + FUNDTYPE ftDest = typeDest.fundType(); + if (ftDest != FUNDTYPE.FT_REF && ftDest != FUNDTYPE.FT_PTR && + (ftDest != FUNDTYPE.FT_VAR || !typeDest.AsTypeParameterType().IsReferenceType()) && + // null is convertible to System.Nullable. + !typeDest.isPredefType(PredefinedType.PT_G_OPTIONAL)) + { + return false; + } + if (needsExprDest) + { + // If the conversion argument is a constant null then return a ZEROINIT. + // Otherwise, bind this as a cast to the destination type. In a later + // rewrite pass we will rewrite the cast as SEQ(side effects, ZEROINIT). + if (exprSrc.isCONSTANT_OK()) + { + exprDest = GetExprFactory().CreateZeroInit(typeDest); + } + else + { + exprDest = GetExprFactory().CreateCast(0x00, typeDest, exprSrc); + } + } + return true; + } + + private bool bindImplicitConversionFromNullable(NullableType nubSrc) + { + // We can convert T? using a boxing conversion, we can convert it to ValueType, and + // we can convert it to any interface implemented by T. + // + // 13.1.5 Boxing Conversions + // + // A nullable-type has a boxing conversion to the same set of types to which the nullable-type's + // underlying type has boxing conversions. A boxing conversion applied to a value of a nullable-type + // proceeds as follows: + // + // * If the HasValue property of the nullable value evaluates to false, then the result of the + // boxing conversion is the null reference of the appropriate type. + // + // Otherwise, the result is obtained by boxing the result of evaluating the Value property on + // the nullable value. + + AggregateType atsNub = nubSrc.GetAts(GetErrorContext()); + if (atsNub == null) + { + return false; + } + if (atsNub == typeDest) + { + // REVIEW : Should this ASSERT? + if (needsExprDest) + { + exprDest = exprSrc; + } + return true; + } + if (GetSymbolLoader().HasBaseConversion(nubSrc.GetUnderlyingType(), typeDest) && !CConversions.FUnwrappingConv(nubSrc, typeDest)) + { + if (needsExprDest) + { + binder.bindSimpleCast(exprSrc, exprTypeDest, out exprDest, EXPRFLAG.EXF_BOX); + if (!typeDest.isPredefType(PredefinedType.PT_OBJECT)) + { + + // DevDiv Bugs 125348: The base type of a nullable is always a non-nullable value type, + // therefore so is typeDest unless typeDest is PT_OBJECT. In this case the conversion + // needs to be unboxed. We only need this if we actually will use the result. + binder.bindSimpleCast(exprDest, exprTypeDest, out exprDest, EXPRFLAG.EXF_FORCE_UNBOX); + } + } + return true; + } + return 0 == (flags & CONVERTTYPE.NOUDC) && binder.bindUserDefinedConversion(exprSrc, nubSrc, typeDest, needsExprDest, out exprDest, true); + } + + private bool bindImplicitConversionFromArray() + { + // 13.1.4 + // + // The implicit reference conversions are: + // + // * From an array-type S with an element type SE to an array-type T with an element + // type TE, provided all of the following are true: + // * S and T differ only in element type. In other words, S and T have the same number of dimensions. + // * An implicit reference conversion exists from SE to TE. + // * From a one-dimensional array-type S[] to System.Collections.Generic.IList, + // System.Collections.Generic.IReadOnlyList and their base interfaces + // * From a one-dimensional array-type S[] to System.Collections.Generic.IList, System.Collections.Generic.IReadOnlyList + // and their base interfaces, provided there is an implicit reference conversion from S to T. + // * From any array-type to System.Array. + // * From any array-type to any interface implemented by System.Array. + + if (!GetSymbolLoader().HasBaseConversion(typeSrc, typeDest)) + { + return false; + } + + EXPRFLAG grfex = 0; + // The above if checks for dest==Array, object or an interface the array implements, + // including IList, ICollection, IEnumerable, IReadOnlyList, IReadOnlyCollection + // and the non-generic versions. + // REVIEW : Determine when we need EXF_REFCHECK! + + if ((typeDest.IsArrayType() || + (typeDest.isInterfaceType() && + typeDest.AsAggregateType().GetTypeArgsAll().Size == 1 && + ((typeDest.AsAggregateType().GetTypeArgsAll().Item(0) != typeSrc.AsArrayType().GetElementType()) || + 0 != (flags & CONVERTTYPE.FORCECAST)))) + && + (0 != (flags & CONVERTTYPE.FORCECAST) || + TypeManager.TypeContainsTyVars(typeSrc, null) || + TypeManager.TypeContainsTyVars(typeDest, null))) + { + grfex = EXPRFLAG.EXF_REFCHECK; + } + if (needsExprDest) + { + binder.bindSimpleCast(exprSrc, exprTypeDest, out exprDest, grfex); + } + return true; + } + + private bool bindImplicitConversionFromPointer() + { + + // 27.4 Pointer conversions + // + // In an unsafe context, the set of available implicit conversions (13.1) is extended to include + // the following implicit pointer conversions: + // + // * From any pointer-type to the type void*. + + if (typeDest.IsPointerType() && typeDest.AsPointerType().GetReferentType() == binder.getVoidType()) + { + if (needsExprDest) + binder.bindSimpleCast(exprSrc, exprTypeDest, out exprDest); + return true; + } + return false; + } + + private bool bindImplicitConversionFromAgg(AggregateType aggTypeSrc) + { + // GENERICS: The case for constructed types is very similar to types with + // no parameters. The parameters are irrelevant for most of the conversions + // below. They could be relevant if we had user-defined conversions on + // generic types. + + AggregateSymbol aggSrc = aggTypeSrc.getAggregate(); + if (aggSrc.IsEnum()) + { + return bindImplicitConversionFromEnum(aggTypeSrc); + } + + if (typeDest.isEnumType()) + { + if (bindImplicitConversionToEnum(aggTypeSrc)) + { + return true; + } + // Even though enum is sealed, a class can derive from enum in LAF scenarios -- + // continue testing for derived to base conversions below. + } + else if (aggSrc.getThisType().isSimpleType() && typeDest.isSimpleType()) + { + if (bindImplicitConversionBetweenSimpleTypes(aggTypeSrc)) + { + return true; + } + // No simple conversion -- continue testing for derived to base conversions below. + } + + return bindImplicitConversionToBase(aggTypeSrc); + } + + private bool bindImplicitConversionToBase(AggregateType pSource) + { + // 13.1.4 Implicit reference conversions + // + // * From any reference-type to object. + // * From any class-type S to any class-type T, provided S is derived from T. + // * From any class-type S to any interface-type T, provided S implements T. + // * From any interface-type S to any interface-type T, provided S is derived from T. + // * From any delegate-type to System.Delegate. + // * From any delegate-type to System.ICloneable. + + if (!typeDest.IsAggregateType() || !GetSymbolLoader().HasBaseConversion(pSource, typeDest)) + { + return false; + } + EXPRFLAG flags = 0x00; + if (pSource.getAggregate().IsStruct() && typeDest.fundType() == FUNDTYPE.FT_REF) + { + flags = EXPRFLAG.EXF_BOX | EXPRFLAG.EXF_CANTBENULL; + } + else if (exprSrc != null) + { + flags = exprSrc.flags & EXPRFLAG.EXF_CANTBENULL; + } + if (needsExprDest) + binder.bindSimpleCast(exprSrc, exprTypeDest, out exprDest, flags); + return true; + } + + private bool bindImplicitConversionFromEnum(AggregateType aggTypeSrc) + { + + // 13.1.5 Boxing conversions + // + // A boxing conversion permits any non-nullable-value-type to be implicitly converted to the type + // object or System.ValueType or to any interface-type implemented by the value-type, and any enum + // type to be implicitly converted to System.Enum as well. Boxing a value of a + // non-nullable-value-type consists of allocating an object instance and copying the value-type + // value into that instance. An enum can be boxed to the type System.Enum, since that is the direct + // base class for all enums (21.4). A struct or enum can be boxed to the type System.ValueType, + // since that is the direct base class for all structs (18.3.2) and a base class for all enums. + + if (typeDest.IsAggregateType() && GetSymbolLoader().HasBaseConversion(aggTypeSrc, typeDest.AsAggregateType())) + { + if (needsExprDest) + binder.bindSimpleCast(exprSrc, exprTypeDest, out exprDest, EXPRFLAG.EXF_BOX | EXPRFLAG.EXF_CANTBENULL); + return true; + } + return false; + } + + private bool bindImplicitConversionToEnum(AggregateType aggTypeSrc) + { + // The spec states: + // ***************** + // 13.1.3 Implicit enumeration conversions + // + // An implicit enumeration conversion permits the decimal-integer-literal 0 to be converted to any + // enum-type. + // ***************** + // However, we actually allow any constant zero, not just the integer literal zero, to be converted + // to enum. The reason for this is for backwards compatibility with a premature optimization + // that used to be in the binding layer. We would optimize away expressions such as 0 | blah to be + // just 0, but not erase the "is literal" bit. This meant that expression such as 0 | 0 | E.X + // would succeed rather than correctly producing an error pointing out that 0 | 0 is not a literal + // zero and therefore does not convert to any enum. + // + // We have removed the premature optimization but want old code to continue to compile. Rather than + // try to emulate the somewhat complex behaviour of the previous optimizer, it is easier to simply + // say that any compile time constant zero is convertible to any enum. This means unfortunately + // expressions such as (7-7) * 12 are convertible to enum, but frankly, that's better than having + // some terribly complex rule about what constitutes a legal zero and what doesn't. + + // Note: Don't use GetConst here since the conversion only applies to bona-fide compile time constants. + if ( + aggTypeSrc.getAggregate().GetPredefType() != PredefinedType.PT_BOOL && + exprSrc != null && + exprSrc.isZero() && + exprSrc.type.isNumericType() && + /*(exprSrc.flags & EXF_LITERALCONST) &&*/ + 0 == (flags & CONVERTTYPE.STANDARD)) + { + // NOTE: This allows conversions from uint, long, ulong, float, double, and hexadecimal int + // NOTE: This is for backwards compatibility with Everett + + // REVIEW: : This is another place where we lose EXPR fidelity. We shouldn't fold this + // into a constant here - we should move this to a later pass. + if (needsExprDest) + { + exprDest = GetExprFactory().CreateConstant(typeDest, ConstValFactory.GetDefaultValue(typeDest.constValKind())); + } + return true; + } + return false; + } + + private bool bindImplicitConversionBetweenSimpleTypes(AggregateType aggTypeSrc) + { + AggregateSymbol aggSrc = aggTypeSrc.getAggregate(); + Debug.Assert(aggSrc.getThisType().isSimpleType()); + Debug.Assert(typeDest.isSimpleType()); + + Debug.Assert(aggSrc.IsPredefined() && typeDest.isPredefined()); + PredefinedType ptSrc = aggSrc.GetPredefType(); + PredefinedType ptDest = typeDest.getPredefType(); + ConvKind convertKind; + bool fConstShrinkCast = false; + + Debug.Assert((int)ptSrc < NUM_SIMPLE_TYPES && (int)ptDest < NUM_SIMPLE_TYPES); + + // 13.1.7 Implicit constant expression conversions + // + // An implicit constant expression conversion permits the following conversions: + // * A constant-expression (14.16) of type int can be converted to type sbyte, byte, short, + // ushort, uint, or ulong, provided the value of the constant-expression is within the range + // of the destination type. + // * A constant-expression of type long can be converted to type ulong, provided the value of + // the constant-expression is not negative. + // Note: Don't use GetConst here since the conversion only applies to bona-fide compile time constants. + if (exprSrc != null && exprSrc.isCONSTANT_OK() && + ((ptSrc == PredefinedType.PT_INT && ptDest != PredefinedType.PT_BOOL && ptDest != PredefinedType.PT_CHAR) || + (ptSrc == PredefinedType.PT_LONG && ptDest == PredefinedType.PT_ULONG)) && + isConstantInRange(exprSrc.asCONSTANT(), typeDest)) + { + // Special case (CLR 6.1.6): if integral constant is in range, the conversion is a legal implicit conversion. + convertKind = ConvKind.Implicit; + fConstShrinkCast = needsExprDest && (GetConvKind(ptSrc, ptDest) != ConvKind.Implicit); + } + else if (ptSrc == ptDest) + { + // Special case: precision limiting casts to float or double + Debug.Assert(ptSrc == PredefinedType.PT_FLOAT || ptSrc == PredefinedType.PT_DOUBLE); + Debug.Assert(0 != (flags & CONVERTTYPE.ISEXPLICIT)); + convertKind = ConvKind.Implicit; + } + else + { + convertKind = GetConvKind(ptSrc, ptDest); + Debug.Assert(convertKind != ConvKind.Identity); + // identity conversion should have been handled at first. + } + + if (convertKind != ConvKind.Implicit) + { + return false; + } + + // An implicit conversion exists. Do the conversion. + if (exprSrc.GetConst() != null) + { + // Fold the constant cast if possible. + ConstCastResult result = binder.bindConstantCast(exprSrc, exprTypeDest, needsExprDest, out exprDest, false); + if (result == ConstCastResult.Success) + { + return true; // else, don't fold and use a regular cast, below. + } + // REVIEW: I don't think this can ever be hit. If exprSrc is a constant then + // it's either a floating point number (which always succeeds), it's a numeric implicit + // conversion (which always succeeds), or it's a constant numeric conversion (which + // has already been checked by isConstantInRange, so should always succeed) + } + + if (isUserDefinedConversion(ptSrc, ptDest)) + { + if (!needsExprDest) + { + return true; + } + // According the language, this is a standard conversion, but it is implemented + // through a user-defined conversion. Because it's a standard conversion, we don't + // test the NOUDC flag here. + return binder.bindUserDefinedConversion(exprSrc, aggTypeSrc, typeDest, needsExprDest, out exprDest, true); + } + if (needsExprDest) + binder.bindSimpleCast(exprSrc, exprTypeDest, out exprDest); + return true; + } + + private bool bindImplicitConversionFromTypeVar(TypeParameterType tyVarSrc) + { + // 13.1.4 + // + // For a type-parameter T that is known to be a reference type (25.7), the following implicit + // reference conversions exist: + // + // * From T to its effective base class C, from T to any base class of C, and from T to any + // interface implemented by C. + // * From T to an interface-type I in T's effective interface set and from T to any base + // interface of I. + // * From T to a type parameter U provided that T depends on U (25.7). [Note: Since T is known + // to be a reference type, within the scope of T, the run-time type of U will always be a + // reference type, even if U is not known to be a reference type at compile-time.] + // * From the null type (11.2.7) to T. + // + // 13.1.5 + // + // For a type-parameter T that is not known to be a reference type (25.7), the following conversions + // involving T are considered to be boxing conversions at compile-time. At run-time, if T is a value + // type, the conversion is executed as a boxing conversion. At run-time, if T is a reference type, + // the conversion is executed as an implicit reference conversion or identity conversion. + // + // * From T to its effective base class C, from T to any base class of C, and from T to any + // interface implemented by C. [Note: C will be one of the types System.Object, System.ValueType, + // or System.Enum (otherwise T would be known to be a reference type and 13.1.4 would apply + // instead of this clause).] + // * From T to an interface-type I in T's effective interface set and from T to any base + // interface of I. + // + // 13.1.6 Implicit type parameter conversions + // + // This clause details implicit conversions involving type parameters that are not classified as + // implicit reference conversions or implicit boxing conversions. + // + // For a type-parameter T that is not known to be a reference type, there is an implicit conversion + // from T to a type parameter U provided T depends on U. At run-time, if T is a value type and U is + // a reference type, the conversion is executed as a boxing conversion. At run-time, if both T and U + // are value types, then T and U are necessarily the same type and no conversion is performed. At + // run-time, if T is a reference type, then U is necessarily also a reference type and the conversion + // is executed as an implicit reference conversion or identity conversion (25.7). + + CType typeTmp = tyVarSrc.GetEffectiveBaseClass(); + TypeArray bnds = tyVarSrc.GetBounds(); + int itype = -1; + for (; ; ) + { + if (binder.canConvert(typeTmp, typeDest, flags | CONVERTTYPE.NOUDC)) + { + if (!needsExprDest) + { + return true; + } + if (typeDest.IsTypeParameterType()) + { + // For a type var destination we need to cast to object then to the other type var. + EXPR exprT; + EXPRCLASS exprObj = GetExprFactory().MakeClass(binder.GetReqPDT(PredefinedType.PT_OBJECT)); + binder.bindSimpleCast(exprSrc, exprObj, out exprT, EXPRFLAG.EXF_FORCE_BOX); + binder.bindSimpleCast(exprT, exprTypeDest, out exprDest, EXPRFLAG.EXF_FORCE_UNBOX); + } + else + { + binder.bindSimpleCast(exprSrc, exprTypeDest, out exprDest, EXPRFLAG.EXF_FORCE_BOX); + } + return true; + } + do + { + if (++itype >= bnds.Size) + { + return false; + } + typeTmp = bnds.Item(itype); + } + while (!typeTmp.isInterfaceType() && !typeTmp.IsTypeParameterType()); + } + } + private SymbolLoader GetSymbolLoader() + { + return binder.GetSymbolLoader(); + } + private ExprFactory GetExprFactory() + { + return binder.GetExprFactory(); + } + private ErrorHandling GetErrorContext() + { + return binder.GetErrorContext(); + } + } + } +} diff --git a/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/InputFile.cs b/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/InputFile.cs new file mode 100644 index 000000000..4003a76c4 --- /dev/null +++ b/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/InputFile.cs @@ -0,0 +1,84 @@ +// ==++== +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// ==--== + +using System.Collections.Generic; +using System.Diagnostics; +using Microsoft.CSharp.RuntimeBinder.Syntax; + +namespace Microsoft.CSharp.RuntimeBinder.Semantics +{ + // ---------------------------------------------------------------------------- + // + // InputFile + // + // InputFile - a symbol that represents an input file, either source + // code or meta-data, of a file we may read. Its parent is the output + // file it contributes to. MetaData files have no parent. + // This should be split in to two classes, one for metadata files + // and another for source files. + // ---------------------------------------------------------------------------- + + class InputFile : FileRecord + { + // Which aliases this INFILE is in. For source INFILESYMs, only bits kaidThisAssembly and kaidGlobal + // should be set. + private HashSet bsetFilter; + private KAID aid; + //#if DEBUG + // private bool fUnionCalled; + //#endif + + public bool isSource; // If true, source code, if false, metadata + // and on the module of added .netmodules + + public InputFile() + { + bsetFilter = new HashSet(); + } + + public void SetAssemblyID(KAID aid) + { + Debug.Assert(this.aid == default(KAID)); + Debug.Assert(KAID.kaidThisAssembly <= aid && aid < KAID.kaidMinModule); + + this.aid = aid; + bsetFilter.Add(aid); + if (aid == KAID.kaidThisAssembly) + bsetFilter.Add(KAID.kaidGlobal); + } + + public void AddToAlias(KAID aid) + { + Debug.Assert(0 <= aid && aid < KAID.kaidMinModule); + + // NOTE: Anything in this assembly should not be added to other aliases! + Debug.Assert(this.aid > KAID.kaidThisAssembly); + Debug.Assert(bsetFilter.Contains(this.aid)); + + bsetFilter.Add(aid); + } + + public void UnionAliasFilter(ref HashSet bsetDst) + { + bsetDst.UnionWith(bsetFilter); + // #if DEBUG + // fUnionCalled = true; + // #endif + } + + public KAID GetAssemblyID() + { + Debug.Assert(aid >= KAID.kaidThisAssembly); + return aid; + } + + public bool InAlias(KAID aid) + { + Debug.Assert(0 <= aid); + return bsetFilter.Contains(aid); + } + } +} \ No newline at end of file diff --git a/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/Interfaces/ITypeOrNamespace.cs b/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/Interfaces/ITypeOrNamespace.cs new file mode 100644 index 000000000..4c62b0b79 --- /dev/null +++ b/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/Interfaces/ITypeOrNamespace.cs @@ -0,0 +1,20 @@ +// ==++== +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// ==--== + +namespace Microsoft.CSharp.RuntimeBinder.Semantics +{ + ///////////////////////////////////////////////////////////////////////////////// + // This is the base interface that Type and Namespace symbol inherit. + + interface ITypeOrNamespace + { + bool IsType(); + bool IsNamespace(); + + AssemblyQualifiedNamespaceSymbol AsNamespace(); + CType AsType(); + } +} diff --git a/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/LangCompiler.cs b/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/LangCompiler.cs new file mode 100644 index 000000000..4a1d72f3b --- /dev/null +++ b/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/LangCompiler.cs @@ -0,0 +1,75 @@ +// ==++== +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// ==--== + +using System.Diagnostics; +using Microsoft.CSharp.RuntimeBinder.Errors; +using Microsoft.CSharp.RuntimeBinder.Syntax; + +namespace Microsoft.CSharp.RuntimeBinder.Semantics +{ + internal class LangCompiler : + CSemanticChecker, + IErrorSink + { + private SymbolLoader m_symbolLoader; + private CController pController; // This is our parent "controller" + private ErrorHandling m_errorContext; + private GlobalSymbolContext globalSymbolContext; + private UserStringBuilder m_userStringBuilder; + + //////////////////////////////////////////////////////////////////////////////// + // Construct a compiler. All the real work is done in the Init() routine. This + // primary initializes all the sub-components. + + public LangCompiler(CController pCtrl, NameManager pNameMgr) + { + Debug.Assert(pCtrl != null); + + pController = pCtrl; + globalSymbolContext = new GlobalSymbolContext(pNameMgr); + m_userStringBuilder = new UserStringBuilder(globalSymbolContext); + m_errorContext = new ErrorHandling(m_userStringBuilder, this, pCtrl.GetErrorFactory()); + m_symbolLoader = new SymbolLoader(globalSymbolContext, null, m_errorContext); + } + + public new ErrorHandling GetErrorContext() + { + return m_errorContext; + } + + public override SymbolLoader SymbolLoader { get { return m_symbolLoader; } } + public override SymbolLoader GetSymbolLoader() { return m_symbolLoader; } + + //////////////////////////////////////////////////////////////////////////////// + // Searches the class [atsSearch] to see if it contains a method which is + // sufficient to implement [mwt]. Does not search base classes. [mwt] is + // typically a method in some interface. We may be implementing this interface + // at some particular type, e.g. IList, and so the required signature is + // the instantiation (i.e. substitution) of [mwt] for that instance. Similarly, + // the implementation may be provided by some base class that exists via + // polymorphic inheritance, e.g. Foo : List, and so we must instantiate + // the parameters for each potential implementation. [atsSearch] may thus be an + // instantiated type. + // + // If fOverride is true, this checks for a method with swtSlot set to the + // particular method. + public void SubmitError(CParameterizedError error) + { + CError pError = GetErrorContext().RealizeError(error); + + if (pError == null) + { + return; + } + pController.SubmitError(pError); + } + + public int ErrorCount() + { + return 0; + } + } +} diff --git a/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/MemberLookup.cs b/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/MemberLookup.cs new file mode 100644 index 000000000..e24e1b841 --- /dev/null +++ b/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/MemberLookup.cs @@ -0,0 +1,847 @@ +// ==++== +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// ==--== + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using Microsoft.CSharp.RuntimeBinder.Errors; +using Microsoft.CSharp.RuntimeBinder.Syntax; + +namespace Microsoft.CSharp.RuntimeBinder.Semantics +{ + internal enum MemLookFlags : uint + { + None = 0, + + Ctor = EXPRFLAG.EXF_CTOR, + NewObj = EXPRFLAG.EXF_NEWOBJCALL, + Operator = EXPRFLAG.EXF_OPERATOR, + Indexer = EXPRFLAG.EXF_INDEXER, + UserCallable = EXPRFLAG.EXF_USERCALLABLE, + BaseCall = EXPRFLAG.EXF_BASECALL, + + // All EXF flags are < 0x01000000 + MustBeInvocable = 0x20000000, + TypeVarsAllowed = 0x40000000, + ExtensionCall = 0x80000000, + + All = Ctor | NewObj | Operator | Indexer | UserCallable | BaseCall | MustBeInvocable | TypeVarsAllowed | ExtensionCall + } + + ///////////////////////////////////////////////////////////////////////////////// + // MemberLookup class handles looking for a member within a type and its + // base types. This only handles AGGTYPESYMs and TYVARSYMs. + // + // Lookup must be called before any other methods. + + internal class MemberLookup + { + // The inputs to Lookup. + private CSemanticChecker m_pSemanticChecker; + private SymbolLoader m_pSymbolLoader; + private CType m_typeSrc; + private EXPR m_obj; + private CType m_typeQual; + private ParentSymbol m_symWhere; + private Name m_name; + private int m_arity; + private MemLookFlags m_flags; + private CMemberLookupResults m_results; + + // For maintaining the type array. We throw the first 8 or so here. + private List m_rgtypeStart; + + // Results of the lookup. + private List m_prgtype; + private int m_csym; // Number of syms found. + private SymWithType m_swtFirst; // The first symbol found. + private List m_methPropWithTypeList; // When we look up methods, we want to keep the list of all candidate methods given a particular name. + + // These are for error reporting. + private SymWithType m_swtAmbig; // An ambiguous symbol. + private SymWithType m_swtInaccess; // An inaccessible symbol. + private SymWithType m_swtBad; // If we're looking for a ructor or indexer, this matched on name, but isn't the right thing. + private SymWithType m_swtBogus; // A bogus member - such as an indexed property. + private SymWithType m_swtBadArity; // An symbol with the wrong arity. + private SymWithType m_swtAmbigWarn; // An ambiguous symbol, but only warn. + + // We have an override symbol, which we've errored on in SymbolPrepare. If we have nothing better, use this. + // This is because if we have: + // + // class C : D + // { + // public override int M() { } + // static void Main() + // { + // C c = new C(); + // c.M(); <-- + // + // We try to look up M, and find the M on C, but throw it out since its an override, and + // we want the virtual that it overrides. However, in this case, we'll never find that + // virtual, since it doesn't exist. We therefore want to use the override anyway, and + // continue on to give results with that. + + private SymWithType m_swtOverride; + private bool m_fMulti; // Whether symFirst is of a kind for which we collect multiples (methods and indexers). + + /*************************************************************************************************** + Another match was found. Increment the count of syms and add the type to our list if it's not + already there. + ***************************************************************************************************/ + private void RecordType(AggregateType type, Symbol sym) + { + Debug.Assert(type != null && sym != null); + + if (!m_prgtype.Contains(type)) + { + m_prgtype.Add(type); + } + + // Now record the sym.... + + m_csym++; + + // If it is first, record it. + if (m_swtFirst == null) + { + m_swtFirst.Set(sym, type); + Debug.Assert(m_csym == 1); + Debug.Assert(m_prgtype[0] == type); + m_fMulti = sym.IsMethodSymbol() || sym.IsPropertySymbol() && sym.AsPropertySymbol().isIndexer(); + } + } + + /****************************************************************************** + Search just the given type (not any bases). Returns true iff it finds + something (which will have been recorded by RecordType). + + pfHideByName is set to true iff something was found that hides all + members of base types (eg, a hidebyname method). + ******************************************************************************/ + private bool SearchSingleType(AggregateType typeCur, out bool pfHideByName) + { + bool fFoundSome = false; + MethPropWithType mwpInsert; + + pfHideByName = false; + + // Make sure this type is accessible. It may not be due to private inheritance + // or friend assemblies. + bool fInaccess = !GetSemanticChecker().CheckTypeAccess(typeCur, m_symWhere); + if (fInaccess && (m_csym != 0 || m_swtInaccess != null)) + return false; + + // Loop through symbols. + Symbol symCur = null; + for (symCur = GetSymbolLoader().LookupAggMember(m_name, typeCur.getAggregate(), symbmask_t.MASK_ALL); + symCur != null; + symCur = GetSymbolLoader().LookupNextSym(symCur, typeCur.getAggregate(), symbmask_t.MASK_ALL)) + { + // Check for arity. + switch (symCur.getKind()) + { + case SYMKIND.SK_MethodSymbol: + // For non-zero arity, only methods of the correct arity are considered. + // For zero arity, don't filter out any methods since we do type argument + // inferencing. + if (m_arity > 0 && symCur.AsMethodSymbol().typeVars.size != m_arity) + { + if (!m_swtBadArity) + m_swtBadArity.Set(symCur, typeCur); + continue; + } + break; + + case SYMKIND.SK_AggregateSymbol: + // For types, always filter on arity. + if (symCur.AsAggregateSymbol().GetTypeVars().size != m_arity) + { + if (!m_swtBadArity) + m_swtBadArity.Set(symCur, typeCur); + continue; + } + break; + + case SYMKIND.SK_TypeParameterSymbol: + if ((m_flags & MemLookFlags.TypeVarsAllowed) == 0) + continue; + if (m_arity > 0) + { + if (!m_swtBadArity) + m_swtBadArity.Set(symCur, typeCur); + continue; + } + break; + + default: + // All others are only considered when arity is zero. + if (m_arity > 0) + { + if (!m_swtBadArity) + m_swtBadArity.Set(symCur, typeCur); + continue; + } + break; + } + + // Check for user callability. + if (symCur.IsOverride() && !symCur.IsHideByName()) + { + if (!m_swtOverride) + { + m_swtOverride.Set(symCur, typeCur); + } + continue; + } + if ((m_flags & MemLookFlags.UserCallable) != 0 && symCur.IsMethodOrPropertySymbol() && !symCur.AsMethodOrPropertySymbol().isUserCallable()) + { + bool bIsIndexedProperty = false; + // If its an indexed property method symbol, let it through. + if (symCur.IsMethodSymbol() && + symCur.AsMethodSymbol().isPropertyAccessor() && + ((symCur.name.Text.StartsWith("set_", StringComparison.Ordinal) && symCur.AsMethodSymbol().Params.size > 1) || + (symCur.name.Text.StartsWith("get_", StringComparison.Ordinal) && symCur.AsMethodSymbol().Params.size > 0))) + { + bIsIndexedProperty = true; + } + + if (!bIsIndexedProperty) + { + if (!m_swtInaccess) + { + m_swtInaccess.Set(symCur, typeCur); + } + continue; + } + } + + if (fInaccess || !GetSemanticChecker().CheckAccess(symCur, typeCur, m_symWhere, m_typeQual)) + { + // Not accessible so get the next sym. + if (!m_swtInaccess) + { + m_swtInaccess.Set(symCur, typeCur); + } + if (fInaccess) + { + return false; + } + continue; + } + + // Make sure that whether we're seeing a ctor, operator, or indexer is consistent with the flags. + if (((m_flags & MemLookFlags.Ctor) == 0) != (!symCur.IsMethodSymbol() || !symCur.AsMethodSymbol().IsConstructor()) || + ((m_flags & MemLookFlags.Operator) == 0) != (!symCur.IsMethodSymbol() || !symCur.AsMethodSymbol().isOperator) || + ((m_flags & MemLookFlags.Indexer) == 0) != (!symCur.IsPropertySymbol() || !symCur.AsPropertySymbol().isIndexer())) + { + if (!m_swtBad) + { + m_swtBad.Set(symCur, typeCur); + } + continue; + } + + // We can't call CheckBogus on methods or indexers because if the method has the wrong + // number of parameters people don't think they should have to /r the assemblies containing + // the parameter types and they complain about the resulting CS0012 errors. + if (!symCur.IsMethodSymbol() && (m_flags & MemLookFlags.Indexer) == 0 && GetSemanticChecker().CheckBogus(symCur)) + { + // A bogus member - we can't use these, so only record them for error reporting. + if (!m_swtBogus) + { + m_swtBogus.Set(symCur, typeCur); + } + continue; + } + + // if we are in a calling context then we should only find a property if it is delegate valued + if ((m_flags & MemLookFlags.MustBeInvocable) != 0) + { + if ((symCur.IsFieldSymbol() && !IsDelegateType(symCur.AsFieldSymbol().GetType(), typeCur) && !IsDynamicMember(symCur)) || + (symCur.IsPropertySymbol() && !IsDelegateType(symCur.AsPropertySymbol().RetType, typeCur) && !IsDynamicMember(symCur))) + { + if (!m_swtBad) + { + m_swtBad.Set(symCur, typeCur); + } + continue; + } + } + + if (symCur.IsMethodOrPropertySymbol()) + { + mwpInsert = new MethPropWithType(symCur.AsMethodOrPropertySymbol(), typeCur); + m_methPropWithTypeList.Add(mwpInsert); + } + + // We have a visible symbol. + fFoundSome = true; + + if (m_swtFirst) + { + if (!typeCur.isInterfaceType()) + { + // Non-interface case. + Debug.Assert(m_fMulti || typeCur == m_prgtype[0]); + if (!m_fMulti) + { + if (m_swtFirst.Sym.IsFieldSymbol() && symCur.IsEventSymbol() +#if !CSEE // The isEvent bit is only set on symbols which come from source... + // This is not a problem for the compiler because the field is only + // accessible in the scope in whcih it is declared, + // but in the EE we ignore accessibility... + && m_swtFirst.Field().isEvent +#endif +) + { + // m_swtFirst is just the field behind the event symCur so ignore symCur. + continue; + } + else if (m_swtFirst.Sym.IsFieldSymbol() && symCur.IsEventSymbol()) + { + // symCur is the matching event. + continue; + } + goto LAmbig; + } + if (m_swtFirst.Sym.getKind() != symCur.getKind()) + { + if (typeCur == m_prgtype[0]) + goto LAmbig; + // This one is hidden by the first one. This one also hides any more in base types. + pfHideByName = true; + continue; + } + } + // Interface case. + // m_fMulti : n n n y y y y y + // same-kind : * * * y n n n n + // fDiffHidden: * * * * y n n n + // meth : * * * * * y n * can n happen? just in case, we better handle it.... + // hack : n * y * * y * n + // meth-2 : * n y * * * * * + // res : A A S R H H A A + else if (!m_fMulti) + { + // Give method groups priority. See Whidbey bug #323923. + if ( /* !GetSymbolLoader().options.fLookupHack ||*/ !symCur.IsMethodSymbol()) + goto LAmbig; + m_swtAmbigWarn = m_swtFirst; + // Erase previous results so we'll record this method as the first. + m_prgtype = new List(); + m_csym = 0; + m_swtFirst.Clear(); + m_swtAmbig.Clear(); + } + else if (m_swtFirst.Sym.getKind() != symCur.getKind()) + { + if (!typeCur.fDiffHidden) + { + // Give method groups priority. See Whidbey bug #323923. + if ( /*!GetSymbolLoader().options.fLookupHack ||*/ !m_swtFirst.Sym.IsMethodSymbol()) + goto LAmbig; + if (!m_swtAmbigWarn) + m_swtAmbigWarn.Set(symCur, typeCur); + } + // This one is hidden by another. This one also hides any more in base types. + pfHideByName = true; + continue; + } + } + + RecordType(typeCur, symCur); + + if (symCur.IsMethodOrPropertySymbol() && symCur.AsMethodOrPropertySymbol().isHideByName) + pfHideByName = true; + + // We've found a symbol in this type but need to make sure there aren't any conflicting + // syms here, so keep searching the type. + } + + Debug.Assert(!fInaccess || !fFoundSome); + + return fFoundSome; + + LAmbig: + // Ambiguous! + if (!m_swtAmbig) + m_swtAmbig.Set(symCur, typeCur); + pfHideByName = true; + return true; + } + + private bool IsDynamicMember(Symbol sym) + { + System.Runtime.CompilerServices.DynamicAttribute da = null; + if (sym.IsFieldSymbol()) + { + if (!sym.AsFieldSymbol().getType().isPredefType(PredefinedType.PT_OBJECT)) + { + return false; + } + object[] o = sym.AsFieldSymbol().AssociatedFieldInfo.GetCustomAttributes(typeof(System.Runtime.CompilerServices.DynamicAttribute), false); + if (o.Length == 1) + { + da = o[0] as System.Runtime.CompilerServices.DynamicAttribute; + } + } + else + { + Debug.Assert(sym.IsPropertySymbol()); + if (!sym.AsPropertySymbol().getType().isPredefType(PredefinedType.PT_OBJECT)) + { + return false; + } + object[] o = sym.AsPropertySymbol().AssociatedPropertyInfo.GetCustomAttributes(typeof(System.Runtime.CompilerServices.DynamicAttribute), false); + if (o.Length == 1) + { + da = o[0] as System.Runtime.CompilerServices.DynamicAttribute; + } + } + + if (da == null) + { + return false; + } + return (da.TransformFlags.Count == 0 || (da.TransformFlags.Count == 1 && da.TransformFlags[0])); + } + + /****************************************************************************** + Lookup in a class and its bases (until *ptypeEnd is hit). + + ptypeEnd [in/out] - *ptypeEnd should be either null or object. If we find + something here that would hide members of object, this sets *ptypeEnd + to null. + + Returns true when searching should continue to the interfaces. + ******************************************************************************/ + private bool LookupInClass(AggregateType typeStart, ref AggregateType ptypeEnd) + { + Debug.Assert(!m_swtFirst || m_fMulti); + Debug.Assert(typeStart != null && !typeStart.isInterfaceType() && (ptypeEnd == null || typeStart != ptypeEnd)); + + AggregateType typeEnd = ptypeEnd; + AggregateType typeCur; + + // Loop through types. Loop until we hit typeEnd (object or null). + for (typeCur = typeStart; typeCur != typeEnd && typeCur != null; typeCur = typeCur.GetBaseClass()) + { + Debug.Assert(!typeCur.isInterfaceType()); + + bool fHideByName = false; + + SearchSingleType(typeCur, out fHideByName); + m_flags &= ~MemLookFlags.TypeVarsAllowed; + + if (m_swtFirst && !m_fMulti) + { + // Everything below this type and in interfaces is hidden. + return false; + } + + if (fHideByName) + { + // This hides everything below it and in object, but not in the interfaces! + ptypeEnd = null; + + // Return true to indicate that it's ok to search additional types. + return true; + } + + if ((m_flags & MemLookFlags.Ctor) != 0) + { + // If we're looking for a constructor, don't check base classes or interfaces. + return false; + } + } + + Debug.Assert(typeCur == typeEnd); + return true; + } + + /****************************************************************************** + Returns true if searching should continue to object. + ******************************************************************************/ + private bool LookupInInterfaces(AggregateType typeStart, TypeArray types) + { + Debug.Assert(!m_swtFirst || m_fMulti); + Debug.Assert(typeStart == null || typeStart.isInterfaceType()); + Debug.Assert(typeStart != null || types.size != 0); + + // Clear all the hidden flags. Anything found in a class hides any other + // kind of member in all the interfaces. + if (typeStart != null) + { + typeStart.fAllHidden = false; + typeStart.fDiffHidden = (m_swtFirst != null); + } + + for (int i = 0; i < types.size; i++) + { + AggregateType type = types.Item(i).AsAggregateType(); + Debug.Assert(type.isInterfaceType()); + type.fAllHidden = false; + type.fDiffHidden = !!m_swtFirst; + } + + bool fHideObject = false; + AggregateType typeCur = typeStart; + int itypeNext = 0; + + if (typeCur == null) + { + typeCur = types.Item(itypeNext++).AsAggregateType(); + } + Debug.Assert(typeCur != null); + + // Loop through the interfaces. + for (; ; ) + { + Debug.Assert(typeCur != null && typeCur.isInterfaceType()); + + bool fHideByName = false; + + if (!typeCur.fAllHidden && SearchSingleType(typeCur, out fHideByName)) + { + fHideByName |= !m_fMulti; + + // Mark base interfaces appropriately. + TypeArray ifaces = typeCur.GetIfacesAll(); + for (int i = 0; i < ifaces.size; i++) + { + AggregateType type = ifaces.Item(i).AsAggregateType(); + Debug.Assert(type.isInterfaceType()); + if (fHideByName) + type.fAllHidden = true; + type.fDiffHidden = true; + } + + // If we hide all base types, that includes object! + if (fHideByName) + fHideObject = true; + } + m_flags &= ~MemLookFlags.TypeVarsAllowed; + + if (itypeNext >= types.size) + return !fHideObject; + + // Substitution has already been done. + typeCur = types.Item(itypeNext++).AsAggregateType(); + } + } + + private SymbolLoader GetSymbolLoader() { return m_pSymbolLoader; } + private CSemanticChecker GetSemanticChecker() { return m_pSemanticChecker; } + private ErrorHandling GetErrorContext() { return GetSymbolLoader().GetErrorContext(); } + + private void ReportBogus(SymWithType swt) + { + Debug.Assert(swt.Sym.hasBogus() && swt.Sym.checkBogus()); + + MethodSymbol meth1; + MethodSymbol meth2; + + switch (swt.Sym.getKind()) + { + case SYMKIND.SK_EventSymbol: + break; + + case SYMKIND.SK_PropertySymbol: + if (swt.Prop().useMethInstead) + { + meth1 = swt.Prop().methGet; + meth2 = swt.Prop().methSet; + ReportBogusForEventsAndProperties(swt, meth1, meth2); + return; + } + break; + + case SYMKIND.SK_MethodSymbol: + if (swt.Meth().name == GetSymbolLoader().GetNameManager().GetPredefName(PredefinedName.PN_INVOKE) && swt.Meth().getClass().IsDelegate()) + { + swt.Set(swt.Meth().getClass(), swt.GetType()); + } + break; + + default: + break; + } + + // Generic bogus error. + GetErrorContext().ErrorRef(ErrorCode.ERR_BindToBogus, swt); + } + + private void ReportBogusForEventsAndProperties(SymWithType swt, MethodSymbol meth1, MethodSymbol meth2) + { + if (meth1 != null && meth2 != null) + { + GetErrorContext().Error(ErrorCode.ERR_BindToBogusProp2, swt.Sym.name, new SymWithType(meth1, swt.GetType()), new SymWithType(meth2, swt.GetType()), new ErrArgRefOnly(swt.Sym)); + return; + } + if (meth1 != null || meth2 != null) + { + GetErrorContext().Error(ErrorCode.ERR_BindToBogusProp1, swt.Sym.name, new SymWithType(meth1 != null ? meth1 : meth2, swt.GetType()), new ErrArgRefOnly(swt.Sym)); + return; + } + throw Error.InternalCompilerError(); + } + + private bool IsDelegateType(CType pSrcType, AggregateType pAggType) + { + CType pInstantiatedType = GetSymbolLoader().GetTypeManager().SubstType(pSrcType, pAggType, pAggType.GetTypeArgsAll()); + return pInstantiatedType.isDelegateType(); + } + + ///////////////////////////////////////////////////////////////////////////////// + // Public methods. + + public MemberLookup() + { + m_methPropWithTypeList = new List(); + m_rgtypeStart = new List(); + m_swtFirst = new SymWithType(); + m_swtAmbig = new SymWithType(); + m_swtInaccess = new SymWithType(); + m_swtBad = new SymWithType(); + m_swtBogus = new SymWithType(); + m_swtBadArity = new SymWithType(); + m_swtAmbigWarn = new SymWithType(); + m_swtOverride = new SymWithType(); + } + + /*************************************************************************************************** + Lookup must be called before anything else can be called. + + typeSrc - Must be an AggregateType or TypeParameterType. + obj - the expression through which the member is being accessed. This is used for accessibility + of protected members and for constructing a MEMGRP from the results of the lookup. + It is legal for obj to be an EK_CLASS, in which case it may be used for accessibility, but + will not be used for MEMGRP construction. + symWhere - the symbol from with the name is being accessed (for checking accessibility). + name - the name to look for. + arity - the number of type args specified. Only members that support this arity are found. + Note that when arity is zero, all methods are considered since we do type argument + inferencing. + + flags - See MemLookFlags. + TypeVarsAllowed only applies to the most derived type (not base types). + ***************************************************************************************************/ + public bool Lookup(CSemanticChecker checker, CType typeSrc, EXPR obj, ParentSymbol symWhere, Name name, int arity, MemLookFlags flags) + { + Debug.Assert((flags & ~MemLookFlags.All) == 0); + Debug.Assert(obj == null || obj.type != null); + Debug.Assert(typeSrc.IsAggregateType() || typeSrc.IsTypeParameterType()); + Debug.Assert(checker != null); + + m_prgtype = m_rgtypeStart; + + // Save the inputs for error handling, etc. + m_pSemanticChecker = checker; + m_pSymbolLoader = checker.GetSymbolLoader(); + m_typeSrc = typeSrc; + m_obj = (obj != null && !obj.isCLASS()) ? obj : null; + m_symWhere = symWhere; + m_name = name; + m_arity = arity; + m_flags = flags; + + if ((m_flags & MemLookFlags.BaseCall) != 0) + m_typeQual = null; + else if ((m_flags & MemLookFlags.Ctor) != 0) + m_typeQual = m_typeSrc; + else if (obj != null) + m_typeQual = (CType)obj.type; + else + m_typeQual = null; + + // Determine what to search. + AggregateType typeCls1 = null; + AggregateType typeIface = null; + TypeArray ifaces = BSYMMGR.EmptyTypeArray(); + AggregateType typeCls2 = null; + + if (typeSrc.IsTypeParameterType()) + { + Debug.Assert((m_flags & (MemLookFlags.Ctor | MemLookFlags.NewObj | MemLookFlags.Operator | MemLookFlags.BaseCall | MemLookFlags.TypeVarsAllowed)) == 0); + m_flags &= ~MemLookFlags.TypeVarsAllowed; + ifaces = typeSrc.AsTypeParameterType().GetInterfaceBounds(); + typeCls1 = typeSrc.AsTypeParameterType().GetEffectiveBaseClass(); + if (ifaces.size > 0 && typeCls1.isPredefType(PredefinedType.PT_OBJECT)) + typeCls1 = null; + } + else if (!typeSrc.isInterfaceType()) + { + typeCls1 = typeSrc.AsAggregateType(); + + if (typeCls1.IsWindowsRuntimeType()) + { + ifaces = typeCls1.GetWinRTCollectionIfacesAll(GetSymbolLoader()); + } + } + else + { + Debug.Assert(typeSrc.isInterfaceType()); + Debug.Assert((m_flags & (MemLookFlags.Ctor | MemLookFlags.NewObj | MemLookFlags.Operator | MemLookFlags.BaseCall)) == 0); + typeIface = typeSrc.AsAggregateType(); + ifaces = typeIface.GetIfacesAll(); + } + + if (typeIface != null || ifaces.size > 0) + typeCls2 = GetSymbolLoader().GetReqPredefType(PredefinedType.PT_OBJECT); + + // Search the class first (except possibly object). + if (typeCls1 == null || LookupInClass(typeCls1, ref typeCls2)) + { + // Search the interfaces. + if ((typeIface != null || ifaces.size > 0) && LookupInInterfaces(typeIface, ifaces) && typeCls2 != null) + { + // Search object last. + Debug.Assert(typeCls2 != null && typeCls2.isPredefType(PredefinedType.PT_OBJECT)); + + AggregateType result = null; + LookupInClass(typeCls2, ref result); + } + } + + // if we are reqested with extension methods + m_results = new CMemberLookupResults(GetAllTypes(), m_name); + + return !FError(); + } + + public CMemberLookupResults GetResults() + { + return m_results; + } + + // Whether there were errors. + public bool FError() + { + return !m_swtFirst || m_swtAmbig; + } + + // The first symbol found. + public Symbol SymFirst() + { + return m_swtFirst.Sym; + } + public SymWithType SwtFirst() + { + return m_swtFirst; + } + public SymWithType SwtInaccessible() + { + return m_swtInaccess; + } + + public EXPR GetObject() + { + return m_obj; + } + + public CType GetSourceType() + { + return m_typeSrc; + } + + public MemLookFlags GetFlags() + { + return m_flags; + } + + // Put all the types in a type array. + public TypeArray GetAllTypes() + { + return GetSymbolLoader().getBSymmgr().AllocParams(m_prgtype.Count, m_prgtype.ToArray()); + } + + /****************************************************************************** + Reports errors. Only call this if FError() is true. + ******************************************************************************/ + public void ReportErrors() + { + Debug.Assert(FError()); + + // Report error. + // NOTE: If the definition of FError changes, this code will need to change. + Debug.Assert(!m_swtFirst || m_swtAmbig); + + if (m_swtFirst) + { + // Ambiguous lookup. + GetErrorContext().ErrorRef(ErrorCode.ERR_AmbigMember, m_swtFirst, m_swtAmbig); + } + else if (m_swtInaccess) + { + if (!m_swtInaccess.Sym.isUserCallable() && ((m_flags & MemLookFlags.UserCallable) != 0)) + GetErrorContext().Error(ErrorCode.ERR_CantCallSpecialMethod, m_swtInaccess); + else + GetSemanticChecker().ReportAccessError(m_swtInaccess, m_symWhere, m_typeQual); + } + else if ((m_flags & MemLookFlags.Ctor) != 0) + { + if (m_arity > 0) + { + GetErrorContext().Error(ErrorCode.ERR_BadCtorArgCount, m_typeSrc.getAggregate(), m_arity); + } + else + { + GetErrorContext().Error(ErrorCode.ERR_NoConstructors, m_typeSrc.getAggregate()); + } + } + else if ((m_flags & MemLookFlags.Operator) != 0) + { + // REVIEW : Will we ever get here? Normally UD op errors are reported elsewhere.... + // This is a bogus message in any event. + GetErrorContext().Error(ErrorCode.ERR_NoSuchMember, m_typeSrc, m_name); + } + else if ((m_flags & MemLookFlags.Indexer) != 0) + { + GetErrorContext().Error(ErrorCode.ERR_BadIndexLHS, m_typeSrc); + } + else if (m_swtBad) + { + GetErrorContext().Error((m_flags & MemLookFlags.MustBeInvocable) != 0 ? ErrorCode.ERR_NonInvocableMemberCalled : ErrorCode.ERR_CantCallSpecialMethod, m_swtBad); + } + else if (m_swtBogus) + { + ReportBogus(m_swtBogus); + } + else if (m_swtBadArity) + { + int cvar; + + switch (m_swtBadArity.Sym.getKind()) + { + case SYMKIND.SK_MethodSymbol: + Debug.Assert(m_arity != 0); + cvar = m_swtBadArity.Sym.AsMethodSymbol().typeVars.size; + GetErrorContext().ErrorRef(cvar > 0 ? ErrorCode.ERR_BadArity : ErrorCode.ERR_HasNoTypeVars, m_swtBadArity, new ErrArgSymKind(m_swtBadArity.Sym), cvar); + break; + case SYMKIND.SK_AggregateSymbol: + cvar = m_swtBadArity.Sym.AsAggregateSymbol().GetTypeVars().size; + GetErrorContext().ErrorRef(cvar > 0 ? ErrorCode.ERR_BadArity : ErrorCode.ERR_HasNoTypeVars, m_swtBadArity, new ErrArgSymKind(m_swtBadArity.Sym), cvar); + break; + default: + Debug.Assert(m_arity != 0); + ExpressionBinder.ReportTypeArgsNotAllowedError(GetSymbolLoader(), m_arity, m_swtBadArity, new ErrArgSymKind(m_swtBadArity.Sym)); + break; + } + } + else + { + if ((m_flags & MemLookFlags.ExtensionCall) != 0) + { + GetErrorContext().Error(ErrorCode.ERR_NoSuchMemberOrExtension, m_typeSrc, m_name); + } + else + { + GetErrorContext().Error(ErrorCode.ERR_NoSuchMember, m_typeSrc, m_name); + } + } + } + } +} diff --git a/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/MemberLookupResults.cs b/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/MemberLookupResults.cs new file mode 100644 index 000000000..ca9bb140f --- /dev/null +++ b/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/MemberLookupResults.cs @@ -0,0 +1,53 @@ +// ==++== +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// ==--== + +using System.Diagnostics; +using Microsoft.CSharp.RuntimeBinder.Syntax; + +namespace Microsoft.CSharp.RuntimeBinder.Semantics +{ + // ---------------------------------------------------------------------------- + // This class encapsulates the results of member lookup, allowing the consumers + // to get at the inaccessible symbols, bogus symbols, and validly bound symbols. + // ---------------------------------------------------------------------------- + + internal partial class CMemberLookupResults + { + public TypeArray ContainingTypes { get; private set; }// Types that contain the member we're looking for. + + private Name m_pName; // The name that we're looking for. + + public CMemberLookupResults() + { + m_pName = null; + ContainingTypes = null; + } + + public CMemberLookupResults( + TypeArray containingTypes, + Name name) + { + m_pName = name; + ContainingTypes = containingTypes; + if (ContainingTypes == null) + { + ContainingTypes = BSYMMGR.EmptyTypeArray(); + } + } + + public CMethodIterator GetMethodIterator(// TODO: Temporary until we move extension method reporting to a later time. + CSemanticChecker pChecker, SymbolLoader pSymLoader, CType pObject, CType pQualifyingType, Declaration pContext, bool allowBogusAndInaccessible, bool allowExtensionMethods, int arity, EXPRFLAG flags, symbmask_t mask) + { + Debug.Assert(pSymLoader != null); + CMethodIterator iterator = new CMethodIterator(pChecker, pSymLoader, m_pName, ContainingTypes, pObject, pQualifyingType, pContext, allowBogusAndInaccessible, allowExtensionMethods, arity, flags, mask); + return iterator; + } + + public partial class CMethodIterator + { + } + } +} diff --git a/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/MetadataToken.cs b/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/MetadataToken.cs new file mode 100644 index 000000000..ffb1ef58e --- /dev/null +++ b/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/MetadataToken.cs @@ -0,0 +1,99 @@ +// ==++== +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// ==--== + +namespace Microsoft.CSharp.RuntimeBinder.Semantics +{ + // Token definitions + using mdModule = mdToken; // Module token (roughly, a scope) + using mdTypeRef = mdToken; // TypeRef reference (this or other scope) + using mdTypeDef = mdToken; // TypeDef in this scope + using mdFieldDef = mdToken; // Field in this scope + using mdMethodDef = mdToken; // Method in this scope + using mdParamDef = mdToken; // param token + using mdInterfaceImpl = mdToken; // interface implementation token + + using mdMemberRef = mdToken; // MemberRef (this or other scope) + using mdCustomAttribute = mdToken; // attribute token + using mdPermission = mdToken; // DeclSecurity + + using mdSignature = mdToken; // Signature object + using mdEvent = mdToken; // event token + using mdProperty = mdToken; // property token + + using mdModuleRef = mdToken; // Module reference (for the imported modules) + + // Assembly tokens. + using mdAssembly = mdToken; // Assembly token. + using mdAssemblyRef = mdToken; // AssemblyRef token. + using mdFile = mdToken; // File token. + using mdExportedType = mdToken; // ExportedType token. + using mdManifestResource = mdToken; // ManifestResource token. + + using mdTypeSpec = mdToken; // TypeSpec object + + using mdGenericParam = mdToken; // formal parameter to generic type or method + using mdMethodSpec = mdToken; // instantiation of a generic method + using mdGenericParamConstraint = mdToken; // constraint on a formal generic parameter + + // Application string. + using mdString = mdToken; // User literal string token. + + using mdCPToken = mdToken; // constantpool token + + enum mdToken + { + mdtModule = 0x00000000, // + mdtTypeRef = 0x01000000, // + mdtTypeDef = 0x02000000, // + mdtFieldDef = 0x04000000, // + mdtMethodDef = 0x06000000, // + mdtParamDef = 0x08000000, // + mdtInterfaceImpl = 0x09000000, // + mdtMemberRef = 0x0a000000, // + mdtCustomAttribute = 0x0c000000, // + mdtPermission = 0x0e000000, // + mdtSignature = 0x11000000, // + mdtEvent = 0x14000000, // + mdtProperty = 0x17000000, // + mdtModuleRef = 0x1a000000, // + mdtTypeSpec = 0x1b000000, // + mdtAssembly = 0x20000000, // + mdtAssemblyRef = 0x23000000, // + mdtFile = 0x26000000, // + mdtExportedType = 0x27000000, // + mdtManifestResource = 0x28000000, // + mdtGenericParam = 0x2a000000, // + mdtMethodSpec = 0x2b000000, // + mdtGenericParamConstraint = 0x2c000000, + + mdtString = 0x70000000, // + mdtName = 0x71000000, // + mdtBaseType = 0x72000000, // Leave this on the high end value. This does not correspond to metadata table + } + + // Note that this must be kept in sync with System.AttributeTargets. + enum CorAttributeTargets + { + catAssembly = 0x0001, + catModule = 0x0002, + catClass = 0x0004, + catStruct = 0x0008, + catEnum = 0x0010, + catConstructor = 0x0020, + catMethod = 0x0040, + catProperty = 0x0080, + catField = 0x0100, + catEvent = 0x0200, + catInterface = 0x0400, + catParameter = 0x0800, + catDelegate = 0x1000, + catGenericParameter = 0x4000, + + catAll = catAssembly | catModule | catClass | catStruct | catEnum | catConstructor | + catMethod | catProperty | catField | catEvent | catInterface | catParameter | catDelegate | catGenericParameter, + catClassMembers = catClass | catStruct | catEnum | catConstructor | catMethod | catProperty | catField | catEvent | catDelegate | catInterface, + } +} \ No newline at end of file diff --git a/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/MethodIterator.cs b/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/MethodIterator.cs new file mode 100644 index 000000000..9fad3e60a --- /dev/null +++ b/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/MethodIterator.cs @@ -0,0 +1,289 @@ +// ==++== +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// ==--== + +using System.Diagnostics; +using Microsoft.CSharp.RuntimeBinder.Syntax; + +namespace Microsoft.CSharp.RuntimeBinder.Semantics +{ + internal partial class CMemberLookupResults + { + public partial class CMethodIterator + { + private SymbolLoader m_pSymbolLoader; + private CSemanticChecker m_pSemanticChecker; + // Inputs. + private AggregateType m_pCurrentType; + private MethodOrPropertySymbol m_pCurrentSym; + private Declaration m_pContext; + private TypeArray m_pContainingTypes; + private CType m_pQualifyingType; + private Name m_pName; + private int m_nArity; + private symbmask_t m_mask; + private EXPRFLAG m_flags; + // Internal state. + private int m_nCurrentTypeCount; + private bool m_bIsCheckingInstanceMethods; + private bool m_bAtEnd; + private bool m_bAllowBogusAndInaccessible; + private bool m_bAllowExtensionMethods; + // Flags for the current sym. + private bool m_bCurrentSymIsBogus; + private bool m_bCurrentSymIsInaccessible; + // if Extension can be part of the results that are returned by the iterator + // this may be false if an applicable instance method was found by bindgrptoArgs + private bool m_bcanIncludeExtensionsInResults; + // we have found a applicable extension and only continue to the end of the current + // Namespace's extension methodlist + private bool m_bEndIterationAtCurrentExtensionList; + + public CMethodIterator(CSemanticChecker checker, SymbolLoader symLoader, Name name, TypeArray containingTypes, CType @object, CType qualifyingType, Declaration context, bool allowBogusAndInaccessible, bool allowExtensionMethods, int arity, EXPRFLAG flags, symbmask_t mask) + { + Debug.Assert(name != null); + Debug.Assert(symLoader != null); + Debug.Assert(checker != null); + Debug.Assert(containingTypes != null); + m_pSemanticChecker = checker; + m_pSymbolLoader = symLoader; + m_pCurrentType = null; + m_pCurrentSym = null; + m_pName = name; + m_pContainingTypes = containingTypes; + m_pQualifyingType = qualifyingType; + m_pContext = context; + m_bAllowBogusAndInaccessible = allowBogusAndInaccessible; + m_bAllowExtensionMethods = allowExtensionMethods; + m_nArity = arity; + m_flags = flags; + m_mask = mask; + m_nCurrentTypeCount = 0; + m_bIsCheckingInstanceMethods = true; + m_bAtEnd = false; + m_bCurrentSymIsBogus = false; + m_bCurrentSymIsInaccessible = false; + m_bcanIncludeExtensionsInResults = m_bAllowExtensionMethods; + m_bEndIterationAtCurrentExtensionList = false; + } + public MethodOrPropertySymbol GetCurrentSymbol() + { + return m_pCurrentSym; + } + public AggregateType GetCurrentType() + { + return m_pCurrentType; + } + public bool IsCurrentSymbolInaccessible() + { + return m_bCurrentSymIsInaccessible; + } + public bool IsCurrentSymbolBogus() + { + return m_bCurrentSymIsBogus; + } + public bool MoveNext(bool canIncludeExtensionsInResults, bool endatCurrentExtensionList) + { + if (m_bcanIncludeExtensionsInResults) + { + m_bcanIncludeExtensionsInResults = canIncludeExtensionsInResults; + } + if (!m_bEndIterationAtCurrentExtensionList) + { + m_bEndIterationAtCurrentExtensionList = endatCurrentExtensionList; + } + + if (m_bAtEnd) + { + return false; + } + + if (m_pCurrentType == null) // First guy. + { + if (m_pContainingTypes.size == 0) + { + // No instance methods, only extensions. + m_bIsCheckingInstanceMethods = false; + m_bAtEnd = true; + return false; + } + else + { + if (!FindNextTypeForInstanceMethods()) + { + // No instance or extensions. + + m_bAtEnd = true; + return false; + } + } + } + if (!FindNextMethod()) + { + m_bAtEnd = true; + return false; + } + return true; + } + public bool AtEnd() + { + return m_pCurrentSym == null; + } + private CSemanticChecker GetSemanticChecker() + { + return m_pSemanticChecker; + } + private SymbolLoader GetSymbolLoader() + { + return m_pSymbolLoader; + } + public bool CanUseCurrentSymbol() + { + m_bCurrentSymIsInaccessible = false; + m_bCurrentSymIsBogus = false; + + // Make sure that whether we're seeing a ctor is consistent with the flag. + // The only properties we handle are indexers. + if (m_mask == symbmask_t.MASK_MethodSymbol && ( + 0 == (m_flags & EXPRFLAG.EXF_CTOR) != !m_pCurrentSym.AsMethodSymbol().IsConstructor() || + 0 == (m_flags & EXPRFLAG.EXF_OPERATOR) != !m_pCurrentSym.AsMethodSymbol().isOperator) || + m_mask == symbmask_t.MASK_PropertySymbol && !m_pCurrentSym.AsPropertySymbol().isIndexer()) + { + // Get the next symbol. + return false; + } + + // If our arity is non-0, we must match arity with this symbol. + if (m_nArity > 0) + { + if (m_mask == symbmask_t.MASK_MethodSymbol && m_pCurrentSym.AsMethodSymbol().typeVars.size != m_nArity) + { + return false; + } + } + + // If this guy's not callable, no good. + if (!ExpressionBinder.IsMethPropCallable(m_pCurrentSym, (m_flags & EXPRFLAG.EXF_USERCALLABLE) != 0)) + { + return false; + } + + // Check access. + if (!GetSemanticChecker().CheckAccess(m_pCurrentSym, m_pCurrentType, m_pContext, m_pQualifyingType)) + { + // Sym is not accessible. However, if we're allowing inaccessible, then let it through and mark it. + if (m_bAllowBogusAndInaccessible) + { + m_bCurrentSymIsInaccessible = true; + } + else + { + return false; + } + } + + // Check bogus. + if (GetSemanticChecker().CheckBogus(m_pCurrentSym)) + { + // Sym is bogus, but if we're allow it, then let it through and mark it. + if (m_bAllowBogusAndInaccessible) + { + m_bCurrentSymIsBogus = true; + } + else + { + return false; + } + } + + // if we are done checking all the instance types ensure that currentsym is an + // extension method and not a simple static method + if (!m_bIsCheckingInstanceMethods) + { + if (!m_pCurrentSym.AsMethodSymbol().IsExtension()) + { + return false; + } + } + + return true; + } + + private bool FindNextMethod() + { + while (true) + { + if (m_pCurrentSym == null) + { + m_pCurrentSym = GetSymbolLoader().LookupAggMember( + m_pName, m_pCurrentType.getAggregate(), m_mask).AsMethodOrPropertySymbol(); + } + else + { + m_pCurrentSym = GetSymbolLoader().LookupNextSym( + m_pCurrentSym, m_pCurrentType.getAggregate(), m_mask).AsMethodOrPropertySymbol(); + } + + // If we couldn't find a sym, we look up the type chain and get the next type. + if (m_pCurrentSym == null) + { + if (m_bIsCheckingInstanceMethods) + { + if (!FindNextTypeForInstanceMethods() && m_bcanIncludeExtensionsInResults) + { + // We didn't find any more instance methods, set us into extension mode. + + m_bIsCheckingInstanceMethods = false; + } + else if (m_pCurrentType == null && !m_bcanIncludeExtensionsInResults) + { + return false; + } + else + { + // Found an instance method. + continue; + } + } + continue; + } + + // Note that we do not filter the current symbol for the user. They must do that themselves. + // This is because for instance, BindGrpToArgs wants to filter on arguments before filtering + // on bogosity. See DevDiv Bug 24236. + + // If we're here, we're good to go. + + break; + } + return true; + } + + private bool FindNextTypeForInstanceMethods() + { + // Otherwise, search through other types listed as well as our base class. + if (m_pContainingTypes.size > 0) + { + if (m_nCurrentTypeCount >= m_pContainingTypes.size) + { + // No more types to check. + m_pCurrentType = null; + } + else + { + m_pCurrentType = m_pContainingTypes.Item(m_nCurrentTypeCount++).AsAggregateType(); + } + } + else + { + // We have no more types to consider, so check out the base class. + + m_pCurrentType = m_pCurrentType.GetBaseClass(); + } + return m_pCurrentType != null; + } + } + } +} diff --git a/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/MethodKind.cs b/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/MethodKind.cs new file mode 100644 index 000000000..8223e2250 --- /dev/null +++ b/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/MethodKind.cs @@ -0,0 +1,38 @@ +// ==++== +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// ==--== + +namespace Microsoft.CSharp.RuntimeBinder.Semantics +{ + internal enum MethodKindEnum + { + None = 0, + Constructor = 1, // Ctor or static ctor + Destructor = 2, + PropAccessor = 3, + EventAccessor = 4, + ExplicitConv = 5, // Explicit user defined conversion + ImplicitConv = 6, // Implicit user defined conversion + Anonymous = 7, + // delegates + Invoke = 8, // Invoke method of a delegate type + BeginInvoke = 9, // BeginInvoke method of a delegate type + EndInvoke = 10, // EndInvoke method of a delegate type + // AnonymousTypes + AnonymousTypeToString = 11, + AnonymousTypeEquals = 12, + AnonymousTypeGetHashCode = 13, + // Iterators + IteratorDispose = 14, + IteratorReset = 15, + IteratorGetEnumerator = 16, + IteratorGetEnumeratorDelegating = 17, + IteratorMoveNext = 18, + // Partial Methods + Latent = 19, + Actual = 20, + IteratorFinally = 21, + } +} diff --git a/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/MethodTypeInferrer.cs b/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/MethodTypeInferrer.cs new file mode 100644 index 000000000..f0c903dc2 --- /dev/null +++ b/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/MethodTypeInferrer.cs @@ -0,0 +1,2294 @@ +// ==++== +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// ==--== + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using Microsoft.CSharp.RuntimeBinder.Syntax; + +namespace Microsoft.CSharp.RuntimeBinder.Semantics +{ + internal class MethodTypeInferrer + { + private enum NewInferenceResult + { + InferenceFailed, + MadeProgress, + NoProgress, + Success + } + private enum Dependency + { + Unknown = 0x00, + NotDependent = 0x01, + DependsMask = 0x10, + Direct = 0x11, + Indirect = 0x12 + } + private SymbolLoader symbolLoader; + private ExpressionBinder binder; + private TypeArray pMethodTypeParameters; + private TypeArray pClassTypeArguments; + private TypeArray pMethodFormalParameterTypes; + private ArgInfos pMethodArguments; + private List[] pExactBounds; + private List[] pUpperBounds; + private List[] pLowerBounds; + private CType[] pFixedResults; + private Dependency[,] ppDependencies; + private bool dependenciesDirty; + + /* + SPEC: + + CType inference occurs as part of the compile-time processing of a method invocation + and takes place before the overload resolution step of the invocation. When a + particular method group is specified in a method invocation, and no CType arguments + are specified as part of the method invocation, CType inference is applied to each + generic method in the method group. If CType inference succeeds, then the inferred + CType arguments are used to determine the types of formal parameters for subsequent + overload resolution. If overload resolution chooses a generic method as the one to + invoke then the inferred CType arguments are used as the actual CType arguments for the + invocation. If CType inference for a particular method fails, that method does not + participate in overload resolution. The failure of CType inference, in and of itself, + does not cause a compile-time error. However, it often leads to a compile-time error + when overload resolution then fails to find any applicable methods. + + If the supplied number of arguments is different than the number of parameters in + the method, then inference immediately fails. Otherwise, assume that the generic + method has the following signature: + + Tr M(T1 x1 ... Tm xm) + + With a method call of the form M(E1...Em) the task of CType inference is to find + unique CType arguments S1...Sn for each of the CType parameters X1...Xn so that the + call M(E1...Em)becomes valid. + + During the process of inference each CType parameter Xi is either fixed to a particular + CType Si or unfixed with an associated set of bounds. Each of the bounds is some CType T. + Each bound is classified as an upper bound, lower bound or exact bound. + Initially each CType variable Xi is unfixed with an empty set of bounds. + + + */ + + // This file contains the implementation for method CType inference on calls (with + // arguments, and method CType inference on conversion of method groups to delegate + // types (which will not have arguments.) + + //////////////////////////////////////////////////////////////////////////////// + + public static bool Infer( + ExpressionBinder binder, + SymbolLoader symbolLoader, + MethodSymbol pMethod, + TypeArray pClassTypeArguments, + TypeArray pMethodFormalParameterTypes, + ArgInfos pMethodArguments, + out TypeArray ppInferredTypeArguments) + { + Debug.Assert(pMethod != null); + Debug.Assert(pMethod.typeVars.size > 0); + Debug.Assert(pMethod.isParamArray || pMethod.Params == pMethodFormalParameterTypes); + ppInferredTypeArguments = null; + if (pMethodFormalParameterTypes.size == 0 || pMethod.InferenceMustFail()) + { + // CONSIDER: fill in with error symbols as when inference fails? + return false; + } + Debug.Assert(pMethodArguments != null); + Debug.Assert(pMethodFormalParameterTypes != null); + Debug.Assert(pMethodArguments.carg <= pMethodFormalParameterTypes.size); + + var inferrer = new MethodTypeInferrer(binder, symbolLoader, + pMethodFormalParameterTypes, pMethodArguments, + pMethod.typeVars, pClassTypeArguments); + bool success; + if (pMethodArguments.fHasExprs) + { + success = inferrer.InferTypeArgs(); + } + else + { + success = inferrer.InferForMethodGroupConversion(); + } + + ppInferredTypeArguments = inferrer.GetResults(); + return success; + } + + //////////////////////////////////////////////////////////////////////////////// + // + // Fixed, unfixed and bounded CType parameters + // + // SPEC: During the process of inference each CType parameter is either fixed to + // SPEC: a particular CType or unfixed with an associated set of bounds. Each of + // SPEC: the bounds is of some CType T. Initially each CType parameter is unfixed + // SPEC: with an empty set of bounds. + + private MethodTypeInferrer( + ExpressionBinder exprBinder, SymbolLoader symLoader, + TypeArray pMethodFormalParameterTypes, ArgInfos pMethodArguments, + TypeArray pMethodTypeParameters, TypeArray pClassTypeArguments) + { + this.binder = exprBinder; + this.symbolLoader = symLoader; + this.pMethodFormalParameterTypes = pMethodFormalParameterTypes; + this.pMethodArguments = pMethodArguments; + this.pMethodTypeParameters = pMethodTypeParameters; + this.pClassTypeArguments = pClassTypeArguments; + this.pFixedResults = new CType[pMethodTypeParameters.size]; + this.pLowerBounds = new List[pMethodTypeParameters.size]; + this.pUpperBounds = new List[pMethodTypeParameters.size]; + this.pExactBounds = new List[pMethodTypeParameters.size]; + for (int iBound = 0; iBound < pMethodTypeParameters.size; ++iBound) + { + pLowerBounds[iBound] = new List(); + pUpperBounds[iBound] = new List(); + pExactBounds[iBound] = new List(); + } + this.ppDependencies = null; + } + + //////////////////////////////////////////////////////////////////////////////// + + TypeArray GetResults() + { + // Anything we didn't infer a CType for, give the error CType. + // Note: the error CType will have the same name as the name + // of the CType parameter we were trying to infer. This will give a + // nice user experience where by we will show something like + // the following: + // + // user types: customers.Select( + // we show : IE IE.Select(Func selector) + // + // Initially we thought we'd just show ?. i.e.: + // + // IE IE.Select(Func selector) + // + // This is nice and concise. However, it falls down if there are multiple + // CType params that we have left. + + for (int iParam = 0; iParam < pMethodTypeParameters.size; iParam++) + { + // We iterate through the resultant types and replace any that are + // null, or an error CType that has less information (e.g null name or + // PredefinedName.PN_MISSING name). + + // (DevDiv Bugs #74826) We get an ErrorType with a null nameText + // for a CType variable that we couldn't infer. + if (pFixedResults[iParam] != null) + { + if (!pFixedResults[iParam].IsErrorType()) + { + continue; + } + + Name pErrorTypeName = pFixedResults[iParam].AsErrorType().nameText; + if (pErrorTypeName != null && + pErrorTypeName != GetGlobalSymbols().GetNameManager().GetPredefName(PredefinedName.PN_MISSING)) + { + continue; + } + } + + pFixedResults[iParam] = GetTypeManager().GetErrorType( + null/*pParentType*/, + null, + pMethodTypeParameters.ItemAsTypeParameterType(iParam).GetName(), + BSYMMGR.EmptyTypeArray()); + } + return GetGlobalSymbols().AllocParams(pMethodTypeParameters.size, pFixedResults); + } + + //////////////////////////////////////////////////////////////////////////////// + + bool IsUnfixed(int iParam) + { + Debug.Assert(0 <= iParam); + Debug.Assert(iParam < pMethodTypeParameters.size); + return pFixedResults[iParam] == null; + } + + //////////////////////////////////////////////////////////////////////////////// + + bool IsUnfixed(TypeParameterType pParam) + { + Debug.Assert(pParam != null); + Debug.Assert(pParam.IsMethodTypeParameter()); + int iParam = pParam.GetIndexInTotalParameters(); + Debug.Assert(pMethodTypeParameters.ItemAsTypeParameterType(iParam) == pParam); + return IsUnfixed(iParam); + } + + //////////////////////////////////////////////////////////////////////////////// + + bool AllFixed() + { + for (int iParam = 0 ; iParam < pMethodTypeParameters.size; ++iParam) + { + if (IsUnfixed(iParam)) + { + return false; + } + } + return true; + } + + //////////////////////////////////////////////////////////////////////////////// + + void AddLowerBound(TypeParameterType pParam, CType pBound) + { + Debug.Assert(IsUnfixed(pParam)); + int iParam = pParam.GetIndexInTotalParameters(); + if (!pLowerBounds[iParam].Contains(pBound)) + { + pLowerBounds[iParam].Add(pBound); + } + } + + //////////////////////////////////////////////////////////////////////////////// + + void AddUpperBound(TypeParameterType pParam, CType pBound) + { + Debug.Assert(IsUnfixed(pParam)); + int iParam = pParam.GetIndexInTotalParameters(); + if (!pUpperBounds[iParam].Contains(pBound)) + { + pUpperBounds[iParam].Add(pBound); + } + } + + //////////////////////////////////////////////////////////////////////////////// + + void AddExactBound(TypeParameterType pParam, CType pBound) + { + Debug.Assert(IsUnfixed(pParam)); + int iParam = pParam.GetIndexInTotalParameters(); + if (!pExactBounds[iParam].Contains(pBound)) + { + pExactBounds[iParam].Add(pBound); + } + } + + //////////////////////////////////////////////////////////////////////////////// + + bool HasBound(int iParam) + { + Debug.Assert(0 <= iParam); + Debug.Assert(iParam < pMethodTypeParameters.size); + return !pLowerBounds[iParam].IsEmpty() || + !pExactBounds[iParam].IsEmpty() || + !pUpperBounds[iParam].IsEmpty(); + } + + //////////////////////////////////////////////////////////////////////////////// + + TypeArray GetFixedDelegateParameters(AggregateType pDelegateType) + { + Debug.Assert(pDelegateType.isDelegateType()); + + // We have a delegate where the input types use no unfixed parameters. Create + // a substitution context; we can substitute unfixed parameters for themselves + // since they don't actually occur in the inputs. (They may occur in the outputs, + // or there may be input parameters fixed to _unfixed_ method CType variables. + // Both of those scenarios are legal.) + + CType[] ppMethodParameters = new CType[pMethodTypeParameters.size]; + for (int iParam = 0 ; iParam < pMethodTypeParameters.size; iParam++) + { + TypeParameterType pParam = pMethodTypeParameters.ItemAsTypeParameterType(iParam); + ppMethodParameters[iParam] = IsUnfixed(iParam) ? pParam : pFixedResults[iParam]; + } + SubstContext subsctx = new SubstContext(pClassTypeArguments.ToArray(), pClassTypeArguments.size, + ppMethodParameters, pMethodTypeParameters.size); + AggregateType pFixedDelegateType = + GetTypeManager().SubstType(pDelegateType, subsctx).AsAggregateType(); + TypeArray pFixedDelegateParams = + pFixedDelegateType.GetDelegateParameters(GetSymbolLoader()); + return pFixedDelegateParams; + } + + //////////////////////////////////////////////////////////////////////////////// + // + // Phases + // + + bool InferTypeArgs() + { + // SPEC: CType inference takes place in phases. Each phase will try to infer CType + // SPEC: arguments for more CType parameters based on the findings of the previous + // SPEC: phase. The first phase makes some initial inferences of bounds, whereas + // SPEC: the second phase fixes CType parameters to specific types and infers further + // SPEC: bounds. The second phase may have to be repeated a number of times. + InferTypeArgsFirstPhase(); + return InferTypeArgsSecondPhase(); + } + + //////////////////////////////////////////////////////////////////////////////// + + static bool IsReallyAType(CType pType) + { + if (pType.IsNullType() || pType.IsBoundLambdaType() || + pType.IsVoidType() || + pType.IsMethodGroupType()) + { + return false; + } + return true; + } + + //////////////////////////////////////////////////////////////////////////////// + // + // The first phase + // + + void InferTypeArgsFirstPhase() + { + Debug.Assert(pMethodFormalParameterTypes != null); + Debug.Assert(pMethodArguments != null); + Debug.Assert(pMethodArguments.carg <= pMethodFormalParameterTypes.size); + + // SPEC: For each of the method arguments Ei: + for (int iArg = 0; iArg < pMethodArguments.carg; iArg++) + { + // SPEC ISSUE: We never deduce anything helpful from an filled-in + // SPEC ISSUE: optional parameter and sometimes deduce something harmful. + // SPEC ISSUE: Ex: Foo(T t = default(T)) -- we do not want to add + // SPEC ISSUE: "T" to the bound set of "T" in this case and produce + // SPEC ISSUE: a "chicken and egg" problem. + // SPEC ISSUE: We should put language in the spec saying that we skip + // SPEC ISSUE: inference on any argument that was created via the + // SPEC ISSUE: optional parameter mechanism. + EXPR pExpr = pMethodArguments.prgexpr[iArg]; + + if (pExpr.IsOptionalArgument) + { + continue; + } + + CType pDest = pMethodFormalParameterTypes.Item(iArg); + + // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + // RUNTIME BINDER ONLY CHANGE + // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + // + // dynamic operands enter method type inference with their + // actual runtime type, and in this way can infer implemented + // types that are not visible on more public types. (for ex., + // private classes that implement IEnumerable, as in iterators). + + CType pSource = pExpr.RuntimeObjectActualType != null + ? pExpr.RuntimeObjectActualType + : pMethodArguments.types.Item(iArg); + + // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + // END RUNTIME BINDER ONLY CHANGE + // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + + + bool wasOutOrRef = false; + if (pDest.IsParameterModifierType()) + { + pDest = pDest.AsParameterModifierType().GetParameterType(); + wasOutOrRef = true; + } + if (pSource.IsParameterModifierType()) + { + pSource = pSource.AsParameterModifierType().GetParameterType(); + } + // If the argument is a TYPEORNAMESPACEERROR and the pSource is an + // error CType, then we want to set it to the generic error CType + // that has no name text. This is because of the following scenario: + // + // void M(T t) { } + // void Foo() + // { + // UnknownType t; + // M(t); + // M(undefinedVariable); + // } + // + // In the first call to M, we'll have an EXPRLOCAL with an error CType, + // which is correct - we want the parameter help to display that we've + // got an inferred CType of UnknownType, which is an error CType since + // its undefined. + // + // However, for the M in the second call, we DON'T want to display parameter + // help that gives undefinedVariable as the CType parameter for T, because + // there is no parameter of that name, let alone that CType. This appears + // as an EXPRTYPEORNAMESPACEERROR with an ErrorType. We create a new error sym + // without the CType name. + + // SPEC: If Ei is an anonymous function, an explicit CType parameter + // SPEC: inference is made from Ei to Ti. + + // (We cannot make an output CType inference from a method group + // at this time because we have no fixed types yet to use for + // overload resolution.) + + // SPEC: Otherwise, if Ei has a CType U then a lower-bound inference + // SPEC: or exact inference is made from U to Ti. + + // SPEC: Otherwise, no inference is made for this argument + + if (IsReallyAType(pSource)) + { + if (wasOutOrRef) + { + ExactInference(pSource, pDest); + } + else + { + LowerBoundInference(pSource, pDest); + } + } + } + } + + //////////////////////////////////////////////////////////////////////////////// + // + // The second phase + // + + bool InferTypeArgsSecondPhase() + { + // SPEC: The second phase proceeds as follows: + // SPEC: If no unfixed CType parameters exist then CType inference succeeds. + // SPEC: Otherwise, if there exists one or more arguments Ei with corresponding + // SPEC: parameter CType Ti such that: + // SPEC: o the output CType of Ei with CType Ti contains at least one unfixed + // SPEC: CType parameter Xj, and + // SPEC: o none of the input types of Ei with CType Ti contains any unfixed + // SPEC: CType parameter Xj, + // SPEC: then an output CType inference is made from all such Ei to Ti. + // SPEC: Whether or not the previous step actually made an inference, we must + // SPEC: now fix at least one CType parameter, as follows: + // SPEC: If there exists one or more CType parameters Xi such that + // SPEC: o Xi is unfixed, and + // SPEC: o Xi has a non-empty set of bounds, and + // SPEC: o Xi does not depend on any Xj + // SPEC: then each such Xi is fixed. If any fixing operation fails then CType + // SPEC: inference fails. + // SPEC: Otherwise, if there exists one or more CType parameters Xi such that + // SPEC: o Xi is unfixed, and + // SPEC: o Xi has a non-empty set of bounds, and + // SPEC: o there is at least one CType parameter Xj that depends on Xi + // SPEC: then each such Xi is fixed. If any fixing operation fails then + // SPEC: CType inference fails. + // SPEC: Otherwise, we are unable to make progress and there are unfixed parameters. + // SPEC: CType inference fails. + // SPEC: If CType inference neither succeeds nor fails then the second phase is + // SPEC: repeated until CType inference succeeds or fails. (Since each repetition of + // SPEC: the second phase either succeeds, fails or fixes an unfixed CType parameter, + // SPEC: the algorithm must terminate with no more repetitions than the number + // SPEC: of CType parameters. + + InitializeDependencies(); + + while(true) + { + NewInferenceResult res = DoSecondPhase(); + Debug.Assert(res != NewInferenceResult.NoProgress); + if (res == NewInferenceResult.InferenceFailed) + { + return false; + } + if (res == NewInferenceResult.Success) + { + return true; + } + // Otherwise, we made some progress last time; do it again. + } + } + + //////////////////////////////////////////////////////////////////////////////// + + NewInferenceResult DoSecondPhase() + { + // SPEC: If no unfixed CType parameters exist then CType inference succeeds. + if (AllFixed()) + { + return NewInferenceResult.Success; + } + // SPEC: Otherwise, if there exists one or more arguments Ei with + // SPEC: corresponding parameter CType Ti such that: + // SPEC: o the output CType of Ei with CType Ti contains at least one unfixed + // SPEC: CType parameter Xj, and + // SPEC: o none of the input types of Ei with CType Ti contains any unfixed + // SPEC: CType parameter Xj, + // SPEC: then an output CType inference is made from all such Ei to Ti. + + MakeOutputTypeInferences(); + + // SPEC: Whether or not the previous step actually made an inference, we + // SPEC: must now fix at least one CType parameter, as follows: + // SPEC: If there exists one or more CType parameters Xi such that + // SPEC: o Xi is unfixed, and + // SPEC: o Xi has a non-empty set of bounds, and + // SPEC: o Xi does not depend on any Xj + // SPEC: then each such Xi is fixed. If any fixing operation fails then + // SPEC: CType inference fails. + + NewInferenceResult res; + res = FixNondependentParameters(); + if (res != NewInferenceResult.NoProgress) + { + return res; + } + // SPEC: Otherwise, if there exists one or more CType parameters Xi such that + // SPEC: o Xi is unfixed, and + // SPEC: o Xi has a non-empty set of bounds, and + // SPEC: o there is at least one CType parameter Xj that depends on Xi + // SPEC: then each such Xi is fixed. If any fixing operation fails then + // SPEC: CType inference fails. + res = FixDependentParameters(); + if (res != NewInferenceResult.NoProgress) + { + return res; + } + // SPEC: Otherwise, we are unable to make progress and there are + // SPEC: unfixed parameters. CType inference fails. + return NewInferenceResult.InferenceFailed; + } + + //////////////////////////////////////////////////////////////////////////////// + + void MakeOutputTypeInferences() + { + // SPEC: Otherwise, for all arguments Ei with corresponding parameter CType Ti + // SPEC: where the output types contain unfixed CType parameters but the input + // SPEC: types do not, an output CType inference is made from Ei to Ti. + + for (int iArg = 0; iArg < pMethodArguments.carg; iArg++) + { + CType pDest = pMethodFormalParameterTypes.Item(iArg); + if (pDest.IsParameterModifierType()) + { + pDest = pDest.AsParameterModifierType().GetParameterType(); + } + EXPR pExpr = pMethodArguments.prgexpr[iArg]; + if (HasUnfixedParamInOutputType(pExpr, pDest) && + !HasUnfixedParamInInputType(pExpr, pDest)) + { + CType pSource = pMethodArguments.types.Item(iArg); + if (pSource.IsParameterModifierType()) + { + pSource = pSource.AsParameterModifierType().GetParameterType(); + } + OutputTypeInference(pExpr, pSource, pDest); + } + } + } + + //////////////////////////////////////////////////////////////////////////////// + + NewInferenceResult FixNondependentParameters() + { + // SPEC: Otherwise, if there exists one or more CType parameters Xi such that + // SPEC: o Xi is unfixed, and + // SPEC: o Xi has a non-empty set of bounds, and + // SPEC: o Xi does not depend on any Xj + // SPEC: then each such Xi is fixed. + + // Dependency is only defined for unfixed parameters. Therefore, fixing + // a parameter may cause all of its dependencies to become no longer + // dependent on anything. We need to first determine which parameters need to be + // fixed, and then fix them all at once. + + bool[] pNeedsFixing = new bool[pMethodTypeParameters.size]; + int iParam; + NewInferenceResult res = NewInferenceResult.NoProgress; + for (iParam = 0; iParam < pMethodTypeParameters.size; iParam++) + { + if (IsUnfixed(iParam) && HasBound(iParam) && !DependsOnAny(iParam)) + { + pNeedsFixing[iParam] = true; + res = NewInferenceResult.MadeProgress; + } + } + for (iParam = 0; iParam < pMethodTypeParameters.size; iParam++) + { + // Fix as much as you can, even if there are errors. That will + // help with intellisense. + if (pNeedsFixing[iParam]) + { + if (!Fix(iParam)) + { + res = NewInferenceResult.InferenceFailed; + } + } + } + return res; + } + + //////////////////////////////////////////////////////////////////////////////// + + NewInferenceResult FixDependentParameters() + { + // SPEC: All unfixed CType parameters Xi are fixed for which all of the following hold: + // SPEC: There is at least one CType parameter Xj that depends on Xi. + // SPEC: Xi has a non-empty set of bounds. + + // As above, we must collect up everything that needs fixing first, + // and then fix them. + + bool[] pNeedsFixing = new bool[pMethodTypeParameters.size]; + int iParam; + NewInferenceResult res = NewInferenceResult.NoProgress; + for (iParam = 0; iParam < pMethodTypeParameters.size; iParam++) + { + if (IsUnfixed(iParam) && HasBound(iParam) && AnyDependsOn(iParam)) + { + pNeedsFixing[iParam] = true; + res = NewInferenceResult.MadeProgress; + } + } + for (iParam = 0; iParam < pMethodTypeParameters.size; iParam++) + { + // Fix as much as you can, even if there are errors. That will + // help with intellisense. + if (pNeedsFixing[iParam]) + { + if (!Fix(iParam)) + { + res = NewInferenceResult.InferenceFailed; + } + } + } + return res; + } + + //////////////////////////////////////////////////////////////////////////////// + // + // Input types + // + bool DoesInputTypeContain(EXPR pSource, CType pDest, + TypeParameterType pParam) + { + // SPEC: If E is a method group or an anonymous function and T is a delegate + // SPEC: CType or expression tree CType then all the parameter types of T are + // SPEC: input types of E with CType T. + + pDest = pDest.GetDelegateTypeOfPossibleExpression(); + if (!pDest.isDelegateType()) + { + return false; // No input types. + } + + if (!pSource.isUNBOUNDLAMBDA() && !pSource.isMEMGRP()) + { + return false; // No input types. + } + + TypeArray pDelegateParameters = + pDest.AsAggregateType().GetDelegateParameters(GetSymbolLoader()); + if (pDelegateParameters == null) + { + return false; + } + return TypeManager.ParametersContainTyVar(pDelegateParameters, pParam); + } + + //////////////////////////////////////////////////////////////////////////////// + + bool HasUnfixedParamInInputType(EXPR pSource, CType pDest) + { + for (int iParam = 0; iParam < pMethodTypeParameters.size; iParam++) + { + if (IsUnfixed(iParam)) + { + if (DoesInputTypeContain(pSource, pDest, + pMethodTypeParameters.ItemAsTypeParameterType(iParam))) + { + return true; + } + } + } + return false; + } + + //////////////////////////////////////////////////////////////////////////////// + // + // Output types + // + bool DoesOutputTypeContain(EXPR pSource, CType pDest, + TypeParameterType pParam) + { + // SPEC: If E is a method group or an anonymous function and T is a delegate + // SPEC: CType or expression tree CType then the return CType of T is an output CType + // SPEC: of E with CType T. + + pDest = pDest.GetDelegateTypeOfPossibleExpression(); + if (!pDest.isDelegateType()) + { + return false; + } + + if (!pSource.isUNBOUNDLAMBDA() && !pSource.isMEMGRP()) + { + return false; + } + + CType pDelegateReturn = pDest.AsAggregateType().GetDelegateReturnType(GetSymbolLoader()); + if (pDelegateReturn == null) + { + return false; + } + return TypeManager.TypeContainsType(pDelegateReturn, pParam); + } + + //////////////////////////////////////////////////////////////////////////////// + + bool HasUnfixedParamInOutputType(EXPR pSource, CType pDest) + { + for (int iParam = 0; iParam < pMethodTypeParameters.size; iParam++) + { + if (IsUnfixed(iParam)) + { + if (DoesOutputTypeContain(pSource, pDest, + pMethodTypeParameters.ItemAsTypeParameterType(iParam))) + { + return true; + } + } + } + return false; + } + + //////////////////////////////////////////////////////////////////////////////// + // + // Dependence + // + + bool DependsDirectlyOn(int iParam, int jParam) + { + Debug.Assert(0 <= iParam && iParam < pMethodTypeParameters.size); + Debug.Assert(0 <= jParam && jParam < pMethodTypeParameters.size); + + // SPEC: An unfixed CType parameter Xi depends directly on an unfixed CType + // SPEC: parameter Xj if for some argument Ek with CType Tk, Xj occurs + // SPEC: in an input CType of Ek and Xi occurs in an output CType of Ek + // SPEC: with CType Tk. + + // We compute and record the Depends Directly On relationship once, in + // InitializeDependencies, below. + + // At this point, everything should be unfixed. + + Debug.Assert(IsUnfixed(iParam)); + Debug.Assert(IsUnfixed(jParam)); + + for (int iArg = 0; iArg < pMethodArguments.carg; iArg++) + { + CType pDest = pMethodFormalParameterTypes.Item(iArg); + if (pDest.IsParameterModifierType()) + { + pDest = pDest.AsParameterModifierType().GetParameterType(); + } + + EXPR pExpr = pMethodArguments.prgexpr[iArg]; + + if (DoesInputTypeContain(pExpr, pDest, + pMethodTypeParameters.ItemAsTypeParameterType(jParam)) && + DoesOutputTypeContain(pExpr, pDest, + pMethodTypeParameters.ItemAsTypeParameterType(iParam))) + { + return true; + } + } + return false; + } + + //////////////////////////////////////////////////////////////////////////////// + + void InitializeDependencies() + { + // We track dependencies by a two-d square array that gives the known + // relationship between every pair of CType parameters. The relationship + // is one of: + // + // Unknown relationship + // known to be not dependent + // known to depend directly + // known to depend indirectly + // + // Since dependency is only defined on unfixed CType parameters, fixing a CType + // parameter causes all dependencies involving that parameter to go to + // the "known to be not dependent" state. Since dependency is a transitive property, + // this means that doing so may require recalculating the indirect dependencies + // from the now possibly smaller set of dependencies. + // + // Therefore, when we detect that the dependency state has possibly changed + // due to fixing, we change all "depends indirectly" back into "unknown" and + // recalculate from the remaining "depends directly". + // + // This algorithm thereby yields an extremely bad (but extremely unlikely) worst + // case for asymptotic performance. Suppose there are n CType parameters. + // "DependsTransitivelyOn" below costs O(n) because it must potentially check + // all n CType parameters to see if there is any k such that Xj => Xk => Xi. + // "DeduceDependencies" calls "DependsTransitivelyOn" for each "Unknown" + // pair, and there could be O(n^2) such pairs, so DependsTransitivelyOn is + // worst-case O(n^3). And we could have to recalculate the dependency graph + // after each CType parameter is fixed in turn, so that would be O(n) calls to + // DependsTransitivelyOn, giving this algorithm a worst case of O(n^4). + // + // Of course, in reality, n is going to almost always be on the order of + // "smaller than 5", and there will not be O(n^2) dependency relationships + // between CType parameters; it is far more likely that the transitivity chains + // will be very short and not branch or loop at all. This is much more likely to + // be an O(n^2) algorithm in practice. + + Debug.Assert(ppDependencies == null); + ppDependencies = new Dependency[pMethodTypeParameters.size, pMethodTypeParameters.size]; + for (int iParam = 0; iParam < pMethodTypeParameters.size; ++iParam) + { + for (int jParam = 0; jParam < pMethodTypeParameters.size; ++jParam) + { + if (DependsDirectlyOn(iParam, jParam)) + { + ppDependencies[iParam, jParam] = Dependency.Direct; + } + } + } + + DeduceAllDependencies(); + } + + //////////////////////////////////////////////////////////////////////////////// + + bool DependsOn(int iParam, int jParam) + { + Debug.Assert(ppDependencies != null); + + // SPEC: Xj depends on Xi if Xj depends directly on Xi, or if Xi depends + // SPEC: directly on Xk and Xk depends on Xj. Thus "depends on" is the + // SPEC: transitive but not reflexive closure of "depends directly on". + + Debug.Assert(0 <= iParam && iParam < pMethodTypeParameters.size); + Debug.Assert(0 <= jParam && jParam < pMethodTypeParameters.size); + + if (dependenciesDirty) + { + SetIndirectsToUnknown(); + DeduceAllDependencies(); + } + return 0 != ((ppDependencies[iParam, jParam]) & Dependency.DependsMask); + } + + //////////////////////////////////////////////////////////////////////////////// + + bool DependsTransitivelyOn(int iParam, int jParam) + { + Debug.Assert(ppDependencies != null); + Debug.Assert(0 <= iParam && iParam < pMethodTypeParameters.size); + Debug.Assert(0 <= jParam && jParam < pMethodTypeParameters.size); + + // Can we find Xk such that Xi depends on Xk and Xk depends on Xj? + // If so, then Xi depends indirectly on Xj. (Note that there is + // a minor optimization here -- the spec comment above notes that + // we want Xi to depend DIRECTLY on Xk, and Xk to depend directly + // or indirectly on Xj. But if we already know that Xi depends + // directly OR indirectly on Xk and Xk depends on Xj, then that's + // good enough.) + + for(int kParam = 0 ; kParam < pMethodTypeParameters.size; ++kParam) + { + if (0 != ((ppDependencies[iParam, kParam]) & Dependency.DependsMask) && + 0 != ((ppDependencies[kParam, jParam]) & Dependency.DependsMask)) + { + return true; + } + } + return false; + } + + //////////////////////////////////////////////////////////////////////////////// + + void DeduceAllDependencies() + { + bool madeProgress; + do + { + madeProgress = DeduceDependencies(); + } while (madeProgress); + SetUnknownsToNotDependent(); + dependenciesDirty = false; + } + + //////////////////////////////////////////////////////////////////////////////// + + bool DeduceDependencies() + { + Debug.Assert(ppDependencies != null); + bool madeProgress = false; + for (int iParam = 0; iParam < pMethodTypeParameters.size; ++iParam) + { + for (int jParam = 0; jParam < pMethodTypeParameters.size; ++jParam) + { + if (ppDependencies[iParam, jParam] == Dependency.Unknown) + { + if (DependsTransitivelyOn(iParam, jParam)) + { + ppDependencies[iParam, jParam] = Dependency.Indirect; + madeProgress = true; + } + } + } + } + return madeProgress; + } + + //////////////////////////////////////////////////////////////////////////////// + + void SetUnknownsToNotDependent() + { + Debug.Assert(ppDependencies != null); + for (int iParam = 0; iParam < pMethodTypeParameters.size; ++iParam) + { + for (int jParam = 0; jParam < pMethodTypeParameters.size; ++jParam) + { + if (ppDependencies[iParam, jParam] == Dependency.Unknown) + { + ppDependencies[iParam, jParam] = Dependency.NotDependent; + } + } + } + } + + //////////////////////////////////////////////////////////////////////////////// + + void SetIndirectsToUnknown() + { + Debug.Assert(ppDependencies != null); + for (int iParam = 0; iParam < pMethodTypeParameters.size; ++iParam) + { + for (int jParam = 0; jParam < pMethodTypeParameters.size; ++jParam) + { + if (ppDependencies[iParam, jParam] == Dependency.Indirect) + { + ppDependencies[iParam, jParam] = Dependency.Unknown; + } + } + } + } + + //////////////////////////////////////////////////////////////////////////////// + // A fixed parameter never depends on anything, nor is depended upon by anything. + + void UpdateDependenciesAfterFix(int iParam) + { + if (ppDependencies == null) + { + return; + } + for (int jParam = 0; jParam < pMethodTypeParameters.size; ++jParam) + { + ppDependencies[iParam, jParam] = Dependency.NotDependent; + ppDependencies[jParam, iParam] = Dependency.NotDependent; + } + dependenciesDirty = true; + } + + //////////////////////////////////////////////////////////////////////////////// + + bool DependsOnAny(int iParam) + { + Debug.Assert(0 <= iParam && iParam < pMethodTypeParameters.size); + for (int jParam = 0 ; jParam < pMethodTypeParameters.size ; ++jParam) + { + if (DependsOn(iParam, jParam)) + { + return true; + } + } + return false; + } + + //////////////////////////////////////////////////////////////////////////////// + + bool AnyDependsOn(int iParam) + { + Debug.Assert(0 <= iParam && iParam < pMethodTypeParameters.size); + for (int jParam = 0 ; jParam < pMethodTypeParameters.size ; ++jParam) + { + if (DependsOn(jParam, iParam)) + { + return true; + } + } + return false; + } + + //////////////////////////////////////////////////////////////////////////////// + // + // Output CType inferences + // + + + + //////////////////////////////////////////////////////////////////////////////// + + void OutputTypeInference(EXPR pExpr, CType pSource, CType pDest) + { + // SPEC: An output CType inference is made from an expression E to a CType T + // SPEC: in the following way: + + // SPEC: If E is an anonymous function with inferred return CType U and + // SPEC: T is a delegate CType or expression tree with return CType Tb + // SPEC: then a lower bound inference is made from U to Tb. + + // SPEC: Otherwise, if E is a method group and T is a delegate CType or + // SPEC: expression tree CType with parameter types T1...Tk and return + // SPEC: CType Tb and overload resolution of E with the types T1...Tk + // SPEC: yields a single method with return CType U then a lower-bound + // SPEC: inference is made from U to Tb. + if (MethodGroupReturnTypeInference(pExpr, pDest)) + { + return; + } + // SPEC: Otherwise, if E is an expression with CType U then a lower-bound + // SPEC: inference is made from U to T. + if (IsReallyAType(pSource)) + { + LowerBoundInference(pSource, pDest); + } + // SPEC: Otherwise, no inferences are made. + } + + //////////////////////////////////////////////////////////////////////////////// + + bool MethodGroupReturnTypeInference(EXPR pSource, CType pType) + { + // SPEC: Otherwise, if E is a method group and T is a delegate CType or + // SPEC: expression tree CType with parameter types T1...Tk and return + // SPEC: CType Tb and overload resolution of E with the types T1...Tk + // SPEC: yields a single method with return CType U then a lower-bound + // SPEC: inference is made from U to Tb. + + if (!pSource.isMEMGRP()) + { + return false; + } + pType = pType.GetDelegateTypeOfPossibleExpression(); + if (!pType.isDelegateType()) + { + return false; + } + AggregateType pDelegateType = pType.AsAggregateType(); + CType pDelegateReturnType = pDelegateType.GetDelegateReturnType(GetSymbolLoader()); + if (pDelegateReturnType == null) + { + return false; + } + if (pDelegateReturnType.IsVoidType()) + { + return false; + } + + // At this point we are in the second phase; we know that all the input types are fixed. + + TypeArray pDelegateParameters = GetFixedDelegateParameters(pDelegateType); + if (pDelegateParameters == null) + { + return false; + } + + ArgInfos argInfo = new ArgInfos() { carg = pDelegateParameters.size, types = pDelegateParameters, fHasExprs = false, prgexpr = null }; + + var argsBinder = new ExpressionBinder.GroupToArgsBinder(binder, 0/* flags */, pSource.asMEMGRP(), argInfo, null, false, pDelegateType); + + bool success = argsBinder.Bind(false); + if (!success) + { + return false; + } + + MethPropWithInst mwi = argsBinder.GetResultsOfBind().GetBestResult(); + CType pMethodReturnType = GetTypeManager().SubstType(mwi.Meth().RetType, + mwi.GetType(), mwi.TypeArgs); + if (pMethodReturnType.IsVoidType()) + { + return false; + } + + LowerBoundInference(pMethodReturnType, pDelegateReturnType); + return true; + } + + //////////////////////////////////////////////////////////////////////////////// + // + // Exact inferences + // + void ExactInference(CType pSource, CType pDest) + { + // SPEC: An exact inference from a CType U to a CType V is made as follows: + + // SPEC: If V is one of the unfixed Xi then U is added to the set of + // SPEC: exact bounds for Xi. + if (ExactTypeParameterInference(pSource, pDest)) + { + return; + } + + // SPEC: Otherwise, if U is an array CType UE[...] and V is an array CType VE[...] + // SPEC: of the same rank then an exact inference from UE to VE is made. + if (ExactArrayInference(pSource, pDest)) + { + return; + } + + // SPEC: Otherwise, if U is the CType U1? and V is the CType V1? then an + // SPEC: exact inference is made from U to V. + + if (ExactNullableInference(pSource, pDest)) + { + return; + } + + // SPEC: Otherwise, if V is a constructed CType C and U is a constructed + // SPEC: CType C then an exact inference is made + // SPEC: from each Ui to the corresponding Vi. + + if (ExactConstructedInference(pSource, pDest)) + { + return; + } + + // SPEC: Otherwise no inferences are made. + } + + //////////////////////////////////////////////////////////////////////////////// + + bool ExactTypeParameterInference(CType pSource, CType pDest) + { + // SPEC: If V is one of the unfixed Xi then U is added to the set of bounds + // SPEC: for Xi. + if (pDest.IsTypeParameterType()) + { + TypeParameterType pTPType = pDest.AsTypeParameterType(); + if (pTPType.IsMethodTypeParameter() && IsUnfixed(pTPType)) + { + AddExactBound(pTPType, pSource); + return true; + } + } + return false; + } + + //////////////////////////////////////////////////////////////////////////////// + + bool ExactArrayInference(CType pSource, CType pDest) + { + // SPEC: Otherwise, if U is an array CType UE[...] and V is an array CType VE[...] + // SPEC: of the same rank then an exact inference from UE to VE is made. + if (!pSource.IsArrayType() || !pDest.IsArrayType()) + { + return false; + } + ArrayType pArraySource = pSource.AsArrayType(); + ArrayType pArrayDest = pDest.AsArrayType(); + if (pArraySource.rank != pArrayDest.rank) + { + return false; + } + ExactInference(pArraySource.GetElementType(), pArrayDest.GetElementType()); + return true; + } + + //////////////////////////////////////////////////////////////////////////////// + + bool ExactNullableInference(CType pSource, CType pDest) + { + // SPEC: Otherwise, if U is the CType U1? and V is the CType V1? + // SPEC: then an exact inference is made from U to V. + if (!pSource.IsNullableType() || !pDest.IsNullableType()) + { + return false; + } + ExactInference(pSource.AsNullableType().GetUnderlyingType(), + pDest.AsNullableType().GetUnderlyingType()); + return true; + } + + //////////////////////////////////////////////////////////////////////////////// + + bool ExactConstructedInference(CType pSource, CType pDest) + { + // SPEC: Otherwise, if V is a constructed CType C and U is a constructed + // SPEC: CType C then an exact inference + // SPEC: is made from each Ui to the corresponding Vi. + + if (!pSource.IsAggregateType() || !pDest.IsAggregateType()) + { + return false; + } + AggregateType pConstructedSource = pSource.AsAggregateType(); + AggregateType pConstructedDest = pDest.AsAggregateType(); + if (pConstructedSource.GetOwningAggregate() != pConstructedDest.GetOwningAggregate()) + { + return false; + } + ExactTypeArgumentInference(pConstructedSource, pConstructedDest); + return true; + } + + //////////////////////////////////////////////////////////////////////////////// + + void ExactTypeArgumentInference( + AggregateType pSource, AggregateType pDest) + + { + Debug.Assert(pSource != null); + Debug.Assert(pDest != null); + Debug.Assert(pSource.GetOwningAggregate() == pDest.GetOwningAggregate()); + + TypeArray pSourceArgs = pSource.GetTypeArgsAll(); + TypeArray pDestArgs = pDest.GetTypeArgsAll(); + + Debug.Assert(pSourceArgs != null); + Debug.Assert(pDestArgs != null); + Debug.Assert(pSourceArgs.size == pDestArgs.size); + + for(int arg = 0; arg < pSourceArgs.size; ++arg) + { + ExactInference(pSourceArgs.Item(arg), pDestArgs.Item(arg)); + } + } + + //////////////////////////////////////////////////////////////////////////////// + // + // Lower-bound inferences + // + void LowerBoundInference(CType pSource, CType pDest) + { + // SPEC: A lower-bound inference from a CType U to a CType V is made as follows: + + // SPEC: If V is one of the unfixed Xi then U is added to the set of + // SPEC: lower bounds for Xi. + + if (LowerBoundTypeParameterInference(pSource, pDest)) + { + return ; + } + + // SPEC: Otherwise, if U is an array CType Ue[...] and V is either an array + // SPEC: CType Ve[...] of the same rank, or if U is a one-dimensional array + // SPEC: CType Ue[] and V is one of IEnumerable, ICollection or + // SPEC: IList then + // SPEC: if Ue is known to be a reference CType then a lower-bound inference + // SPEC: from Ue to Ve is made. + // SPEC: otherwise an exact inference from Ue to Ve is made. + + if (LowerBoundArrayInference(pSource, pDest)) + { + return; + } + + // SPEC: Otherwise, if V is nullable CType V1? and U is nullable CType U1? + // SPEC: then an exact inference is made from U1 to V1. + + if (ExactNullableInference(pSource, pDest)) + { + return; + } + + // UNDONE: At this point we could also do an inference from non-nullable U + // UNDONE: to nullable V. + // UNDONE: + // UNDONE: We tried implementing lower bound nullable inference as follows: + // UNDONE: + // UNDONE: Otherwise, if V is nullable CType V1? and U is a non-nullable + // UNDONE: struct CType then an exact inference is made from U to V1. + // UNDONE: + // UNDONE: However, this causes an unfortunate interaction with what + // UNDONE: looks like a bug in our implementation of section 15.2 of + // UNDONE: the specification. Namely, it appears that the code which + // UNDONE: checks whether a given method is compatible with + // UNDONE: a delegate CType assumes that if method CType inference succeeds, + // UNDONE: then the inferred types are compatible with the delegate types. + // UNDONE: This is not necessarily so; the inferred types could be compatible + // UNDONE: via a conversion other than reference or identity. + // UNDONE: + // UNDONE: We should take an action item to investigate this problem. + // UNDONE: Until then, we will turn off the proposed lower bound nullable + // UNDONE: inference. + + // if (LowerBoundNullableInference(pSource, pDest)) + // { + // return; + // } + + // SPEC: Otherwise... many cases for constructed generic types. + + if (LowerBoundConstructedInference(pSource, pDest)) + { + return; + } + + // SPEC: Otherwise, no inferences are made. + } + + //////////////////////////////////////////////////////////////////////////////// + + bool LowerBoundTypeParameterInference(CType pSource, CType pDest) + { + // SPEC: If V is one of the unfixed Xi then U is added to the set of bounds + // SPEC: for Xi. + if (pDest.IsTypeParameterType()) + { + TypeParameterType pTPType = pDest.AsTypeParameterType(); + if (pTPType.IsMethodTypeParameter() && IsUnfixed(pTPType)) + { + AddLowerBound(pTPType, pSource); + return true; + } + } + return false; + } + + //////////////////////////////////////////////////////////////////////////////// + + bool LowerBoundArrayInference(CType pSource, CType pDest) + { + // SPEC: Otherwise, if U is an array CType Ue[...] and V is either an array + // SPEC: CType Ve[...] of the same rank, or if U is a one-dimensional array + // SPEC: CType Ue[] and V is one of IEnumerable, ICollection, + // SPEC: IList, IReadOnlyCollection or IReadOnlyList then + // SPEC: if Ue is known to be a reference CType then a lower-bound inference + // SPEC: from Ue to Ve is made. + // SPEC: otherwise an exact inference from Ue to Ve is made. + + // Consider the following: + // + // abstract class B { public abstract M(U u) : where U : T; } + // class D : B { + // static void M(X[] x) { } + // public override M(U u) { M(u); } // should infer M + // } + + if (pSource.IsTypeParameterType()) + { + pSource = pSource.AsTypeParameterType().GetEffectiveBaseClass(); + } + + if (!pSource.IsArrayType()) + { + return false; + } + ArrayType pArraySource = pSource.AsArrayType(); + CType pElementSource = pArraySource.GetElementType(); + CType pElementDest = null; + + if (pDest.IsArrayType()) + { + ArrayType pArrayDest = pDest.AsArrayType(); + if (pArrayDest.rank != pArraySource.rank) + { + return false; + } + pElementDest = pArrayDest.GetElementType(); + } + else if (pDest.isPredefType(PredefinedType.PT_G_IENUMERABLE) || + pDest.isPredefType(PredefinedType.PT_G_ICOLLECTION) || + pDest.isPredefType(PredefinedType.PT_G_ILIST) || + pDest.isPredefType(PredefinedType.PT_G_IREADONLYCOLLECTION) || + pDest.isPredefType(PredefinedType.PT_G_IREADONLYLIST)) + { + if (pArraySource.rank != 1) + { + return false; + } + AggregateType pAggregateDest = pDest.AsAggregateType(); + pElementDest = pAggregateDest.GetTypeArgsThis().Item(0); + } + else + { + return false; + } + + if (pElementSource.IsRefType()) + { + LowerBoundInference(pElementSource, pElementDest); + } + else + { + ExactInference(pElementSource, pElementDest); + } + return true; + } + + //////////////////////////////////////////////////////////////////////////////// + + /* + bool LowerBoundNullableInference(CType pSource, CType pDest) + { + // SPEC ISSUE: As noted above, the spec does not clearly call out how + // SPEC ISSUE: to do CType inference to a nullable target. I propose the + // SPEC ISSUE: following: + // SPEC ISSUE: + // SPEC ISSUE: Otherwise, if V is nullable CType V1? and U is a + // SPEC ISSUE: non-nullable struct CType then an exact inference is made from U to V1. + + if (!pDest.IsNullableType() || !pSource.isStructType() || pSource.IsNullableType()) + { + return false; + } + ExactInference(pSource, pDest.AsNullableType().GetUnderlyingType()); + return true; + } + * */ + + //////////////////////////////////////////////////////////////////////////////// + + bool LowerBoundConstructedInference(CType pSource, CType pDest) + { + if (!pDest.IsAggregateType()) + { + return false; + } + + AggregateType pConstructedDest = pDest.AsAggregateType(); + TypeArray pDestArgs = pConstructedDest.GetTypeArgsAll(); + if (pDestArgs.size == 0) + { + return false; + } + + // SPEC: Otherwise, if V is a constructed class or struct CType C + // SPEC: and U is C then an exact inference + // SPEC: is made from each Ui to the corresponding Vi. + + // SPEC: Otherwise, if V is a constructed interface or delegate CType C + // SPEC: and U is C then an exact inference, + // SPEC: lower bound inference or upper bound inference + // SPEC: is made from each Ui to the corresponding Vi. + + if (pSource.IsAggregateType() && + pSource.AsAggregateType().GetOwningAggregate() == pConstructedDest.GetOwningAggregate()) + { + if (pSource.isInterfaceType() || pSource.isDelegateType()) + { + LowerBoundTypeArgumentInference(pSource.AsAggregateType(), pConstructedDest); + } + else + { + ExactTypeArgumentInference(pSource.AsAggregateType(), pConstructedDest); + } + return true; + } + + // SPEC: Otherwise, if V is a class CType C and U is a class CType which + // SPEC: inherits directly or indirectly from C then an exact ... + // SPEC: ... and U is a CType parameter with effective base class ... + // SPEC: ... and U is a CType parameter with an effective base class which inherits ... + + if (LowerBoundClassInference(pSource, pConstructedDest)) + { + return true; + } + + // SPEC: Otherwise, if V is an interface CType C and U is a class CType + // SPEC: or struct CType and there is a unique set U1...Uk such that U directly + // SPEC: or indirectly implements C then an exact ... + // SPEC: ... and U is an interface CType ... + // SPEC: ... and U is a CType parameter ... + + if (LowerBoundInterfaceInference(pSource, pConstructedDest)) + { + return true; + } + + return false; + } + + //////////////////////////////////////////////////////////////////////////////// + + bool LowerBoundClassInference(CType pSource, AggregateType pDest) + { + if (!pDest.isClassType()) + { + return false; + } + + // SPEC: Otherwise, if V is a class CType C and U is a class CType which + // SPEC: inherits directly or indirectly from C + // SPEC: then an exact inference is made from each Ui to the corresponding Vi. + // SPEC: Otherwise, if V is a class CType C and U is a CType parameter + // SPEC: with effective base class C + // SPEC: then an exact inference is made from each Ui to the corresponding Vi. + // SPEC: Otherwise, if V is a class CType C and U is a CType parameter + // SPEC: with an effective base class which inherits directly or indirectly from + // SPEC: C then an exact inference is made + // SPEC: from each Ui to the corresponding Vi. + + AggregateType pSourceBase = null; + + if (pSource.isClassType()) + { + pSourceBase = pSource.AsAggregateType().GetBaseClass(); + } + else if (pSource.IsTypeParameterType()) + { + pSourceBase = pSource.AsTypeParameterType().GetEffectiveBaseClass(); + } + + while(pSourceBase != null) + { + if (pSourceBase.GetOwningAggregate() == pDest.GetOwningAggregate()) + { + ExactTypeArgumentInference(pSourceBase, pDest); + return true; + } + pSourceBase = pSourceBase.GetBaseClass(); + } + return false; + } + + //////////////////////////////////////////////////////////////////////////////// + + bool LowerBoundInterfaceInference(CType pSource, AggregateType pDest) + { + if (!pDest.isInterfaceType()) + { + return false; + } + + // SPEC: Otherwise, if V is an interface CType C and U is a class CType + // SPEC: or struct CType and there is a unique set U1...Uk such that U directly + // SPEC: or indirectly implements C then an + // SPEC: exact, upper-bound, or lower-bound inference ... + // SPEC: ... and U is an interface CType ... + // SPEC: ... and U is a CType parameter ... + + //TypeArray pInterfaces = null; + + if (!pSource.isStructType() && !pSource.isClassType() && + !pSource.isInterfaceType() && !pSource.IsTypeParameterType()) + { + return false; + } + + var interfaces = pSource.AllPossibleInterfaces(); + AggregateType pInterface = null; + foreach (AggregateType pCurrent in interfaces) + { + if (pCurrent.GetOwningAggregate() == pDest.GetOwningAggregate()) + { + if (pInterface == null) + { + pInterface = pCurrent; + } + else if (pInterface != pCurrent) + { + // Not unique. Bail out. + return false; + } + } + } + if (pInterface == null) + { + return false; + } + LowerBoundTypeArgumentInference(pInterface, pDest); + return true; + } + + //////////////////////////////////////////////////////////////////////////////// + + void LowerBoundTypeArgumentInference( + AggregateType pSource, AggregateType pDest) + { + // SPEC: The choice of inference for the i-th CType argument is made + // SPEC: based on the declaration of the i-th CType parameter of C, as + // SPEC: follows: + // SPEC: if Ui is known to be of reference CType and the i-th CType parameter + // SPEC: was declared as covariant then a lower bound inference is made. + // SPEC: if Ui is known to be of reference CType and the i-th CType parameter + // SPEC: was declared as contravariant then an upper bound inference is made. + // SPEC: otherwise, an exact inference is made. + + Debug.Assert(pSource != null); + Debug.Assert(pDest != null); + Debug.Assert(pSource.GetOwningAggregate() == pDest.GetOwningAggregate()); + + TypeArray pTypeParams = pSource.GetOwningAggregate().GetTypeVarsAll(); + TypeArray pSourceArgs = pSource.GetTypeArgsAll(); + TypeArray pDestArgs = pDest.GetTypeArgsAll(); + + Debug.Assert(pTypeParams != null); + Debug.Assert(pSourceArgs != null); + Debug.Assert(pDestArgs != null); + + Debug.Assert(pTypeParams.size == pSourceArgs.size); + Debug.Assert(pTypeParams.size == pDestArgs.size); + + for(int arg = 0; arg < pSourceArgs.size; ++arg) + { + TypeParameterType pTypeParam = pTypeParams.ItemAsTypeParameterType(arg); + CType pSourceArg = pSourceArgs.Item(arg); + CType pDestArg = pDestArgs.Item(arg); + + if (pSourceArg.IsRefType() && pTypeParam.Covariant) + { + LowerBoundInference(pSourceArg, pDestArg); + } + else if (pSourceArg.IsRefType() && pTypeParam.Contravariant) + { + UpperBoundInference(pSourceArgs.Item(arg), pDestArgs.Item(arg)); + } + else + { + ExactInference(pSourceArgs.Item(arg), pDestArgs.Item(arg)); + } + } + } + + //////////////////////////////////////////////////////////////////////////////// + // + // Upper-bound inferences + // + void UpperBoundInference(CType pSource, CType pDest) + { + // SPEC: An upper-bound inference from a CType U to a CType V is made as follows: + + // SPEC: If V is one of the unfixed Xi then U is added to the set of + // SPEC: uppper bounds for Xi. + + if (UpperBoundTypeParameterInference(pSource, pDest)) + { + return ; + } + + // SPEC: Otherwise, if V is an array CType Ve[...] and U is an array + // SPEC: CType Ue[...] of the same rank, or if V is a one-dimensional array + // SPEC: CType Ve[] and U is one of IEnumerable, ICollection or + // SPEC: IList then + // SPEC: if Ue is known to be a reference CType then an upper-bound inference + // SPEC: from Ue to Ve is made. + // SPEC: otherwise an exact inference from Ue to Ve is made. + + if (UpperBoundArrayInference(pSource, pDest)) + { + return; + } + + // SPEC: Otherwise, if V is nullable CType V1? and U is nullable CType U1? + // SPEC: then an exact inference is made from U1 to V1. + + if (ExactNullableInference(pSource, pDest)) + { + return; + } + + // SPEC: Otherwise... cases for constructed types + + if (UpperBoundConstructedInference(pSource, pDest)) + { + return; + } + + // SPEC: Otherwise, no inferences are made. + } + + //////////////////////////////////////////////////////////////////////////////// + + bool UpperBoundTypeParameterInference(CType pSource, CType pDest) + { + // SPEC: If V is one of the unfixed Xi then U is added to the set of upper bounds + // SPEC: for Xi. + if (pDest.IsTypeParameterType()) + { + TypeParameterType pTPType = pDest.AsTypeParameterType(); + if (pTPType.IsMethodTypeParameter() && IsUnfixed(pTPType)) + { + AddUpperBound(pTPType, pSource); + return true; + } + } + return false; + } + + //////////////////////////////////////////////////////////////////////////////// + + bool UpperBoundArrayInference(CType pSource, CType pDest) + { + // SPEC: Otherwise, if V is an array CType Ve[...] and U is an array + // SPEC: CType Ue[...] of the same rank, or if V is a one-dimensional array + // SPEC: CType Ve[] and U is one of IEnumerable, ICollection, + // SPEC: IList, IReadOnlyCollection or IReadOnlyList then + // SPEC: if Ue is known to be a reference CType then an upper-bound inference + // SPEC: from Ue to Ve is made. + // SPEC: otherwise an exact inference from Ue to Ve is made. + + if (!pDest.IsArrayType()) + { + return false; + } + ArrayType pArrayDest = pDest.AsArrayType(); + CType pElementDest = pArrayDest.GetElementType(); + CType pElementSource = null; + + if (pSource.IsArrayType()) + { + ArrayType pArraySource = pSource.AsArrayType(); + if (pArrayDest.rank != pArraySource.rank) + { + return false; + } + pElementSource = pArraySource.GetElementType(); + } + else if (pSource.isPredefType(PredefinedType.PT_G_IENUMERABLE) || + pSource.isPredefType(PredefinedType.PT_G_ICOLLECTION) || + pSource.isPredefType(PredefinedType.PT_G_ILIST) || + pSource.isPredefType(PredefinedType.PT_G_IREADONLYLIST) || + pSource.isPredefType(PredefinedType.PT_G_IREADONLYCOLLECTION)) + { + if (pArrayDest.rank != 1) + { + return false; + } + AggregateType pAggregateSource = pSource.AsAggregateType(); + pElementSource = pAggregateSource.GetTypeArgsThis().Item(0); + } + else + { + return false; + } + + if (pElementSource.IsRefType()) + { + UpperBoundInference(pElementSource, pElementDest); + } + else + { + ExactInference(pElementSource, pElementDest); + } + return true; + } + + //////////////////////////////////////////////////////////////////////////////// + + bool UpperBoundConstructedInference(CType pSource, CType pDest) + { + if (!pSource.IsAggregateType()) + { + return false; + } + + AggregateType pConstructedSource = pSource.AsAggregateType(); + TypeArray pSourceArgs = pConstructedSource.GetTypeArgsAll(); + if (pSourceArgs.size == 0) + { + return false; + } + + // SPEC: Otherwise, if V is a constructed CType C and U is + // SPEC: C then an exact inference, + // SPEC: lower bound inference or upper bound inference + // SPEC: is made from each Ui to the corresponding Vi. + + if (pDest.IsAggregateType() && + pConstructedSource.GetOwningAggregate() == pDest.AsAggregateType().GetOwningAggregate()) + { + if (pDest.isInterfaceType() || pDest.isDelegateType()) + { + UpperBoundTypeArgumentInference(pConstructedSource, pDest.AsAggregateType()); + } + else + { + ExactTypeArgumentInference(pConstructedSource, pDest.AsAggregateType()); + } + return true; + } + + // SPEC: Otherwise, if U is a class CType C and V is a class CType which + // SPEC: inherits directly or indirectly from C then an exact ... + + if (UpperBoundClassInference(pConstructedSource, pDest)) + { + return true; + } + + // SPEC: Otherwise, if U is an interface CType C and V is a class CType + // SPEC: or struct CType and there is a unique set V1...Vk such that V directly + // SPEC: or indirectly implements C then an exact ... + // SPEC: ... and U is an interface CType ... + + if (UpperBoundInterfaceInference(pConstructedSource, pDest)) + { + return true; + } + + return false; + } + + //////////////////////////////////////////////////////////////////////////////// + + bool UpperBoundClassInference(AggregateType pSource, CType pDest) + { + if (!pSource.isClassType() || !pDest.isClassType()) + { + return false; + } + + // SPEC: Otherwise, if U is a class CType C and V is a class CType which + // SPEC: inherits directly or indirectly from C then an exact + // SPEC: inference is made from each Ui to the corresponding Vi. + + AggregateType pDestBase = pDest.AsAggregateType().GetBaseClass(); + + while(pDestBase != null) + { + if (pDestBase.GetOwningAggregate() == pSource.GetOwningAggregate()) + { + ExactTypeArgumentInference(pSource, pDestBase); + return true; + } + pDestBase = pDestBase.GetBaseClass(); + } + return false; + } + + //////////////////////////////////////////////////////////////////////////////// + + bool UpperBoundInterfaceInference(AggregateType pSource, CType pDest) + { + if (!pSource.isInterfaceType()) + { + return false; + } + + // SPEC: Otherwise, if U is an interface CType C and V is a class CType + // SPEC: or struct CType and there is a unique set V1...Vk such that V directly + // SPEC: or indirectly implements C then an exact ... + // SPEC: ... and U is an interface CType ... + + if (!pDest.isStructType() && !pDest.isClassType() && + !pDest.isInterfaceType()) + { + return false; + } + + var interfaces = pDest.AllPossibleInterfaces(); + AggregateType pInterface = null; + foreach (AggregateType pCurrent in interfaces) + { + if (pCurrent.GetOwningAggregate() == pSource.GetOwningAggregate()) + { + if (pInterface == null) + { + pInterface = pCurrent; + } + else if (pInterface != pCurrent) + { + // Not unique. Bail out. + return false; + } + } + } + if (pInterface == null) + { + return false; + } + UpperBoundTypeArgumentInference(pInterface, pDest.AsAggregateType()); + return true; + } + + //////////////////////////////////////////////////////////////////////////////// + + void UpperBoundTypeArgumentInference( + AggregateType pSource, AggregateType pDest) + { + // SPEC: The choice of inference for the i-th CType argument is made + // SPEC: based on the declaration of the i-th CType parameter of C, as + // SPEC: follows: + // SPEC: if Ui is known to be of reference CType and the i-th CType parameter + // SPEC: was declared as covariant then an upper-bound inference is made. + // SPEC: if Ui is known to be of reference CType and the i-th CType parameter + // SPEC: was declared as contravariant then a lower-bound inference is made. + // SPEC: otherwise, an exact inference is made. + + Debug.Assert(pSource != null); + Debug.Assert(pDest != null); + Debug.Assert(pSource.GetOwningAggregate() == pDest.GetOwningAggregate()); + + TypeArray pTypeParams = pSource.GetOwningAggregate().GetTypeVarsAll(); + TypeArray pSourceArgs = pSource.GetTypeArgsAll(); + TypeArray pDestArgs = pDest.GetTypeArgsAll(); + + Debug.Assert(pTypeParams != null); + Debug.Assert(pSourceArgs != null); + Debug.Assert(pDestArgs != null); + + Debug.Assert(pTypeParams.size == pSourceArgs.size); + Debug.Assert(pTypeParams.size == pDestArgs.size); + + for(int arg = 0; arg < pSourceArgs.size; ++arg) + { + TypeParameterType pTypeParam = pTypeParams.ItemAsTypeParameterType(arg); + CType pSourceArg = pSourceArgs.Item(arg); + CType pDestArg = pDestArgs.Item(arg); + + if (pSourceArg.IsRefType() && pTypeParam.Covariant) + { + UpperBoundInference(pSourceArg, pDestArg); + } + else if (pSourceArg.IsRefType() && pTypeParam.Contravariant) + { + LowerBoundInference(pSourceArgs.Item(arg), pDestArgs.Item(arg)); + } + else + { + ExactInference(pSourceArgs.Item(arg), pDestArgs.Item(arg)); + } + } + } + + //////////////////////////////////////////////////////////////////////////////// + // + // Fixing + // + bool Fix(int iParam) + { + Debug.Assert(IsUnfixed(iParam)); + + // SPEC: An unfixed CType parameter with a set of bounds is fixed as follows: + + // SPEC: The set of candidate types starts out as the set of all types in + // SPEC: the bounds. + + // SPEC: We then examine each bound in turn. For each exact bound U of Xi, + // SPEC: all types which are not identical to U are removed from the candidate set. + + // Optimization: if we have two or more exact bounds, fixing is impossible. + + if (pExactBounds[iParam].Count >= 2) + { + return false; + } + + List initialCandidates = new List(); + + // Optimization: if we have one exact bound then we need not add any + // inexact bounds; we're just going to remove them anyway. + + if (pExactBounds[iParam].IsEmpty()) + { + HashSet typeSet = new HashSet(); + + foreach (CType pCurrent in pLowerBounds[iParam]) + { + if (!typeSet.Contains(pCurrent)) + { + typeSet.Add(pCurrent); + initialCandidates.Add(pCurrent); + } + } + foreach (CType pCurrent in pUpperBounds[iParam]) + { + if (!typeSet.Contains(pCurrent)) + { + typeSet.Add(pCurrent); + initialCandidates.Add(pCurrent); + } + } + } + else + { + initialCandidates.Add(pExactBounds[iParam].Head()); + } + + if (initialCandidates.IsEmpty()) + { + return false; + } + + // SPEC: For each lower bound U of Xi all types to which there is not an + // SPEC: implicit conversion from U are removed from the candidate set. + + foreach (CType pBound in pLowerBounds[iParam]) + { + List removeList = new List(); + foreach (CType pCandidate in initialCandidates) + { + if (pBound != pCandidate && !binder.canConvert(pBound, pCandidate)) + { + removeList.Add(pCandidate); + } + } + foreach (CType pRemove in removeList) + { + initialCandidates.Remove(pRemove); + } + } + + // SPEC: For each upper bound U of Xi all types from which there is not an + // SPEC: implicit conversion to U are removed from the candidate set. + foreach (CType pBound in pUpperBounds[iParam]) + { + List removeList = new List(); + foreach (CType pCandidate in initialCandidates) + { + if (pBound != pCandidate && !binder.canConvert(pCandidate, pBound)) + { + removeList.Add(pCandidate); + } + } + foreach (CType pRemove in removeList) + { + initialCandidates.Remove(pRemove); + } + } + + // SPEC: If among the remaining candidate types there is a unique CType V from + // SPEC: which there is an implicit conversion to all the other candidate + // SPEC: types, then the parameter is fixed to V. + + CType pBest = null; + foreach (CType pCandidate in initialCandidates) + { + foreach (CType pCandidate2 in initialCandidates) + { + if (pCandidate != pCandidate2 && !binder.canConvert(pCandidate2, pCandidate)) + { + goto OuterBreak; + } + } + if (pBest != null) + { + // best candidate is not unique + return false; + } + pBest = pCandidate; + OuterBreak: + ; + } + + if (pBest == null) + { + // no best candidate + return false; + } + + // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + // RUNTIME BINDER ONLY CHANGE + // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + // + // just as we fix each individual type parameter, we need to + // ensure that we infer accessible type parameters, and so we + // widen them when necessary using the same technique that we + // used to alter the types at the beginning of binding. that + // way we get an accessible type, and if it so happens that + // the selected type is inappropriate (for conversions) then + // we let overload resolution sort it out. + // + // since we can never infer ref/out or pointer types here, we + // are more or less guaranteed a best accessible type. However, + // in the interest of safety, if it becomes impossible to + // choose a "best accessible" type, then we will fail type + // inference so we do not try to pass the inaccessible type + // back to overload resolution. + + CType pBestAccessible; + if (GetTypeManager().GetBestAccessibleType(binder.GetSemanticChecker(), binder.GetContext(), pBest, out pBestAccessible)) + { + pBest = pBestAccessible; + } + else + { + Debug.Assert(false, "Method type inference could not find an accessible type over the best candidate in fixed"); + return false; + } + + // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + // END RUNTIME BINDER ONLY CHANGE + // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + + pFixedResults[iParam] = pBest; + UpdateDependenciesAfterFix(iParam); + return true; + } + + //////////////////////////////////////////////////////////////////////////////// + // + // CType inference for conversion of method groups + // + bool InferForMethodGroupConversion() + { + // SPEC: Similar to calls of generic methods, CType inference must + // SPEC: also be applied when a method group M containing a generic + // SPEC: method is converted to a given delegate CType D. Given a method + // SPEC: Tr M(T1 x1, ... Tm xm) and the method group M being + // SPEC: assigned to the delegate CType D the task of CType inference is + // SPEC: to find CType arguments S1...Sn so that the expression M + // SPEC: becomes compatible with D. + // SPEC: Unlike the CType inference algorithm for generic method calls, in + // SPEC: this case there are only argument types, no argument expressions. + // SPEC: In particular, there are no anonymous functions and hence no need + // SPEC: for multiple phases of inference. + // SPEC: Instead, all Xi are considered unfixed and a lower-bound inference + // SPEC: is made from each argument CType Uj of D to the corresponding parameter + // SPEC: CType Tj of M. If for any of the Xi no bounds were found, CType + // SPEC: inference fails. Otherwise, all Xi are fixed to corresponding Si, + // SPEC: which are the result of CType inference. + + Debug.Assert(pMethodFormalParameterTypes != null); + Debug.Assert(pMethodArguments != null); + Debug.Assert(pMethodArguments.carg <= pMethodFormalParameterTypes.size); + + for (int iArg = 0; iArg < pMethodArguments.carg; iArg++) + { + CType pDest = pMethodFormalParameterTypes.Item(iArg); + CType pSource = pMethodArguments.types.Item(iArg); + if (pDest.IsParameterModifierType()) + { + pDest = pDest.AsParameterModifierType().GetParameterType(); + } + if (pSource.IsParameterModifierType()) + { + pSource = pSource.AsParameterModifierType().GetParameterType(); + } + + LowerBoundInference(pSource, pDest); + } + + bool success = true; + + // In the event of failure we still want to fix as much as we can, so + // that intellisense gives the best possible result. + + for (int iParam = 0; iParam < pMethodTypeParameters.size; iParam++) + { + if (!HasBound(iParam) || !Fix(iParam)) + { + success = false; + } + } + return success; + } + + //////////////////////////////////////////////////////////////////////////////// + // + // Helper methods + // + + //////////////////////////////////////////////////////////////////////////////// + + + SymbolLoader GetSymbolLoader() + { + return symbolLoader; + } + + //////////////////////////////////////////////////////////////////////////////// + + TypeManager GetTypeManager() + { + return GetSymbolLoader().GetTypeManager(); + } + + //////////////////////////////////////////////////////////////////////////////// + + BSYMMGR GetGlobalSymbols() + { + return GetSymbolLoader().getBSymmgr(); + } + + //////////////////////////////////////////////////////////////////////////////// + // + // In error recovery and reporting scenarios we sometimes end up in a situation + // like this: + // + // x.Foo( y=> + // + // and the question is, "is Foo a valid extension method of x?" If Foo is + // generic, then Foo will be something like: + // + // static Blah Foo(this Bar bar, Func f){ ... } + // + // What we would like to know is: given _only_ the expression x, can we infer + // what T is in Bar ? If we can, then for error recovery and reporting + // we can provisionally consider Foo to be an extension method of x. If we + // cannot deduce this just from x then we should consider Foo to not be an + // extension method of x, at least until we have more information. + // + // Clearly it is pointless to run multiple phases + + public static bool CanObjectOfExtensionBeInferred( + ExpressionBinder binder, + SymbolLoader symbolLoader, + MethodSymbol pMethod, + TypeArray pClassTypeArguments, + TypeArray pMethodFormalParameterTypes, + ArgInfos pMethodArguments) + { + Debug.Assert(pMethod != null); + Debug.Assert(pMethod.typeVars.size > 0); + Debug.Assert(pMethodFormalParameterTypes != null); + Debug.Assert(pMethod.isParamArray || pMethod.Params == pMethodFormalParameterTypes); + // We need at least one formal parameter type and at least one argument. + if (pMethodFormalParameterTypes.size < 1 || pMethod.InferenceMustFail()) + { + return false; + } + Debug.Assert(pMethodArguments != null); + Debug.Assert(pMethodArguments.carg <= pMethodFormalParameterTypes.size); + if (pMethodArguments.carg < 1) + { + return false; + } + var inferrer = new MethodTypeInferrer(binder, symbolLoader, + pMethodFormalParameterTypes, pMethodArguments, pMethod.typeVars, pClassTypeArguments); + return inferrer.CanInferExtensionObject(); + } + + //////////////////////////////////////////////////////////////////////////////// + + bool CanInferExtensionObject() + { + Debug.Assert(pMethodFormalParameterTypes != null); + Debug.Assert(pMethodFormalParameterTypes.size >= 1); + Debug.Assert(pMethodArguments != null); + Debug.Assert(pMethodArguments.carg >= 1); + CType pDest = pMethodFormalParameterTypes.Item(0); + CType pSource = pMethodArguments.types.Item(0); + if (pDest.IsParameterModifierType()) + { + pDest = pDest.AsParameterModifierType().GetParameterType(); + } + if (pSource.IsParameterModifierType()) + { + // This seems impossible, but this is an error scenario, so + // who knows? We'll err on the side of caution. + pSource = pSource.AsParameterModifierType().GetParameterType(); + } + // Rule out lambdas, nulls, and so on. + if (!IsReallyAType(pSource)) + { + return false; + } + LowerBoundInference(pSource, pDest); + // Now check to see that every CType parameter used by the first + // formal parameter CType was successfully inferred. + for (int iParam = 0; iParam < pMethodTypeParameters.size; ++iParam) + { + TypeParameterType pParam = pMethodTypeParameters.ItemAsTypeParameterType(iParam); + if (!TypeManager.TypeContainsType(pDest, pParam)) + { + continue; + } + Debug.Assert(IsUnfixed(iParam)); + if (!HasBound(iParam) || !Fix(iParam)) + { + return false; + } + } + return true; + } + } +} diff --git a/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/NameGenerator.cs b/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/NameGenerator.cs new file mode 100644 index 000000000..3378472ca --- /dev/null +++ b/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/NameGenerator.cs @@ -0,0 +1,12 @@ +// ==--== +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// ==--== + +namespace Microsoft.CSharp.RuntimeBinder.Semantics +{ + internal sealed class NameGenerator + { + } +} diff --git a/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/Nullable.cs b/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/Nullable.cs new file mode 100644 index 000000000..59c13c266 --- /dev/null +++ b/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/Nullable.cs @@ -0,0 +1,165 @@ +// ==++== +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// ==--== + +using System.Diagnostics; +using Microsoft.CSharp.RuntimeBinder.Errors; +using Microsoft.CSharp.RuntimeBinder.Syntax; + +namespace Microsoft.CSharp.RuntimeBinder.Semantics +{ + internal class CNullable + { + private SymbolLoader m_pSymbolLoader; + private ExprFactory m_exprFactory; + private ErrorHandling m_pErrorContext; + + private SymbolLoader GetSymbolLoader() + { + return m_pSymbolLoader; + } + private ExprFactory GetExprFactory() + { + return m_exprFactory; + } + private ErrorHandling GetErrorContext() + { + return m_pErrorContext; + } + public static bool IsNullableConstructor(EXPR expr) + { + Debug.Assert(expr != null); + + if (!expr.isCALL()) + { + return false; + } + + EXPRCALL pCall = expr.asCALL(); + if (pCall.GetMemberGroup().GetOptionalObject() != null) + { + return false; + } + + MethodSymbol meth = pCall.mwi.Meth(); + if (meth == null) + { + return false; + } + return meth.IsNullableConstructor(); + } + public static EXPR StripNullableConstructor(EXPR pExpr) + { + while (IsNullableConstructor(pExpr)) + { + Debug.Assert(pExpr.isCALL()); + pExpr = pExpr.asCALL().GetOptionalArguments(); + Debug.Assert(pExpr != null && !pExpr.isLIST()); + } + return pExpr; + } + + // Value + public EXPR BindValue(EXPR exprSrc) + { + Debug.Assert(exprSrc != null && exprSrc.type.IsNullableType()); + + // For new T?(x), the answer is x. + if (CNullable.IsNullableConstructor(exprSrc)) + { + Debug.Assert(exprSrc.asCALL().GetOptionalArguments() != null && !exprSrc.asCALL().GetOptionalArguments().isLIST()); + return exprSrc.asCALL().GetOptionalArguments(); + } + + CType typeBase = exprSrc.type.AsNullableType().GetUnderlyingType(); + AggregateType ats = exprSrc.type.AsNullableType().GetAts(GetErrorContext()); + if (ats == null) + { + EXPRPROP rval = GetExprFactory().CreateProperty(typeBase, exprSrc); + rval.SetError(); + return rval; + } + + // UNDONE: move this to transform pass ... + PropertySymbol prop = GetSymbolLoader().getBSymmgr().propNubValue; + if (prop == null) + { + prop = GetSymbolLoader().getPredefinedMembers().GetProperty(PREDEFPROP.PP_G_OPTIONAL_VALUE); + GetSymbolLoader().getBSymmgr().propNubValue = prop; + } + + PropWithType pwt = new PropWithType(prop, ats); + MethWithType mwt = new MethWithType(prop != null ? prop.methGet : null, ats); + MethPropWithInst mpwi = new MethPropWithInst(prop, ats); + EXPRMEMGRP pMemGroup = GetExprFactory().CreateMemGroup(exprSrc, mpwi); + EXPRPROP exprRes = GetExprFactory().CreateProperty(typeBase, null, null, pMemGroup, pwt, mwt, null); + + if (prop == null) + { + exprRes.SetError(); + } + + return exprRes; + } + + public EXPRCALL BindNew(EXPR pExprSrc) + { + Debug.Assert(pExprSrc != null); + + NullableType pNubSourceType = GetSymbolLoader().GetTypeManager().GetNullable(pExprSrc.type); + + AggregateType pSourceType = pNubSourceType.GetAts(GetErrorContext()); + if (pSourceType == null) + { + MethWithInst mwi = new MethWithInst(null, null); + EXPRMEMGRP pMemGroup = GetExprFactory().CreateMemGroup(pExprSrc, mwi); + EXPRCALL rval = GetExprFactory().CreateCall(0, pNubSourceType, null, pMemGroup, null); + rval.SetError(); + return rval; + } + + // UNDONE: move this to transform pass + MethodSymbol meth = GetSymbolLoader().getBSymmgr().methNubCtor; + if (meth == null) + { + meth = GetSymbolLoader().getPredefinedMembers().GetMethod(PREDEFMETH.PM_G_OPTIONAL_CTOR); + GetSymbolLoader().getBSymmgr().methNubCtor = meth; + } + + MethWithInst methwithinst = new MethWithInst(meth, pSourceType, BSYMMGR.EmptyTypeArray()); + EXPRMEMGRP memgroup = GetExprFactory().CreateMemGroup(null, methwithinst); + EXPRCALL pExprRes = GetExprFactory().CreateCall(EXPRFLAG.EXF_NEWOBJCALL | EXPRFLAG.EXF_CANTBENULL, pNubSourceType, pExprSrc, memgroup, methwithinst); + + if (meth == null) + { + pExprRes.SetError(); + } + + return pExprRes; + } + public CNullable(SymbolLoader symbolLoader, ErrorHandling errorContext, ExprFactory exprFactory) + { + m_pSymbolLoader = symbolLoader; + m_pErrorContext = errorContext; + m_exprFactory = exprFactory; + } + } + + internal partial class ExpressionBinder + { + // Create an expr for exprSrc.Value where exprSrc.type is a NullableType. + internal EXPR BindNubValue(EXPR exprSrc) + { + return m_nullable.BindValue(exprSrc); + } + + // Create an expr for new T?(exprSrc) where T is exprSrc.type. + private EXPRCALL BindNubNew(EXPR exprSrc) + { + return m_nullable.BindNew(exprSrc); + } + + } +} diff --git a/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/NullableLift.cs b/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/NullableLift.cs new file mode 100644 index 000000000..45bde057c --- /dev/null +++ b/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/NullableLift.cs @@ -0,0 +1,21 @@ +// ==++== +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// ==--== + +namespace Microsoft.CSharp.RuntimeBinder.Semantics +{ + internal enum NullableCallLiftKind + { + NotLifted, + Operator, + EqualityOperator, + InequalityOperator, + UserDefinedConversion, + NullableConversion, + NullableConversionConstructor, + NullableIntermediateConversion, + NotLiftedIntermediateConversion + } +} diff --git a/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/Operators.cs b/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/Operators.cs new file mode 100644 index 000000000..5bd280e56 --- /dev/null +++ b/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/Operators.cs @@ -0,0 +1,3677 @@ +// ==++== +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// ==--== + +using System.Collections.Generic; +using System.Diagnostics; +using Microsoft.CSharp.RuntimeBinder.Errors; +using Microsoft.CSharp.RuntimeBinder.Syntax; + +namespace Microsoft.CSharp.RuntimeBinder.Semantics +{ + internal partial class ExpressionBinder + { + + /* + These are the predefined binary operator signatures + + (object, object) : == != + (string, string) : == != + (string, string) : + + (string, object) : + + (object, string) : + + + (int, int) : / % + - << >> == != < > <= >=&| ^ + (uint, uint) : / % + - == != < > <= >=&| ^ + (long, long) : / % + - == != < > <= >=&| ^ + (ulong, ulong) : / % + - == != < > <= >=&| ^ + (uint, int) : << >> + (long, int) : << >> + (ulong, int) : << >> + + (float, float) : / % + - == != < > <= >= + (double, double) : / % + - == != < > <= >= + (decimal, decimal) : / % + - == != < > <= >= + + (bool, bool) : == != &| ^ && || + + (Sys.Del, Sys.Del) : == != + + // Below here the types cannot be represented entirely by a PREDEFTYPE. + (delegate, delegate) : + - == != + + (enum, enum) : - == != < > <= >=&| ^ + (enum, under) : + - + (under, enum) : + + + (ptr, ptr) : - + (ptr, int) : + - + (ptr, uint) : + - + (ptr, long) : + - + (ptr, ulong) : + - + (int, ptr) : + + (uint, ptr) : + + (long, ptr) : + + (ulong, ptr) : + + + (void, void) : == != < > <= >= + + There are the predefined unary operator signatures: + + int : + - ~ + uint : + ~ + long : + - ~ + ulong : + ~ + + float : + - + double : + - + decimal : + - + + bool : ! + + // Below here the types cannot be represented entirely by a PREDEFTYPE. + enum : ~ + ptr : + + Note that pointer operators cannot be lifted over nullable. + */ + + // BinOpBindMethod and UnaOpBindMethod are method pointer arrays to dispatch the appropriate operator binder. + // Method pointers must be in the order of the corresponding enums. We check check this when the full signature is set. + // When the binding method is looked up in these arrays we ASSERT + // if the array is out of bounds of the corresponding array. + + protected readonly BinOpSig[] g_binopSignatures; + + // We want unary minus to bind to "operator -(ulong)" and then we + // produce an error (since there is no pfn). We can't let - bind to a floating point type, + // since they lose precision. See the language spec. + + // Increment and decrement operators are special. + + protected readonly UnaOpSig[] g_rguos; + + protected EXPR bindUserDefinedBinOp(ExpressionKind ek, BinOpArgInfo info) + { + MethPropWithInst pmpwi = null; + if (info.pt1 <= PredefinedType.PT_ULONG && info.pt2 <= PredefinedType.PT_ULONG) + { + // REVIEW : What should we use as the cutoff? + return null; + } + + EXPR expr = null; + + switch (info.binopKind) + { + case BinOpKind.Logical: + { + // Logical operators cannot be overloaded, but use the bitwise overloads. + EXPRCALL call = BindUDBinop((ExpressionKind)(ek - ExpressionKind.EK_LOGAND + ExpressionKind.EK_BITAND), info.arg1, info.arg2, true, out pmpwi); + if (call != null) + { + if (call.isOK()) + { + expr = BindUserBoolOp(ek, call); + } + else + { + expr = call; + } + } + break; + } + default: + expr = BindUDBinop(ek, info.arg1, info.arg2, false, out pmpwi); + break; + } + + if (expr == null) + { + return null; + } + + return GetExprFactory().CreateUserDefinedBinop(ek, expr.type, info.arg1, info.arg2, expr, pmpwi); + } + + // Adds special signatures to the candidate list. If we find an exact match + // then it will be the last item on the list and we return true. + protected bool GetSpecialBinopSignatures(List prgbofs, BinOpArgInfo info) + { + Debug.Assert(prgbofs != null); + if (info.pt1 <= PredefinedType.PT_ULONG && info.pt2 <= PredefinedType.PT_ULONG) + { + return false; + } + return GetDelBinOpSigs(prgbofs, info) || + GetEnumBinOpSigs(prgbofs, info) || + GetPtrBinOpSigs(prgbofs, info) || + GetRefEqualSigs(prgbofs, info); + } + + // Adds standard and lifted signatures to the candidate list. If we find an exact match + // then it will be the last item on the list and we return true. + + protected bool GetStandardAndLiftedBinopSignatures(List rgbofs, BinOpArgInfo info) + { + Debug.Assert(rgbofs != null); + + int ibos; + int ibosMinLift; + + ibosMinLift = GetSymbolLoader().FCanLift() ? 0 : g_binopSignatures.Length; + for (ibos = 0; ibos < g_binopSignatures.Length; ibos++) + { + BinOpSig bos = g_binopSignatures[ibos]; + if ((bos.mask & info.mask) == 0) + { + continue; + } + + CType typeSig1 = GetOptPDT(bos.pt1, PredefinedTypes.isRequired(bos.pt1)); + CType typeSig2 = GetOptPDT(bos.pt2, PredefinedTypes.isRequired(bos.pt2)); + if (typeSig1 == null || typeSig2 == null) + continue; + + ConvKind cv1 = GetConvKind(info.pt1, bos.pt1); + ConvKind cv2 = GetConvKind(info.pt2, bos.pt2); + LiftFlags grflt = LiftFlags.None; + + switch (cv1) + { + default: + VSFAIL("Shouldn't happen!"); + continue; + + case ConvKind.None: + continue; + case ConvKind.Explicit: + if (!info.arg1.isCONSTANT_OK()) + { + continue; + } + // Need to try to convert. + if (canConvert(info.arg1, typeSig1)) + { + break; + } + if (ibos < ibosMinLift || !bos.CanLift()) + { + continue; + } + Debug.Assert(typeSig1.IsValType()); + + typeSig1 = GetSymbolLoader().GetTypeManager().GetNullable(typeSig1); + if (!canConvert(info.arg1, typeSig1)) + { + continue; + } + switch (GetConvKind(info.ptRaw1, bos.pt1)) + { + default: + grflt = grflt | LiftFlags.Convert1; + break; + case ConvKind.Implicit: + case ConvKind.Identity: + grflt = grflt | LiftFlags.Lift1; + break; + } + break; + case ConvKind.Unknown: + if (canConvert(info.arg1, typeSig1)) + { + break; + } + if (ibos < ibosMinLift || !bos.CanLift()) + { + continue; + } + Debug.Assert(typeSig1.IsValType()); + + typeSig1 = GetSymbolLoader().GetTypeManager().GetNullable(typeSig1); + if (!canConvert(info.arg1, typeSig1)) + { + continue; + } + switch (GetConvKind(info.ptRaw1, bos.pt1)) + { + default: + grflt = grflt | LiftFlags.Convert1; + break; + case ConvKind.Implicit: + case ConvKind.Identity: + grflt = grflt | LiftFlags.Lift1; + break; + } + break; + case ConvKind.Implicit: + break; + case ConvKind.Identity: + if (cv2 == ConvKind.Identity) + { + BinOpFullSig newsig = new BinOpFullSig(this, bos); + if (newsig.Type1() != null && newsig.Type2() != null) + { + // Exact match. + rgbofs.Add(newsig); + return true; + } + } + break; + } + + switch (cv2) + { + default: + VSFAIL("Shouldn't happen!"); + continue; + case ConvKind.None: + continue; + case ConvKind.Explicit: + if (!info.arg2.isCONSTANT_OK()) + { + continue; + } + // Need to try to convert. + if (canConvert(info.arg2, typeSig2)) + { + break; + } + if (ibos < ibosMinLift || !bos.CanLift()) + { + continue; + } + Debug.Assert(typeSig2.IsValType()); + + typeSig2 = GetSymbolLoader().GetTypeManager().GetNullable(typeSig2); + if (!canConvert(info.arg2, typeSig2)) + { + continue; + } + switch (GetConvKind(info.ptRaw2, bos.pt2)) + { + default: + grflt = grflt | LiftFlags.Convert2; + break; + case ConvKind.Implicit: + case ConvKind.Identity: + grflt = grflt | LiftFlags.Lift2; + break; + } + break; + case ConvKind.Unknown: + if (canConvert(info.arg2, typeSig2)) + { + break; + } + if (ibos < ibosMinLift || !bos.CanLift()) + { + continue; + } + Debug.Assert(typeSig2.IsValType()); + + typeSig2 = GetSymbolLoader().GetTypeManager().GetNullable(typeSig2); + if (!canConvert(info.arg2, typeSig2)) + { + continue; + } + switch (GetConvKind(info.ptRaw2, bos.pt2)) + { + default: + grflt = grflt | LiftFlags.Convert2; + break; + case ConvKind.Implicit: + case ConvKind.Identity: + grflt = grflt | LiftFlags.Lift2; + break; + } + break; + case ConvKind.Identity: + case ConvKind.Implicit: + break; + } + + if (grflt != LiftFlags.None) + { + // We have a lifted signature. + rgbofs.Add(new BinOpFullSig(typeSig1, typeSig2, bos.pfn, bos.grfos, grflt, bos.fnkind)); + + // NOTE: Can't skip any if we use a lifted signature because the + // type might convert to int? and to long (but not to int) in which + // case we should get an ambiguity. But we can skip the lifted ones.... + ibosMinLift = ibos + bos.cbosSkip + 1; + } + else + { + // Record it as applicable and skip accordingly. + rgbofs.Add(new BinOpFullSig(this, bos)); + ibos += bos.cbosSkip; + } + } + return false; + } + + // Returns the index of the best match, or -1 if there is no best match. + protected int FindBestSignatureInList( + List binopSignatures, + BinOpArgInfo info) + { + Debug.Assert(binopSignatures != null); + + if (binopSignatures.Count == 1) + { + return 0; + } + + int bestSignature = 0; + int index; + // Try to find a candidate for the best. + for (index = 1; index < binopSignatures.Count; index++) + { + if (bestSignature < 0) + { + bestSignature = index; + } + else + { + int nT = WhichBofsIsBetter(binopSignatures[bestSignature], binopSignatures[index], info.type1, info.type2); + if (nT == 0) + { + bestSignature = -1; + } + else if (nT > 0) + { + bestSignature = index; + } + } + } + + if (bestSignature == -1) + { + return -1; + } + + // Verify that the candidate really is not worse than all others. + // CONSIDER: Do we need to loop over the whole list here, or just + // CONSIDER: from 0 . bestSignature - 1? + for (index = 0; index < binopSignatures.Count; index++) + { + if (index == bestSignature) + { + continue; + } + if (WhichBofsIsBetter(binopSignatures[bestSignature], binopSignatures[index], info.type1, info.type2) >= 0) + { + return -1; + } + } + return bestSignature; + } + + protected EXPRBINOP bindNullEqualityComparison(ExpressionKind ek, BinOpArgInfo info) + { + EXPR arg1 = info.arg1; + EXPR arg2 = info.arg2; + if (info.binopKind == BinOpKind.Equal) + { + CType typeBool = GetReqPDT(PredefinedType.PT_BOOL); + EXPRBINOP exprRes = null; + if (info.type1.IsNullableType() && info.type2.IsNullType()) + { + arg2 = GetExprFactory().CreateZeroInit(info.type1); + exprRes = GetExprFactory().CreateBinop(ek, typeBool, arg1, arg2); + + } + if (info.type1.IsNullType() && info.type2.IsNullableType()) + { + arg1 = GetExprFactory().CreateZeroInit(info.type2); + exprRes = GetExprFactory().CreateBinop(ek, typeBool, arg1, arg2); + } + if (exprRes != null) + { + exprRes.isLifted = true; + return exprRes; + } + } + EXPR pExpr = BadOperatorTypesError(ek, info.arg1, info.arg2, GetTypes().GetErrorSym()); + Debug.Assert(pExpr.isBIN()); + return pExpr.asBIN(); + } + + /* + This handles binding binary operators by first checking for user defined operators, then + applying overload resolution to the predefined operators. It handles lifting over nullable. + */ + public EXPR BindStandardBinop(ExpressionKind ek, EXPR arg1, EXPR arg2) + { + Debug.Assert(arg1 != null); + Debug.Assert(arg2 != null); + + EXPRFLAG flags = 0; + + BinOpArgInfo info = new BinOpArgInfo(arg1, arg2); + if (!GetBinopKindAndFlags(ek, out info.binopKind, out flags)) + { + // If we dont get the BinopKind and the flags, then we must have had some bad operator types. + + return BadOperatorTypesError(ek, arg1, arg2); + } + + // UNDONE: Consider making this an accessor. + info.mask = (BinOpMask)(1 << (int)info.binopKind); + + // REVIEW : What's the correct number to use? + List binopSignatures = new List(); + int bestBinopSignature = -1; + + // First check if this is a user defined binop. If it is, return it. + EXPR exprUD = bindUserDefinedBinOp(ek, info); + if (exprUD != null) + { + return exprUD; + } + + // Get the special binop signatures. If successful, the special binop signature will be + // the last item in the array of signatures that we give it. + + bool exactMatch = GetSpecialBinopSignatures(binopSignatures, info); + if (!exactMatch) + { + // No match, try to get standard and lifted binop signatures. + + exactMatch = GetStandardAndLiftedBinopSignatures(binopSignatures, info); + } + + // If we have an exact match in either the special binop signatures or the standard/lifted binop + // signatures, then we set our best match. Otherwise, we check if we had any signatures at all. + // If we didn't, then its possible where we have x == null, where x is nullable, so try to bind + // the null equality comparison. Otherwise, we had some ambiguity - we have a match, but its not exact. + + if (exactMatch) + { + Debug.Assert(binopSignatures.Count > 0); + bestBinopSignature = binopSignatures.Count - 1; + } + else if (binopSignatures.Count == 0) + { + // If we got no matches then it's possible that we're in the case + // x == null, where x is nullable. + return bindNullEqualityComparison(ek, info); + } + else + { + // We had some matches, try to find the best one. FindBestSignatureInList returns < 0 if + // we dont have a best one, otherwise it returns the index of the best one in our list that + // we give it. + + bestBinopSignature = FindBestSignatureInList(binopSignatures, info); + if (bestBinopSignature < 0) + { + // Ambiguous. + + return ambiguousOperatorError(ek, arg1, arg2); + } + } + + // If we're here, we should have a binop signature that exactly matches. + + Debug.Assert(bestBinopSignature < binopSignatures.Count); + + // We've found the one to use, so lets go and bind it. + + return BindStandardBinopCore(info, binopSignatures[bestBinopSignature], ek, flags); + } + + protected EXPR BindStandardBinopCore(BinOpArgInfo info, BinOpFullSig bofs, ExpressionKind ek, EXPRFLAG flags) + { + if (bofs.pfn == null) + { + return BadOperatorTypesError(ek, info.arg1, info.arg2); + } + + if (!bofs.isLifted() || !bofs.AutoLift()) + { + EXPR expr1 = info.arg1; + EXPR expr2 = info.arg2; + if (bofs.ConvertOperandsBeforeBinding()) + { + expr1 = mustConvert(expr1, bofs.Type1()); + expr2 = mustConvert(expr2, bofs.Type2()); + } + if (bofs.fnkind == BinOpFuncKind.BoolBitwiseOp) + { + return BindBoolBitwiseOp(ek, flags, expr1, expr2, bofs); + } + return bofs.pfn(ek, flags, expr1, expr2); + } + Debug.Assert(bofs.fnkind != BinOpFuncKind.BoolBitwiseOp); + return BindLiftedStandardBinOp(info, bofs, ek, flags); + } + EXPR BindLiftedStandardBinOp(BinOpArgInfo info, BinOpFullSig bofs, ExpressionKind ek, EXPRFLAG flags) + { + Debug.Assert(bofs.Type1().IsNullableType() || bofs.Type2().IsNullableType()); + + EXPR arg1 = info.arg1; + EXPR arg2 = info.arg2; + + // We want to get the base types of the arguments and attempt to bind the non-lifted form of the + // method so that we error report (ie divide by zero etc), and then we store in the resulting + // binop that we have a lifted operator. + + EXPR pArgument1 = null; + EXPR pArgument2 = null; + EXPR nonLiftedArg1 = null; + EXPR nonLiftedArg2 = null; + EXPR nonLiftedResult = null; + CType resultType = null; + + LiftArgument(arg1, bofs.Type1(), bofs.ConvertFirst(), out pArgument1, out nonLiftedArg1); + LiftArgument(arg2, bofs.Type2(), bofs.ConvertSecond(), out pArgument2, out nonLiftedArg2); + + // Now call the non-lifted method to generate errors, and stash the result. + if (!nonLiftedArg1.isNull() && !nonLiftedArg2.isNull()) + { + // Only compute the method if theres no nulls. If there are, we'll special case it + // later, since operations with a null operand are null. + nonLiftedResult = bofs.pfn(ek, flags, nonLiftedArg1, nonLiftedArg2); + } + + // Check if we have a comparison. If so, set the result type to bool. + if (info.binopKind == BinOpKind.Compare || info.binopKind == BinOpKind.Equal) + { + resultType = GetReqPDT(PredefinedType.PT_BOOL); + } + else + { + if (bofs.fnkind == BinOpFuncKind.EnumBinOp) + { + AggregateType enumType; + resultType = GetEnumBinOpType(ek, nonLiftedArg1.type, nonLiftedArg2.type, out enumType); + } + else + { + resultType = pArgument1.type; + } + resultType = resultType.IsNullableType() ? resultType : GetSymbolLoader().GetTypeManager().GetNullable(resultType); + } + + EXPRBINOP exprRes = GetExprFactory().CreateBinop(ek, resultType, pArgument1, pArgument2); + mustCast(nonLiftedResult, resultType, 0); + exprRes.isLifted = true; + exprRes.flags |= flags; + Debug.Assert((exprRes.flags & EXPRFLAG.EXF_LVALUE) == 0); + + return exprRes; + } + + ///////////////////////////////////////////////////////////////////////////////// + + void LiftArgument(EXPR pArgument, CType pParameterType, bool bConvertBeforeLift, + out EXPR ppLiftedArgument, out EXPR ppNonLiftedArgument) + { + EXPR pLiftedArgument = mustConvert(pArgument, pParameterType); + if (pLiftedArgument != pArgument) + { + MarkAsIntermediateConversion(pLiftedArgument); + } + + EXPR pNonLiftedArgument = pArgument; + if (pParameterType.IsNullableType()) + { + if (pNonLiftedArgument.isNull()) + { + pNonLiftedArgument = mustCast(pNonLiftedArgument, pParameterType); + } + pNonLiftedArgument = mustCast(pNonLiftedArgument, pParameterType.AsNullableType().GetUnderlyingType()); + if (bConvertBeforeLift) + { + MarkAsIntermediateConversion(pNonLiftedArgument); + } + } + else + { + pNonLiftedArgument = pLiftedArgument; + } + ppLiftedArgument = pLiftedArgument; + ppNonLiftedArgument = pNonLiftedArgument; + } + + /* + Get the special signatures when at least one of the args is a delegate instance. + Returns true iff an exact signature match is found. + */ + protected bool GetDelBinOpSigs(List prgbofs, BinOpArgInfo info) + { + if (!info.ValidForDelegate()) + { + return false; + } + if (!info.type1.isDelegateType() && !info.type2.isDelegateType()) + { + return false; + } + + // Don't allow comparison with an anonymous method or lambda. It's just too weird. + if (((info.mask & BinOpMask.Equal) != 0) && (info.type1.IsBoundLambdaType() || info.type2.IsBoundLambdaType())) + return false; + + // No conversions needed. Determine the lifting. This is the common case. + if (info.type1 == info.type2) + { + prgbofs.Add(new BinOpFullSig(info.type1, info.type2, BindDelBinOp, OpSigFlags.Reference, LiftFlags.None, BinOpFuncKind.DelBinOp)); + return true; + } + + // Now, for each delegate type, if both arguments convert to that delegate type, that is a candidate + // for this binary operator. It's possible that we add two candidates, in which case they will compete + // in overload resolution. Or we could add no candidates. + + bool t1tot2 = info.type2.isDelegateType() && canConvert(info.arg1, info.type2); + bool t2tot1 = info.type1.isDelegateType() && canConvert(info.arg2, info.type1); + + if (t1tot2) + { + prgbofs.Add(new BinOpFullSig(info.type2, info.type2, BindDelBinOp, OpSigFlags.Reference, LiftFlags.None, BinOpFuncKind.DelBinOp)); + } + + if (t2tot1) + { + prgbofs.Add(new BinOpFullSig(info.type1, info.type1, BindDelBinOp, OpSigFlags.Reference, LiftFlags.None, BinOpFuncKind.DelBinOp)); + } + + // Might be ambiguous so return false. + return false; + } + + /* + Utility method to determine whether arg1 is convertible to typeDst, either in a regular + scenario or lifted scenario. Sets pgrflt, ptypeSig1 and ptypeSig2 accordingly. + */ + bool CanConvertArg1(BinOpArgInfo info, CType typeDst, out LiftFlags pgrflt, + out CType ptypeSig1, out CType ptypeSig2) + { + ptypeSig1 = null; + ptypeSig2 = null; + Debug.Assert(!typeDst.IsNullableType()); + + if (canConvert(info.arg1, typeDst)) + pgrflt = LiftFlags.None; + else + { + pgrflt = LiftFlags.None; + if (!GetSymbolLoader().FCanLift()) + return false; + typeDst = GetSymbolLoader().GetTypeManager().GetNullable(typeDst); + if (!canConvert(info.arg1, typeDst)) + return false; + pgrflt = LiftFlags.Convert1; + } + ptypeSig1 = typeDst; + + if (info.type2.IsNullableType()) + { + pgrflt = pgrflt | LiftFlags.Lift2; + ptypeSig2 = GetSymbolLoader().GetTypeManager().GetNullable(info.typeRaw2); + } + else + ptypeSig2 = info.typeRaw2; + + return true; + } + + + /* + Same as CanConvertArg1 but with the indices interchanged! + */ + bool CanConvertArg2(BinOpArgInfo info, CType typeDst, out LiftFlags pgrflt, + out CType ptypeSig1, out CType ptypeSig2) + { + Debug.Assert(!typeDst.IsNullableType()); + ptypeSig1 = null; + ptypeSig2 = null; + + if (canConvert(info.arg2, typeDst)) + pgrflt = LiftFlags.None; + else + { + pgrflt = LiftFlags.None; + if (!GetSymbolLoader().FCanLift()) + return false; + typeDst = GetSymbolLoader().GetTypeManager().GetNullable(typeDst); + if (!canConvert(info.arg2, typeDst)) + return false; + pgrflt = LiftFlags.Convert2; + } + ptypeSig2 = typeDst; + + if (info.type1.IsNullableType()) + { + pgrflt = pgrflt | LiftFlags.Lift1; + ptypeSig1 = GetSymbolLoader().GetTypeManager().GetNullable(info.typeRaw1); + } + else + ptypeSig1 = info.typeRaw1; + + return true; + } + + + /* + Record the appropriate binary operator full signature from the given BinOpArgInfo. This assumes + that any NullableType valued args should be lifted. + */ + void RecordBinOpSigFromArgs(List prgbofs, BinOpArgInfo info) + { + LiftFlags grflt = LiftFlags.None; + CType typeSig1; + CType typeSig2; + + if (info.type1 != info.typeRaw1) + { + Debug.Assert(info.type1.IsNullableType()); + grflt = grflt | LiftFlags.Lift1; + typeSig1 = GetSymbolLoader().GetTypeManager().GetNullable(info.typeRaw1); + } + else + typeSig1 = info.typeRaw1; + + if (info.type2 != info.typeRaw2) + { + Debug.Assert(info.type2.IsNullableType()); + grflt = grflt | LiftFlags.Lift2; + typeSig2 = GetSymbolLoader().GetTypeManager().GetNullable(info.typeRaw2); + } + else + typeSig2 = info.typeRaw2; + + prgbofs.Add(new BinOpFullSig(typeSig1, typeSig2, BindEnumBinOp, OpSigFlags.Value, grflt, BinOpFuncKind.EnumBinOp)); + } + + /* + Get the special signatures when at least one of the args is an enum. Return true if + we find an exact match. + */ + protected bool GetEnumBinOpSigs(List prgbofs, BinOpArgInfo info) + { + if (!info.typeRaw1.isEnumType() && !info.typeRaw2.isEnumType()) + { + return false; + } + + // (enum, enum) : - == != < > <= >=&| ^ + // (enum, under) : + - + // (under, enum) : + + CType typeSig1 = null; + CType typeSig2 = null; + LiftFlags grflt = LiftFlags.None; + + // Look for the no conversions cases. Still need to determine the lifting. These are the common case. + if (info.typeRaw1 == info.typeRaw2) + { + if (!info.ValidForEnum()) + { + return false; + } + RecordBinOpSigFromArgs(prgbofs, info); + return true; + } + + bool isValidForEnum; + + if (info.typeRaw1.isEnumType()) + { + isValidForEnum = (info.typeRaw2 == info.typeRaw1.underlyingEnumType() && info.ValidForEnumAndUnderlyingType()); + } + else + { + isValidForEnum = (info.typeRaw1 == info.typeRaw2.underlyingEnumType() && info.ValidForUnderlyingTypeAndEnum()); + } + + if (isValidForEnum) + { + RecordBinOpSigFromArgs(prgbofs, info); + return true; + } + + // Now deal with the conversion cases. Since there are no conversions from enum types to other + // enum types we never need to do both cases. + + if (info.typeRaw1.isEnumType()) + { + isValidForEnum = info.ValidForEnum() && CanConvertArg2(info, info.typeRaw1, out grflt, out typeSig1, out typeSig2) || + info.ValidForEnumAndUnderlyingType() && CanConvertArg2(info, info.typeRaw1.underlyingEnumType(), out grflt, out typeSig1, out typeSig2); + } + else + { + isValidForEnum = info.ValidForEnum() && CanConvertArg1(info, info.typeRaw2, out grflt, out typeSig1, out typeSig2) || + info.ValidForEnumAndUnderlyingType() && CanConvertArg1(info, info.typeRaw2.underlyingEnumType(), out grflt, out typeSig1, out typeSig2); + } + + if (isValidForEnum) + { + prgbofs.Add(new BinOpFullSig(typeSig1, typeSig2, BindEnumBinOp, OpSigFlags.Value, grflt, BinOpFuncKind.EnumBinOp)); + } + return false; + } + + + /* + Get the special signatures when at least one of the args is a pointer. Since pointers can't be + type arguments, a nullable pointer is illegal, so no sense trying to lift any of these. + + NOTE: We don't filter out bad operators on void pointers since BindPtrBinOp gives better + error messages than the operator overload resolution does. + */ + protected bool GetPtrBinOpSigs(List prgbofs, BinOpArgInfo info) + { + if (!info.type1.IsPointerType() && !info.type2.IsPointerType()) + { + return false; + } + + // (ptr, ptr) : - + // (ptr, int) : + - + // (ptr, uint) : + - + // (ptr, long) : + - + // (ptr, ulong) : + - + // (int, ptr) : + + // (uint, ptr) : + + // (long, ptr) : + + // (ulong, ptr) : + + // (void, void) : == != < > <= >= + + // Check the common case first. + if (info.type1.IsPointerType() && info.type2.IsPointerType()) + { + if (info.ValidForVoidPointer()) + { + prgbofs.Add(new BinOpFullSig(info.type1, info.type2, BindPtrCmpOp, OpSigFlags.None, LiftFlags.None, BinOpFuncKind.PtrCmpOp)); + return true; + } + if (info.type1 == info.type2 && info.ValidForPointer()) + { + prgbofs.Add(new BinOpFullSig(info.type1, info.type2, BindPtrBinOp, OpSigFlags.None, LiftFlags.None, BinOpFuncKind.PtrBinOp)); + return true; + } + return false; + } + + CType typeT; + + if (info.type1.IsPointerType()) + { + if (info.type2.IsNullType()) + { + if (!info.ValidForVoidPointer()) + { + return false; + } + prgbofs.Add(new BinOpFullSig(info.type1, info.type1, BindPtrCmpOp, OpSigFlags.Convert, LiftFlags.None, BinOpFuncKind.PtrCmpOp)); + return true; + } + if (!info.ValidForPointerAndNumber()) + { + return false; + } + + for (uint i = 0; i < rgptIntOp.Length; i++) + { + if (canConvert(info.arg2, typeT = GetReqPDT(rgptIntOp[i]))) + { + prgbofs.Add(new BinOpFullSig(info.type1, typeT, BindPtrBinOp, OpSigFlags.Convert, LiftFlags.None, BinOpFuncKind.PtrBinOp)); + return true; + } + } + return false; + } + + Debug.Assert(info.type2.IsPointerType()); + if (info.type1.IsNullType()) + { + if (!info.ValidForVoidPointer()) + { + return false; + } + prgbofs.Add(new BinOpFullSig(info.type2, info.type2, BindPtrCmpOp, OpSigFlags.Convert, LiftFlags.None, BinOpFuncKind.PtrCmpOp)); + return true; + } + if (!info.ValidForNumberAndPointer()) + { + return false; + } + + for (uint i = 0; i < rgptIntOp.Length; i++) + { + if (canConvert(info.arg1, typeT = GetReqPDT(rgptIntOp[i]))) + { + prgbofs.Add(new BinOpFullSig(typeT, info.type2, BindPtrBinOp, OpSigFlags.Convert, LiftFlags.None, BinOpFuncKind.PtrBinOp)); + return true; + } + } + return false; + } + + + /* + See if standard reference equality applies. Make sure not to return true if another == operator + may be applicable and better (or ambiguous)! This also handles == on System.Delegate, since + it has special rules as well. + */ + protected bool GetRefEqualSigs(List prgbofs, BinOpArgInfo info) + { + if (info.mask != BinOpMask.Equal) + { + return false; + } + + if (info.type1 != info.typeRaw1 || info.type2 != info.typeRaw2) + { + return false; + } + + bool fRet = false; + CType type1 = info.type1; + CType type2 = info.type2; + CType typeObj = GetReqPDT(PredefinedType.PT_OBJECT); + CType typeCls = null; + + if (type1.IsNullType() && type2.IsNullType()) + { + typeCls = typeObj; + fRet = true; + goto LRecord; + } + + // Check for: operator ==(System.Delegate, System.Delegate). + CType typeDel; + typeDel = GetReqPDT(PredefinedType.PT_DELEGATE); + + if (canConvert(info.arg1, typeDel) && canConvert(info.arg2, typeDel) && + !type1.isDelegateType() && !type2.isDelegateType()) + { + prgbofs.Add(new BinOpFullSig(typeDel, typeDel, BindDelBinOp, OpSigFlags.Convert, LiftFlags.None, BinOpFuncKind.DelBinOp)); + } + + // The reference type equality operators only handle reference types. + FUNDTYPE ft1; + ft1 = type1.fundType(); + FUNDTYPE ft2; + ft2 = type2.fundType(); + + switch (ft1) + { + default: + return false; + case FUNDTYPE.FT_REF: + break; + case FUNDTYPE.FT_VAR: + if (type1.AsTypeParameterType().IsValueType() || (!type1.AsTypeParameterType().IsReferenceType() && !type2.IsNullType())) + return false; + type1 = type1.AsTypeParameterType().GetEffectiveBaseClass(); + break; + } + if (type2.IsNullType()) + { + fRet = true; + // We don't need to determine the actual best type since we're + // returning true - indicating that we've found the best operator. + typeCls = typeObj; + goto LRecord; + } + + switch (ft2) + { + default: + return false; + case FUNDTYPE.FT_REF: + break; + case FUNDTYPE.FT_VAR: + if (type2.AsTypeParameterType().IsValueType() || (!type2.AsTypeParameterType().IsReferenceType() && !type1.IsNullType())) + return false; + type2 = type2.AsTypeParameterType().GetEffectiveBaseClass(); + break; + } + if (type1.IsNullType()) + { + fRet = true; + // We don't need to determine the actual best type since we're + // returning true - indicating that we've found the best operator. + typeCls = typeObj; + goto LRecord; + } + + if (!canCast(type1, type2, CONVERTTYPE.NOUDC) && !canCast(type2, type1, CONVERTTYPE.NOUDC)) + return false; + + if (type1.isInterfaceType() || type1.isPredefType(PredefinedType.PT_STRING) || GetSymbolLoader().HasBaseConversion(type1, typeDel)) + type1 = typeObj; + else if (type1.IsArrayType()) + type1 = GetReqPDT(PredefinedType.PT_ARRAY); + else if (!type1.isClassType()) + return false; + + if (type2.isInterfaceType() || type2.isPredefType(PredefinedType.PT_STRING) || GetSymbolLoader().HasBaseConversion(type2, typeDel)) + type2 = typeObj; + else if (type2.IsArrayType()) + type2 = GetReqPDT(PredefinedType.PT_ARRAY); + else if (!type2.isClassType()) + return false; + + Debug.Assert(type1.isClassType() && !type1.isPredefType(PredefinedType.PT_STRING) && !type1.isPredefType(PredefinedType.PT_DELEGATE)); + Debug.Assert(type2.isClassType() && !type2.isPredefType(PredefinedType.PT_STRING) && !type2.isPredefType(PredefinedType.PT_DELEGATE)); + + if (GetSymbolLoader().HasBaseConversion(type2, type1)) + typeCls = type1; + else if (GetSymbolLoader().HasBaseConversion(type1, type2)) + typeCls = type2; + + LRecord: + prgbofs.Add(new BinOpFullSig(typeCls, typeCls, BindRefCmpOp, OpSigFlags.None, LiftFlags.None, BinOpFuncKind.RefCmpOp)); + return fRet; + } + + /* + Determine which BinOpSig is better for overload resolution. + Better means: at least as good in all Params, and better in at least one param. + + Better w/r to a param means: + 1) same type as argument + 2) implicit conversion from this one's param type to the other's param type + Because of user defined conversion operators this relation is not transitive. + + Returns negative if ibos1 is better, positive if ibos2 is better, 0 if neither. + */ + + // UNDONE: It would be nice if this returned Neither, Left, Right rather than -1, 0, 1 + int WhichBofsIsBetter(BinOpFullSig bofs1, BinOpFullSig bofs2, CType type1, CType type2) + { + BetterType bt1; + BetterType bt2; + + if (bofs1.FPreDef() && bofs2.FPreDef()) + { + // Faster to compare predefs. + bt1 = WhichTypeIsBetter(bofs1.pt1, bofs2.pt1, type1); + bt2 = WhichTypeIsBetter(bofs1.pt2, bofs2.pt2, type2); + } + else + { + bt1 = WhichTypeIsBetter(bofs1.Type1(), bofs2.Type1(), type1); + bt2 = WhichTypeIsBetter(bofs1.Type2(), bofs2.Type2(), type2); + } + + int res = 0; + + switch (bt1) + { + default: + VSFAIL("Shouldn't happen"); + break; + case BetterType.Same: + case BetterType.Neither: + break; + case BetterType.Left: + res--; + break; + case BetterType.Right: + res++; + break; + } + + switch (bt2) + { + default: + VSFAIL("Shouldn't happen"); + break; + case BetterType.Same: + case BetterType.Neither: + break; + case BetterType.Left: + res--; + break; + case BetterType.Right: + res++; + break; + } + + return res; + } + + + ///////////////////////////////////////////////////////////////////////////////// + // Bind a standard unary operator. Takes care of user defined operators, predefined operators + // and lifting over nullable. + + static bool CalculateExprAndUnaryOpKinds( + OperatorKind op, + bool bChecked, + out /*out*/ ExpressionKind ek, + out /*out*/ UnaOpKind uok, + out /*out*/ EXPRFLAG flags) + { + flags = 0; + ek = 0; + uok = 0; + switch (op) + { + case OperatorKind.OP_UPLUS: + uok = UnaOpKind.Plus; + ek = ExpressionKind.EK_UPLUS; + break; + + case OperatorKind.OP_NEG: + if (bChecked) + { + flags |= EXPRFLAG.EXF_CHECKOVERFLOW; + } + uok = UnaOpKind.Minus; + ek = ExpressionKind.EK_NEG; + break; + + case OperatorKind.OP_BITNOT: + uok = UnaOpKind.Tilde; + ek = ExpressionKind.EK_BITNOT; + break; + + case OperatorKind.OP_LOGNOT: + uok = UnaOpKind.Bang; + ek = ExpressionKind.EK_LOGNOT; + break; + + case OperatorKind.OP_POSTINC: + flags |= EXPRFLAG.EXF_ISPOSTOP; + if (bChecked) + { + flags |= EXPRFLAG.EXF_CHECKOVERFLOW; + } + uok = UnaOpKind.IncDec; + ek = ExpressionKind.EK_ADD; + break; + + case OperatorKind.OP_PREINC: + if (bChecked) + { + flags |= EXPRFLAG.EXF_CHECKOVERFLOW; + } + uok = UnaOpKind.IncDec; + ek = ExpressionKind.EK_ADD; + break; + + case OperatorKind.OP_POSTDEC: + flags |= EXPRFLAG.EXF_ISPOSTOP; + if (bChecked) + { + flags |= EXPRFLAG.EXF_CHECKOVERFLOW; + } + uok = UnaOpKind.IncDec; + ek = ExpressionKind.EK_SUB; + break; + + case OperatorKind.OP_PREDEC: + if (bChecked) + { + flags |= EXPRFLAG.EXF_CHECKOVERFLOW; + } + uok = UnaOpKind.IncDec; + ek = ExpressionKind.EK_SUB; + break; + + default: + VSFAIL("Bad op"); + return false; + } + return true; + } + + public EXPR BindStandardUnaryOperator(OperatorKind op, EXPR pArgument) + { + RETAILVERIFY(pArgument != null); + + ExpressionKind ek; + UnaOpKind unaryOpKind; + EXPRFLAG flags; + + if (pArgument.type == null || + !CalculateExprAndUnaryOpKinds( + op, + Context.CheckedNormal, + out ek/*out*/, + out unaryOpKind/*out*/, + out flags/*out*/)) + { + return BadOperatorTypesError(ExpressionKind.EK_UNARYOP, pArgument, null); + } + + UnaOpMask unaryOpMask = (UnaOpMask)(1 << (int)unaryOpKind); + CType type = pArgument.type; + + // REVIEW : What's the correct number to use? + List pSignatures = new List(); + + EXPR pResult = null; + UnaryOperatorSignatureFindResult eResultOfSignatureFind = PopulateSignatureList(pArgument, unaryOpKind, unaryOpMask, ek, flags, pSignatures, out pResult); + + // nBestSignature is a 0-based index. + int nBestSignature = pSignatures.Count - 1; + + if (eResultOfSignatureFind == UnaryOperatorSignatureFindResult.Return) + { + Debug.Assert(pResult != null); + return pResult; + } + else if (eResultOfSignatureFind != UnaryOperatorSignatureFindResult.Match) + { + // If we didn't find a best match while populating, try to find while doing + // applicability testing. + if (!FindApplicableSignatures( + pArgument, + unaryOpMask, + pSignatures)) + { + if (pSignatures.Count == 0) + { + return BadOperatorTypesError(ek, pArgument, null); + } + + nBestSignature = 0; + // If we couldn't find exactly one, then we need to do some betterness testing. + if (pSignatures.Count != 1) + { + // Determine which is best. + for (int iuofs = 1; iuofs < pSignatures.Count; iuofs++) + { + if (nBestSignature < 0) + { + nBestSignature = iuofs; + } + else + { + int nT = WhichUofsIsBetter(pSignatures[nBestSignature], pSignatures[iuofs], type); + if (nT == 0) + { + nBestSignature = -1; + } + else if (nT > 0) + { + nBestSignature = iuofs; + } + } + } + if (nBestSignature < 0) + { + // Ambigous. + return ambiguousOperatorError(ek, pArgument, null); + } + + // Verify that our answer works. + for (int iuofs = 0; iuofs < pSignatures.Count; iuofs++) + { + if (iuofs == nBestSignature) + { + continue; + } + if (WhichUofsIsBetter(pSignatures[nBestSignature], pSignatures[iuofs], type) >= 0) + { + return ambiguousOperatorError(ek, pArgument, null); + } + } + } + } + else + { + nBestSignature = pSignatures.Count - 1; + } + } + + RETAILVERIFY(nBestSignature < pSignatures.Count); + + UnaOpFullSig uofs = pSignatures[nBestSignature]; + + if (uofs.pfn == null) + { + if (unaryOpKind == UnaOpKind.IncDec) + { + return BindIncOp(ek, flags, pArgument, uofs); + } + return BadOperatorTypesError(ek, pArgument, null); + } + + if (uofs.isLifted()) + { + return BindLiftedStandardUnop(ek, flags, pArgument, uofs); + } + + // Try the conversion - if it fails, do a cast without user defined casts. + EXPR arg = tryConvert(pArgument, uofs.GetType()); + if (arg == null) + { + arg = mustCast(pArgument, uofs.GetType(), CONVERTTYPE.NOUDC); + } + return uofs.pfn(ek, flags, arg); + } + + ///////////////////////////////////////////////////////////////////////////////// + + UnaryOperatorSignatureFindResult PopulateSignatureList(EXPR pArgument, UnaOpKind unaryOpKind, UnaOpMask unaryOpMask, ExpressionKind exprKind, EXPRFLAG flags, List pSignatures, out EXPR ppResult) + { + // We should have already checked argument != null and argument.type != null. + Debug.Assert(pArgument != null); + Debug.Assert(pArgument.type != null); + + ppResult = null; + CType pArgumentType = pArgument.type; + CType pRawType = pArgumentType.StripNubs(); + PredefinedType ptRaw = pRawType.isPredefined() ? pRawType.getPredefType() : PredefinedType.PT_COUNT; + + // Find all applicable operator signatures. + // First check for special ones (enum, ptr) and check for user defined ops. + + // REVIEW : What should we use as the cutoff? + if (ptRaw > PredefinedType.PT_ULONG) + { + // Enum types are special in that they carry a set of "predefined" operators (~ and inc/dec). + if (pRawType.isEnumType()) + { + if ((unaryOpMask & (UnaOpMask.Tilde | UnaOpMask.IncDec)) != 0) + { + // We have an exact match. + LiftFlags liftFlags = LiftFlags.None; + CType typeSig = pArgumentType; + + if (typeSig.IsNullableType()) + { + if (typeSig.AsNullableType().GetUnderlyingType() != pRawType) + { + typeSig = GetSymbolLoader().GetTypeManager().GetNullable(pRawType); + } + liftFlags = LiftFlags.Lift1; + } + if (unaryOpKind == UnaOpKind.Tilde) + { + pSignatures.Add(new UnaOpFullSig( + typeSig.getAggregate().GetUnderlyingType(), + BindEnumUnaOp, + liftFlags, + UnaOpFuncKind.EnumUnaOp)); + } + else + { + // For enums, we want to add the signature as the underlying type so that we'll + // perform the conversions to and from the enum type. + pSignatures.Add(new UnaOpFullSig( + typeSig.getAggregate().GetUnderlyingType(), + null, + liftFlags, + UnaOpFuncKind.None)); + } + return UnaryOperatorSignatureFindResult.Match; + } + } + else if (unaryOpKind == UnaOpKind.IncDec) + { + // Check for pointers + if (pArgumentType.IsPointerType()) + { + pSignatures.Add(new UnaOpFullSig( + pArgumentType, + null, + LiftFlags.None, + UnaOpFuncKind.None)); + return UnaryOperatorSignatureFindResult.Match; + } + + // Check for user defined inc/dec +#if ! CSEE + EXPRMULTIGET exprGet = GetExprFactory().CreateMultiGet(0, pArgumentType, null); +#else // CSEE + + EXPR exprGet = pArgument; +#endif // CSEE + + EXPR exprVal = bindUDUnop((ExpressionKind)(exprKind - ExpressionKind.EK_ADD + ExpressionKind.EK_INC), exprGet); + if (exprVal != null) + { + if (exprVal.type != null && !exprVal.type.IsErrorType() && exprVal.type != pArgumentType) + { + exprVal = mustConvert(exprVal, pArgumentType); + } + + Debug.Assert(pArgument != null); + EXPRMULTI exprMulti = GetExprFactory().CreateMulti(EXPRFLAG.EXF_ASSGOP | flags, pArgumentType, pArgument, exprVal); +#if ! CSEE + exprGet.SetOptionalMulti(exprMulti); +#endif // !CSEE + + // Check whether Lvalue can be assigned. checkLvalue may return true + // despite reporting an error. + if (!checkLvalue(pArgument, CheckLvalueKind.Increment)) + { + // TODO: This seems like it can never be reached - exprVal is only valid if + // we have a UDUnop, and in order for checkLValue to return false, either the + // arg has to not be OK, in which case we shouldn't get here, or we have an + // AnonMeth, Lambda, or Constant, all of which cannot have UDUnops defined for them. + exprMulti.SetError(); + } + ppResult = exprMulti; + return UnaryOperatorSignatureFindResult.Return; + } + // Try for a predefined increment operator. + } + else + { + // Check for user defined. + EXPR expr = bindUDUnop(exprKind, pArgument); + if (expr != null) + { + ppResult = expr; + return UnaryOperatorSignatureFindResult.Return; + } + } + } + + return UnaryOperatorSignatureFindResult.Continue; + } + + ///////////////////////////////////////////////////////////////////////////////// + + bool FindApplicableSignatures( + EXPR pArgument, + UnaOpMask unaryOpMask, + List pSignatures) + { + // All callers should already assert this to be the case. + Debug.Assert(pArgument != null); + Debug.Assert(pArgument.type != null); + + long iuosMinLift = GetSymbolLoader().FCanLift() ? 0 : g_rguos.Length; + + CType pArgumentType = pArgument.type; + CType pRawType = pArgumentType.StripNubs(); + PredefinedType pt = pArgumentType.isPredefined() ? pArgumentType.getPredefType() : PredefinedType.PT_COUNT; + PredefinedType ptRaw = pRawType.isPredefined() ? pRawType.getPredefType() : PredefinedType.PT_COUNT; + + for (int index = 0; index < g_rguos.Length; index++) + { + UnaOpSig uos = g_rguos[index]; + if ((uos.grfuom & unaryOpMask) == 0) + { + continue; + } + + ConvKind cv = GetConvKind(pt, g_rguos[index].pt); + CType typeSig = null; + + switch (cv) + { + default: + VSFAIL("Shouldn't happen!"); + continue; + + case ConvKind.None: + continue; + + case ConvKind.Explicit: + if (!pArgument.isCONSTANT_OK()) + { + continue; + } + if (canConvert(pArgument, typeSig = GetOptPDT(uos.pt))) + { + break; + } + if (index < iuosMinLift) + { + continue; + } + typeSig = GetSymbolLoader().GetTypeManager().GetNullable(typeSig); + if (!canConvert(pArgument, typeSig)) + { + continue; + } + break; + + case ConvKind.Unknown: + if (canConvert(pArgument, typeSig = GetOptPDT(uos.pt))) + { + break; + } + if (index < iuosMinLift) + { + continue; + } + typeSig = GetSymbolLoader().GetTypeManager().GetNullable(typeSig); + if (!canConvert(pArgument, typeSig)) + { + continue; + } + break; + + case ConvKind.Implicit: + break; + + case ConvKind.Identity: + { + UnaOpFullSig result = new UnaOpFullSig(this, uos); + if (result.GetType() != null) + { + pSignatures.Add(result); + return true; + } + } + break; + } + + if (typeSig != null && typeSig.IsNullableType()) + { + // Need to use a lifted signature. + LiftFlags grflt = LiftFlags.None; + + switch (GetConvKind(ptRaw, uos.pt)) + { + default: + grflt = grflt | LiftFlags.Convert1; + break; + case ConvKind.Implicit: + case ConvKind.Identity: + grflt = grflt | LiftFlags.Lift1; + break; + } + + pSignatures.Add(new UnaOpFullSig(typeSig, uos.pfn, grflt, uos.fnkind)); + + // NOTE: Can't skip any if we use the lifted signature because the + // type might convert to int? and to long (but not to int) in which + // case we should get an ambiguity. But we can skip the lifted ones.... + iuosMinLift = index + uos.cuosSkip + 1; + } + else + { + // Record it as applicable and skip accordingly. + UnaOpFullSig newResult = new UnaOpFullSig(this, uos); + if (newResult.GetType() != null) + { + pSignatures.Add(newResult); + } + index += uos.cuosSkip; + } + } + return false; + } + + EXPR BindLiftedStandardUnop(ExpressionKind ek, EXPRFLAG flags, EXPR arg, UnaOpFullSig uofs) + { + NullableType type = uofs.GetType().AsNullableType(); + Debug.Assert(arg != null && arg.type != null); + if (arg.type.IsNullType()) + { + return BadOperatorTypesError(ek, arg, null, type); + } + + EXPR pArgument = null; + EXPR nonLiftedArg = null; + + LiftArgument(arg, uofs.GetType(), uofs.Convert(), out pArgument, out nonLiftedArg); + + // Now call the function with the non lifted arguments to report errors. + EXPR nonLiftedResult = uofs.pfn(ek, flags, nonLiftedArg); + EXPRUNARYOP exprRes = GetExprFactory().CreateUnaryOp(ek, type, pArgument); + mustCast(nonLiftedResult, type, 0); + exprRes.flags |= flags; + + Debug.Assert((exprRes.flags & EXPRFLAG.EXF_LVALUE) == 0); + return exprRes; + } + + /* + Determine which UnaOpSig is better for overload resolution. + Returns negative if iuos1 is better, positive if iuos2 is better, 0 if neither. + */ + int WhichUofsIsBetter(UnaOpFullSig uofs1, UnaOpFullSig uofs2, CType typeArg) + { + BetterType bt; + + if (uofs1.FPreDef() && uofs2.FPreDef()) + { + // Faster to compare predefs. + bt = WhichTypeIsBetter(uofs1.pt, uofs2.pt, typeArg); + } + else + { + bt = WhichTypeIsBetter(uofs1.GetType(), uofs2.GetType(), typeArg); + } + + switch (bt) + { + default: + VSFAIL("Shouldn't happen"); + return 0; + case BetterType.Same: + case BetterType.Neither: + return 0; + case BetterType.Left: + return -1; + case BetterType.Right: + return +1; + } + } + + /* + Handles standard binary integer based operators. + */ + EXPR BindIntBinOp(ExpressionKind ek, EXPRFLAG flags, EXPR arg1, EXPR arg2) + { + Debug.Assert(arg1.type.isPredefined() && arg2.type.isPredefined() && arg1.type.getPredefType() == arg2.type.getPredefType()); + return BindIntOp(ek, flags, arg1, arg2, arg1.type.getPredefType()); + } + + + /* + Handles standard unary integer based operators. + */ + EXPR BindIntUnaOp(ExpressionKind ek, EXPRFLAG flags, EXPR arg) + { + Debug.Assert(arg.type.isPredefined()); + return BindIntOp(ek, flags, arg, null, arg.type.getPredefType()); + } + + + /* + Handles standard binary floating point (float, double) based operators. + */ + EXPR BindRealBinOp(ExpressionKind ek, EXPRFLAG flags, EXPR arg1, EXPR arg2) + { + Debug.Assert(arg1.type.isPredefined() && arg2.type.isPredefined() && arg1.type.getPredefType() == arg2.type.getPredefType()); + return bindFloatOp(ek, flags, arg1, arg2); + } + + + /* + Handles standard unary floating point (float, double) based operators. + */ + EXPR BindRealUnaOp(ExpressionKind ek, EXPRFLAG flags, EXPR arg) + { + Debug.Assert(arg.type.isPredefined()); + return bindFloatOp(ek, flags, arg, null); + } + + + /* + Handles standard increment and decrement operators. + */ + EXPR BindIncOp(ExpressionKind ek, EXPRFLAG flags, EXPR arg, UnaOpFullSig uofs) + { + Debug.Assert(ek == ExpressionKind.EK_ADD || ek == ExpressionKind.EK_SUB); + if (!checkLvalue(arg, CheckLvalueKind.Increment)) + { + EXPR rval = GetExprFactory().CreateBinop(ek, arg.type, arg, null); + rval.SetError(); + return rval; + } + + CType typeRaw = uofs.GetType().StripNubs(); + + FUNDTYPE ft = typeRaw.fundType(); + if (ft == FUNDTYPE.FT_R8 || ft == FUNDTYPE.FT_R4) + { + flags = ~EXPRFLAG.EXF_CHECKOVERFLOW; + } + + if (uofs.isLifted()) + { + return BindLiftedIncOp(ek, flags, arg, uofs); + } + else + { + return BindNonliftedIncOp(ek, flags, arg, uofs); + } + } + + EXPR BindIncOpCore(ExpressionKind ek, EXPRFLAG flags, EXPR exprVal, CType type) + { + Debug.Assert(ek == ExpressionKind.EK_ADD || ek == ExpressionKind.EK_SUB); + CONSTVAL cv = new CONSTVAL(); + EXPR pExprResult = null; + + if (type.isEnumType() && type.fundType() > FUNDTYPE.FT_LASTINTEGRAL) + { + // This is an error case when enum derives from an illegal type. Just treat it as an int. + type = GetReqPDT(PredefinedType.PT_INT); + } + + FUNDTYPE ft = type.fundType(); + CType typeTmp = type; + + switch (ft) + { + default: + { + Debug.Assert(type.isPredefType(PredefinedType.PT_DECIMAL)); + ek = ek == ExpressionKind.EK_ADD ? ExpressionKind.EK_DECIMALINC : ExpressionKind.EK_DECIMALDEC; + PREDEFMETH predefMeth = ek == ExpressionKind.EK_DECIMALINC ? PREDEFMETH.PM_DECIMAL_OPINCREMENT : PREDEFMETH.PM_DECIMAL_OPDECREMENT; + pExprResult = CreateUnaryOpForPredefMethodCall(ek, predefMeth, type, exprVal); + } + break; + case FUNDTYPE.FT_PTR: + cv.iVal = 1; + pExprResult = BindPtrBinOp(ek, flags, exprVal, GetExprFactory().CreateConstant(GetReqPDT(PredefinedType.PT_INT), cv)); + break; + case FUNDTYPE.FT_I1: + case FUNDTYPE.FT_I2: + case FUNDTYPE.FT_U1: + case FUNDTYPE.FT_U2: + typeTmp = GetReqPDT(PredefinedType.PT_INT); + cv.iVal = 1; + pExprResult = LScalar(ek, flags, exprVal, type, cv, pExprResult, typeTmp); + break; + case FUNDTYPE.FT_I4: + case FUNDTYPE.FT_U4: + cv.iVal = 1; + pExprResult = LScalar(ek, flags, exprVal, type, cv, pExprResult, typeTmp); + break; + case FUNDTYPE.FT_I8: + case FUNDTYPE.FT_U8: + cv = GetExprConstants().Create((long)1); + pExprResult = LScalar(ek, flags, exprVal, type, cv, pExprResult, typeTmp); + break; + case FUNDTYPE.FT_R4: + case FUNDTYPE.FT_R8: + cv = GetExprConstants().Create(1.0); + pExprResult = LScalar(ek, flags, exprVal, type, cv, pExprResult, typeTmp); + break; + } + Debug.Assert(pExprResult != null); + Debug.Assert(!pExprResult.type.IsNullableType()); + return pExprResult; + } + + private EXPR LScalar(ExpressionKind ek, EXPRFLAG flags, EXPR exprVal, CType type, CONSTVAL cv, EXPR pExprResult, CType typeTmp) + { + CType typeOne = type; + if (typeOne.isEnumType()) + { + typeOne = typeOne.underlyingEnumType(); + } + pExprResult = GetExprFactory().CreateBinop(ek, typeTmp, exprVal, GetExprFactory().CreateConstant(typeOne, cv)); + pExprResult.flags |= flags; + if (typeTmp != type) + { + pExprResult = mustCast(pExprResult, type, CONVERTTYPE.NOUDC); + } + return pExprResult; + } + + EXPRMULTI BindNonliftedIncOp(ExpressionKind ek, EXPRFLAG flags, EXPR arg, UnaOpFullSig uofs) + { + Debug.Assert(ek == ExpressionKind.EK_ADD || ek == ExpressionKind.EK_SUB); + Debug.Assert(!uofs.isLifted()); + + Debug.Assert(arg != null); + EXPR exprVal; +#if ! CSEE + EXPRMULTIGET exprGet = GetExprFactory().CreateMultiGet(EXPRFLAG.EXF_ASSGOP, arg.type, null); + exprVal = exprGet; +#else + exprVal = arg; +#endif + + CType type = uofs.GetType(); + Debug.Assert(!type.IsNullableType()); + + // [] These used to be converts, but we're making them casts now - this is because + // we need to remove the ability to call inc(sbyte) etc for all types smaller than int. + // Note however, that this will give us different error messages on compile time versus runtime + // for checked increments. + // + // Also, we changed it so that we now generate the cast to and from enum for enum increments. + exprVal = mustCast(exprVal, type); + exprVal = BindIncOpCore(ek, flags, exprVal, type); + EXPR op = mustCast(exprVal, arg.type, CONVERTTYPE.NOUDC); + + EXPRMULTI exprMulti = GetExprFactory().CreateMulti(EXPRFLAG.EXF_ASSGOP | flags, arg.type, arg, op); + +#if ! CSEE + exprGet.SetOptionalMulti(exprMulti); +#endif + return exprMulti; + } + + EXPRMULTI BindLiftedIncOp(ExpressionKind ek, EXPRFLAG flags, EXPR arg, UnaOpFullSig uofs) + { + Debug.Assert(ek == ExpressionKind.EK_ADD || ek == ExpressionKind.EK_SUB); + Debug.Assert(uofs.isLifted()); + + NullableType type = uofs.GetType().AsNullableType(); + Debug.Assert(arg != null); + EXPR exprVal; + +#if ! CSEE + EXPRMULTIGET exprGet = GetExprFactory().CreateMultiGet(EXPRFLAG.EXF_ASSGOP, arg.type, null); + exprVal = exprGet; +#else + exprVal = arg; +#endif + + EXPR nonLiftedResult = null; + EXPR nonLiftedArg = exprVal; + + // We want to give the lifted argument as the binop, but use the non-lifted argument as the + // argument of the call. + //Debug.Assert(uofs.LiftArg() || type.IsValType()); + nonLiftedArg = mustCast(nonLiftedArg, type.GetUnderlyingType()); + nonLiftedResult = BindIncOpCore(ek, flags, nonLiftedArg, type.GetUnderlyingType()); + exprVal = mustCast(exprVal, type); + EXPRUNARYOP exprRes = GetExprFactory().CreateUnaryOp((ek == ExpressionKind.EK_ADD) ? ExpressionKind.EK_INC : ExpressionKind.EK_DEC, arg.type/* type */, exprVal); + mustCast(mustCast(nonLiftedResult, type), arg.type); + exprRes.flags |= flags; + + EXPRMULTI exprMulti = GetExprFactory().CreateMulti(EXPRFLAG.EXF_ASSGOP | flags, arg.type, arg, exprRes); + +#if ! CSEE + exprGet.SetOptionalMulti(exprMulti); +#endif + return exprMulti; + } + + /* + Handles standard binary decimal based operators. + This function is called twice by the EE for every binary operator it evaluates + Here is how it works. + 1. The EE on finding an Expr asks the Expression binder to bind it. + 2. At this time the expression binder just creates a new binopexpr and returns it to the EE, + the EE then uses the runtimesystem to find if any of the arguments of the expr can be evaluated to constants. + 3. If so it creates new arguments and expr, aliases the original expr to the new one and passes + it new expr to Expressionbinder to be bound. + 4. This time the expresson binder realizes that the 2 arguments are constants and tries to fold them. + If the folding is successful the value is used by the EE (and we have avoided a funceval) + 5. if the constant binding fails, then the Expression binders returns the same exp as it would have + created for the compile case ( we func eval the same function as what would be executed at runtime). + */ + EXPR BindDecBinOp(ExpressionKind ek, EXPRFLAG flags, EXPR arg1, EXPR arg2) + { + Debug.Assert(arg1.type.isPredefType(PredefinedType.PT_DECIMAL) && arg2.type.isPredefType(PredefinedType.PT_DECIMAL)); + + CType typeDec = GetOptPDT(PredefinedType.PT_DECIMAL); + Debug.Assert(typeDec != null); + + EXPR argConst1 = arg1.GetConst(); + EXPR argConst2 = arg2.GetConst(); + + CType typeRet = null; + + switch (ek) + { + default: + VSFAIL("Bad kind"); + break; + case ExpressionKind.EK_ADD: + case ExpressionKind.EK_SUB: + case ExpressionKind.EK_MUL: + case ExpressionKind.EK_DIV: + case ExpressionKind.EK_MOD: + typeRet = typeDec; + break; + case ExpressionKind.EK_LT: + case ExpressionKind.EK_LE: + case ExpressionKind.EK_GT: + case ExpressionKind.EK_GE: + case ExpressionKind.EK_EQ: + case ExpressionKind.EK_NE: + typeRet = GetReqPDT(PredefinedType.PT_BOOL); + break; + } + +#if CSEE + // In the EE, even if we don't have two constants, we want to emit an EXPRBINOP with the + // right EK so that when we evalsync we can just do the work ourselves instead of + // delegating to method calls. + + if (!argConst1 || !argConst2) + { + // We don't have 2 constants, so just emit an EXPRBINOP... + return GetExprFactory().CreateBinop(tree, ek, typeRet, arg1, arg2); + } + else + { + goto LBothConst; + } + + LUserDefined: + +#endif // CSEE + +#if ! CSEE + if (argConst2 != null && argConst1 != null) + { + goto LBothConst; + } +#endif + + // At this point, for the compiler we dont want to optimize the binop just yet. Maintain the correct tree until + // the arithmetic optimizer pass. + return GetExprFactory().CreateBinop(ek, typeRet, arg1, arg2); + + LBothConst: + decimal dec1; + decimal dec2; + decimal decRes = 0; + bool fRes = false; + bool fBool = false; + + dec1 = argConst1.asCONSTANT().getVal().decVal; + dec2 = argConst2.asCONSTANT().getVal().decVal; + + // Do the operation. + switch (ek) + { + case ExpressionKind.EK_ADD: + decRes = dec1 + dec2; + break; + case ExpressionKind.EK_SUB: + decRes = dec1 - dec2; + break; + case ExpressionKind.EK_MUL: + decRes = dec1 * dec2; + break; + case ExpressionKind.EK_DIV: + if (dec2 == 0) + { + GetErrorContext().Error(ErrorCode.ERR_IntDivByZero); + EXPR rval = GetExprFactory().CreateBinop(ek, typeDec, arg1, arg2); + rval.SetError(); + return rval; + } + + decRes = dec1 / dec2; + break; + + case ExpressionKind.EK_MOD: + { + // REVIEW : The decimal library should have % functionality. Computing it + // this way can overflow when % really doesn't need to. + + /* n % d = n - d truncate(n/d) */ + decimal decDiv; + + if (dec2 == 0) + { + GetErrorContext().Error(ErrorCode.ERR_IntDivByZero); + EXPR rval = GetExprFactory().CreateBinop(ek, typeDec, arg1, arg2); + rval.SetError(); + return rval; + } + + decDiv = dec1 % dec2; + break; + } + + default: + fBool = true; + + switch (ek) + { + default: + VSFAIL("Bad ek"); + break; + case ExpressionKind.EK_EQ: + fRes = dec1 == dec2; + break; + case ExpressionKind.EK_NE: + fRes = dec1 != dec2; + break; + case ExpressionKind.EK_LE: + fRes = dec1 <= dec2; + break; + case ExpressionKind.EK_LT: + fRes = dec1 < dec2; + break; + case ExpressionKind.EK_GE: + fRes = dec1 >= dec2; + break; + case ExpressionKind.EK_GT: + fRes = dec1 > dec2; + break; + } + break; + } + + // Allocate the result node. + CONSTVAL cv; + EXPR exprRes; + + if (fBool) + { + cv = ConstValFactory.GetBool(fRes); + exprRes = GetExprFactory().CreateConstant(GetReqPDT(PredefinedType.PT_BOOL), cv); + } + else + { + cv = GetExprConstants().Create(decRes); + exprRes = GetExprFactory().CreateConstant(typeDec, cv); + } + + return exprRes; + } + + + /* + Handles standard unary decimal based operators. + */ + EXPR BindDecUnaOp(ExpressionKind ek, EXPRFLAG flags, EXPR arg) + { + Debug.Assert(arg.type.isPredefType(PredefinedType.PT_DECIMAL)); + Debug.Assert(ek == ExpressionKind.EK_NEG || ek == ExpressionKind.EK_UPLUS); + + CType typeDec = GetOptPDT(PredefinedType.PT_DECIMAL); + Debug.Assert(typeDec != null); + ek = ek == ExpressionKind.EK_NEG ? ExpressionKind.EK_DECIMALNEG : ExpressionKind.EK_UPLUS; + + // We want to fold if the argument is constant. Otherwise, keep the regular op. + EXPR argConst = arg.GetConst(); + if (argConst == null) // Non-constant. + { + if (ek == ExpressionKind.EK_DECIMALNEG) + { + PREDEFMETH predefMeth = PREDEFMETH.PM_DECIMAL_OPUNARYMINUS; + return CreateUnaryOpForPredefMethodCall(ek, predefMeth, typeDec, arg); + } + return GetExprFactory().CreateUnaryOp(ek, typeDec, arg); + } + + // If its a uplus, just return it. + if (ek == ExpressionKind.EK_UPLUS) + { + return arg; + } + + decimal dec = argConst.asCONSTANT().getVal().decVal; + dec = dec * -1; + + // Allocate the result node. + CONSTVAL cv = GetExprConstants().Create(dec); + + EXPR exprRes = GetExprFactory().CreateConstant(typeDec, cv); + + return exprRes; + } + + + /* + Handles string concatenation. + */ + EXPR BindStrBinOp(ExpressionKind ek, EXPRFLAG flags, EXPR arg1, EXPR arg2) + { + Debug.Assert(ek == ExpressionKind.EK_ADD); + Debug.Assert(arg1.type.isPredefType(PredefinedType.PT_STRING) || arg2.type.isPredefType(PredefinedType.PT_STRING)); + return bindStringConcat(arg1, arg2); + } + + + /* + Bind a shift operator: <<, >>. These can have integer or long first operands, + and second operand must be int. + */ + EXPR BindShiftOp(ExpressionKind ek, EXPRFLAG flags, EXPR arg1, EXPR arg2) + { + Debug.Assert(ek == ExpressionKind.EK_LSHIFT || ek == ExpressionKind.EK_RSHIFT); + Debug.Assert(arg1.type.isPredefined()); + Debug.Assert(arg2.type.isPredefType(PredefinedType.PT_INT)); + + PredefinedType ptOp = arg1.type.getPredefType(); + Debug.Assert(ptOp == PredefinedType.PT_INT || ptOp == PredefinedType.PT_UINT || ptOp == PredefinedType.PT_LONG || ptOp == PredefinedType.PT_ULONG); + + // We want to check up front if we have two constants, because constant folding is supposed to + // happen in the initial binding pass. + EXPR argConst1 = arg1.GetConst(); + EXPR argConst2 = arg2.GetConst(); + + if (argConst1 == null || argConst2 == null) // One or more aren't constants, so dont fold anything. + { + return GetExprFactory().CreateBinop(ek, arg1.type, arg1, arg2); + } + + // Both constants, so fold them. + CONSTVAL cv = new CONSTVAL(); + int cbit = (ptOp == PredefinedType.PT_LONG || ptOp == PredefinedType.PT_ULONG) ? 0x3f : 0x1f; + cv.iVal = argConst2.asCONSTANT().getVal().iVal & cbit; + cbit = cv.iVal; + + // Fill in the CONSTVAL. + if (ptOp == PredefinedType.PT_LONG || ptOp == PredefinedType.PT_ULONG) + { + Debug.Assert(0 <= cbit && cbit < 0x40); + ulong u1 = argConst1.asCONSTANT().getVal().ulongVal; + ulong uval; + + switch (ek) + { + case ExpressionKind.EK_LSHIFT: + uval = u1 << cbit; + break; + case ExpressionKind.EK_RSHIFT: + uval = (ptOp == PredefinedType.PT_LONG) ? (ulong)((long)u1 >> cbit) : (u1 >> cbit); + break; + default: + VSFAIL("Unknown op"); + uval = 0; + break; + } + cv = GetExprConstants().Create(uval); + } + else + { + Debug.Assert(0 <= cbit && cbit < 0x20); + uint u1 = argConst1.asCONSTANT().getVal().uiVal; + + switch (ek) + { + case ExpressionKind.EK_LSHIFT: + cv.uiVal = u1 << cbit; + break; + case ExpressionKind.EK_RSHIFT: + cv.uiVal = (ptOp == PredefinedType.PT_INT) ? (uint)((int)u1 >> cbit) : (u1 >> cbit); + break; + default: + VSFAIL("Unknown op"); + cv.uiVal = 0; + break; + } + } + + EXPR exprRes = GetExprFactory().CreateConstant(GetReqPDT(ptOp), cv); + return exprRes; + } + + /* + Bind a bool binary operator: ==, !=, &&, ||, , |, ^. If both operands are constant, the + result will be a constant also. + */ + EXPR BindBoolBinOp(ExpressionKind ek, EXPRFLAG flags, EXPR arg1, EXPR arg2) + { + Debug.Assert(arg1 != null); + Debug.Assert(arg2 != null); + Debug.Assert(arg1.type.isPredefType(PredefinedType.PT_BOOL) || (arg1.type.IsNullableType() && arg2.type.AsNullableType().GetUnderlyingType().isPredefType(PredefinedType.PT_BOOL))); + Debug.Assert(arg2.type.isPredefType(PredefinedType.PT_BOOL) || (arg2.type.IsNullableType() && arg2.type.AsNullableType().GetUnderlyingType().isPredefType(PredefinedType.PT_BOOL))); + + EXPR exprRes = GetExprFactory().CreateBinop(ek, GetReqPDT(PredefinedType.PT_BOOL), arg1, arg2); + + return exprRes; + } + + EXPR BindBoolBitwiseOp(ExpressionKind ek, EXPRFLAG flags, EXPR expr1, EXPR expr2, BinOpFullSig bofs) + { + Debug.Assert(ek == ExpressionKind.EK_BITAND || ek == ExpressionKind.EK_BITOR); + Debug.Assert(expr1.type.isPredefType(PredefinedType.PT_BOOL) || expr1.type.IsNullableType() && expr1.type.AsNullableType().GetUnderlyingType().isPredefType(PredefinedType.PT_BOOL)); + Debug.Assert(expr2.type.isPredefType(PredefinedType.PT_BOOL) || expr2.type.IsNullableType() && expr2.type.AsNullableType().GetUnderlyingType().isPredefType(PredefinedType.PT_BOOL)); + + if (expr1.type.IsNullableType() || expr2.type.IsNullableType()) + { + CType typeBool = GetReqPDT(PredefinedType.PT_BOOL); + CType typeRes = GetSymbolLoader().GetTypeManager().GetNullable(typeBool); + + // Get the non-lifted result. + EXPR nonLiftedArg1 = CNullable.StripNullableConstructor(expr1); + EXPR nonLiftedArg2 = CNullable.StripNullableConstructor(expr2); + EXPR nonLiftedResult = null; + + if (!nonLiftedArg1.type.IsNullableType() && !nonLiftedArg2.type.IsNullableType()) + { + nonLiftedResult = BindBoolBinOp(ek, flags, nonLiftedArg1, nonLiftedArg2); + } + + // Make the binop and set that its lifted. + EXPRBINOP exprRes = GetExprFactory().CreateBinop(ek, typeRes, expr1, expr2); + if (nonLiftedResult != null) + { + // Bitwise operators can have null non-lifted results if we have a nub sym somewhere. + mustCast(nonLiftedResult, typeRes, 0); + } + exprRes.isLifted = true; + exprRes.flags |= flags; + Debug.Assert((exprRes.flags & EXPRFLAG.EXF_LVALUE) == 0); + return exprRes; + } + return BindBoolBinOp(ek, flags, expr1, expr2); + } + + EXPR BindLiftedBoolBitwiseOp(ExpressionKind ek, EXPRFLAG flags, EXPR expr1, EXPR expr2) + { + return null; + } + + + /* + Handles boolean unary operater (!). + */ + EXPR BindBoolUnaOp(ExpressionKind ek, EXPRFLAG flags, EXPR arg) + { + Debug.Assert(arg.type.isPredefType(PredefinedType.PT_BOOL)); + Debug.Assert(ek == ExpressionKind.EK_LOGNOT); + + // Get the result type and operand type. + CType typeBool = GetReqPDT(PredefinedType.PT_BOOL); + + // Determine if arg has a constant value. + // Strip off EXPRKIND.EK_SEQUENCE for constant checking. + + EXPR argConst = arg.GetConst(); + + if (argConst == null) + return GetExprFactory().CreateUnaryOp(ExpressionKind.EK_LOGNOT, typeBool, arg); + + bool fRes = argConst.asCONSTANT().getVal().iVal != 0; + EXPR rval = GetExprFactory().CreateConstant(typeBool, ConstValFactory.GetBool(!fRes)); + + return rval; + } + + + /* + Handles string equality. + */ + EXPR BindStrCmpOp(ExpressionKind ek, EXPRFLAG flags, EXPR arg1, EXPR arg2) + { + Debug.Assert(ek == ExpressionKind.EK_EQ || ek == ExpressionKind.EK_NE); + Debug.Assert(arg1.type.isPredefType(PredefinedType.PT_STRING) && arg2.type.isPredefType(PredefinedType.PT_STRING)); + + // Get the predefined method for string comparison, and then stash it in the EXPR so we can + // transform it later. + + PREDEFMETH predefMeth = ek == ExpressionKind.EK_EQ ? PREDEFMETH.PM_STRING_OPEQUALITY : PREDEFMETH.PM_STRING_OPINEQUALITY; + ek = ek == ExpressionKind.EK_EQ ? ExpressionKind.EK_STRINGEQ : ExpressionKind.EK_STRINGNE; + return CreateBinopForPredefMethodCall(ek, predefMeth, GetReqPDT(PredefinedType.PT_BOOL), arg1, arg2); + } + + + /* + Handles reference equality operators. Type variables come through here. + */ + EXPR BindRefCmpOp(ExpressionKind ek, EXPRFLAG flags, EXPR arg1, EXPR arg2) + { + Debug.Assert(ek == ExpressionKind.EK_EQ || ek == ExpressionKind.EK_NE); + + // Must box type variables for the verifier. + arg1 = mustConvert(arg1, GetReqPDT(PredefinedType.PT_OBJECT), CONVERTTYPE.NOUDC); + arg2 = mustConvert(arg2, GetReqPDT(PredefinedType.PT_OBJECT), CONVERTTYPE.NOUDC); + + return GetExprFactory().CreateBinop(ek, GetReqPDT(PredefinedType.PT_BOOL), arg1, arg2); + } + + + /* + Handles delegate binary operators. + */ + EXPR BindDelBinOp(ExpressionKind ek, EXPRFLAG flags, EXPR arg1, EXPR arg2) + { + Debug.Assert(ek == ExpressionKind.EK_ADD || ek == ExpressionKind.EK_SUB || ek == ExpressionKind.EK_EQ || ek == ExpressionKind.EK_NE); + Debug.Assert(arg1.type == arg2.type && (arg1.type.isDelegateType() || arg1.type.isPredefType(PredefinedType.PT_DELEGATE))); + + PREDEFMETH predefMeth = (PREDEFMETH)0; + CType RetType = null; + switch (ek) + { + case ExpressionKind.EK_ADD: + predefMeth = PREDEFMETH.PM_DELEGATE_COMBINE; + RetType = arg1.type; + ek = ExpressionKind.EK_DELEGATEADD; + break; + + case ExpressionKind.EK_SUB: + predefMeth = PREDEFMETH.PM_DELEGATE_REMOVE; + RetType = arg1.type; + ek = ExpressionKind.EK_DELEGATESUB; + break; + + case ExpressionKind.EK_EQ: + predefMeth = PREDEFMETH.PM_DELEGATE_OPEQUALITY; + RetType = GetReqPDT(PredefinedType.PT_BOOL); + ek = ExpressionKind.EK_DELEGATEEQ; + break; + + case ExpressionKind.EK_NE: + predefMeth = PREDEFMETH.PM_DELEGATE_OPINEQUALITY; + RetType = GetReqPDT(PredefinedType.PT_BOOL); + ek = ExpressionKind.EK_DELEGATENE; + break; + } + return CreateBinopForPredefMethodCall(ek, predefMeth, RetType, arg1, arg2); + } + + + /* + Handles enum binary operators. + */ + EXPR BindEnumBinOp(ExpressionKind ek, EXPRFLAG flags, EXPR arg1, EXPR arg2) + { + AggregateType typeEnum = null; + AggregateType typeDst = GetEnumBinOpType(ek, arg1.type, arg2.type, out typeEnum); + + Debug.Assert(typeEnum != null); + PredefinedType ptOp; + + switch (typeEnum.fundType()) + { + default: + // Promote all smaller types to int. + ptOp = PredefinedType.PT_INT; + break; + case FUNDTYPE.FT_U4: + ptOp = PredefinedType.PT_UINT; + break; + case FUNDTYPE.FT_I8: + ptOp = PredefinedType.PT_LONG; + break; + case FUNDTYPE.FT_U8: + ptOp = PredefinedType.PT_ULONG; + break; + } + + CType typeOp = GetReqPDT(ptOp); + arg1 = mustCast(arg1, typeOp, CONVERTTYPE.NOUDC); + arg2 = mustCast(arg2, typeOp, CONVERTTYPE.NOUDC); + + EXPR exprRes = BindIntOp(ek, flags, arg1, arg2, ptOp); + + if (!exprRes.isOK()) + { + return exprRes; + } + + if (exprRes.type != typeDst) + { + Debug.Assert(!typeDst.isPredefType(PredefinedType.PT_BOOL)); + exprRes = mustCast(exprRes, typeDst, CONVERTTYPE.NOUDC); + } + + return exprRes; + } + + + /* + Handles enum unary operator (~). + */ + EXPR BindEnumUnaOp(ExpressionKind ek, EXPRFLAG flags, EXPR arg) + { + Debug.Assert(ek == ExpressionKind.EK_BITNOT); + Debug.Assert(arg.isCAST()); + Debug.Assert(arg.asCAST().GetArgument().type.isEnumType()); + + PredefinedType ptOp; + CType typeEnum = arg.asCAST().GetArgument().type; + + switch (typeEnum.fundType()) + { + default: + // Promote all smaller types to int. + ptOp = PredefinedType.PT_INT; + break; + case FUNDTYPE.FT_U4: + ptOp = PredefinedType.PT_UINT; + break; + case FUNDTYPE.FT_I8: + ptOp = PredefinedType.PT_LONG; + break; + case FUNDTYPE.FT_U8: + ptOp = PredefinedType.PT_ULONG; + break; + } + + CType typeOp = GetReqPDT(ptOp); + arg = mustCast(arg, typeOp, CONVERTTYPE.NOUDC); + + EXPR exprRes = BindIntOp(ek, flags, arg, null, ptOp); + + if (!exprRes.isOK()) + { + return exprRes; + } + + return mustCastInUncheckedContext(exprRes, typeEnum, CONVERTTYPE.NOUDC); + } + + + /* + Handles pointer binary operators (+ and -). + */ + EXPR BindPtrBinOp(ExpressionKind ek, EXPRFLAG flags, EXPR arg1, EXPR arg2) + { + return null; + } + + + /* + Handles pointer comparison operators. + */ + EXPR BindPtrCmpOp(ExpressionKind ek, EXPRFLAG flags, EXPR arg1, EXPR arg2) + { + return null; + } + + + /* + Given a binary operator EXPRKIND, get the BinOpKind and flags. + + REVIEW : Use a lookup table of some sort. It'd work best if we had the OPERATOR instead + of EXPRKIND. + */ + bool GetBinopKindAndFlags(ExpressionKind ek, out BinOpKind pBinopKind, out EXPRFLAG flags) + { + flags = 0; + switch (ek) + { + case ExpressionKind.EK_ADD: + if (Context.CheckedNormal) + { + flags |= EXPRFLAG.EXF_CHECKOVERFLOW; + } + pBinopKind = BinOpKind.Add; + break; + case ExpressionKind.EK_SUB: + if (Context.CheckedNormal) + { + flags |= EXPRFLAG.EXF_CHECKOVERFLOW; + } + pBinopKind = BinOpKind.Sub; + break; + case ExpressionKind.EK_DIV: + case ExpressionKind.EK_MOD: + // EXPRKIND.EK_DIV and EXPRKIND.EK_MOD need to be treated special for hasSideEffects, + // hence the EXPRFLAG.EXF_ASSGOP. Yes, this is a hack. + flags |= EXPRFLAG.EXF_ASSGOP; + if (Context.CheckedNormal) + { + flags |= EXPRFLAG.EXF_CHECKOVERFLOW; + } + pBinopKind = BinOpKind.Mul; + break; + case ExpressionKind.EK_MUL: + if (Context.CheckedNormal) + { + flags |= EXPRFLAG.EXF_CHECKOVERFLOW; + } + pBinopKind = BinOpKind.Mul; + break; + case ExpressionKind.EK_BITAND: + case ExpressionKind.EK_BITOR: + pBinopKind = BinOpKind.Bitwise; + break; + case ExpressionKind.EK_BITXOR: + pBinopKind = BinOpKind.BitXor; + break; + case ExpressionKind.EK_LSHIFT: + case ExpressionKind.EK_RSHIFT: + pBinopKind = BinOpKind.Shift; + break; + case ExpressionKind.EK_LOGOR: + case ExpressionKind.EK_LOGAND: + pBinopKind = BinOpKind.Logical; + break; + case ExpressionKind.EK_LT: + case ExpressionKind.EK_LE: + case ExpressionKind.EK_GT: + case ExpressionKind.EK_GE: + pBinopKind = BinOpKind.Compare; + break; + case ExpressionKind.EK_EQ: + case ExpressionKind.EK_NE: + pBinopKind = BinOpKind.Equal; + break; + default: + VSFAIL("Bad ek"); + pBinopKind = BinOpKind.Add; + return false; + } + return true; + } + + static bool isDivByZero(ExpressionKind kind, EXPR op2) + { + return false; + } + + EXPR FoldIntegerConstants(ExpressionKind kind, EXPRFLAG flags, EXPR op1, EXPR op2, PredefinedType ptOp) + { + //Debug.Assert(kind.isRelational() || kind.isArithmetic() || kind.isBitwise()); + Debug.Assert(ptOp == PredefinedType.PT_INT || ptOp == PredefinedType.PT_UINT || ptOp == PredefinedType.PT_LONG || ptOp == PredefinedType.PT_ULONG); + CType typeOp = GetReqPDT(ptOp); + Debug.Assert(typeOp != null); + Debug.Assert(op1 != null && op1.type == typeOp); + Debug.Assert(op2 == null || op2.type == typeOp); + Debug.Assert((op2 == null) == (kind == ExpressionKind.EK_NEG || kind == ExpressionKind.EK_UPLUS || kind == ExpressionKind.EK_BITNOT)); + + EXPRCONSTANT opConst1 = op1.GetConst().asCONSTANT(); + EXPRCONSTANT opConst2 = (op2 != null) ? op2.GetConst().asCONSTANT() : null; + + // Fold operation if both args are constant. + if (opConst1 != null && (op2 == null || opConst2 != null)) + { + if (ptOp == PredefinedType.PT_LONG || ptOp == PredefinedType.PT_ULONG) + { + return FoldConstI8Op(kind, op1, opConst1, op2, opConst2, ptOp); + } + else + { + return FoldConstI4Op(kind, op1, opConst1, op2, opConst2, ptOp); + } + } + + return null; + } + + + /* + Convert and constant fold an expression involving I4, U4, I8 or U8 operands. The operands are + assumed to be already converted to the correct types. + */ + EXPR BindIntOp(ExpressionKind kind, EXPRFLAG flags, EXPR op1, EXPR op2, PredefinedType ptOp) + { + //Debug.Assert(kind.isRelational() || kind.isArithmetic() || kind.isBitwise()); + Debug.Assert(ptOp == PredefinedType.PT_INT || ptOp == PredefinedType.PT_UINT || ptOp == PredefinedType.PT_LONG || ptOp == PredefinedType.PT_ULONG); + CType typeOp = GetReqPDT(ptOp); + Debug.Assert(typeOp != null); + Debug.Assert(op1 != null && op1.type == typeOp); + Debug.Assert(op2 == null || op2.type == typeOp); + Debug.Assert((op2 == null) == (kind == ExpressionKind.EK_NEG || kind == ExpressionKind.EK_UPLUS || kind == ExpressionKind.EK_BITNOT)); + + if (isDivByZero(kind, op2)) + { + GetErrorContext().Error(ErrorCode.ERR_IntDivByZero); + EXPR rval = GetExprFactory().CreateBinop(kind, typeOp, op1, op2); + rval.SetError(); + return rval; + } + + // This optimization CANNOT be moved to a later pass. See comments in + // FoldIntegerConstants. + EXPR exprFolded = FoldIntegerConstants(kind, flags, op1, op2, ptOp); + if (exprFolded != null) + { + return exprFolded; + } + + if (kind == ExpressionKind.EK_NEG) + { + return BindIntegerNeg(flags, op1, ptOp); + } + + CType typeDest = kind.isRelational() ? GetReqPDT(PredefinedType.PT_BOOL) : typeOp; + + EXPR exprRes = GetExprFactory().CreateOperator(kind, typeDest, op1, op2); + exprRes.flags |= flags; + Debug.Assert((exprRes.flags & EXPRFLAG.EXF_LVALUE) == 0); + return exprRes; + } + + EXPR BindIntegerNeg(EXPRFLAG flags, EXPR op, PredefinedType ptOp) + { + + // 14.6.2 Unary minus operator + // For an operation of the form -x, unary operator overload resolution (14.2.3) is applied to select + // a specific operator implementation. The operand is converted to the parameter type of the selected + // operator, and the type of the result is the return type of the operator. The predefined negation + // operators are: + // + // Integer negation: + // + // int operator -(int x); + // long operator -(long x); + // + // The result is computed by subtracting x from zero. In a checked context, if the value of x is the + // smallest int or long (-2^31 or -2^63, respectively), a System.OverflowException is thrown. In an + // unchecked context, if the value of x is the smallest int or long, the result is that same value + // and the overflow is not reported. + // + // If the operand of the negation operator is of type uint, it is converted to type long, and the + // type of the result is long. An exception is the rule that permits the int value -2147483648 (-2^31) + // to be written as a decimal integer literal (9.4.4.2). + // + // Negation of ulong is an error: + // + // void operator -(ulong x); + // + // Selection of this operator by unary operator overload resolution (14.2.3) always results in a + // compile-time error. Consequently, if the operand of the negation operator is of type ulong, a + // compile-time error occurs. An exception is the rule that permits the long value + // -9223372036854775808 (-2^63) to be written as a decimal integer literal (9.4.4.2). + + + Debug.Assert(ptOp == PredefinedType.PT_INT || ptOp == PredefinedType.PT_UINT || ptOp == PredefinedType.PT_LONG || ptOp == PredefinedType.PT_ULONG); + CType typeOp = GetReqPDT(ptOp); + Debug.Assert(typeOp != null); + Debug.Assert(op != null && op.type == typeOp); + + if (ptOp == PredefinedType.PT_ULONG) + { + return BadOperatorTypesError(ExpressionKind.EK_NEG, op, null); + } + + if (ptOp == PredefinedType.PT_UINT && op.type.fundType() == FUNDTYPE.FT_U4) + { + EXPRCLASS exprObj = GetExprFactory().MakeClass(GetReqPDT(PredefinedType.PT_LONG)); + op = mustConvertCore(op, exprObj, CONVERTTYPE.NOUDC); + } + + EXPR exprRes = GetExprFactory().CreateNeg(flags, op); + Debug.Assert(0 == (exprRes.flags & EXPRFLAG.EXF_LVALUE)); + return exprRes; + } + + EXPR FoldConstI4Op(ExpressionKind kind, EXPR op1, EXPRCONSTANT opConst1, EXPR op2, EXPRCONSTANT opConst2, PredefinedType ptOp) + { + Debug.Assert(ptOp == PredefinedType.PT_INT || ptOp == PredefinedType.PT_UINT); + Debug.Assert(opConst1.isCONSTANT_OK()); + Debug.Assert(op1.type.isPredefType(ptOp) && op1.type == opConst1.type); + Debug.Assert(op2 == null && opConst2 == null || + op2 != null && opConst2 != null && opConst2.isCONSTANT_OK() && op1.type == op2.type && op1.type == opConst2.type); + + bool fSigned = (ptOp == PredefinedType.PT_INT); + + // Get the operands + uint u1 = opConst1.asCONSTANT().getVal().uiVal; + uint u2 = opConst2 != null ? opConst2.asCONSTANT().getVal().uiVal : 0; + uint uRes; + + // The code below doesn't work if uint isn't 4 bytes! + Debug.Assert(sizeof(uint) == 4); + + // The sign bit. + uint uSign = 0x80000000; + + // Do the operation. + switch (kind) + { + case ExpressionKind.EK_ADD: + uRes = u1 + u2; + // For signed, we want either sign(u1) != sign(u2) or sign(u1) == sign(uRes). + // For unsigned, the result should be at least as big as either operand (if it's bigger than + // one, it will be bigger than the other as well). + if (fSigned) + { + EnsureChecked(0 != (((u1 ^ u2) | (u1 ^ uRes ^ uSign)) & uSign)); + } + else + { + EnsureChecked(uRes >= u1); + } + break; + + case ExpressionKind.EK_SUB: + uRes = u1 - u2; + // For signed, we want either sign(u1) == sign(u2) or sign(u1) == sign(uRes). + // For unsigned, the result should be no bigger than the first operand. + if (fSigned) + { + EnsureChecked(0 != (((u1 ^ u2 ^ uSign) | (u1 ^ uRes ^ uSign)) & uSign)); + } + else + { + EnsureChecked(uRes <= u1); + } + break; + + case ExpressionKind.EK_MUL: + // Multiply mod 2^32 doesn't depend on signed vs unsigned. + uRes = u1 * u2; + // Note that divide depends on signed-ness. + // For signed, the first check detects 0x80000000 / 0xFFFFFFFF == 0x80000000. + // This test needs to come first to avoid an integer overflow exception - yes we get this + // in native code. + if (u1 == 0 || u2 == 0) + { + break; + } + + if (fSigned) + { + EnsureChecked((u2 != uRes || u1 == 1) && (int)uRes / (int)u1 == (int)u2); + } + else + { + EnsureChecked(uRes / u1 == u2); + } + break; + + case ExpressionKind.EK_DIV: + Debug.Assert(u2 != 0); // Caller should have handled this. + if (!fSigned) + { + uRes = u1 / u2; + } + else if (u2 != 0) + { + uRes = (uint)((int)u1 / (int)u2); + } + else + { + uRes = (uint)-(int)u1; + EnsureChecked(u1 != uSign); + } + break; + + case ExpressionKind.EK_MOD: + Debug.Assert(u2 != 0); // Caller should have handled this. + if (!fSigned) + { + uRes = u1 % u2; + } + else if (u2 != 0) + { + uRes = (uint)((int)u1 % (int)u2); + } + else + { + uRes = 0; + } + break; + + case ExpressionKind.EK_NEG: + if (!fSigned) + { + // Special case: a unary minus promotes a uint to a long + CONSTVAL cv = GetExprConstants().Create(-(long)u1); + EXPRCONSTANT foldedConst = GetExprFactory().CreateConstant(GetReqPDT(PredefinedType.PT_LONG), cv); + + return foldedConst; + } + + uRes = (uint)-(int)u1; + EnsureChecked(u1 != uSign); + break; + + case ExpressionKind.EK_UPLUS: + uRes = u1; + break; + case ExpressionKind.EK_BITAND: + uRes = u1 & u2; + break; + case ExpressionKind.EK_BITOR: + uRes = u1 | u2; + break; + case ExpressionKind.EK_BITXOR: + uRes = u1 ^ u2; + break; + case ExpressionKind.EK_BITNOT: + uRes = ~u1; + break; + case ExpressionKind.EK_EQ: + uRes = (uint)((u1 == u2) ? 1 : 0); + break; + case ExpressionKind.EK_NE: + uRes = (uint)((u1 != u2) ? 1 : 0); + break; + case ExpressionKind.EK_LE: + uRes = (uint)((fSigned ? (int)u1 <= (int)u2 : u1 <= u2) ? 1 : 0); + break; + case ExpressionKind.EK_LT: + uRes = (uint)((fSigned ? (int)u1 < (int)u2 : u1 < u2) ? 1 : 0); + break; + case ExpressionKind.EK_GE: + uRes = (uint)((fSigned ? (int)u1 >= (int)u2 : u1 >= u2) ? 1 : 0); + break; + case ExpressionKind.EK_GT: + uRes = (uint)((fSigned ? (int)u1 > (int)u2 : u1 > u2) ? 1 : 0); + break; + default: + VSFAIL("Unknown op"); + uRes = 0; + break; + } + + CType typeDest = GetOptPDT(kind.isRelational() ? PredefinedType.PT_BOOL : ptOp); + Debug.Assert(typeDest != null); + + // Allocate the result node. + EXPR exprRes = GetExprFactory().CreateConstant(typeDest, ConstValFactory.GetUInt(uRes)); + + return exprRes; + } + + void EnsureChecked(bool b) + { + if (!b && Context.CheckedConstant) + { + GetErrorContext().Error(ErrorCode.ERR_CheckedOverflow); + } + } + + EXPR FoldConstI8Op(ExpressionKind kind, EXPR op1, EXPRCONSTANT opConst1, EXPR op2, EXPRCONSTANT opConst2, PredefinedType ptOp) + { + Debug.Assert(ptOp == PredefinedType.PT_LONG || ptOp == PredefinedType.PT_ULONG); + Debug.Assert(opConst1.isCONSTANT_OK()); + Debug.Assert(op1.type.isPredefType(ptOp) && op1.type == opConst1.type); + Debug.Assert(op2 == null && opConst2 == null || + op2 != null && opConst2 != null && opConst2.isCONSTANT_OK() && op1.type == op2.type && op1.type == opConst2.type); + + bool fSigned = (ptOp == PredefinedType.PT_LONG); + bool fRes = false; + // Allocate the result node. + CType typeDest; + CONSTVAL cv = new CONSTVAL(); + + + if (fSigned) + { + // long. + long u1 = opConst1.asCONSTANT().getVal().longVal; + long u2 = opConst2 != null ? opConst2.asCONSTANT().getVal().longVal : 0; + long uRes = 0; + switch (kind) + { + case ExpressionKind.EK_ADD: + uRes = u1 + u2; + break; + + case ExpressionKind.EK_SUB: + uRes = u1 - u2; + break; + + case ExpressionKind.EK_MUL: + uRes = u1 * u2; + break; + + case ExpressionKind.EK_DIV: + Debug.Assert(u2 != 0); // Caller should have handled this. + uRes = u1 / u2; + break; + + case ExpressionKind.EK_MOD: + Debug.Assert(u2 != 0); // Caller should have handled this. + uRes = u1 % u2; + break; + + case ExpressionKind.EK_NEG: + uRes = -u1; + break; + + case ExpressionKind.EK_UPLUS: + uRes = u1; + break; + case ExpressionKind.EK_BITAND: + uRes = u1 & u2; + break; + case ExpressionKind.EK_BITOR: + uRes = u1 | u2; + break; + case ExpressionKind.EK_BITXOR: + uRes = u1 ^ u2; + break; + case ExpressionKind.EK_BITNOT: + uRes = ~u1; + break; + case ExpressionKind.EK_EQ: + fRes = (u1 == u2); + break; + case ExpressionKind.EK_NE: + fRes = (u1 != u2); + break; + case ExpressionKind.EK_LE: + fRes = u1 <= u2; + break; + case ExpressionKind.EK_LT: + fRes = u1 < u2; + break; + case ExpressionKind.EK_GE: + fRes = u1 >= u2; + break; + case ExpressionKind.EK_GT: + fRes = u1 > u2; + break; + default: + VSFAIL("Unknown op"); + uRes = 0; + break; + } + + if (kind.isRelational()) + { + cv.iVal = fRes ? 1 : 0; + typeDest = GetReqPDT(PredefinedType.PT_BOOL); + } + else + { + cv = GetExprConstants().Create(uRes); + typeDest = GetOptPDT(ptOp); + Debug.Assert(typeDest != null); + } + } + else + { + // ulong. + // Get the operands + ulong u1 = opConst1.asCONSTANT().getVal().ulongVal; + ulong u2 = opConst2 != null ? opConst2.asCONSTANT().getVal().ulongVal : 0; + ulong uRes = 0; + + // Do the operation. + switch (kind) + { + case ExpressionKind.EK_ADD: + uRes = u1 + u2; + break; + + case ExpressionKind.EK_SUB: + uRes = u1 - u2; + break; + + case ExpressionKind.EK_MUL: + uRes = u1 * u2; + break; + + case ExpressionKind.EK_DIV: + Debug.Assert(u2 != 0); // Caller should have handled this. + uRes = u1 / u2; + break; + + case ExpressionKind.EK_MOD: + Debug.Assert(u2 != 0); // Caller should have handled this. + uRes = u1 % u2; + break; + + case ExpressionKind.EK_NEG: + // You can't do this! + return BadOperatorTypesError(kind, op1, op2); + + case ExpressionKind.EK_UPLUS: + uRes = u1; + break; + case ExpressionKind.EK_BITAND: + uRes = u1 & u2; + break; + case ExpressionKind.EK_BITOR: + uRes = u1 | u2; + break; + case ExpressionKind.EK_BITXOR: + uRes = u1 ^ u2; + break; + case ExpressionKind.EK_BITNOT: + uRes = ~u1; + break; + case ExpressionKind.EK_EQ: + fRes = (u1 == u2); + break; + case ExpressionKind.EK_NE: + fRes = (u1 != u2); + break; + case ExpressionKind.EK_LE: + fRes = u1 <= u2; + break; + case ExpressionKind.EK_LT: + fRes = u1 < u2; + break; + case ExpressionKind.EK_GE: + fRes = u1 >= u2; + break; + case ExpressionKind.EK_GT: + fRes = u1 > u2; + break; + default: + VSFAIL("Unknown op"); + uRes = 0; + break; + } + + if (kind.isRelational()) + { + cv.iVal = fRes ? 1 : 0; + typeDest = GetReqPDT(PredefinedType.PT_BOOL); + } + else + { + cv = GetExprConstants().Create(uRes); + typeDest = GetOptPDT(ptOp); + Debug.Assert(typeDest != null); + } + } + + + // Allocate the result node. + EXPR exprRes = GetExprFactory().CreateConstant(typeDest, cv); + + return exprRes; + } + + /* + Bind an float/double operator: +, -, , /, %, <, >, <=, >=, ==, !=. If both operations are constants, the result + will be a constant also. op2 can be null for a unary operator. The operands are assumed + to be already converted to the correct type. + */ + // We have an intentional divide by 0 there, so disable the warning... +#if _MSC_VER +#pragma warning( disable : 4723 ) +#endif + EXPR bindFloatOp(ExpressionKind kind, EXPRFLAG flags, EXPR op1, EXPR op2) + { + //Debug.Assert(kind.isRelational() || kind.isArithmetic()); + Debug.Assert(op2 == null || op1.type == op2.type); + Debug.Assert(op1.type.isPredefType(PredefinedType.PT_FLOAT) || op1.type.isPredefType(PredefinedType.PT_DOUBLE)); + + EXPR exprRes; + EXPR opConst1 = op1.GetConst(); + EXPR opConst2 = op2 != null ? op2.GetConst() : null; + + // Check for constants and fold them. + if (opConst1 != null && (op2 == null || opConst2 != null)) + { + // Get the operands + double d1 = opConst1.asCONSTANT().getVal().doubleVal; + double d2 = opConst2 != null ? opConst2.asCONSTANT().getVal().doubleVal : 0.0; + double result = 0; // if isBoolResult is false + bool result_b = false; // if isBoolResult is true + + // Do the operation. + switch (kind) + { + case ExpressionKind.EK_ADD: + result = d1 + d2; + break; + case ExpressionKind.EK_SUB: + result = d1 - d2; + break; + case ExpressionKind.EK_MUL: + result = d1 * d2; + break; + case ExpressionKind.EK_DIV: + result = d1 / d2; + break; + case ExpressionKind.EK_NEG: + result = -d1; + break; + case ExpressionKind.EK_UPLUS: + result = d1; + break; + case ExpressionKind.EK_MOD: + result = d1 % d2; + break; + case ExpressionKind.EK_EQ: + result_b = (d1 == d2); + break; + case ExpressionKind.EK_NE: + result_b = (d1 != d2); + break; + case ExpressionKind.EK_LE: + result_b = (d1 <= d2); + break; + case ExpressionKind.EK_LT: + result_b = (d1 < d2); + break; + case ExpressionKind.EK_GE: + result_b = (d1 >= d2); + break; + case ExpressionKind.EK_GT: + result_b = (d1 > d2); + break; + default: + Debug.Assert(false); + result = 0.0; // unexpected operation. + break; + } + + CType typeDest; + CONSTVAL cv = new CONSTVAL(); + + // Allocate the result node. + if (kind.isRelational()) + { + cv.iVal = result_b ? 1 : 0; + typeDest = GetReqPDT(PredefinedType.PT_BOOL); + } + else + { + // NaN has some implementation defined bits that differ between platforms. + // Normalize it to produce identical images accross all platforms + /* + * TODO: How do we get here? + if (_isnan(result)) + { + cv = ConstValFactory.GetNan(); + } + else + { + * */ + cv = GetExprConstants().Create(result); + + typeDest = op1.type; + } + exprRes = GetExprFactory().CreateConstant(typeDest, cv); + } + else + { + // Allocate the result expression. + CType typeDest = kind.isRelational() ? GetReqPDT(PredefinedType.PT_BOOL) : op1.type; + + exprRes = GetExprFactory().CreateOperator(kind, typeDest, op1, op2); + flags = ~EXPRFLAG.EXF_CHECKOVERFLOW; + exprRes.flags |= flags; + } + + return exprRes; + } + +#if _MSC_VER +#pragma warning( default : 4723 ) +#endif + + EXPR bindStringConcat(EXPR op1, EXPR op2) + { + // If the concatenation consists solely of two constants then we must + // realize the concatenation into a single constant node at this time. + // Why? Because we have to know whether + // + // string x = "c" + "d"; + // + // is legal or not. We also need to be able to determine during flow + // checking that + // + // switch("a" + "b"){ case "a": ++foo; break; } + // + // contains unreachable code. + // + // However we can defer further merging of concatenation trees until + // the optimization pass after flow checking. + + Debug.Assert(op1 != null); + Debug.Assert(op2 != null); + return GetExprFactory().CreateConcat(op1, op2); + } + + /* + Report an ambiguous operator types error. + */ + EXPR ambiguousOperatorError(ExpressionKind ek, EXPR op1, EXPR op2) + { + RETAILVERIFY(op1 != null); + + // This is exactly the same "hack" that BadOperatorError uses. The first operand contains the + // name of the operator in its errorString. + string strOp = op1.errorString; + + // Bad arg types - report error to user. + if (op2 != null) + { + GetErrorContext().Error(ErrorCode.ERR_AmbigBinaryOps, strOp, op1.type, op2.type); + } + else + { + GetErrorContext().Error(ErrorCode.ERR_AmbigUnaryOp, strOp, op1.type); + } + + EXPR rval = GetExprFactory().CreateOperator(ek, null, op1, op2); + rval.SetError(); + return rval; + } + + EXPR BindUserBoolOp(ExpressionKind kind, EXPRCALL pCall) + { + RETAILVERIFY(pCall != null); + RETAILVERIFY(pCall.mwi.Meth() != null); + RETAILVERIFY(pCall.GetOptionalArguments() != null); + Debug.Assert(kind == ExpressionKind.EK_LOGAND || kind == ExpressionKind.EK_LOGOR); + + CType typeRet = pCall.type; + + Debug.Assert(pCall.mwi.Meth().Params.size == 2); + if (!GetTypes().SubstEqualTypes(typeRet, pCall.mwi.Meth().Params.Item(0), typeRet) || + !GetTypes().SubstEqualTypes(typeRet, pCall.mwi.Meth().Params.Item(1), typeRet)) + { + MethWithInst mwi = new MethWithInst(null, null); + EXPRMEMGRP pMemGroup = GetExprFactory().CreateMemGroup(null, mwi); + EXPRCALL pCallTF = GetExprFactory().CreateCall(0, null, null, pMemGroup, null); + pCallTF.SetError(); + GetErrorContext().Error(ErrorCode.ERR_BadBoolOp, pCall.mwi); + return GetExprFactory().CreateUserLogOpError(typeRet, pCallTF, pCall); + } + + Debug.Assert(pCall.GetOptionalArguments().isLIST()); + Debug.Assert(pCall.GetOptionalArguments().asLIST().GetOptionalElement().type == typeRet); + Debug.Assert(pCall.GetOptionalArguments().asLIST().GetOptionalNextListNode().type == typeRet); + + EXPR pExpr = pCall.GetOptionalArguments().asLIST().GetOptionalElement(); + EXPR pExprWrap = WrapShortLivedExpression(pExpr); + pCall.GetOptionalArguments().asLIST().SetOptionalElement(pExprWrap); + + // Reflection load the true and false methods. + SymbolLoader.RuntimeBinderSymbolTable.PopulateSymbolTableWithName(SpecialNames.CLR_True, null, pExprWrap.type.AssociatedSystemType); + SymbolLoader.RuntimeBinderSymbolTable.PopulateSymbolTableWithName(SpecialNames.CLR_False, null, pExprWrap.type.AssociatedSystemType); + + EXPR pCallT = bindUDUnop(ExpressionKind.EK_TRUE, pExprWrap); + EXPR pCallF = bindUDUnop(ExpressionKind.EK_FALSE, pExprWrap); + + if (pCallT == null || pCallF == null) + { + EXPR pCallTorF = pCallT != null ? pCallT : pCallF; + if (pCallTorF == null) + { + MethWithInst mwi = new MethWithInst(null, null); + EXPRMEMGRP pMemGroup = GetExprFactory().CreateMemGroup(null, mwi); + pCallTorF = GetExprFactory().CreateCall(0, null, pExprWrap, pMemGroup, null); + pCall.SetError(); + } + GetErrorContext().Error(ErrorCode.ERR_MustHaveOpTF, typeRet); + return GetExprFactory().CreateUserLogOpError(typeRet, pCallTorF, pCall); + } + pCallT = mustConvert(pCallT, GetReqPDT(PredefinedType.PT_BOOL)); + pCallF = mustConvert(pCallF, GetReqPDT(PredefinedType.PT_BOOL)); + return GetExprFactory().CreateUserLogOp(typeRet, kind == ExpressionKind.EK_LOGAND ? pCallF : pCallT, pCall); + } + + AggregateType GetUserDefinedBinopArgumentType(CType type) + { + for (; ; ) + { + switch (type.GetTypeKind()) + { + case TypeKind.TK_NullableType: + type = type.StripNubs(); + break; + case TypeKind.TK_TypeParameterType: + type = type.AsTypeParameterType().GetEffectiveBaseClass(); + break; + case TypeKind.TK_AggregateType: + if ((type.isClassType() || type.isStructType()) && !type.AsAggregateType().getAggregate().IsSkipUDOps()) + { + return type.AsAggregateType(); + } + return null; + default: + return null; + } + } + } + + int GetUserDefinedBinopArgumentTypes(CType type1, CType type2, AggregateType[] rgats) + { + int cats = 0; + rgats[0] = GetUserDefinedBinopArgumentType(type1); + if (rgats[0] != null) + { + ++cats; + } + rgats[cats] = GetUserDefinedBinopArgumentType(type2); + if (rgats[cats] != null) + { + ++cats; + } + if (cats == 2 && rgats[0] == rgats[1]) + { + // Common case: they're the same. + cats = 1; + } + return cats; + } + + bool UserDefinedBinaryOperatorCanBeLifted(ExpressionKind ek, MethodSymbol method, AggregateType ats, + TypeArray Params) + { + if (!Params.Item(0).IsNonNubValType()) + { + return false; + } + if (!Params.Item(1).IsNonNubValType()) + { + return false; + } + CType typeRet = GetTypes().SubstType(method.RetType, ats); + if (!typeRet.IsNonNubValType()) + { + return false; + } + switch (ek) + { + case ExpressionKind.EK_EQ: + case ExpressionKind.EK_NE: + if (!typeRet.isPredefType(PredefinedType.PT_BOOL)) + { + return false; + } + if (Params.Item(0) != Params.Item(1)) + { + return false; + } + return true; + case ExpressionKind.EK_GT: + case ExpressionKind.EK_GE: + case ExpressionKind.EK_LT: + case ExpressionKind.EK_LE: + if (!typeRet.isPredefType(PredefinedType.PT_BOOL)) + { + return false; + } + return true; + default: + return true; + } + } + + // If the operator is applicable in either its regular or lifted forms, + // add it to the candidate set and return true, otherwise return false. + bool UserDefinedBinaryOperatorIsApplicable(List candidateList, + ExpressionKind ek, MethodSymbol method, AggregateType ats, EXPR arg1, EXPR arg2, bool fDontLift) + { + if (!method.isOperator || method.Params.size != 2) + { + return false; + } + Debug.Assert(method.typeVars.size == 0); + TypeArray paramsCur = GetTypes().SubstTypeArray(method.Params, ats); + if (canConvert(arg1, paramsCur.Item(0)) && canConvert(arg2, paramsCur.Item(1))) + { + candidateList.Add(new CandidateFunctionMember( + new MethPropWithInst(method, ats, BSYMMGR.EmptyTypeArray()), + paramsCur, + 0, // No lifted arguments + false)); + return true; + } + if (fDontLift || !GetSymbolLoader().FCanLift() || + !UserDefinedBinaryOperatorCanBeLifted(ek, method, ats, paramsCur)) + { + return false; + } + CType[] rgtype = new CType[2]; + rgtype[0] = GetTypes().GetNullable(paramsCur.Item(0)); + rgtype[1] = GetTypes().GetNullable(paramsCur.Item(1)); + if (!canConvert(arg1, rgtype[0]) || !canConvert(arg2, rgtype[1])) + { + return false; + } + candidateList.Add(new CandidateFunctionMember( + new MethPropWithInst(method, ats, BSYMMGR.EmptyTypeArray()), + GetGlobalSymbols().AllocParams(2, rgtype), + 2, // two lifted arguments + false)); + return true; + } + + bool GetApplicableUserDefinedBinaryOperatorCandidates( + List candidateList, ExpressionKind ek, AggregateType type, + EXPR arg1, EXPR arg2, bool fDontLift) + { + Name name = ekName(ek); + Debug.Assert(name != null); + bool foundSome = false; + for (MethodSymbol methCur = GetSymbolLoader().LookupAggMember(name, type.getAggregate(), symbmask_t.MASK_MethodSymbol).AsMethodSymbol(); + methCur != null; + methCur = GetSymbolLoader().LookupNextSym(methCur, type.getAggregate(), symbmask_t.MASK_MethodSymbol).AsMethodSymbol()) + { + if (UserDefinedBinaryOperatorIsApplicable(candidateList, ek, methCur, type, arg1, arg2, fDontLift)) + { + foundSome = true; + } + } + return foundSome; + } + + AggregateType GetApplicableUserDefinedBinaryOperatorCandidatesInBaseTypes( + List candidateList, ExpressionKind ek, AggregateType type, + EXPR arg1, EXPR arg2, bool fDontLift, AggregateType atsStop) + { + for (AggregateType atsCur = type; atsCur != null && atsCur != atsStop; atsCur = atsCur.GetBaseClass()) + { + if (GetApplicableUserDefinedBinaryOperatorCandidates(candidateList, ek, atsCur, arg1, arg2, fDontLift)) + { + return atsCur; + } + } + return null; + } + + EXPRCALL BindUDBinop(ExpressionKind ek, EXPR arg1, EXPR arg2, bool fDontLift, out MethPropWithInst ppmpwi) + { + List methFirst = new List(); + + ppmpwi = null; + + AggregateType[] rgats = { null, null }; + int cats = GetUserDefinedBinopArgumentTypes(arg1.type, arg2.type, rgats); + if (cats == 0) + { + return null; + } + else if (cats == 1) + { + GetApplicableUserDefinedBinaryOperatorCandidatesInBaseTypes(methFirst, ek, + rgats[0], arg1, arg2, fDontLift, null); + } + else + { + Debug.Assert(cats == 2); + AggregateType atsStop = GetApplicableUserDefinedBinaryOperatorCandidatesInBaseTypes(methFirst, ek, + rgats[0], arg1, arg2, fDontLift, null); + GetApplicableUserDefinedBinaryOperatorCandidatesInBaseTypes(methFirst, ek, + rgats[1], arg1, arg2, fDontLift, atsStop); + } + if (methFirst.IsEmpty()) + { + return null; + } + + EXPRLIST args = GetExprFactory().CreateList(arg1, arg2); + ArgInfos info = new ArgInfos(); + info.carg = 2; + FillInArgInfoFromArgList(info, args); + CandidateFunctionMember pmethAmbig1; + CandidateFunctionMember pmethAmbig2; + CandidateFunctionMember pmethBest = FindBestMethod(methFirst, null, info, out pmethAmbig1, out pmethAmbig2); + + if (pmethBest == null) + { + // No winner, so its an ambigous call... + GetErrorContext().Error(ErrorCode.ERR_AmbigCall, pmethAmbig1.mpwi, pmethAmbig2.mpwi); + + EXPRMEMGRP pMemGroup = GetExprFactory().CreateMemGroup(null, pmethAmbig1.mpwi); + EXPRCALL rval = GetExprFactory().CreateCall(0, null, GetExprFactory().CreateList(arg1, arg2), pMemGroup, null); + rval.SetError(); + return rval; + } + + if (GetSemanticChecker().CheckBogus(pmethBest.mpwi.Meth())) + { + GetErrorContext().ErrorRef(ErrorCode.ERR_BindToBogus, pmethBest.mpwi); + + EXPRMEMGRP pMemGroup = GetExprFactory().CreateMemGroup(null, pmethBest.mpwi); + EXPRCALL rval = GetExprFactory().CreateCall(0, null, GetExprFactory().CreateList(arg1, arg2), pMemGroup, null); + rval.SetError(); + return rval; + } + + ppmpwi = pmethBest.mpwi; + + if (pmethBest.ctypeLift != 0) + { + Debug.Assert(pmethBest.ctypeLift == 2); + + return BindLiftedUDBinop(ek, arg1, arg2, pmethBest.@params, pmethBest.mpwi); + } + + CType typeRetRaw = GetTypes().SubstType(pmethBest.mpwi.Meth().RetType, pmethBest.mpwi.GetType()); + + return BindUDBinopCall(arg1, arg2, pmethBest.@params, typeRetRaw, pmethBest.mpwi); + } + + EXPRCALL BindUDBinopCall(EXPR arg1, EXPR arg2, TypeArray Params, CType typeRet, MethPropWithInst mpwi) + { + arg1 = mustConvert(arg1, Params.Item(0)); + arg2 = mustConvert(arg2, Params.Item(1)); + EXPRLIST args = GetExprFactory().CreateList(arg1, arg2); + + checkUnsafe(arg1.type); // added to the binder so we don't bind to pointer ops + checkUnsafe(arg2.type); // added to the binder so we don't bind to pointer ops + checkUnsafe(typeRet); // added to the binder so we don't bind to pointer ops + + + EXPRMEMGRP pMemGroup = GetExprFactory().CreateMemGroup(null, mpwi); + EXPRCALL call = GetExprFactory().CreateCall(0, typeRet, args, pMemGroup, null); + call.mwi = new MethWithInst(mpwi); + verifyMethodArgs(call, mpwi.GetType()); + return call; + } + + EXPRCALL BindLiftedUDBinop(ExpressionKind ek, EXPR arg1, EXPR arg2, TypeArray Params, MethPropWithInst mpwi) + { + EXPR exprVal1 = arg1; + EXPR exprVal2 = arg2; + TypeArray paramsRaw; + CType typeRet; + CType typeRetRaw = GetTypes().SubstType(mpwi.Meth().RetType, mpwi.GetType()); + + // This is a lifted user defined operator. We know that both arguments + // go to the nullable formal parameter types, and that at least one + // of the arguments does not go to the non-nullable formal parameter type. + // (If both went to the non-nullable types then we would not be lifting.) + // We also know that the non-nullable type of the argument goes to the + // non-nullable type of formal parameter. However, if it does so only via + // a user-defined conversion then we should bind the conversion from the + // argument to the nullable formal parameter type first, before we then + // do the cast for the non-nullable call. + + paramsRaw = GetTypes().SubstTypeArray(mpwi.Meth().Params, mpwi.GetType()); + Debug.Assert(Params != paramsRaw); + Debug.Assert(paramsRaw.Item(0) == Params.Item(0).GetBaseOrParameterOrElementType()); + Debug.Assert(paramsRaw.Item(1) == Params.Item(1).GetBaseOrParameterOrElementType()); + + if (!canConvert(arg1.type.StripNubs(), paramsRaw.Item(0), CONVERTTYPE.NOUDC)) + { + exprVal1 = mustConvert(arg1, Params.Item(0)); + } + if (!canConvert(arg2.type.StripNubs(), paramsRaw.Item(1), CONVERTTYPE.NOUDC)) + { + exprVal2 = mustConvert(arg2, Params.Item(1)); + } + EXPR nonLiftedArg1 = mustCast(exprVal1, paramsRaw.Item(0)); + EXPR nonLiftedArg2 = mustCast(exprVal2, paramsRaw.Item(1)); + switch (ek) + { + default: + typeRet = GetTypes().GetNullable(typeRetRaw); + break; + case ExpressionKind.EK_EQ: + case ExpressionKind.EK_NE: + Debug.Assert(paramsRaw.Item(0) == paramsRaw.Item(1)); + Debug.Assert(typeRetRaw.isPredefType(PredefinedType.PT_BOOL)); + // These ones don't lift the return type. Instead, if either side is null, the result is false. + typeRet = typeRetRaw; + break; + case ExpressionKind.EK_GT: + case ExpressionKind.EK_GE: + case ExpressionKind.EK_LT: + case ExpressionKind.EK_LE: + Debug.Assert(typeRetRaw.isPredefType(PredefinedType.PT_BOOL)); + // These ones don't lift the return type. Instead, if either side is null, the result is false. + typeRet = typeRetRaw; + break; + } + + // Now get the result for the pre-lifted call. + + Debug.Assert(!(ek == ExpressionKind.EK_EQ || ek == ExpressionKind.EK_NE) || nonLiftedArg1.type == nonLiftedArg2.type); + + EXPRCALL nonLiftedResult = BindUDBinopCall(nonLiftedArg1, nonLiftedArg2, paramsRaw, typeRetRaw, mpwi); + + EXPRLIST args = GetExprFactory().CreateList(exprVal1, exprVal2); + EXPRMEMGRP pMemGroup = GetExprFactory().CreateMemGroup(null, mpwi); + EXPRCALL call = GetExprFactory().CreateCall(0, typeRet, args, pMemGroup, null); + call.mwi = new MethWithInst(mpwi); + + switch (ek) + { + case ExpressionKind.EK_EQ: + call.nubLiftKind = NullableCallLiftKind.EqualityOperator; + break; + + case ExpressionKind.EK_NE: + call.nubLiftKind = NullableCallLiftKind.InequalityOperator; + break; + + default: + call.nubLiftKind = NullableCallLiftKind.Operator; + break; + } + + call.castOfNonLiftedResultToLiftedType = mustCast(nonLiftedResult, typeRet, 0); + return call; + } + + AggregateType GetEnumBinOpType(ExpressionKind ek, CType argType1, CType argType2, out AggregateType ppEnumType) + { + Debug.Assert(argType1.isEnumType() || argType2.isEnumType()); + + AggregateType type1 = argType1.AsAggregateType(); + AggregateType type2 = argType2.AsAggregateType(); + + AggregateType typeEnum = type1.isEnumType() ? type1 : type2; + + Debug.Assert(type1 == typeEnum || type1 == typeEnum.underlyingEnumType()); + Debug.Assert(type2 == typeEnum || type2 == typeEnum.underlyingEnumType()); + + AggregateType typeDst = typeEnum; + + switch (ek) + { + case ExpressionKind.EK_BITAND: + case ExpressionKind.EK_BITOR: + case ExpressionKind.EK_BITXOR: + Debug.Assert(type1 == type2); + break; + + case ExpressionKind.EK_ADD: + Debug.Assert(type1 != type2); + break; + + case ExpressionKind.EK_SUB: + if (type1 == type2) + typeDst = typeEnum.underlyingEnumType(); + break; + + default: + Debug.Assert(ek.isRelational()); + typeDst = GetReqPDT(PredefinedType.PT_BOOL); + break; + } + + ppEnumType = typeEnum; + return typeDst; + } + + EXPRBINOP CreateBinopForPredefMethodCall(ExpressionKind ek, PREDEFMETH predefMeth, CType RetType, EXPR arg1, EXPR arg2) + { + MethodSymbol methSym = GetSymbolLoader().getPredefinedMembers().GetMethod(predefMeth); + EXPRBINOP binop = GetExprFactory().CreateBinop(ek, RetType, arg1, arg2); + + // Set the predefined method to call. + if (methSym != null) + { + AggregateSymbol agg = methSym.getClass(); + AggregateType callingType = GetTypes().GetAggregate(agg, BSYMMGR.EmptyTypeArray()); + binop.predefinedMethodToCall = new MethWithInst(methSym, callingType, null); + binop.SetUserDefinedCallMethod(binop.predefinedMethodToCall); + } + else + { + // Couldn't find it. + binop.SetError(); + } + return binop; + } + + EXPRUNARYOP CreateUnaryOpForPredefMethodCall(ExpressionKind ek, PREDEFMETH predefMeth, CType pRetType, EXPR pArg) + { + MethodSymbol methSym = GetSymbolLoader().getPredefinedMembers().GetMethod(predefMeth); + EXPRUNARYOP pUnaryOp = GetExprFactory().CreateUnaryOp(ek, pRetType, pArg); + + // Set the predefined method to call. + if (methSym != null) + { + AggregateSymbol pAgg = methSym.getClass(); + AggregateType pCallingType = GetTypes().GetAggregate(pAgg, BSYMMGR.EmptyTypeArray()); + pUnaryOp.predefinedMethodToCall = new MethWithInst(methSym, pCallingType, null); + pUnaryOp.UserDefinedCallMethod = pUnaryOp.predefinedMethodToCall; + } + else + { + pUnaryOp.SetError(); + } + return pUnaryOp; + } + } +} diff --git a/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/OriginalExpressions.cs b/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/OriginalExpressions.cs new file mode 100644 index 000000000..54171a1bf --- /dev/null +++ b/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/OriginalExpressions.cs @@ -0,0 +1,47 @@ +// ==++== +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// ==--== + +namespace Microsoft.CSharp.RuntimeBinder.Semantics +{ + internal enum CONSTRESKIND + { + ConstTrue, + ConstFalse, + ConstNotConst, + } + internal enum LambdaParams + { + FromDelegate, + FromLambda, + Error + } + internal enum TypeOrSimpleNameResolution + { + Unknown, + CType, + SimpleName + } + internal enum InitializerKind + { + CollectionInitializer, + ObjectInitializer + } + + internal enum ConstantStringConcatenation + { + NotAString, + NotYetCalculated, + Calculated + } + + internal enum ForeachKind + { + Array, + String, + Enumerator + } + +} diff --git a/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/PredefinedAttributes.cs b/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/PredefinedAttributes.cs new file mode 100644 index 000000000..5b648b419 --- /dev/null +++ b/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/PredefinedAttributes.cs @@ -0,0 +1,48 @@ +// ==++== +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// ==--== + +namespace Microsoft.CSharp.RuntimeBinder.Semantics +{ + /* + * PREDEFATTR - enum of predefined attributes + */ + enum PREDEFATTR + { + PA_ATTRIBUTEUSAGE, + PA_OBSOLETE, + PA_CLSCOMPLIANT, + PA_CONDITIONAL, + PA_REQUIRED, + PA_FIXED, + PA_DEBUGGABLE, + PA_ASSEMBLYFLAGS, + PA_ASSEMBLYVERSION, + PA_ASSEMBLYCULTURE, + PA_NAME, + PA_DLLIMPORT, + PA_COMIMPORT, + PA_GUID, + PA_IN, + PA_OUT, + PA_STRUCTOFFSET, + PA_STRUCTLAYOUT, + PA_PARAMARRAY, + PA_COCLASS, + PA_DEFAULTCHARSET, + PA_DEFAULTVALUE, + PA_UNMANAGEDFUNCTIONPOINTER, + PA_COMPILATIONRELAXATIONS, + PA_RUNTIMECOMPATIBILITY, + PA_FRIENDASSEMBLY, + PA_KEYFILE, + PA_KEYNAME, + PA_DELAYSIGN, + PA_DEFAULTMEMBER, + PA_TYPEFORWARDER, + PA_EXTENSION, + PA_COUNT + } +} \ No newline at end of file diff --git a/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/PredefinedMembers.cs b/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/PredefinedMembers.cs new file mode 100644 index 000000000..751364154 --- /dev/null +++ b/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/PredefinedMembers.cs @@ -0,0 +1,891 @@ +// ==++== +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// ==--== + +using System.Diagnostics; +using Microsoft.CSharp.RuntimeBinder; +using Microsoft.CSharp.RuntimeBinder.Errors; +using Microsoft.CSharp.RuntimeBinder.Syntax; + +namespace Microsoft.CSharp.RuntimeBinder.Semantics +{ + // enum identifying all predefined methods used in the C# compiler + // + // Naming convention is PREDEFMETH.PM_ _ < Predefined Name of Method> + // if methods can only be disambiguated by signature, then follow the + // above with _ + // + // Keep this list sorted by containing type and name. + internal enum PREDEFMETH + { + PM_FIRST = 0, + + PM_ARRAY_GETLENGTH, + + PM_DECIMAL_OPDECREMENT, + PM_DECIMAL_OPDIVISION, + PM_DECIMAL_OPEQUALITY, + PM_DECIMAL_OPGREATERTHAN, + PM_DECIMAL_OPGREATERTHANOREQUAL, + PM_DECIMAL_OPINCREMENT, + PM_DECIMAL_OPINEQUALITY, + PM_DECIMAL_OPLESSTHAN, + PM_DECIMAL_OPLESSTHANOREQUAL, + PM_DECIMAL_OPMINUS, + PM_DECIMAL_OPMODULUS, + PM_DECIMAL_OPMULTIPLY, + PM_DECIMAL_OPPLUS, + PM_DECIMAL_OPUNARYMINUS, + PM_DECIMAL_OPUNARYPLUS, + + PM_DELEGATE_COMBINE, + PM_DELEGATE_OPEQUALITY, + PM_DELEGATE_OPINEQUALITY, + PM_DELEGATE_REMOVE, + + PM_EXPRESSION_ADD, + PM_EXPRESSION_ADD_USER_DEFINED, + PM_EXPRESSION_ADDCHECKED, + PM_EXPRESSION_ADDCHECKED_USER_DEFINED, + PM_EXPRESSION_AND, + PM_EXPRESSION_AND_USER_DEFINED, + PM_EXPRESSION_ANDALSO, + PM_EXPRESSION_ANDALSO_USER_DEFINED, + PM_EXPRESSION_ARRAYINDEX, + PM_EXPRESSION_ARRAYINDEX2, + PM_EXPRESSION_ASSIGN, + + PM_EXPRESSION_CONDITION, + + PM_EXPRESSION_CONSTANT_OBJECT_TYPE, + PM_EXPRESSION_CONVERT, + PM_EXPRESSION_CONVERT_USER_DEFINED, + PM_EXPRESSION_CONVERTCHECKED, + PM_EXPRESSION_CONVERTCHECKED_USER_DEFINED, + PM_EXPRESSION_DIVIDE, + PM_EXPRESSION_DIVIDE_USER_DEFINED, + + PM_EXPRESSION_EQUAL, + PM_EXPRESSION_EQUAL_USER_DEFINED, + PM_EXPRESSION_EXCLUSIVEOR, + PM_EXPRESSION_EXCLUSIVEOR_USER_DEFINED, + PM_EXPRESSION_FIELD, + PM_EXPRESSION_GREATERTHAN, + PM_EXPRESSION_GREATERTHAN_USER_DEFINED, + PM_EXPRESSION_GREATERTHANOREQUAL, + PM_EXPRESSION_GREATERTHANOREQUAL_USER_DEFINED, + PM_EXPRESSION_LAMBDA, + + PM_EXPRESSION_LEFTSHIFT, + PM_EXPRESSION_LEFTSHIFT_USER_DEFINED, + PM_EXPRESSION_LESSTHAN, + PM_EXPRESSION_LESSTHAN_USER_DEFINED, + PM_EXPRESSION_LESSTHANOREQUAL, + PM_EXPRESSION_LESSTHANOREQUAL_USER_DEFINED, + PM_EXPRESSION_MODULO, + PM_EXPRESSION_MODULO_USER_DEFINED, + PM_EXPRESSION_MULTIPLY, + PM_EXPRESSION_MULTIPLY_USER_DEFINED, + PM_EXPRESSION_MULTIPLYCHECKED, + PM_EXPRESSION_MULTIPLYCHECKED_USER_DEFINED, + PM_EXPRESSION_NOTEQUAL, + PM_EXPRESSION_NOTEQUAL_USER_DEFINED, + PM_EXPRESSION_OR, + PM_EXPRESSION_OR_USER_DEFINED, + PM_EXPRESSION_ORELSE, + PM_EXPRESSION_ORELSE_USER_DEFINED, + PM_EXPRESSION_PARAMETER, + PM_EXPRESSION_RIGHTSHIFT, + PM_EXPRESSION_RIGHTSHIFT_USER_DEFINED, + PM_EXPRESSION_SUBTRACT, + PM_EXPRESSION_SUBTRACT_USER_DEFINED, + PM_EXPRESSION_SUBTRACTCHECKED, + PM_EXPRESSION_SUBTRACTCHECKED_USER_DEFINED, + PM_EXPRESSION_UNARYPLUS_USER_DEFINED, + PM_EXPRESSION_NEGATE, + PM_EXPRESSION_NEGATE_USER_DEFINED, + PM_EXPRESSION_NEGATECHECKED, + PM_EXPRESSION_NEGATECHECKED_USER_DEFINED, + PM_EXPRESSION_CALL, + PM_EXPRESSION_NEW, + PM_EXPRESSION_NEW_MEMBERS, + PM_EXPRESSION_NEW_TYPE, + PM_EXPRESSION_QUOTE, + PM_EXPRESSION_ARRAYLENGTH, + + + PM_EXPRESSION_NOT, + PM_EXPRESSION_NOT_USER_DEFINED, + + + PM_EXPRESSION_NEWARRAYINIT, + PM_EXPRESSION_PROPERTY, + + + PM_EXPRESSION_INVOKE, + PM_DELEGATE_CREATEDELEGATE_TYPE_OBJ_METHINFO, + + PM_G_OPTIONAL_CTOR, + PM_G_OPTIONAL_GETHASVALUE, + PM_G_OPTIONAL_GETVALUE, + PM_G_OPTIONAL_GET_VALUE_OR_DEF, + + PM_STRING_CONCAT_OBJECT_1, // NOTE: these 3 must be sequential. See RealizeConcats + PM_STRING_CONCAT_OBJECT_2, + PM_STRING_CONCAT_OBJECT_3, + PM_STRING_CONCAT_STRING_1, // NOTE: these 4 must be sequential. See RealizeConcats + PM_STRING_CONCAT_STRING_2, + PM_STRING_CONCAT_STRING_3, + PM_STRING_CONCAT_STRING_4, + PM_STRING_CONCAT_SZ_OBJECT, + PM_STRING_CONCAT_SZ_STRING, + PM_STRING_GETCHARS, + PM_STRING_GETLENGTH, + PM_STRING_OPEQUALITY, + PM_STRING_OPINEQUALITY, + + PM_COUNT + } + + // enum identifying all predefined properties used in the C# compiler + // Naming convention is PREDEFMETH.PM_ _ < Predefined Name of Property> + // Keep this list sorted by containing type and name. + internal enum PREDEFPROP + { + PP_FIRST = 0, + PP_ARRAY_LENGTH, + PP_G_OPTIONAL_VALUE, + PP_COUNT, + }; + + internal enum MethodRequiredEnum + { + Required, + Optional + } + // Consider: distinguishing Constructor's from regular MethodCallingConventionEnum.Instance methods + internal enum MethodCallingConventionEnum + { + Static, + Virtual, + Instance + } + // Enum used to encode a method signature + // A signature is encoded as a sequence of int values. + // The grammar for signatures is: + // + // signature + // return_type count_of_parameters parameter_types + // + // type + // any predefined type (ex: PredefinedType.PT_OBJECT, PredefinedType.PT_VOID) type_args + // MethodSignatureEnum.SIG_CLASS_TYVAR index_of_class_tyvar + // MethodSignatureEnum.SIG_METH_TYVAR index_of_method_tyvar + // MethodSignatureEnum.SIG_SZ_ARRAY type + // MethodSignatureEnum.SIG_REF type + // MethodSignatureEnum.SIG_OUT type + // + // UNDONE: + // Add new types as needed: + // pointer + internal enum MethodSignatureEnum + { + // Values 0 to PredefinedType.PT_VOID are reserved for predefined types in signatures + // start next value at PredefinedType.PT_VOID + 1, + SIG_CLASS_TYVAR = (int)PredefinedType.PT_VOID + 1, // next element in signature is index of class tyvar + SIG_METH_TYVAR, // next element in signature is index of method tyvar + SIG_SZ_ARRAY, // must be followed by signature type of array elements + SIG_REF, // must be followed by signature of ref type + SIG_OUT, // must be followed by signature of out type + } + + // A description of a method the compiler uses while compiling. + // Consider: adding bounds for type variables. + internal class PredefinedMethodInfo + { + public PREDEFMETH method; + public PredefinedType type; + public PredefinedName name; + public MethodCallingConventionEnum callingConvention; + public ACCESS access; // ACCESS.ACC_UNKNOWN means any accessibility is ok + public int cTypeVars; + public int[] signature; // Size 8. expand this if a new method has a signature which doesn't fit in the current space + + public PredefinedMethodInfo(PREDEFMETH method, MethodRequiredEnum required, PredefinedType type, PredefinedName name, MethodCallingConventionEnum callingConvention, ACCESS access, int cTypeVars, int[] signature) + { + this.method = method; + this.type = type; + this.name = name; + this.callingConvention = callingConvention; + this.access = access; + this.cTypeVars = cTypeVars; + this.signature = signature; + } + } + + + // A description of a method the compiler uses while compiling. + // Consider: adding bounds for type variables. + internal class PredefinedPropertyInfo + { + public PREDEFPROP property; + public PredefinedName name; + public PREDEFMETH getter; + public PREDEFMETH setter; + + public PredefinedPropertyInfo(PREDEFPROP property, MethodRequiredEnum required, PredefinedName name, PREDEFMETH getter, PREDEFMETH setter) + { + this.property = property; + this.name = name; + this.getter = getter; + this.setter = setter; + } + }; + + // Loads and caches predefined members. + // Also finds constructors on delegate types. + internal class PredefinedMembers + { + protected static void RETAILVERIFY(bool f) + { + if (!f) + Debug.Assert(false, "panic!"); + } + + private SymbolLoader m_loader; + internal SymbolTable RuntimeBinderSymbolTable; + private MethodSymbol[] m_methods = new MethodSymbol[(int)PREDEFMETH.PM_COUNT]; + private PropertySymbol[] m_properties = new PropertySymbol[(int)PREDEFPROP.PP_COUNT]; + + private Name GetMethName(PREDEFMETH method) + { + return GetPredefName(GetMethPredefName(method)); + } + + private AggregateSymbol GetMethParent(PREDEFMETH method) + { + return GetOptPredefAgg(GetMethPredefType(method)); + } + + // delegate specific helpers + private MethodSymbol FindDelegateConstructor(AggregateSymbol delegateType, int[] signature) + { + Debug.Assert(delegateType != null && delegateType.IsDelegate()); + Debug.Assert(signature != null); + + return LoadMethod( + delegateType, + signature, + 0, // meth ty vars + GetPredefName(PredefinedName.PN_CTOR), + ACCESS.ACC_PUBLIC, + false, // MethodCallingConventionEnum.Static + false); // MethodCallingConventionEnum.Virtual + } + + private MethodSymbol FindDelegateConstructor(AggregateSymbol delegateType) + { + Debug.Assert(delegateType != null && delegateType.IsDelegate()); + + MethodSymbol ctor = FindDelegateConstructor(delegateType, g_DelegateCtorSignature1); + if (ctor == null) + { + ctor = FindDelegateConstructor(delegateType, g_DelegateCtorSignature2); + } + + return ctor; + } + + public MethodSymbol FindDelegateConstructor(AggregateSymbol delegateType, bool fReportErrors) + { + MethodSymbol ctor = FindDelegateConstructor(delegateType); + if (ctor == null && fReportErrors) + { + GetErrorContext().Error(ErrorCode.ERR_BadDelegateConstructor, delegateType); + } + + return ctor; + throw Error.InternalCompilerError(); + } + + // property specific helpers + private PropertySymbol EnsureProperty(PREDEFPROP property) + { + RETAILVERIFY((int)property > (int)PREDEFMETH.PM_FIRST && (int)property < (int)PREDEFMETH.PM_COUNT); + + if (m_properties[(int)property] == null) + { + m_properties[(int)property] = LoadProperty(property); + } + return m_properties[(int)property]; + } + private PropertySymbol LoadProperty(PREDEFPROP property) + { + return LoadProperty( + property, + GetPropName(property), + GetPropGetter(property), + GetPropSetter(property)); + } + + private Name GetPropName(PREDEFPROP property) + { + return GetPredefName(GetPropPredefName(property)); + } + private PropertySymbol LoadProperty( + PREDEFPROP predefProp, + Name propertyName, + PREDEFMETH propertyGetter, + PREDEFMETH propertySetter) + { + Debug.Assert(propertyName != null); + Debug.Assert(propertyGetter > PREDEFMETH.PM_FIRST && propertyGetter < PREDEFMETH.PM_COUNT); + Debug.Assert(propertySetter > PREDEFMETH.PM_FIRST && propertySetter <= PREDEFMETH.PM_COUNT); + + MethodSymbol getter = GetOptionalMethod(propertyGetter); + MethodSymbol setter = null; + if (propertySetter != PREDEFMETH.PM_COUNT) + { + setter = GetOptionalMethod(propertySetter); + } + + if (getter == null && setter == null) + { + RuntimeBinderSymbolTable.AddPredefinedPropertyToSymbolTable(GetOptPredefAgg(GetPropPredefType(predefProp)), propertyName); + getter = GetOptionalMethod(propertyGetter); + if (propertySetter != PREDEFMETH.PM_COUNT) + { + setter = GetOptionalMethod(propertySetter); + } + } + + if (setter != null) + { + setter.SetMethKind(MethodKindEnum.PropAccessor); + } + + PropertySymbol property = null; + if (getter != null) + { + getter.SetMethKind(MethodKindEnum.PropAccessor); + property = getter.getProperty(); + + // Didn't find it, so load it. + if (property == null) + { + RuntimeBinderSymbolTable.AddPredefinedPropertyToSymbolTable(GetOptPredefAgg(GetPropPredefType(predefProp)), propertyName); + } + property = getter.getProperty(); + Debug.Assert(property != null); + + if (property.name != propertyName || + (propertySetter != PREDEFMETH.PM_COUNT && + (setter == null || + !setter.isPropertyAccessor() || + setter.getProperty() != property)) || + property.getBogus()) + { + property = null; + } + } + + return property; + } + + private SymbolLoader GetSymbolLoader() + { + Debug.Assert(m_loader != null); + + return m_loader; + } + private ErrorHandling GetErrorContext() + { + return GetSymbolLoader().GetErrorContext(); + } + private NameManager GetNameManager() + { + return GetSymbolLoader().GetNameManager(); + } + private TypeManager GetTypeManager() + { + return GetSymbolLoader().GetTypeManager(); + } + private BSYMMGR getBSymmgr() + { + return GetSymbolLoader().getBSymmgr(); + } + + private Name GetPredefName(PredefinedName pn) + { + return GetNameManager().GetPredefName(pn); + } + private AggregateSymbol GetOptPredefAgg(PredefinedType pt) + { + return GetSymbolLoader().GetOptPredefAgg(pt); + } + + private CType LoadTypeFromSignature(int[] signature, ref int indexIntoSignatures, TypeArray classTyVars) + { + Debug.Assert(signature != null && signature != null); + + MethodSignatureEnum current = (MethodSignatureEnum)signature[indexIntoSignatures]; + indexIntoSignatures++; + + switch (current) + { + case MethodSignatureEnum.SIG_REF: + { + CType refType = LoadTypeFromSignature(signature, ref indexIntoSignatures, classTyVars); + if (refType == null) + { + return null; + } + return GetTypeManager().GetParameterModifier(refType, false); + } + case MethodSignatureEnum.SIG_OUT: + { + CType outType = LoadTypeFromSignature(signature, ref indexIntoSignatures, classTyVars); + if (outType == null) + { + return null; + } + return GetTypeManager().GetParameterModifier(outType, true); + } + case MethodSignatureEnum.SIG_SZ_ARRAY: + { + CType elementType = LoadTypeFromSignature(signature, ref indexIntoSignatures, classTyVars); + if (elementType == null) + { + return null; + } + return GetTypeManager().GetArray(elementType, 1); + } + case MethodSignatureEnum.SIG_METH_TYVAR: + { + int index = signature[indexIntoSignatures]; + indexIntoSignatures++; + return GetTypeManager().GetStdMethTypeVar(index); + } + case MethodSignatureEnum.SIG_CLASS_TYVAR: + { + int index = signature[indexIntoSignatures]; + indexIntoSignatures++; + return classTyVars.Item(index); + } + case (MethodSignatureEnum)PredefinedType.PT_VOID: + return GetTypeManager().GetVoid(); + default: + { + Debug.Assert(current >= 0 && (int)current < (int)PredefinedType.PT_COUNT); + AggregateSymbol agg = GetOptPredefAgg((PredefinedType)current); + if (agg != null) + { + CType[] typeArgs = new CType[agg.GetTypeVars().size]; + for (int iTypeArg = 0; iTypeArg < agg.GetTypeVars().size; iTypeArg++) + { + typeArgs[iTypeArg] = LoadTypeFromSignature(signature, ref indexIntoSignatures, classTyVars); + if (typeArgs[iTypeArg] == null) + { + return null; + } + } + AggregateType type = GetTypeManager().GetAggregate(agg, getBSymmgr().AllocParams(agg.GetTypeVars().size, typeArgs)); + if (type.isPredefType(PredefinedType.PT_G_OPTIONAL)) + { + return GetTypeManager().GetNubFromNullable(type); + } + + return type; + } + } + break; + } + + return null; + } + private TypeArray LoadTypeArrayFromSignature(int[] signature, ref int indexIntoSignatures, TypeArray classTyVars) + { + Debug.Assert(signature != null); + + int count = signature[indexIntoSignatures]; + indexIntoSignatures++; + + Debug.Assert(count >= 0); + + CType[] ptypes = new CType[count]; + for (int i = 0; i < count; i++) + { + ptypes[i] = LoadTypeFromSignature(signature, ref indexIntoSignatures, classTyVars); + if (ptypes[i] == null) + { + return null; + } + } + return getBSymmgr().AllocParams(count, ptypes); + } + + public PredefinedMembers(SymbolLoader loader) + { + m_loader = loader; + Debug.Assert(m_loader != null); + + m_methods = new MethodSymbol[(int)PREDEFMETH.PM_COUNT]; + m_properties = new PropertySymbol[(int)PREDEFPROP.PP_COUNT]; + +#if DEBUG + // validate the tables + for (int i = (int)PREDEFMETH.PM_FIRST + 1; i < (int)PREDEFMETH.PM_COUNT; i++) + { + Debug.Assert((int)GetMethInfo((PREDEFMETH)i).method == i); + } + for (int i = (int)PREDEFPROP.PP_FIRST + 1; i < (int)PREDEFPROP.PP_COUNT; i++) + { + Debug.Assert((int)GetPropInfo((PREDEFPROP)i).property == i); + } +#endif + } + + public PropertySymbol GetProperty(PREDEFPROP property) // Reports an error if the property is not found. + { + PropertySymbol result = EnsureProperty(property); + if (result == null) + { + ReportError(property); + } + + return result; + } + + public MethodSymbol GetMethod(PREDEFMETH method) + { + MethodSymbol result = EnsureMethod(method); + if (result == null) + { + ReportError(method); + } + + return result; + } + + public MethodSymbol GetOptionalMethod(PREDEFMETH method) + { + return EnsureMethod(method); + } + + private MethodSymbol EnsureMethod(PREDEFMETH method) + { + RETAILVERIFY(method > PREDEFMETH.PM_FIRST && method < PREDEFMETH.PM_COUNT); + if (m_methods[(int)method] == null) + { + m_methods[(int)method] = LoadMethod(method); + } + return m_methods[(int)method]; + } + + private MethodSymbol LoadMethod( + AggregateSymbol type, + int[] signature, + int cMethodTyVars, + Name methodName, + ACCESS methodAccess, + bool isStatic, + bool isVirtual + ) + { + Debug.Assert(signature != null); + Debug.Assert(cMethodTyVars >= 0); + Debug.Assert(methodName != null); + + if (type == null) + { + return null; + } + TypeArray classTyVars = type.GetTypeVarsAll(); + + int index = 0; + CType returnType = LoadTypeFromSignature(signature, ref index, classTyVars); + if (returnType == null) + { + return null; + } + TypeArray argumentTypes = LoadTypeArrayFromSignature(signature, ref index, classTyVars); + if (argumentTypes == null) + { + return null; + } + TypeArray standardMethodTyVars = GetTypeManager().GetStdMethTyVarArray(cMethodTyVars); + + MethodSymbol ret = LookupMethodWhileLoading(type, cMethodTyVars, methodName, methodAccess, isStatic, isVirtual, returnType, argumentTypes); + + if (ret == null) + { + RuntimeBinderSymbolTable.AddPredefinedMethodToSymbolTable(type, methodName); + ret = LookupMethodWhileLoading(type, cMethodTyVars, methodName, methodAccess, isStatic, isVirtual, returnType, argumentTypes); + } + return ret; + } + + private MethodSymbol LookupMethodWhileLoading(AggregateSymbol type, int cMethodTyVars, Name methodName, ACCESS methodAccess, bool isStatic, bool isVirtual, CType returnType, TypeArray argumentTypes) + { + for (Symbol sym = GetSymbolLoader().LookupAggMember(methodName, type, symbmask_t.MASK_ALL); + sym != null; + sym = GetSymbolLoader().LookupNextSym(sym, type, symbmask_t.MASK_ALL)) + { + if (sym.IsMethodSymbol()) + { + MethodSymbol methsym = sym.AsMethodSymbol(); + if ((methsym.GetAccess() == methodAccess || methodAccess == ACCESS.ACC_UNKNOWN) && + methsym.isStatic == isStatic && + methsym.isVirtual == isVirtual && + methsym.typeVars.size == cMethodTyVars && + GetTypeManager().SubstEqualTypes(methsym.RetType, returnType, null, methsym.typeVars, SubstTypeFlags.DenormMeth) && + GetTypeManager().SubstEqualTypeArrays(methsym.Params, argumentTypes, (TypeArray)null, + methsym.typeVars, SubstTypeFlags.DenormMeth) && + !methsym.getBogus()) + { + return methsym; + } + } + } + return null; + } + + private MethodSymbol LoadMethod(PREDEFMETH method) + { + return LoadMethod( + GetMethParent(method), + GetMethSignature(method), + GetMethTyVars(method), + GetMethName(method), + GetMethAccess(method), + IsMethStatic(method), + IsMethVirtual(method)); + } + + private void ReportError(PREDEFMETH method) + { + ReportError(GetMethPredefType(method), GetMethPredefName(method)); + } + + private void ReportError(PredefinedType type, PredefinedName name) + { + GetErrorContext().Error(ErrorCode.ERR_MissingPredefinedMember, PredefinedTypes.GetFullName(type), GetPredefName(name)); + } + + private static int[] g_DelegateCtorSignature1 = { (int)PredefinedType.PT_VOID, 2, (int)PredefinedType.PT_OBJECT, (int)PredefinedType.PT_INTPTR }; + private static int[] g_DelegateCtorSignature2 = { (int)PredefinedType.PT_VOID, 2, (int)PredefinedType.PT_OBJECT, (int)PredefinedType.PT_UINTPTR }; + + private static PredefinedName GetPropPredefName(PREDEFPROP property) + { + return GetPropInfo(property).name; + } + + private static PREDEFMETH GetPropGetter(PREDEFPROP property) + { + PREDEFMETH result = GetPropInfo(property).getter; + + // getters are MethodRequiredEnum.Required + Debug.Assert(result > PREDEFMETH.PM_FIRST && result < PREDEFMETH.PM_COUNT); + + return result; + } + + private static PredefinedType GetPropPredefType(PREDEFPROP property) + { + return GetMethInfo(GetPropGetter(property)).type; + } + + private static PREDEFMETH GetPropSetter(PREDEFPROP property) + { + PREDEFMETH result = GetPropInfo(property).setter; + + // setters are not MethodRequiredEnum.Required + Debug.Assert(result > PREDEFMETH.PM_FIRST && result <= PREDEFMETH.PM_COUNT); + + return GetPropInfo(property).setter; + } + + private void ReportError(PREDEFPROP property) + { + ReportError(GetPropPredefType(property), GetPropPredefName(property)); + } + + // the list of predefined property definitions. + // This list must be in the same order as the PREDEFPROP enum. + private static PredefinedPropertyInfo[] g_predefinedProperties = { + new PredefinedPropertyInfo( PREDEFPROP.PP_FIRST, MethodRequiredEnum.Optional, PredefinedName.PN_COUNT, PREDEFMETH.PM_COUNT, PREDEFMETH.PM_COUNT ), + + new PredefinedPropertyInfo( PREDEFPROP.PP_ARRAY_LENGTH, MethodRequiredEnum.Optional, PredefinedName.PN_LENGTH, PREDEFMETH.PM_ARRAY_GETLENGTH, PREDEFMETH.PM_COUNT ), + new PredefinedPropertyInfo( PREDEFPROP.PP_G_OPTIONAL_VALUE, MethodRequiredEnum.Optional, PredefinedName.PN_CAP_VALUE, PREDEFMETH.PM_G_OPTIONAL_GETVALUE, PREDEFMETH.PM_COUNT ), + }; + + public static PredefinedPropertyInfo GetPropInfo(PREDEFPROP property) + { + RETAILVERIFY(property > PREDEFPROP.PP_FIRST && property < PREDEFPROP.PP_COUNT); + RETAILVERIFY(g_predefinedProperties[(int)property].property == property); + + return g_predefinedProperties[(int)property]; + } + // UNDONE: use same enum in FieldSymbol definition + + + public static PredefinedMethodInfo GetMethInfo(PREDEFMETH method) + { + RETAILVERIFY(method > PREDEFMETH.PM_FIRST && method < PREDEFMETH.PM_COUNT); + RETAILVERIFY(g_predefinedMethods[(int)method].method == method); + + return g_predefinedMethods[(int)method]; + } + + private static PredefinedName GetMethPredefName(PREDEFMETH method) + { + return GetMethInfo(method).name; + } + + private static PredefinedType GetMethPredefType(PREDEFMETH method) + { + return GetMethInfo(method).type; + } + + private static bool IsMethStatic(PREDEFMETH method) + { + return GetMethInfo(method).callingConvention == MethodCallingConventionEnum.Static; + } + + private static bool IsMethVirtual(PREDEFMETH method) + { + return GetMethInfo(method).callingConvention == MethodCallingConventionEnum.Virtual; + } + + private static ACCESS GetMethAccess(PREDEFMETH method) + { + return GetMethInfo(method).access; + } + + private static int GetMethTyVars(PREDEFMETH method) + { + return GetMethInfo(method).cTypeVars; + } + + private static int[] GetMethSignature(PREDEFMETH method) + { + return GetMethInfo(method).signature; + } + + // the list of predefined method definitions. + // This list must be in the same order as the PREDEFMETH enum. + private static PredefinedMethodInfo[] g_predefinedMethods = new PredefinedMethodInfo[(int)PREDEFMETH.PM_COUNT] { + new PredefinedMethodInfo( PREDEFMETH.PM_FIRST, MethodRequiredEnum.Optional, PredefinedType.PT_COUNT, PredefinedName.PN_COUNT, MethodCallingConventionEnum.Static, ACCESS.ACC_PUBLIC, 0, new int[] { (int)PredefinedType.PT_VOID, 0 }), + new PredefinedMethodInfo( PREDEFMETH.PM_ARRAY_GETLENGTH, MethodRequiredEnum.Optional, PredefinedType.PT_ARRAY, PredefinedName.PN_GETLENGTH, MethodCallingConventionEnum.Instance, ACCESS.ACC_PUBLIC, 0, new int[] { (int)PredefinedType.PT_INT, 0 }), + new PredefinedMethodInfo( PREDEFMETH.PM_DECIMAL_OPDECREMENT, MethodRequiredEnum.Optional, PredefinedType.PT_DECIMAL, PredefinedName.PN_OPDECREMENT, MethodCallingConventionEnum.Static, ACCESS.ACC_PUBLIC, 0, new int[] { (int)PredefinedType.PT_DECIMAL, 1, (int)PredefinedType.PT_DECIMAL }), + new PredefinedMethodInfo( PREDEFMETH.PM_DECIMAL_OPDIVISION, MethodRequiredEnum.Optional, PredefinedType.PT_DECIMAL, PredefinedName.PN_OPDIVISION, MethodCallingConventionEnum.Static, ACCESS.ACC_PUBLIC, 0, new int[] { (int)PredefinedType.PT_DECIMAL, 2, (int)PredefinedType.PT_DECIMAL, (int)PredefinedType.PT_DECIMAL }), + new PredefinedMethodInfo( PREDEFMETH.PM_DECIMAL_OPEQUALITY, MethodRequiredEnum.Optional, PredefinedType.PT_DECIMAL, PredefinedName.PN_OPEQUALITY, MethodCallingConventionEnum.Static, ACCESS.ACC_PUBLIC, 0, new int[] { (int)PredefinedType.PT_BOOL, 2, (int)PredefinedType.PT_DECIMAL, (int)PredefinedType.PT_DECIMAL }), + new PredefinedMethodInfo( PREDEFMETH.PM_DECIMAL_OPGREATERTHAN, MethodRequiredEnum.Optional, PredefinedType.PT_DECIMAL, PredefinedName.PN_OPGREATERTHAN, MethodCallingConventionEnum.Static, ACCESS.ACC_PUBLIC, 0, new int[] { (int)PredefinedType.PT_BOOL, 2, (int)PredefinedType.PT_DECIMAL, (int)PredefinedType.PT_DECIMAL }), + new PredefinedMethodInfo( PREDEFMETH.PM_DECIMAL_OPGREATERTHANOREQUAL, MethodRequiredEnum.Optional, PredefinedType.PT_DECIMAL, PredefinedName.PN_OPGREATERTHANOREQUAL, MethodCallingConventionEnum.Static, ACCESS.ACC_PUBLIC, 0, new int[] { (int)PredefinedType.PT_BOOL, 2, (int)PredefinedType.PT_DECIMAL, (int)PredefinedType.PT_DECIMAL }), + new PredefinedMethodInfo( PREDEFMETH.PM_DECIMAL_OPINCREMENT, MethodRequiredEnum.Optional, PredefinedType.PT_DECIMAL, PredefinedName.PN_OPINCREMENT, MethodCallingConventionEnum.Static, ACCESS.ACC_PUBLIC, 0, new int[] { (int)PredefinedType.PT_DECIMAL, 1, (int)PredefinedType.PT_DECIMAL }), + new PredefinedMethodInfo( PREDEFMETH.PM_DECIMAL_OPINEQUALITY, MethodRequiredEnum.Optional, PredefinedType.PT_DECIMAL, PredefinedName.PN_OPINEQUALITY, MethodCallingConventionEnum.Static, ACCESS.ACC_PUBLIC, 0, new int[] { (int)PredefinedType.PT_BOOL, 2, (int)PredefinedType.PT_DECIMAL, (int)PredefinedType.PT_DECIMAL }), + new PredefinedMethodInfo( PREDEFMETH.PM_DECIMAL_OPLESSTHAN, MethodRequiredEnum.Optional, PredefinedType.PT_DECIMAL, PredefinedName.PN_OPLESSTHAN, MethodCallingConventionEnum.Static, ACCESS.ACC_PUBLIC, 0, new int[] { (int)PredefinedType.PT_BOOL, 2, (int)PredefinedType.PT_DECIMAL, (int)PredefinedType.PT_DECIMAL }), + new PredefinedMethodInfo( PREDEFMETH.PM_DECIMAL_OPLESSTHANOREQUAL, MethodRequiredEnum.Optional, PredefinedType.PT_DECIMAL, PredefinedName.PN_OPLESSTHANOREQUAL, MethodCallingConventionEnum.Static, ACCESS.ACC_PUBLIC, 0, new int[] { (int)PredefinedType.PT_BOOL, 2, (int)PredefinedType.PT_DECIMAL, (int)PredefinedType.PT_DECIMAL }), + new PredefinedMethodInfo( PREDEFMETH.PM_DECIMAL_OPMINUS, MethodRequiredEnum.Optional, PredefinedType.PT_DECIMAL, PredefinedName.PN_OPMINUS, MethodCallingConventionEnum.Static, ACCESS.ACC_PUBLIC, 0, new int[] { (int)PredefinedType.PT_DECIMAL, 2, (int)PredefinedType.PT_DECIMAL, (int)PredefinedType.PT_DECIMAL }), + new PredefinedMethodInfo( PREDEFMETH.PM_DECIMAL_OPMODULUS, MethodRequiredEnum.Optional, PredefinedType.PT_DECIMAL, PredefinedName.PN_OPMODULUS, MethodCallingConventionEnum.Static, ACCESS.ACC_PUBLIC, 0, new int[] { (int)PredefinedType.PT_DECIMAL, 2, (int)PredefinedType.PT_DECIMAL, (int)PredefinedType.PT_DECIMAL }), + new PredefinedMethodInfo( PREDEFMETH.PM_DECIMAL_OPMULTIPLY, MethodRequiredEnum.Optional, PredefinedType.PT_DECIMAL, PredefinedName.PN_OPMULTIPLY, MethodCallingConventionEnum.Static, ACCESS.ACC_PUBLIC, 0, new int[] { (int)PredefinedType.PT_DECIMAL, 2, (int)PredefinedType.PT_DECIMAL, (int)PredefinedType.PT_DECIMAL }), + new PredefinedMethodInfo( PREDEFMETH.PM_DECIMAL_OPPLUS, MethodRequiredEnum.Optional, PredefinedType.PT_DECIMAL, PredefinedName.PN_OPPLUS, MethodCallingConventionEnum.Static, ACCESS.ACC_PUBLIC, 0, new int[] { (int)PredefinedType.PT_DECIMAL, 2, (int)PredefinedType.PT_DECIMAL, (int)PredefinedType.PT_DECIMAL }), + new PredefinedMethodInfo( PREDEFMETH.PM_DECIMAL_OPUNARYMINUS, MethodRequiredEnum.Optional, PredefinedType.PT_DECIMAL, PredefinedName.PN_OPUNARYMINUS, MethodCallingConventionEnum.Static, ACCESS.ACC_PUBLIC, 0, new int[] { (int)PredefinedType.PT_DECIMAL, 1, (int)PredefinedType.PT_DECIMAL }), + new PredefinedMethodInfo( PREDEFMETH.PM_DECIMAL_OPUNARYPLUS, MethodRequiredEnum.Optional, PredefinedType.PT_DECIMAL, PredefinedName.PN_OPUNARYPLUS, MethodCallingConventionEnum.Static, ACCESS.ACC_PUBLIC, 0, new int[] { (int)PredefinedType.PT_DECIMAL, 1, (int)PredefinedType.PT_DECIMAL }), + new PredefinedMethodInfo( PREDEFMETH.PM_DELEGATE_COMBINE, MethodRequiredEnum.Optional, PredefinedType.PT_DELEGATE, PredefinedName.PN_COMBINE, MethodCallingConventionEnum.Static, ACCESS.ACC_PUBLIC, 0, new int[] { (int)PredefinedType.PT_DELEGATE, 2, (int)PredefinedType.PT_DELEGATE, (int)PredefinedType.PT_DELEGATE }), + new PredefinedMethodInfo( PREDEFMETH.PM_DELEGATE_OPEQUALITY, MethodRequiredEnum.Optional, PredefinedType.PT_DELEGATE, PredefinedName.PN_OPEQUALITY, MethodCallingConventionEnum.Static, ACCESS.ACC_PUBLIC, 0, new int[] { (int)PredefinedType.PT_BOOL, 2, (int)PredefinedType.PT_DELEGATE, (int)PredefinedType.PT_DELEGATE }), + new PredefinedMethodInfo( PREDEFMETH.PM_DELEGATE_OPINEQUALITY, MethodRequiredEnum.Optional, PredefinedType.PT_DELEGATE, PredefinedName.PN_OPINEQUALITY, MethodCallingConventionEnum.Static, ACCESS.ACC_PUBLIC, 0, new int[] { (int)PredefinedType.PT_BOOL, 2, (int)PredefinedType.PT_DELEGATE, (int)PredefinedType.PT_DELEGATE }), + new PredefinedMethodInfo( PREDEFMETH.PM_DELEGATE_REMOVE, MethodRequiredEnum.Optional, PredefinedType.PT_DELEGATE, PredefinedName.PN_REMOVE, MethodCallingConventionEnum.Static, ACCESS.ACC_PUBLIC, 0, new int[] { (int)PredefinedType.PT_DELEGATE, 2, (int)PredefinedType.PT_DELEGATE, (int)PredefinedType.PT_DELEGATE }), + new PredefinedMethodInfo( PREDEFMETH.PM_EXPRESSION_ADD, MethodRequiredEnum.Optional, PredefinedType.PT_EXPRESSION, PredefinedName.PN_ADD, MethodCallingConventionEnum.Static, ACCESS.ACC_PUBLIC, 0, new int[] { (int)PredefinedType.PT_BINARYEXPRESSION, 2, (int)PredefinedType.PT_EXPRESSION, (int)PredefinedType.PT_EXPRESSION }), + new PredefinedMethodInfo( PREDEFMETH.PM_EXPRESSION_ADD_USER_DEFINED, MethodRequiredEnum.Optional, PredefinedType.PT_EXPRESSION, PredefinedName.PN_ADD, MethodCallingConventionEnum.Static, ACCESS.ACC_PUBLIC, 0, new int[] { (int)PredefinedType.PT_BINARYEXPRESSION, 3, (int)PredefinedType.PT_EXPRESSION, (int)PredefinedType.PT_EXPRESSION, (int)PredefinedType.PT_METHODINFO }), + new PredefinedMethodInfo( PREDEFMETH.PM_EXPRESSION_ADDCHECKED, MethodRequiredEnum.Optional, PredefinedType.PT_EXPRESSION, PredefinedName.PN_ADDCHECKED, MethodCallingConventionEnum.Static, ACCESS.ACC_PUBLIC, 0, new int[] { (int)PredefinedType.PT_BINARYEXPRESSION, 2, (int)PredefinedType.PT_EXPRESSION, (int)PredefinedType.PT_EXPRESSION }), + new PredefinedMethodInfo( PREDEFMETH.PM_EXPRESSION_ADDCHECKED_USER_DEFINED, MethodRequiredEnum.Optional, PredefinedType.PT_EXPRESSION, PredefinedName.PN_ADDCHECKED, MethodCallingConventionEnum.Static, ACCESS.ACC_PUBLIC, 0, new int[] { (int)PredefinedType.PT_BINARYEXPRESSION, 3, (int)PredefinedType.PT_EXPRESSION, (int)PredefinedType.PT_EXPRESSION, (int)PredefinedType.PT_METHODINFO }), + new PredefinedMethodInfo( PREDEFMETH.PM_EXPRESSION_AND, MethodRequiredEnum.Optional, PredefinedType.PT_EXPRESSION, PredefinedName.PN_AND, MethodCallingConventionEnum.Static, ACCESS.ACC_PUBLIC, 0, new int[] { (int)PredefinedType.PT_BINARYEXPRESSION, 2, (int)PredefinedType.PT_EXPRESSION, (int)PredefinedType.PT_EXPRESSION }), + new PredefinedMethodInfo( PREDEFMETH.PM_EXPRESSION_AND_USER_DEFINED, MethodRequiredEnum.Optional, PredefinedType.PT_EXPRESSION, PredefinedName.PN_AND, MethodCallingConventionEnum.Static, ACCESS.ACC_PUBLIC, 0, new int[] { (int)PredefinedType.PT_BINARYEXPRESSION, 3, (int)PredefinedType.PT_EXPRESSION, (int)PredefinedType.PT_EXPRESSION, (int)PredefinedType.PT_METHODINFO }), + new PredefinedMethodInfo( PREDEFMETH.PM_EXPRESSION_ANDALSO, MethodRequiredEnum.Optional, PredefinedType.PT_EXPRESSION, PredefinedName.PN_ANDALSO, MethodCallingConventionEnum.Static, ACCESS.ACC_PUBLIC, 0, new int[] { (int)PredefinedType.PT_BINARYEXPRESSION, 2, (int)PredefinedType.PT_EXPRESSION, (int)PredefinedType.PT_EXPRESSION }), + new PredefinedMethodInfo( PREDEFMETH.PM_EXPRESSION_ANDALSO_USER_DEFINED, MethodRequiredEnum.Optional, PredefinedType.PT_EXPRESSION, PredefinedName.PN_ANDALSO, MethodCallingConventionEnum.Static, ACCESS.ACC_PUBLIC, 0, new int[] { (int)PredefinedType.PT_BINARYEXPRESSION, 3, (int)PredefinedType.PT_EXPRESSION, (int)PredefinedType.PT_EXPRESSION, (int)PredefinedType.PT_METHODINFO }), + new PredefinedMethodInfo( PREDEFMETH.PM_EXPRESSION_ARRAYINDEX, MethodRequiredEnum.Optional, PredefinedType.PT_EXPRESSION, PredefinedName.PN_ARRAYINDEX, MethodCallingConventionEnum.Static, ACCESS.ACC_PUBLIC, 0, new int[] { (int)PredefinedType.PT_BINARYEXPRESSION, 2, (int)PredefinedType.PT_EXPRESSION, (int)PredefinedType.PT_EXPRESSION }), + new PredefinedMethodInfo( PREDEFMETH.PM_EXPRESSION_ARRAYINDEX2, MethodRequiredEnum.Optional, PredefinedType.PT_EXPRESSION, PredefinedName.PN_ARRAYINDEX, MethodCallingConventionEnum.Static, ACCESS.ACC_PUBLIC, 0, new int[] { (int)PredefinedType.PT_METHODCALLEXPRESSION, 2, (int)PredefinedType.PT_EXPRESSION, (int)MethodSignatureEnum.SIG_SZ_ARRAY, (int)PredefinedType.PT_EXPRESSION }), + new PredefinedMethodInfo( PREDEFMETH.PM_EXPRESSION_ASSIGN, MethodRequiredEnum.Optional, PredefinedType.PT_EXPRESSION, PredefinedName.PN_ASSIGN, MethodCallingConventionEnum.Static, ACCESS.ACC_PUBLIC, 0, new int[] { (int)PredefinedType.PT_BINARYEXPRESSION, 2, (int)PredefinedType.PT_EXPRESSION, (int)PredefinedType.PT_EXPRESSION }), + new PredefinedMethodInfo( PREDEFMETH.PM_EXPRESSION_CONDITION, MethodRequiredEnum.Optional, PredefinedType.PT_EXPRESSION, PredefinedName.PN_CONDITION, MethodCallingConventionEnum.Static, ACCESS.ACC_PUBLIC, 0, new int[] { (int)PredefinedType.PT_CONDITIONALEXPRESSION, 3, (int)PredefinedType.PT_EXPRESSION, (int)PredefinedType.PT_EXPRESSION, (int)PredefinedType.PT_EXPRESSION }), + new PredefinedMethodInfo( PREDEFMETH.PM_EXPRESSION_CONSTANT_OBJECT_TYPE, MethodRequiredEnum.Optional, PredefinedType.PT_EXPRESSION, PredefinedName.PN_CONSTANT, MethodCallingConventionEnum.Static, ACCESS.ACC_PUBLIC, 0, new int[] { (int)PredefinedType.PT_CONSTANTEXPRESSION, 2, (int)PredefinedType.PT_OBJECT, (int)PredefinedType.PT_TYPE }), + new PredefinedMethodInfo( PREDEFMETH.PM_EXPRESSION_CONVERT, MethodRequiredEnum.Optional, PredefinedType.PT_EXPRESSION, PredefinedName.PN_CONVERT, MethodCallingConventionEnum.Static, ACCESS.ACC_PUBLIC, 0, new int[] { (int)PredefinedType.PT_UNARYEXPRESSION, 2, (int)PredefinedType.PT_EXPRESSION, (int)PredefinedType.PT_TYPE }), + new PredefinedMethodInfo( PREDEFMETH.PM_EXPRESSION_CONVERT_USER_DEFINED, MethodRequiredEnum.Optional, PredefinedType.PT_EXPRESSION, PredefinedName.PN_CONVERT, MethodCallingConventionEnum.Static, ACCESS.ACC_PUBLIC, 0, new int[] { (int)PredefinedType.PT_UNARYEXPRESSION, 3, (int)PredefinedType.PT_EXPRESSION, (int)PredefinedType.PT_TYPE, (int)PredefinedType.PT_METHODINFO }), + new PredefinedMethodInfo( PREDEFMETH.PM_EXPRESSION_CONVERTCHECKED, MethodRequiredEnum.Optional, PredefinedType.PT_EXPRESSION, PredefinedName.PN_CONVERTCHECKED, MethodCallingConventionEnum.Static, ACCESS.ACC_PUBLIC, 0, new int[] { (int)PredefinedType.PT_UNARYEXPRESSION, 2, (int)PredefinedType.PT_EXPRESSION, (int)PredefinedType.PT_TYPE }), + new PredefinedMethodInfo( PREDEFMETH.PM_EXPRESSION_CONVERTCHECKED_USER_DEFINED, MethodRequiredEnum.Optional, PredefinedType.PT_EXPRESSION, PredefinedName.PN_CONVERTCHECKED, MethodCallingConventionEnum.Static, ACCESS.ACC_PUBLIC, 0, new int[] { (int)PredefinedType.PT_UNARYEXPRESSION, 3, (int)PredefinedType.PT_EXPRESSION, (int)PredefinedType.PT_TYPE, (int)PredefinedType.PT_METHODINFO }), + new PredefinedMethodInfo( PREDEFMETH.PM_EXPRESSION_DIVIDE, MethodRequiredEnum.Optional, PredefinedType.PT_EXPRESSION, PredefinedName.PN_DIVIDE, MethodCallingConventionEnum.Static, ACCESS.ACC_PUBLIC, 0, new int[] { (int)PredefinedType.PT_BINARYEXPRESSION, 2, (int)PredefinedType.PT_EXPRESSION, (int)PredefinedType.PT_EXPRESSION }), + new PredefinedMethodInfo( PREDEFMETH.PM_EXPRESSION_DIVIDE_USER_DEFINED, MethodRequiredEnum.Optional, PredefinedType.PT_EXPRESSION, PredefinedName.PN_DIVIDE, MethodCallingConventionEnum.Static, ACCESS.ACC_PUBLIC, 0, new int[] { (int)PredefinedType.PT_BINARYEXPRESSION, 3, (int)PredefinedType.PT_EXPRESSION, (int)PredefinedType.PT_EXPRESSION, (int)PredefinedType.PT_METHODINFO }), + new PredefinedMethodInfo( PREDEFMETH.PM_EXPRESSION_EQUAL, MethodRequiredEnum.Optional, PredefinedType.PT_EXPRESSION, PredefinedName.PN_EQUAL, MethodCallingConventionEnum.Static, ACCESS.ACC_PUBLIC, 0, new int[] { (int)PredefinedType.PT_BINARYEXPRESSION, 2, (int)PredefinedType.PT_EXPRESSION, (int)PredefinedType.PT_EXPRESSION }), + new PredefinedMethodInfo( PREDEFMETH.PM_EXPRESSION_EQUAL_USER_DEFINED, MethodRequiredEnum.Optional, PredefinedType.PT_EXPRESSION, PredefinedName.PN_EQUAL, MethodCallingConventionEnum.Static, ACCESS.ACC_PUBLIC, 0, new int[] { (int)PredefinedType.PT_BINARYEXPRESSION, 4, (int)PredefinedType.PT_EXPRESSION, (int)PredefinedType.PT_EXPRESSION, (int)PredefinedType.PT_BOOL, (int)PredefinedType.PT_METHODINFO }), + new PredefinedMethodInfo( PREDEFMETH.PM_EXPRESSION_EXCLUSIVEOR, MethodRequiredEnum.Optional, PredefinedType.PT_EXPRESSION, PredefinedName.PN_EXCLUSIVEOR, MethodCallingConventionEnum.Static, ACCESS.ACC_PUBLIC, 0, new int[] { (int)PredefinedType.PT_BINARYEXPRESSION, 2, (int)PredefinedType.PT_EXPRESSION, (int)PredefinedType.PT_EXPRESSION }), + new PredefinedMethodInfo( PREDEFMETH.PM_EXPRESSION_EXCLUSIVEOR_USER_DEFINED, MethodRequiredEnum.Optional, PredefinedType.PT_EXPRESSION, PredefinedName.PN_EXCLUSIVEOR, MethodCallingConventionEnum.Static, ACCESS.ACC_PUBLIC, 0, new int[] { (int)PredefinedType.PT_BINARYEXPRESSION, 3, (int)PredefinedType.PT_EXPRESSION, (int)PredefinedType.PT_EXPRESSION, (int)PredefinedType.PT_METHODINFO }), + new PredefinedMethodInfo( PREDEFMETH.PM_EXPRESSION_FIELD, MethodRequiredEnum.Optional, PredefinedType.PT_EXPRESSION, PredefinedName.PN_CAP_FIELD, MethodCallingConventionEnum.Static, ACCESS.ACC_PUBLIC, 0, new int[] { (int)PredefinedType.PT_MEMBEREXPRESSION, 2, (int)PredefinedType.PT_EXPRESSION, (int)PredefinedType.PT_FIELDINFO }), + new PredefinedMethodInfo( PREDEFMETH.PM_EXPRESSION_GREATERTHAN, MethodRequiredEnum.Optional, PredefinedType.PT_EXPRESSION, PredefinedName.PN_GREATERTHAN, MethodCallingConventionEnum.Static, ACCESS.ACC_PUBLIC, 0, new int[] { (int)PredefinedType.PT_BINARYEXPRESSION, 2, (int)PredefinedType.PT_EXPRESSION, (int)PredefinedType.PT_EXPRESSION }), + new PredefinedMethodInfo( PREDEFMETH.PM_EXPRESSION_GREATERTHAN_USER_DEFINED, MethodRequiredEnum.Optional, PredefinedType.PT_EXPRESSION, PredefinedName.PN_GREATERTHAN, MethodCallingConventionEnum.Static, ACCESS.ACC_PUBLIC, 0, new int[] { (int)PredefinedType.PT_BINARYEXPRESSION, 4, (int)PredefinedType.PT_EXPRESSION, (int)PredefinedType.PT_EXPRESSION, (int)PredefinedType.PT_BOOL, (int)PredefinedType.PT_METHODINFO }), + new PredefinedMethodInfo( PREDEFMETH.PM_EXPRESSION_GREATERTHANOREQUAL, MethodRequiredEnum.Optional, PredefinedType.PT_EXPRESSION, PredefinedName.PN_GREATERTHANOREQUAL, MethodCallingConventionEnum.Static, ACCESS.ACC_PUBLIC, 0, new int[] { (int)PredefinedType.PT_BINARYEXPRESSION, 2, (int)PredefinedType.PT_EXPRESSION, (int)PredefinedType.PT_EXPRESSION }), + new PredefinedMethodInfo( PREDEFMETH.PM_EXPRESSION_GREATERTHANOREQUAL_USER_DEFINED, MethodRequiredEnum.Optional, PredefinedType.PT_EXPRESSION, PredefinedName.PN_GREATERTHANOREQUAL, MethodCallingConventionEnum.Static, ACCESS.ACC_PUBLIC, 0, new int[] { (int)PredefinedType.PT_BINARYEXPRESSION, 4, (int)PredefinedType.PT_EXPRESSION, (int)PredefinedType.PT_EXPRESSION, (int)PredefinedType.PT_BOOL, (int)PredefinedType.PT_METHODINFO }), + new PredefinedMethodInfo( PREDEFMETH.PM_EXPRESSION_LAMBDA, MethodRequiredEnum.Optional, PredefinedType.PT_EXPRESSION, PredefinedName.PN_LAMBDA, MethodCallingConventionEnum.Static, ACCESS.ACC_PUBLIC, 1, new int[] { (int)PredefinedType.PT_G_EXPRESSION, (int)MethodSignatureEnum.SIG_METH_TYVAR, 0, 2, (int)PredefinedType.PT_EXPRESSION, (int)MethodSignatureEnum.SIG_SZ_ARRAY, (int)PredefinedType.PT_PARAMETEREXPRESSION }), + new PredefinedMethodInfo( PREDEFMETH.PM_EXPRESSION_LEFTSHIFT, MethodRequiredEnum.Optional, PredefinedType.PT_EXPRESSION, PredefinedName.PN_LEFTSHIFT, MethodCallingConventionEnum.Static, ACCESS.ACC_PUBLIC, 0, new int[] { (int)PredefinedType.PT_BINARYEXPRESSION, 2, (int)PredefinedType.PT_EXPRESSION, (int)PredefinedType.PT_EXPRESSION }), + new PredefinedMethodInfo( PREDEFMETH.PM_EXPRESSION_LEFTSHIFT_USER_DEFINED, MethodRequiredEnum.Optional, PredefinedType.PT_EXPRESSION, PredefinedName.PN_LEFTSHIFT, MethodCallingConventionEnum.Static, ACCESS.ACC_PUBLIC, 0, new int[] { (int)PredefinedType.PT_BINARYEXPRESSION, 3, (int)PredefinedType.PT_EXPRESSION, (int)PredefinedType.PT_EXPRESSION, (int)PredefinedType.PT_METHODINFO }), + new PredefinedMethodInfo( PREDEFMETH.PM_EXPRESSION_LESSTHAN, MethodRequiredEnum.Optional, PredefinedType.PT_EXPRESSION, PredefinedName.PN_LESSTHAN, MethodCallingConventionEnum.Static, ACCESS.ACC_PUBLIC, 0, new int[] { (int)PredefinedType.PT_BINARYEXPRESSION, 2, (int)PredefinedType.PT_EXPRESSION, (int)PredefinedType.PT_EXPRESSION }), + new PredefinedMethodInfo( PREDEFMETH.PM_EXPRESSION_LESSTHAN_USER_DEFINED, MethodRequiredEnum.Optional, PredefinedType.PT_EXPRESSION, PredefinedName.PN_LESSTHAN, MethodCallingConventionEnum.Static, ACCESS.ACC_PUBLIC, 0, new int[] { (int)PredefinedType.PT_BINARYEXPRESSION, 4, (int)PredefinedType.PT_EXPRESSION, (int)PredefinedType.PT_EXPRESSION, (int)PredefinedType.PT_BOOL, (int)PredefinedType.PT_METHODINFO }), + new PredefinedMethodInfo( PREDEFMETH.PM_EXPRESSION_LESSTHANOREQUAL, MethodRequiredEnum.Optional, PredefinedType.PT_EXPRESSION, PredefinedName.PN_LESSTHANOREQUAL, MethodCallingConventionEnum.Static, ACCESS.ACC_PUBLIC, 0, new int[] { (int)PredefinedType.PT_BINARYEXPRESSION, 2, (int)PredefinedType.PT_EXPRESSION, (int)PredefinedType.PT_EXPRESSION }), + new PredefinedMethodInfo( PREDEFMETH.PM_EXPRESSION_LESSTHANOREQUAL_USER_DEFINED, MethodRequiredEnum.Optional, PredefinedType.PT_EXPRESSION, PredefinedName.PN_LESSTHANOREQUAL, MethodCallingConventionEnum.Static, ACCESS.ACC_PUBLIC, 0, new int[] { (int)PredefinedType.PT_BINARYEXPRESSION, 4, (int)PredefinedType.PT_EXPRESSION, (int)PredefinedType.PT_EXPRESSION, (int)PredefinedType.PT_BOOL, (int)PredefinedType.PT_METHODINFO }), + new PredefinedMethodInfo( PREDEFMETH.PM_EXPRESSION_MODULO, MethodRequiredEnum.Optional, PredefinedType.PT_EXPRESSION, PredefinedName.PN_MODULO, MethodCallingConventionEnum.Static, ACCESS.ACC_PUBLIC, 0, new int[] { (int)PredefinedType.PT_BINARYEXPRESSION, 2, (int)PredefinedType.PT_EXPRESSION, (int)PredefinedType.PT_EXPRESSION }), + new PredefinedMethodInfo( PREDEFMETH.PM_EXPRESSION_MODULO_USER_DEFINED, MethodRequiredEnum.Optional, PredefinedType.PT_EXPRESSION, PredefinedName.PN_MODULO, MethodCallingConventionEnum.Static, ACCESS.ACC_PUBLIC, 0, new int[] { (int)PredefinedType.PT_BINARYEXPRESSION, 3, (int)PredefinedType.PT_EXPRESSION, (int)PredefinedType.PT_EXPRESSION, (int)PredefinedType.PT_METHODINFO }), + new PredefinedMethodInfo( PREDEFMETH.PM_EXPRESSION_MULTIPLY, MethodRequiredEnum.Optional, PredefinedType.PT_EXPRESSION, PredefinedName.PN_MULTIPLY, MethodCallingConventionEnum.Static, ACCESS.ACC_PUBLIC, 0, new int[] { (int)PredefinedType.PT_BINARYEXPRESSION, 2, (int)PredefinedType.PT_EXPRESSION, (int)PredefinedType.PT_EXPRESSION }), + new PredefinedMethodInfo( PREDEFMETH.PM_EXPRESSION_MULTIPLY_USER_DEFINED, MethodRequiredEnum.Optional, PredefinedType.PT_EXPRESSION, PredefinedName.PN_MULTIPLY, MethodCallingConventionEnum.Static, ACCESS.ACC_PUBLIC, 0, new int[] { (int)PredefinedType.PT_BINARYEXPRESSION, 3, (int)PredefinedType.PT_EXPRESSION, (int)PredefinedType.PT_EXPRESSION, (int)PredefinedType.PT_METHODINFO }), + new PredefinedMethodInfo( PREDEFMETH.PM_EXPRESSION_MULTIPLYCHECKED, MethodRequiredEnum.Optional, PredefinedType.PT_EXPRESSION, PredefinedName.PN_MULTIPLYCHECKED, MethodCallingConventionEnum.Static, ACCESS.ACC_PUBLIC, 0, new int[] { (int)PredefinedType.PT_BINARYEXPRESSION, 2, (int)PredefinedType.PT_EXPRESSION, (int)PredefinedType.PT_EXPRESSION }), + new PredefinedMethodInfo( PREDEFMETH.PM_EXPRESSION_MULTIPLYCHECKED_USER_DEFINED, MethodRequiredEnum.Optional, PredefinedType.PT_EXPRESSION, PredefinedName.PN_MULTIPLYCHECKED, MethodCallingConventionEnum.Static, ACCESS.ACC_PUBLIC, 0, new int[] { (int)PredefinedType.PT_BINARYEXPRESSION, 3, (int)PredefinedType.PT_EXPRESSION, (int)PredefinedType.PT_EXPRESSION, (int)PredefinedType.PT_METHODINFO }), + new PredefinedMethodInfo( PREDEFMETH.PM_EXPRESSION_NOTEQUAL, MethodRequiredEnum.Optional, PredefinedType.PT_EXPRESSION, PredefinedName.PN_NOTEQUAL, MethodCallingConventionEnum.Static, ACCESS.ACC_PUBLIC, 0, new int[] { (int)PredefinedType.PT_BINARYEXPRESSION, 2, (int)PredefinedType.PT_EXPRESSION, (int)PredefinedType.PT_EXPRESSION }), + new PredefinedMethodInfo( PREDEFMETH.PM_EXPRESSION_NOTEQUAL_USER_DEFINED, MethodRequiredEnum.Optional, PredefinedType.PT_EXPRESSION, PredefinedName.PN_NOTEQUAL, MethodCallingConventionEnum.Static, ACCESS.ACC_PUBLIC, 0, new int[] { (int)PredefinedType.PT_BINARYEXPRESSION, 4, (int)PredefinedType.PT_EXPRESSION, (int)PredefinedType.PT_EXPRESSION, (int)PredefinedType.PT_BOOL, (int)PredefinedType.PT_METHODINFO }), + new PredefinedMethodInfo( PREDEFMETH.PM_EXPRESSION_OR, MethodRequiredEnum.Optional, PredefinedType.PT_EXPRESSION, PredefinedName.PN_OR, MethodCallingConventionEnum.Static, ACCESS.ACC_PUBLIC, 0, new int[] { (int)PredefinedType.PT_BINARYEXPRESSION, 2, (int)PredefinedType.PT_EXPRESSION, (int)PredefinedType.PT_EXPRESSION }), + new PredefinedMethodInfo( PREDEFMETH.PM_EXPRESSION_OR_USER_DEFINED, MethodRequiredEnum.Optional, PredefinedType.PT_EXPRESSION, PredefinedName.PN_OR, MethodCallingConventionEnum.Static, ACCESS.ACC_PUBLIC, 0, new int[] { (int)PredefinedType.PT_BINARYEXPRESSION, 3, (int)PredefinedType.PT_EXPRESSION, (int)PredefinedType.PT_EXPRESSION, (int)PredefinedType.PT_METHODINFO }), + new PredefinedMethodInfo( PREDEFMETH.PM_EXPRESSION_ORELSE, MethodRequiredEnum.Optional, PredefinedType.PT_EXPRESSION, PredefinedName.PN_ORELSE, MethodCallingConventionEnum.Static, ACCESS.ACC_PUBLIC, 0, new int[] { (int)PredefinedType.PT_BINARYEXPRESSION, 2, (int)PredefinedType.PT_EXPRESSION, (int)PredefinedType.PT_EXPRESSION }), + new PredefinedMethodInfo( PREDEFMETH.PM_EXPRESSION_ORELSE_USER_DEFINED, MethodRequiredEnum.Optional, PredefinedType.PT_EXPRESSION, PredefinedName.PN_ORELSE, MethodCallingConventionEnum.Static, ACCESS.ACC_PUBLIC, 0, new int[] { (int)PredefinedType.PT_BINARYEXPRESSION, 3, (int)PredefinedType.PT_EXPRESSION, (int)PredefinedType.PT_EXPRESSION, (int)PredefinedType.PT_METHODINFO }), + new PredefinedMethodInfo( PREDEFMETH.PM_EXPRESSION_PARAMETER, MethodRequiredEnum.Optional, PredefinedType.PT_EXPRESSION, PredefinedName.PN_PARAMETER, MethodCallingConventionEnum.Static, ACCESS.ACC_PUBLIC, 0, new int[] { (int)PredefinedType.PT_PARAMETEREXPRESSION, 2, (int)PredefinedType.PT_TYPE, (int)PredefinedType.PT_STRING }), + new PredefinedMethodInfo( PREDEFMETH.PM_EXPRESSION_RIGHTSHIFT, MethodRequiredEnum.Optional, PredefinedType.PT_EXPRESSION, PredefinedName.PN_RIGHTSHIFT, MethodCallingConventionEnum.Static, ACCESS.ACC_PUBLIC, 0, new int[] { (int)PredefinedType.PT_BINARYEXPRESSION, 2, (int)PredefinedType.PT_EXPRESSION, (int)PredefinedType.PT_EXPRESSION }), + new PredefinedMethodInfo( PREDEFMETH.PM_EXPRESSION_RIGHTSHIFT_USER_DEFINED, MethodRequiredEnum.Optional, PredefinedType.PT_EXPRESSION, PredefinedName.PN_RIGHTSHIFT, MethodCallingConventionEnum.Static, ACCESS.ACC_PUBLIC, 0, new int[] { (int)PredefinedType.PT_BINARYEXPRESSION, 3, (int)PredefinedType.PT_EXPRESSION, (int)PredefinedType.PT_EXPRESSION, (int)PredefinedType.PT_METHODINFO }), + new PredefinedMethodInfo( PREDEFMETH.PM_EXPRESSION_SUBTRACT, MethodRequiredEnum.Optional, PredefinedType.PT_EXPRESSION, PredefinedName.PN_SUBTRACT, MethodCallingConventionEnum.Static, ACCESS.ACC_PUBLIC, 0, new int[] { (int)PredefinedType.PT_BINARYEXPRESSION, 2, (int)PredefinedType.PT_EXPRESSION, (int)PredefinedType.PT_EXPRESSION }), + new PredefinedMethodInfo( PREDEFMETH.PM_EXPRESSION_SUBTRACT_USER_DEFINED, MethodRequiredEnum.Optional, PredefinedType.PT_EXPRESSION, PredefinedName.PN_SUBTRACT, MethodCallingConventionEnum.Static, ACCESS.ACC_PUBLIC, 0, new int[] { (int)PredefinedType.PT_BINARYEXPRESSION, 3, (int)PredefinedType.PT_EXPRESSION, (int)PredefinedType.PT_EXPRESSION, (int)PredefinedType.PT_METHODINFO }), + new PredefinedMethodInfo( PREDEFMETH.PM_EXPRESSION_SUBTRACTCHECKED, MethodRequiredEnum.Optional, PredefinedType.PT_EXPRESSION, PredefinedName.PN_SUBTRACTCHECKED, MethodCallingConventionEnum.Static, ACCESS.ACC_PUBLIC, 0, new int[] { (int)PredefinedType.PT_BINARYEXPRESSION, 2, (int)PredefinedType.PT_EXPRESSION, (int)PredefinedType.PT_EXPRESSION }), + new PredefinedMethodInfo( PREDEFMETH.PM_EXPRESSION_SUBTRACTCHECKED_USER_DEFINED, MethodRequiredEnum.Optional, PredefinedType.PT_EXPRESSION, PredefinedName.PN_SUBTRACTCHECKED, MethodCallingConventionEnum.Static, ACCESS.ACC_PUBLIC, 0, new int[] { (int)PredefinedType.PT_BINARYEXPRESSION, 3, (int)PredefinedType.PT_EXPRESSION, (int)PredefinedType.PT_EXPRESSION, (int)PredefinedType.PT_METHODINFO }), + new PredefinedMethodInfo( PREDEFMETH.PM_EXPRESSION_UNARYPLUS_USER_DEFINED, MethodRequiredEnum.Optional, PredefinedType.PT_EXPRESSION, PredefinedName.PN_PLUS , MethodCallingConventionEnum.Static, ACCESS.ACC_PUBLIC, 0, new int[] { (int)PredefinedType.PT_UNARYEXPRESSION, 2, (int)PredefinedType.PT_EXPRESSION, (int)PredefinedType.PT_METHODINFO }), + new PredefinedMethodInfo( PREDEFMETH.PM_EXPRESSION_NEGATE, MethodRequiredEnum.Optional, PredefinedType.PT_EXPRESSION, PredefinedName.PN_NEGATE, MethodCallingConventionEnum.Static, ACCESS.ACC_PUBLIC, 0, new int[] { (int)PredefinedType.PT_UNARYEXPRESSION, 1, (int)PredefinedType.PT_EXPRESSION }), + new PredefinedMethodInfo( PREDEFMETH.PM_EXPRESSION_NEGATE_USER_DEFINED, MethodRequiredEnum.Optional, PredefinedType.PT_EXPRESSION, PredefinedName.PN_NEGATE, MethodCallingConventionEnum.Static, ACCESS.ACC_PUBLIC, 0, new int[] { (int)PredefinedType.PT_UNARYEXPRESSION, 2, (int)PredefinedType.PT_EXPRESSION, (int)PredefinedType.PT_METHODINFO }), + new PredefinedMethodInfo( PREDEFMETH.PM_EXPRESSION_NEGATECHECKED, MethodRequiredEnum.Optional, PredefinedType.PT_EXPRESSION, PredefinedName.PN_NEGATECHECKED, MethodCallingConventionEnum.Static, ACCESS.ACC_PUBLIC, 0, new int[] { (int)PredefinedType.PT_UNARYEXPRESSION, 1, (int)PredefinedType.PT_EXPRESSION }), + new PredefinedMethodInfo( PREDEFMETH.PM_EXPRESSION_NEGATECHECKED_USER_DEFINED, MethodRequiredEnum.Optional, PredefinedType.PT_EXPRESSION, PredefinedName.PN_NEGATECHECKED, MethodCallingConventionEnum.Static, ACCESS.ACC_PUBLIC, 0, new int[] { (int)PredefinedType.PT_UNARYEXPRESSION, 2, (int)PredefinedType.PT_EXPRESSION, (int)PredefinedType.PT_METHODINFO }), + new PredefinedMethodInfo( PREDEFMETH.PM_EXPRESSION_CALL, MethodRequiredEnum.Optional, PredefinedType.PT_EXPRESSION, PredefinedName.PN_CALL, MethodCallingConventionEnum.Static, ACCESS.ACC_PUBLIC, 0, new int[] { (int)PredefinedType.PT_METHODCALLEXPRESSION, 3, (int)PredefinedType.PT_EXPRESSION, (int)PredefinedType.PT_METHODINFO, (int)MethodSignatureEnum.SIG_SZ_ARRAY, (int)PredefinedType.PT_EXPRESSION }), + new PredefinedMethodInfo( PREDEFMETH.PM_EXPRESSION_NEW, MethodRequiredEnum.Optional, PredefinedType.PT_EXPRESSION, PredefinedName.PN_NEW, MethodCallingConventionEnum.Static, ACCESS.ACC_PUBLIC, 0, new int[] { (int)PredefinedType.PT_NEWEXPRESSION, 2, (int)PredefinedType.PT_CONSTRUCTORINFO, (int)MethodSignatureEnum.SIG_SZ_ARRAY, (int)PredefinedType.PT_EXPRESSION }), + new PredefinedMethodInfo( PREDEFMETH.PM_EXPRESSION_NEW_MEMBERS, MethodRequiredEnum.Optional, PredefinedType.PT_EXPRESSION, PredefinedName.PN_NEW, MethodCallingConventionEnum.Static, ACCESS.ACC_PUBLIC, 0, new int[] { (int)PredefinedType.PT_NEWEXPRESSION, 3, (int)PredefinedType.PT_CONSTRUCTORINFO, (int)PredefinedType.PT_G_IENUMERABLE, (int)PredefinedType.PT_EXPRESSION, (int)MethodSignatureEnum.SIG_SZ_ARRAY, (int)PredefinedType.PT_MEMBERINFO }), + new PredefinedMethodInfo( PREDEFMETH.PM_EXPRESSION_NEW_TYPE, MethodRequiredEnum.Optional, PredefinedType.PT_EXPRESSION, PredefinedName.PN_NEW, MethodCallingConventionEnum.Static, ACCESS.ACC_PUBLIC, 0, new int[] { (int)PredefinedType.PT_NEWEXPRESSION, 1, (int)PredefinedType.PT_TYPE }), + new PredefinedMethodInfo( PREDEFMETH.PM_EXPRESSION_QUOTE, MethodRequiredEnum.Optional, PredefinedType.PT_EXPRESSION, PredefinedName.PN_QUOTE, MethodCallingConventionEnum.Static, ACCESS.ACC_PUBLIC, 0, new int[] { (int)PredefinedType.PT_UNARYEXPRESSION, 1, (int)PredefinedType.PT_EXPRESSION }), + new PredefinedMethodInfo( PREDEFMETH.PM_EXPRESSION_ARRAYLENGTH, MethodRequiredEnum.Optional, PredefinedType.PT_EXPRESSION, PredefinedName.PN_ARRAYLENGTH, MethodCallingConventionEnum.Static, ACCESS.ACC_PUBLIC, 0, new int[] { (int)PredefinedType.PT_UNARYEXPRESSION, 1, (int)PredefinedType.PT_EXPRESSION }), + new PredefinedMethodInfo( PREDEFMETH.PM_EXPRESSION_NOT, MethodRequiredEnum.Optional, PredefinedType.PT_EXPRESSION, PredefinedName.PN_NOT, MethodCallingConventionEnum.Static, ACCESS.ACC_PUBLIC, 0, new int[] { (int)PredefinedType.PT_UNARYEXPRESSION, 1, (int)PredefinedType.PT_EXPRESSION }), + new PredefinedMethodInfo( PREDEFMETH.PM_EXPRESSION_NOT_USER_DEFINED, MethodRequiredEnum.Optional, PredefinedType.PT_EXPRESSION, PredefinedName.PN_NOT, MethodCallingConventionEnum.Static, ACCESS.ACC_PUBLIC, 0, new int[] { (int)PredefinedType.PT_UNARYEXPRESSION, 2, (int)PredefinedType.PT_EXPRESSION, (int)PredefinedType.PT_METHODINFO }), + new PredefinedMethodInfo( PREDEFMETH.PM_EXPRESSION_NEWARRAYINIT, MethodRequiredEnum.Optional, PredefinedType.PT_EXPRESSION, PredefinedName.PN_NEWARRAYINIT, MethodCallingConventionEnum.Static, ACCESS.ACC_PUBLIC, 0, new int[] { (int)PredefinedType.PT_NEWARRAYEXPRESSION, 2, (int)PredefinedType.PT_TYPE, (int)MethodSignatureEnum.SIG_SZ_ARRAY, (int)PredefinedType.PT_EXPRESSION }), + new PredefinedMethodInfo( PREDEFMETH.PM_EXPRESSION_PROPERTY, MethodRequiredEnum.Optional, PredefinedType.PT_EXPRESSION, PredefinedName.PN_EXPRESSION_PROPERTY, MethodCallingConventionEnum.Static, ACCESS.ACC_PUBLIC, 0, new int[] { (int)PredefinedType.PT_MEMBEREXPRESSION, 2, (int)PredefinedType.PT_EXPRESSION, (int)PredefinedType.PT_PROPERTYINFO }), + new PredefinedMethodInfo( PREDEFMETH.PM_EXPRESSION_INVOKE, MethodRequiredEnum.Optional, PredefinedType.PT_EXPRESSION, PredefinedName.PN_INVOKE, MethodCallingConventionEnum.Static, ACCESS.ACC_PUBLIC, 0, new int[] { (int)PredefinedType.PT_INVOCATIONEXPRESSION, 2, (int)PredefinedType.PT_EXPRESSION, (int)MethodSignatureEnum.SIG_SZ_ARRAY, (int)PredefinedType.PT_EXPRESSION }), + new PredefinedMethodInfo( PREDEFMETH.PM_DELEGATE_CREATEDELEGATE_TYPE_OBJ_METHINFO, MethodRequiredEnum.Optional, PredefinedType.PT_DELEGATE, PredefinedName.PN_CREATEDELEGATE, MethodCallingConventionEnum.Static, ACCESS.ACC_PUBLIC, 0, new int[] { (int)PredefinedType.PT_DELEGATE, 3, (int)PredefinedType.PT_TYPE, (int)PredefinedType.PT_OBJECT, (int)PredefinedType.PT_METHODINFO }), + new PredefinedMethodInfo( PREDEFMETH.PM_G_OPTIONAL_CTOR, MethodRequiredEnum.Optional, PredefinedType.PT_G_OPTIONAL, PredefinedName.PN_CTOR, MethodCallingConventionEnum.Instance, ACCESS.ACC_PUBLIC, 0, new int[] { (int)PredefinedType.PT_VOID, 1, (int)MethodSignatureEnum.SIG_CLASS_TYVAR, 0 }), + new PredefinedMethodInfo( PREDEFMETH.PM_G_OPTIONAL_GETHASVALUE, MethodRequiredEnum.Optional, PredefinedType.PT_G_OPTIONAL, PredefinedName.PN_GETHASVALUE, MethodCallingConventionEnum.Instance, ACCESS.ACC_PUBLIC, 0, new int[] { (int)PredefinedType.PT_BOOL, 0 }), + new PredefinedMethodInfo( PREDEFMETH.PM_G_OPTIONAL_GETVALUE, MethodRequiredEnum.Optional, PredefinedType.PT_G_OPTIONAL, PredefinedName.PN_GETVALUE, MethodCallingConventionEnum.Instance, ACCESS.ACC_PUBLIC, 0, new int[] { (int)MethodSignatureEnum.SIG_CLASS_TYVAR, 0, 0 }), + new PredefinedMethodInfo( PREDEFMETH.PM_G_OPTIONAL_GET_VALUE_OR_DEF, MethodRequiredEnum.Optional, PredefinedType.PT_G_OPTIONAL, PredefinedName.PN_GET_VALUE_OR_DEF, MethodCallingConventionEnum.Instance, ACCESS.ACC_PUBLIC, 0, new int[] { (int)MethodSignatureEnum.SIG_CLASS_TYVAR, 0, 0 }), + new PredefinedMethodInfo( PREDEFMETH.PM_STRING_CONCAT_OBJECT_1, MethodRequiredEnum.Optional, PredefinedType.PT_STRING, PredefinedName.PN_CONCAT, MethodCallingConventionEnum.Static, ACCESS.ACC_PUBLIC, 0, new int[] { (int)PredefinedType.PT_STRING, 1, (int)PredefinedType.PT_OBJECT }), + new PredefinedMethodInfo( PREDEFMETH.PM_STRING_CONCAT_OBJECT_2, MethodRequiredEnum.Optional, PredefinedType.PT_STRING, PredefinedName.PN_CONCAT, MethodCallingConventionEnum.Static, ACCESS.ACC_PUBLIC, 0, new int[] { (int)PredefinedType.PT_STRING, 2, (int)PredefinedType.PT_OBJECT, (int)PredefinedType.PT_OBJECT }), + new PredefinedMethodInfo( PREDEFMETH.PM_STRING_CONCAT_OBJECT_3, MethodRequiredEnum.Optional, PredefinedType.PT_STRING, PredefinedName.PN_CONCAT, MethodCallingConventionEnum.Static, ACCESS.ACC_PUBLIC, 0, new int[] { (int)PredefinedType.PT_STRING, 3, (int)PredefinedType.PT_OBJECT, (int)PredefinedType.PT_OBJECT, (int)PredefinedType.PT_OBJECT }), + new PredefinedMethodInfo( PREDEFMETH.PM_STRING_CONCAT_STRING_1, MethodRequiredEnum.Optional, PredefinedType.PT_STRING, PredefinedName.PN_CONCAT, MethodCallingConventionEnum.Static, ACCESS.ACC_PUBLIC, 0, new int[] { (int)PredefinedType.PT_STRING, 1, (int)PredefinedType.PT_STRING }), + new PredefinedMethodInfo( PREDEFMETH.PM_STRING_CONCAT_STRING_2, MethodRequiredEnum.Optional, PredefinedType.PT_STRING, PredefinedName.PN_CONCAT, MethodCallingConventionEnum.Static, ACCESS.ACC_PUBLIC, 0, new int[] { (int)PredefinedType.PT_STRING, 2, (int)PredefinedType.PT_STRING, (int)PredefinedType.PT_STRING }), + new PredefinedMethodInfo( PREDEFMETH.PM_STRING_CONCAT_STRING_3, MethodRequiredEnum.Optional, PredefinedType.PT_STRING, PredefinedName.PN_CONCAT, MethodCallingConventionEnum.Static, ACCESS.ACC_PUBLIC, 0, new int[] { (int)PredefinedType.PT_STRING, 3, (int)PredefinedType.PT_STRING, (int)PredefinedType.PT_STRING, (int)PredefinedType.PT_STRING }), + new PredefinedMethodInfo( PREDEFMETH.PM_STRING_CONCAT_STRING_4, MethodRequiredEnum.Optional, PredefinedType.PT_STRING, PredefinedName.PN_CONCAT, MethodCallingConventionEnum.Static, ACCESS.ACC_PUBLIC, 0, new int[] { (int)PredefinedType.PT_STRING, 4, (int)PredefinedType.PT_STRING, (int)PredefinedType.PT_STRING, (int)PredefinedType.PT_STRING, (int)PredefinedType.PT_STRING }), + new PredefinedMethodInfo( PREDEFMETH.PM_STRING_CONCAT_SZ_OBJECT, MethodRequiredEnum.Optional, PredefinedType.PT_STRING, PredefinedName.PN_CONCAT, MethodCallingConventionEnum.Static, ACCESS.ACC_PUBLIC, 0, new int[] { (int)PredefinedType.PT_STRING, 1, (int)MethodSignatureEnum.SIG_SZ_ARRAY, (int)PredefinedType.PT_OBJECT }), + new PredefinedMethodInfo( PREDEFMETH.PM_STRING_CONCAT_SZ_STRING, MethodRequiredEnum.Optional, PredefinedType.PT_STRING, PredefinedName.PN_CONCAT, MethodCallingConventionEnum.Static, ACCESS.ACC_PUBLIC, 0, new int[] { (int)PredefinedType.PT_STRING, 1, (int)MethodSignatureEnum.SIG_SZ_ARRAY, (int)PredefinedType.PT_STRING }), + new PredefinedMethodInfo( PREDEFMETH.PM_STRING_GETCHARS, MethodRequiredEnum.Optional, PredefinedType.PT_STRING, PredefinedName.PN_GETCHARS, MethodCallingConventionEnum.Instance, ACCESS.ACC_PUBLIC, 0, new int[] { (int)PredefinedType.PT_CHAR, 1, (int)PredefinedType.PT_INT }), + new PredefinedMethodInfo( PREDEFMETH.PM_STRING_GETLENGTH, MethodRequiredEnum.Optional, PredefinedType.PT_STRING, PredefinedName.PN_GETLENGTH, MethodCallingConventionEnum.Instance, ACCESS.ACC_PUBLIC, 0, new int[] { (int)PredefinedType.PT_INT, 0, }), + new PredefinedMethodInfo( PREDEFMETH.PM_STRING_OPEQUALITY, MethodRequiredEnum.Optional, PredefinedType.PT_STRING, PredefinedName.PN_OPEQUALITY, MethodCallingConventionEnum.Static, ACCESS.ACC_PUBLIC, 0, new int[] { (int)PredefinedType.PT_BOOL, 2, (int)PredefinedType.PT_STRING, (int)PredefinedType.PT_STRING }), + new PredefinedMethodInfo( PREDEFMETH.PM_STRING_OPINEQUALITY, MethodRequiredEnum.Optional, PredefinedType.PT_STRING, PredefinedName.PN_OPINEQUALITY, MethodCallingConventionEnum.Static, ACCESS.ACC_PUBLIC, 0, new int[] { (int)PredefinedType.PT_BOOL, 2, (int)PredefinedType.PT_STRING, (int)PredefinedType.PT_STRING }), + }; + } +} diff --git a/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/SemanticChecker.cs b/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/SemanticChecker.cs new file mode 100644 index 000000000..abf60706b --- /dev/null +++ b/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/SemanticChecker.cs @@ -0,0 +1,363 @@ +// ==++== +// +// CopyRight (c) Microsoft Corporation. All Rights reserved. +// +// ==--== + +using System.Diagnostics; +using Microsoft.CSharp.RuntimeBinder.Errors; +using Microsoft.CSharp.RuntimeBinder.Syntax; + +namespace Microsoft.CSharp.RuntimeBinder.Semantics +{ + internal enum ACCESSERROR + { + ACCESSERROR_NOACCESS, + ACCESSERROR_NOACCESSTHRU, + ACCESSERROR_NOERROR + }; + + + // + // Semantic check methods on SymbolLoader + // + internal abstract class CSemanticChecker + { + // Generate an error if CType is static. + public bool CheckForStaticClass(Symbol symCtx, CType CType, ErrorCode err) + { + if (!CType.isStaticClass()) + return false; + ReportStaticClassError(symCtx, CType, err); + return true; + } + + public virtual ACCESSERROR CheckAccess2(Symbol symCheck, AggregateType atsCheck, Symbol symWhere, CType typeThru) + { + Debug.Assert(symCheck != null); + Debug.Assert(atsCheck == null || symCheck.parent == atsCheck.getAggregate()); + Debug.Assert(typeThru == null || + typeThru.IsAggregateType() || + typeThru.IsTypeParameterType() || + typeThru.IsArrayType() || + typeThru.IsNullableType() || + typeThru.IsErrorType()); + +#if DEBUG + + switch (symCheck.getKind()) + { + default: + break; + case SYMKIND.SK_MethodSymbol: + case SYMKIND.SK_PropertySymbol: + case SYMKIND.SK_FieldSymbol: + case SYMKIND.SK_EventSymbol: + Debug.Assert(atsCheck != null); + break; + } + +#endif // DEBUG + + ACCESSERROR error = CheckAccessCore(symCheck, atsCheck, symWhere, typeThru); + if (ACCESSERROR.ACCESSERROR_NOERROR != error) + { + return error; + } + + // Check the accessibility of the return CType. + CType CType = symCheck.getType(); + if (CType == null) + { + return ACCESSERROR.ACCESSERROR_NOERROR; + } + + // For members of AGGSYMs, atsCheck should always be specified! + Debug.Assert(atsCheck != null); + + if (atsCheck.getAggregate().IsSource()) + { + // We already check the "at least as accessible as" rules. + // REVIEW : Does this always work for generics? + // Could we get a bad CType argument in typeThru? + // Maybe call CheckTypeAccess on typeThru? + return ACCESSERROR.ACCESSERROR_NOERROR; + } + + // Substitute on the CType. + if (atsCheck.GetTypeArgsAll().size > 0) + { + CType = SymbolLoader.GetTypeManager().SubstType(CType, atsCheck); + } + + return CheckTypeAccess(CType, symWhere) ? ACCESSERROR.ACCESSERROR_NOERROR : ACCESSERROR.ACCESSERROR_NOACCESS; + } + public virtual bool CheckTypeAccess(CType type, Symbol symWhere) + { + Debug.Assert(type != null); + + // Array, Ptr, Nub, etc don't matter. + type = type.GetNakedType(true); + + if (!type.IsAggregateType()) + { + Debug.Assert(type.IsVoidType() || type.IsErrorType() || type.IsTypeParameterType()); + return true; + } + + for (AggregateType ats = type.AsAggregateType(); ats != null; ats = ats.outerType) + { + if (ACCESSERROR.ACCESSERROR_NOERROR != CheckAccessCore(ats.GetOwningAggregate(), ats.outerType, symWhere, null)) + { + return false; + } + } + + TypeArray typeArgs = type.AsAggregateType().GetTypeArgsAll(); + for (int i = 0; i < typeArgs.size; i++) + { + if (!CheckTypeAccess(typeArgs.Item(i), symWhere)) + return false; + } + + return true; + } + + // Generates an error for static classes + public void ReportStaticClassError(Symbol symCtx, CType CType, ErrorCode err) + { + if (symCtx != null) + ErrorContext.Error(err, CType, new ErrArgRef(symCtx)); + else + ErrorContext.Error(err, CType); + } + + public abstract SymbolLoader SymbolLoader { get; } + public abstract SymbolLoader GetSymbolLoader(); + + ///////////////////////////////////////////////////////////////////////////////// + // SymbolLoader forwarders (begin) + // + + private ErrorHandling ErrorContext + { + get + { + return SymbolLoader.ErrorContext; + } + } + public ErrorHandling GetErrorContext() { return ErrorContext; } + public NameManager GetNameManager() { return SymbolLoader.GetNameManager(); } + public TypeManager GetTypeManager() { return SymbolLoader.GetTypeManager(); } + public BSYMMGR getBSymmgr() { return SymbolLoader.getBSymmgr(); } + public SymFactory GetGlobalSymbolFactory() { return SymbolLoader.GetGlobalSymbolFactory(); } + public MiscSymFactory GetGlobalMiscSymFactory() { return SymbolLoader.GetGlobalMiscSymFactory(); } + + //protected CompilerPhase GetCompPhase() { return SymbolLoader.CompPhase(); } + //protected void SetCompPhase(CompilerPhase compPhase) { SymbolLoader.compPhase = compPhase; } + public PredefinedTypes getPredefTypes() { return SymbolLoader.getPredefTypes(); } + // + // SymbolLoader forwarders (end) + ///////////////////////////////////////////////////////////////////////////////// + + // + // Utility methods + // + protected ACCESSERROR CheckAccessCore(Symbol symCheck, AggregateType atsCheck, Symbol symWhere, CType typeThru) + { + Debug.Assert(symCheck != null); + Debug.Assert(atsCheck == null || symCheck.parent == atsCheck.getAggregate()); + Debug.Assert(typeThru == null || + typeThru.IsAggregateType() || + typeThru.IsTypeParameterType() || + typeThru.IsArrayType() || + typeThru.IsNullableType() || + typeThru.IsErrorType()); + + switch (symCheck.GetAccess()) + { + default: + throw Error.InternalCompilerError(); + //return ACCESSERROR.ACCESSERROR_NOACCESS; + + case ACCESS.ACC_UNKNOWN: + return ACCESSERROR.ACCESSERROR_NOACCESS; + + case ACCESS.ACC_PUBLIC: + return ACCESSERROR.ACCESSERROR_NOERROR; + + case ACCESS.ACC_PRIVATE: + case ACCESS.ACC_PROTECTED: + if (symWhere == null) + { + return ACCESSERROR.ACCESSERROR_NOACCESS; + } + break; + + case ACCESS.ACC_INTERNAL: + case ACCESS.ACC_INTERNALPROTECTED: // Check internal, then protected. + + if (symWhere == null) + { + return ACCESSERROR.ACCESSERROR_NOACCESS; + } + if (symWhere.SameAssemOrFriend(symCheck)) + { + return ACCESSERROR.ACCESSERROR_NOERROR; + } + if (symCheck.GetAccess() == ACCESS.ACC_INTERNAL) + { + return ACCESSERROR.ACCESSERROR_NOACCESS; + } + break; + } + + // Should always have atsCheck for private and protected access check. + // We currently don't need it since access doesn't respect instantiation. + // We just use symWhere.parent.AsAggregateSymbol() instead. + AggregateSymbol aggCheck = symCheck.parent.AsAggregateSymbol(); + + // Find the inner-most enclosing AggregateSymbol. + AggregateSymbol aggWhere = null; + + for (Symbol symT = symWhere; symT != null; symT = symT.parent) + { + if (symT.IsAggregateSymbol()) + { + aggWhere = symT.AsAggregateSymbol(); + break; + } + if (symT.IsAggregateDeclaration()) + { + aggWhere = symT.AsAggregateDeclaration().Agg(); + break; + } + } + + if (aggWhere == null) + { + return ACCESSERROR.ACCESSERROR_NOACCESS; + } + + // First check for private access. + for (AggregateSymbol agg = aggWhere; agg != null; agg = agg.GetOuterAgg()) + { + if (agg == aggCheck) + { + return ACCESSERROR.ACCESSERROR_NOERROR; + } + } + + if (symCheck.GetAccess() == ACCESS.ACC_PRIVATE) + { + return ACCESSERROR.ACCESSERROR_NOACCESS; + } + + // Handle the protected case - which is the only real complicated one. + Debug.Assert(symCheck.GetAccess() == ACCESS.ACC_PROTECTED || symCheck.GetAccess() == ACCESS.ACC_INTERNALPROTECTED); + + // Check if symCheck is in aggWhere or a base of aggWhere, + // or in an outer agg of aggWhere or a base of an outer agg of aggWhere. + + AggregateType atsThru = null; + + if (typeThru != null && !symCheck.isStatic) + { + atsThru = SymbolLoader.GetAggTypeSym(typeThru); + } + + // Look for aggCheck among the base classes of aggWhere and outer aggs. + bool found = false; + for (AggregateSymbol agg = aggWhere; agg != null; agg = agg.GetOuterAgg()) + { + Debug.Assert(agg != aggCheck); // We checked for this above. + + // Look for aggCheck among the base classes of agg. + if (agg.FindBaseAgg(aggCheck)) + { + found = true; + // aggCheck is a base class of agg. Check atsThru. + // For non-static protected access to be legal, atsThru must be an instantiation of + // agg or a CType derived from an instantiation of agg. In this case + // all that matters is that agg is in the base AggregateSymbol chain of atsThru. The + // actual AGGTYPESYMs involved don't matter. + if (atsThru == null || atsThru.getAggregate().FindBaseAgg(agg)) + { + return ACCESSERROR.ACCESSERROR_NOERROR; + } + } + } + + // the CType in whice the method is being called has no relationship with the + // CType on which the method is defined surely this is NOACCESS and not NOACCESSTHRU + if (found == false) + return ACCESSERROR.ACCESSERROR_NOACCESS; + + return (atsThru == null) ? ACCESSERROR.ACCESSERROR_NOACCESS : ACCESSERROR.ACCESSERROR_NOACCESSTHRU; + } + + public bool CheckBogus(Symbol sym) + { + if (sym == null) + { + return false; + } + + if (!sym.hasBogus()) + { + bool fBogus = sym.computeCurrentBogusState(); + + if (fBogus) + { + // Only set this if everything is declared or + // at least 1 declared thing is bogus + sym.setBogus(fBogus); + } + } + + return sym.hasBogus() && sym.checkBogus(); + } + + public bool CheckBogus(CType pType) + { + if (pType == null) + { + return false; + } + + if (!pType.hasBogus()) + { + bool fBogus = pType.computeCurrentBogusState(); + + if (fBogus) + { + // Only set this if everything is declared or + // at least 1 declared thing is bogus + pType.setBogus(fBogus); + } + } + + return pType.hasBogus() && pType.checkBogus(); + } + + public void ReportAccessError(SymWithType swtBad, Symbol symWhere, CType typeQual) + { + Debug.Assert(!CheckAccess(swtBad.Sym, swtBad.GetType(), symWhere, typeQual) || + !CheckTypeAccess(swtBad.GetType(), symWhere)); + + if (CheckAccess2(swtBad.Sym, swtBad.GetType(), symWhere, typeQual) == ACCESSERROR.ACCESSERROR_NOACCESSTHRU) + { + ErrorContext.Error(ErrorCode.ERR_BadProtectedAccess, swtBad, typeQual, symWhere); + } + else + { + ErrorContext.ErrorRef(ErrorCode.ERR_BadAccess, swtBad); + } + } + + public bool CheckAccess(Symbol symCheck, AggregateType atsCheck, Symbol symWhere, CType typeThru) + { + return CheckAccess2(symCheck, atsCheck, symWhere, typeThru) == ACCESSERROR.ACCESSERROR_NOERROR; + } + } +} diff --git a/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/SubstitutionContext.cs b/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/SubstitutionContext.cs new file mode 100644 index 000000000..145628248 --- /dev/null +++ b/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/SubstitutionContext.cs @@ -0,0 +1,107 @@ +// ==++== +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// ==--== + +using System; + +namespace Microsoft.CSharp.RuntimeBinder.Semantics +{ + // Used to specify whether and which type variables should be normalized. + [Flags] + internal enum SubstTypeFlags + { + NormNone = 0x00, + NormClass = 0x01, // Replace class type variables with the normalized (standard) ones. + NormMeth = 0x02, // Replace method type variables with the normalized (standard) ones. + NormAll = NormClass | NormMeth, + DenormClass = 0x04, // Replace normalized (standard) class type variables with the given class type args. + DenormMeth = 0x08, // Replace normalized (standard) method type variables with the given method type args. + DenormAll = DenormClass | DenormMeth, + NoRefOutDifference = 0x10 + } + + // TODO: Make this a struct + internal class SubstContext + { + public CType[] prgtypeCls; + public int ctypeCls; + public CType[] prgtypeMeth; + public int ctypeMeth; + public SubstTypeFlags grfst; + + public SubstContext(TypeArray typeArgsCls, TypeArray typeArgsMeth, SubstTypeFlags grfst) + { + Init(typeArgsCls, typeArgsMeth, grfst); + } + + public SubstContext(AggregateType type) + : this(type, null, SubstTypeFlags.NormNone) + { + } + + public SubstContext(AggregateType type, TypeArray typeArgsMeth) + : this(type, typeArgsMeth, SubstTypeFlags.NormNone) + { + } + + public SubstContext(AggregateType type, TypeArray typeArgsMeth, SubstTypeFlags grfst) + { + Init(type != null ? type.GetTypeArgsAll() : null, typeArgsMeth, grfst); + } + + public SubstContext(CType[] prgtypeCls, int ctypeCls, CType[] prgtypeMeth, int ctypeMeth) + : this(prgtypeCls, ctypeCls, prgtypeMeth, ctypeMeth, SubstTypeFlags.NormNone) + { + } + public SubstContext(CType[] prgtypeCls, int ctypeCls, CType[] prgtypeMeth, int ctypeMeth, SubstTypeFlags grfst) + { + this.prgtypeCls = prgtypeCls; + this.ctypeCls = ctypeCls; + this.prgtypeMeth = prgtypeMeth; + this.ctypeMeth = ctypeMeth; + this.grfst = grfst; + } + + public bool FNop() + { + return 0 == ctypeCls && 0 == ctypeMeth && 0 == (grfst & SubstTypeFlags.NormAll); + } + + // Initializes a substitution context. Returns false iff no substitutions will ever be performed. + public void Init(TypeArray typeArgsCls, TypeArray typeArgsMeth, SubstTypeFlags grfst) + { + if (typeArgsCls != null) + { +#if DEBUG + typeArgsCls.AssertValid(); +#endif + ctypeCls = typeArgsCls.size; + prgtypeCls = typeArgsCls.ToArray(); + } + else + { + ctypeCls = 0; + prgtypeCls = null; + } + + if (typeArgsMeth != null) + { +#if DEBUG + typeArgsMeth.AssertValid(); +#endif + + ctypeMeth = typeArgsMeth.size; + prgtypeMeth = typeArgsMeth.ToArray(); + } + else + { + ctypeMeth = 0; + prgtypeMeth = null; + } + + this.grfst = grfst; + } + } +} diff --git a/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/Symbols/AggregateSymbol.cs b/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/Symbols/AggregateSymbol.cs new file mode 100644 index 000000000..1b37eeae2 --- /dev/null +++ b/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/Symbols/AggregateSymbol.cs @@ -0,0 +1,534 @@ +// ==++== +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// ==--== + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Security; +using System.Security.Permissions; +using Microsoft.CSharp.RuntimeBinder.Syntax; + +namespace Microsoft.CSharp.RuntimeBinder.Semantics +{ + // Name used for AGGDECLs in the symbol table. + + // AggregateSymbol - a symbol representing an aggregate type. These are classes, + // interfaces, and structs. Parent is a namespace or class. Children are methods, + // properties, and member variables, and types (including its own AGGTYPESYMs). + + class AggregateSymbol : NamespaceOrAggregateSymbol + { + public Type AssociatedSystemType; + public Assembly AssociatedAssembly; + + // This InputFile is some infile for the assembly containing this AggregateSymbol. + // It is used for fast access to the filter BitSet and assembly ID. + InputFile infile; + + // The instance type. Created when first needed. + AggregateType atsInst; + + AggregateType m_pBaseClass; // For a class/struct/enum, the base class. For iface: unused. + AggregateType m_pUnderlyingType; // For enum, the underlying type. For iface, the resolved CoClass. Not used for class/struct. + + TypeArray m_ifaces; // The explicit base interfaces for a class or interface. + TypeArray m_ifacesAll; // Recursive closure of base interfaces ordered so an iface appears before all of its base ifaces. + + TypeArray m_typeVarsThis; // Type variables for this generic class, as declarations. + TypeArray m_typeVarsAll; // The type variables for this generic class and all containing classes. + + TypeManager m_pTypeManager; // This is so AGGTYPESYMs can instantiate their baseClass and ifacesAll members on demand. + + // First UD conversion operator. This chain is for this type only (not base types). + // The hasConversion flag indicates whether this or any base types have UD conversions. + MethodSymbol m_pConvFirst; + + // ------------------------------------------------------------------------ + // + // Put members that are bits under here in a contiguous section. + // + // ------------------------------------------------------------------------ + + AggKindEnum aggKind; + + bool m_isLayoutError; // Whether there is a cycle in the layout for the struct + + // Where this came from - fabricated, source, import + // REVIEW : Remove isSource? Since incremental is gone. + // Fabricated AGGs have isSource == true but hasParseTree == false. + // N.B.: in incremental builds, it is quite possible for + // isSource==TRUE and hasParseTree==FALSE. Be + // sure you use the correct variable for what you are trying to do! + bool m_isSource; // This class is defined in source, although the + // source might not be being read during this compile. + + // Predefined + bool m_isPredefined; // A special predefined type. + PredefinedType m_iPredef; // index of the predefined type, if isPredefined. + + // Flags + bool m_isAbstract; // Can it be instantiated? + bool m_isSealed; // Can it be derived from? + + // Attribute + + bool m_isUnmanagedStruct; // Set if the struct is known to be un-managed (for unsafe code). Set in FUNCBREC. + bool m_isManagedStruct; // Set if the struct is known to be managed (for unsafe code). Set during import. + + // Constructors + bool m_hasPubNoArgCtor; // Whether it has a public instance ructor taking no args + + // private struct members should not be checked for assignment or references + bool m_hasExternReference; + + // User defined operators + + bool m_isSkipUDOps; // Never check for user defined operators on this type (eg, decimal, string, delegate). + + bool m_isComImport; // Does it have [ComImport] + + bool isAnonymousType; // true if the class is an anonymous type + // When this is unset we don't know if we have conversions. When this + // is set it indicates if this type or any base type has user defined + // conversion operators + bool? m_hasConversion; + + // ---------------------------------------------------------------------------- + // AggregateSymbol + // ---------------------------------------------------------------------------- + + public AggregateSymbol GetBaseAgg() + { + return m_pBaseClass == null ? null : m_pBaseClass.getAggregate(); + } + + public AggregateType getThisType() + { + if (atsInst == null) + { + Debug.Assert(GetTypeVars() == GetTypeVarsAll() || isNested()); + + AggregateType pOuterType = this.isNested() ? GetOuterAgg().getThisType() : null; + + atsInst = m_pTypeManager.GetAggregate(this, pOuterType, GetTypeVars()); + } + + //Debug.Assert(GetTypeVars().Size == atsInst.GenericArguments.Count); + return atsInst; + } + + public void InitFromInfile(InputFile infile) + { + this.infile = infile; + m_isSource = infile.isSource; + } + + public bool FindBaseAgg(AggregateSymbol agg) + { + for (AggregateSymbol aggT = this; aggT != null; aggT = aggT.GetBaseAgg()) + { + if (aggT == agg) + return true; + } + return false; + } + + public NamespaceOrAggregateSymbol Parent + { + get { return parent.AsNamespaceOrAggregateSymbol(); } + } + + public new AggregateDeclaration DeclFirst() + { + return (AggregateDeclaration)base.DeclFirst(); + } + + public AggregateDeclaration DeclOnly() + { + //Debug.Assert(DeclFirst() != null && DeclFirst().DeclNext() == null); + return DeclFirst(); + } + + public bool InAlias(KAID aid) + { + Debug.Assert(infile != null); + //Debug.Assert(DeclFirst() == null || DeclFirst().GetAssemblyID() == infile.GetAssemblyID()); + Debug.Assert(0 <= aid); + if (aid < KAID.kaidMinModule) + return infile.InAlias(aid); + return (aid == GetModuleID()); + } + + public KAID GetModuleID() + { + return 0; + } + + public KAID GetAssemblyID() + { + Debug.Assert(infile != null); + //Debug.Assert(DeclFirst() == null || DeclFirst().GetAssemblyID() == infile.GetAssemblyID()); + return infile.GetAssemblyID(); + } + + public bool IsUnresolved() + { + return infile != null && infile.GetAssemblyID() == KAID.kaidUnresolved; + } + + public bool isNested() + { + return parent != null && parent.IsAggregateSymbol(); + } + + public AggregateSymbol GetOuterAgg() + { + return parent != null && parent.IsAggregateSymbol() ? parent.AsAggregateSymbol() : null; + } + +#if false + IMetaDataImport2 * GetMetaImportV2() + { + return this.GetModule().GetMetaImportV2(); + } + + IMetaDataImport * GetMetaImport() + { + return this.GetModule().GetMetaImport(); + } +#endif + + public bool isPredefAgg(PredefinedType pt) + { + return this.m_isPredefined && (PredefinedType)this.m_iPredef == pt; + } + + // ---------------------------------------------------------------------------- + // The following are the Accessor functions for AggregateSymbol. + // ---------------------------------------------------------------------------- + + public AggKindEnum AggKind() + { + return (AggKindEnum)aggKind; + } + + public void SetAggKind(AggKindEnum aggKind) + { + // NOTE: When importing can demote types: + // - enums with no underlying type go to struct + // - delegates which are abstract or have no .ctor/Invoke method goto class + this.aggKind = aggKind; + + //An interface is always abstract + if (aggKind == AggKindEnum.Interface) + { + this.SetAbstract(true); + } + } + + public bool IsClass() + { + return AggKind() == AggKindEnum.Class; + } + + public bool IsDelegate() + { + return AggKind() == AggKindEnum.Delegate; + } + + public bool IsInterface() + { + return AggKind() == AggKindEnum.Interface; + } + + public bool IsStruct() + { + return AggKind() == AggKindEnum.Struct; + } + + public bool IsEnum() + { + return AggKind() == AggKindEnum.Enum; + } + + public bool IsValueType() + { + return AggKind() == AggKindEnum.Struct || AggKind() == AggKindEnum.Enum; + } + + public bool IsRefType() + { + return AggKind() == AggKindEnum.Class || + AggKind() == AggKindEnum.Interface || AggKind() == AggKindEnum.Delegate; + } + + public bool IsStatic() + { + return (m_isAbstract && m_isSealed); + } + + + + public bool IsAnonymousType() + { + return isAnonymousType; + } + + public void SetAnonymousType(bool isAnonymousType) + { + this.isAnonymousType = isAnonymousType; + } + + public bool IsAbstract() + { + return m_isAbstract; + } + + public void SetAbstract(bool @abstract) + { + m_isAbstract = @abstract; + } + + public bool IsPredefined() + { + return m_isPredefined; + } + + public void SetPredefined(bool predefined) + { + m_isPredefined = predefined; + } + + public PredefinedType GetPredefType() + { + // UNDONE: add this back in some day ... Debug.Assert(IsPredefined()); + return (PredefinedType)m_iPredef; + } + + public void SetPredefType(PredefinedType predef) + { + m_iPredef = predef; + } + + public bool IsLayoutError() + { + return m_isLayoutError == true; + } + + public void SetLayoutError(bool layoutError) + { + m_isLayoutError = layoutError; + } + + public bool IsSealed() + { + return m_isSealed == true; + } + + public void SetSealed(bool @sealed) + { + m_isSealed = @sealed; + } + + //////////////////////////////////////////////////////////////////////////////// + + public bool HasConversion(SymbolLoader pLoader) + { + pLoader.RuntimeBinderSymbolTable.AddConversionsForType(AssociatedSystemType); + + if (!m_hasConversion.HasValue) + { + // ok, we tried defining all the conversions, and we didn't get anything + // for this type. However, we will still think this type has conversions + // if it's base type has conversions. + m_hasConversion = GetBaseAgg() != null && GetBaseAgg().HasConversion(pLoader); + } + + return m_hasConversion.Value; + } + + //////////////////////////////////////////////////////////////////////////////// + + public void SetHasConversion() + { + m_hasConversion = true; + } + + //////////////////////////////////////////////////////////////////////////////// + + public bool IsUnmanagedStruct() + { + return m_isUnmanagedStruct == true; + } + + public void SetUnmanagedStruct(bool unmanagedStruct) + { + m_isUnmanagedStruct = unmanagedStruct; + } + + public bool IsManagedStruct() + { + return m_isManagedStruct == true; + } + + public void SetManagedStruct(bool managedStruct) + { + m_isManagedStruct = managedStruct; + } + + public bool IsKnownManagedStructStatus() + { + Debug.Assert(this.IsStruct()); + Debug.Assert(!IsManagedStruct() || !IsUnmanagedStruct()); + return IsManagedStruct() || IsUnmanagedStruct(); + } + + public bool HasPubNoArgCtor() + { + return m_hasPubNoArgCtor == true; + } + + public void SetHasPubNoArgCtor(bool hasPubNoArgCtor) + { + m_hasPubNoArgCtor = hasPubNoArgCtor; + } + + public bool HasExternReference() + { + return m_hasExternReference == true; + } + + public void SetHasExternReference(bool hasExternReference) + { + m_hasExternReference = hasExternReference; + } + + + public bool IsSkipUDOps() + { + return m_isSkipUDOps == true; + } + + public void SetSkipUDOps(bool skipUDOps) + { + m_isSkipUDOps = skipUDOps; + } + + public void SetComImport(bool comImport) + { + m_isComImport = comImport; + } + + public bool IsSource() + { + return m_isSource == true; + } + + public TypeArray GetTypeVars() + { + return m_typeVarsThis; + } + + public void SetTypeVars(TypeArray typeVars) + { + if (typeVars == null) + { + m_typeVarsThis = null; + m_typeVarsAll = null; + } + else + { + TypeArray outerTypeVars; + if (this.GetOuterAgg() != null) + { + Debug.Assert(this.GetOuterAgg().GetTypeVars() != null); + Debug.Assert(this.GetOuterAgg().GetTypeVarsAll() != null); + + outerTypeVars = this.GetOuterAgg().GetTypeVarsAll(); + } + else + { + outerTypeVars = BSYMMGR.EmptyTypeArray(); + } + + m_typeVarsThis = typeVars; + m_typeVarsAll = m_pTypeManager.ConcatenateTypeArrays(outerTypeVars, typeVars); + } + } + + public TypeArray GetTypeVarsAll() + { + return m_typeVarsAll; + } + + public AggregateType GetBaseClass() + { + return m_pBaseClass; + } + + public void SetBaseClass(AggregateType baseClass) + { + m_pBaseClass = baseClass; + } + + public AggregateType GetUnderlyingType() + { + return m_pUnderlyingType; + } + + public void SetUnderlyingType(AggregateType underlyingType) + { + m_pUnderlyingType = underlyingType; + } + + public TypeArray GetIfaces() + { + return m_ifaces; + } + + public void SetIfaces(TypeArray ifaces) + { + m_ifaces = ifaces; + } + + public TypeArray GetIfacesAll() + { + return m_ifacesAll; + } + + public void SetIfacesAll(TypeArray ifacesAll) + { + m_ifacesAll = ifacesAll; + } + + public TypeManager GetTypeManager() + { + return m_pTypeManager; + } + + public void SetTypeManager(TypeManager typeManager) + { + m_pTypeManager = typeManager; + } + + public MethodSymbol GetFirstUDConversion() + { + return m_pConvFirst; + } + + public void SetFirstUDConversion(MethodSymbol conv) + { + m_pConvFirst = conv; + } + + public new bool InternalsVisibleTo(Assembly assembly) + { + return m_pTypeManager.InternalsVisibleTo(AssociatedAssembly, assembly); + } + } +} diff --git a/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/Symbols/AssemblyQualifiedNamespaceSymbol.cs b/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/Symbols/AssemblyQualifiedNamespaceSymbol.cs new file mode 100644 index 000000000..2a54017cf --- /dev/null +++ b/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/Symbols/AssemblyQualifiedNamespaceSymbol.cs @@ -0,0 +1,49 @@ +// ==++== +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// ==--== + +namespace Microsoft.CSharp.RuntimeBinder.Semantics +{ + // ---------------------------------------------------------------------------- + // + // AssemblyQualifiedNamespaceSymbol + // + // Parented by an NamespaceSymbol. Represents an NamespaceSymbol within an aid (assembly/alias id). + // The name is a form of the aid. + // ---------------------------------------------------------------------------- + + class AssemblyQualifiedNamespaceSymbol : ParentSymbol, ITypeOrNamespace + { + + // ---------------------------------------------------------------------------- + // AssemblyQualifiedNamespaceSymbol + // ---------------------------------------------------------------------------- + + public bool IsType() + { + return false; + } + + public bool IsNamespace() + { + return true; + } + + public AssemblyQualifiedNamespaceSymbol AsNamespace() + { + return this; + } + + public CType AsType() + { + return null; + } + + public NamespaceSymbol GetNS() + { + return parent.AsNamespaceSymbol(); + } + } +} diff --git a/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/Symbols/EventSymbol.cs b/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/Symbols/EventSymbol.cs new file mode 100644 index 000000000..afc0c2601 --- /dev/null +++ b/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/Symbols/EventSymbol.cs @@ -0,0 +1,52 @@ +// ==++== +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// ==--== + +using System.Reflection; + +namespace Microsoft.CSharp.RuntimeBinder.Semantics +{ + // ---------------------------------------------------------------------------- + // + // EventSymbol + // + // EventSymbol - a symbol representing an event. The symbol points to the AddOn and RemoveOn methods + // that handle adding and removing delegates to the event. If the event wasn't imported, it + // also points to the "implementation" of the event -- a field or property symbol that is always + // private. + // ---------------------------------------------------------------------------- + + class EventSymbol : Symbol + { + public EventInfo AssociatedEventInfo; + + //public: + //IS_A(EventSymbol) + + public new bool isStatic; // Static member? + + // If this is true then tell the user to call the accessors directly. + + public bool isOverride; + + public CType type; // Type of the event. + + public MethodSymbol methAdd; // Adder method (always has same parent) + public MethodSymbol methRemove; // Remover method (always has same parent) + + public AggregateDeclaration declaration; // containing declaration + + public bool IsWindowsRuntimeEvent { get; set; } + + // ---------------------------------------------------------------------------- + // EventSymbol + // ---------------------------------------------------------------------------- + + public AggregateDeclaration containingDeclaration() + { + return declaration; + } + } +} diff --git a/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/Symbols/FieldSymbol.cs b/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/Symbols/FieldSymbol.cs new file mode 100644 index 000000000..ba5fe909c --- /dev/null +++ b/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/Symbols/FieldSymbol.cs @@ -0,0 +1,70 @@ +// ==++== +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// ==--== + +using System.Diagnostics; +using System.Reflection; + +namespace Microsoft.CSharp.RuntimeBinder.Semantics +{ + // ---------------------------------------------------------------------------- + // + // FieldSymbol + // + // FieldSymbol - a symbol representing a member variable of a class. Parent + // is a struct or class. + // + // ---------------------------------------------------------------------------- + + class FieldSymbol : VariableSymbol + { + public new bool isStatic; // Static member? + public bool isReadOnly; // Can only be changed from within ructor. + public bool isEvent; // This field is the implementation for an event. + + public bool isAssigned; // Has this ever been assigned by the user? + // Set if the field's ibit (for definite assignment checking) varies depending on the generic + // instantiation of the containing type. For example: + // struct S { T x; int y; } + // The ibit value for y depends on what T is bound to. For S, y's ibit is 2. For S, y's + // ibit is 1. This flag is set the first time a calculated ibit for the member is found to not + // match the return result of GetIbitInst(). + public FieldInfo AssociatedFieldInfo; + + // If fixedAgg is non-null, the ant of the fixed buffer length + + public AggregateDeclaration declaration; // containing declaration + + public void SetType(CType pType) + { + type = pType; + } + + public new CType GetType() + { + return type; + } + + public AggregateSymbol getClass() + { + return parent.AsAggregateSymbol(); + } + + public AggregateDeclaration containingDeclaration() + { + return declaration; + } + + public EventSymbol getEvent(SymbolLoader symbolLoader) + { + Debug.Assert(this.isEvent == true); + EventSymbol evt = symbolLoader.LookupAggMember(this.name, + this.getClass(), + symbmask_t.MASK_EventSymbol).AsEventSymbol(); + + return evt; + } + } +} diff --git a/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/Symbols/IndexerSymbol.cs b/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/Symbols/IndexerSymbol.cs new file mode 100644 index 000000000..acd1f1e5d --- /dev/null +++ b/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/Symbols/IndexerSymbol.cs @@ -0,0 +1,12 @@ +// ==++== +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// ==--== + +namespace Microsoft.CSharp.RuntimeBinder.Semantics +{ + class IndexerSymbol : PropertySymbol + { + } +} diff --git a/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/Symbols/LabelSymbol.cs b/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/Symbols/LabelSymbol.cs new file mode 100644 index 000000000..d4cca2b0b --- /dev/null +++ b/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/Symbols/LabelSymbol.cs @@ -0,0 +1,13 @@ +// ==++== +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// ==--== + +namespace Microsoft.CSharp.RuntimeBinder.Semantics +{ + class LabelSymbol : Symbol + { + + } +} diff --git a/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/Symbols/LocalVariableSymbol.cs b/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/Symbols/LocalVariableSymbol.cs new file mode 100644 index 000000000..4e51c84ef --- /dev/null +++ b/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/Symbols/LocalVariableSymbol.cs @@ -0,0 +1,33 @@ +// ==++== +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// ==--== + +namespace Microsoft.CSharp.RuntimeBinder.Semantics +{ + class LocalVariableSymbol : VariableSymbol + { + // UNDONE: To do expression tree rewriting we need to keep a map between a + // UNDONE: local in an expression tree and the result of a ParameterExpression + // UNDONE: creation. We really ought to build a table to do the mapping in the + // UNDONE: rewriter, but in the interests of expediency I've just put the mapping here + // UNDONE: for now. + + public EXPRWRAP wrap; + + public bool isThis; // Is this the one and only pointer? + // movedToField should have iIteratorLocal set appropriately + public bool fUsedInAnonMeth; // Set if the local is ever used in an anon method + + public void SetType(CType pType) + { + type = pType; + } + + public new CType GetType() + { + return type; + } + } +} diff --git a/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/Symbols/MethodOrPropertySymbol.cs b/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/Symbols/MethodOrPropertySymbol.cs new file mode 100644 index 000000000..6f013e390 --- /dev/null +++ b/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/Symbols/MethodOrPropertySymbol.cs @@ -0,0 +1,224 @@ +// ==++== +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// ==--== + +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; +using System.Runtime.InteropServices; +using Microsoft.CSharp.RuntimeBinder.Syntax; + +namespace Microsoft.CSharp.RuntimeBinder.Semantics +{ + // ---------------------------------------------------------------------------- + // + // MethodOrPropertySymbol + // + // MethodOrPropertySymbol - abstract class representing a method or a property. There + // are a bunch of algorithms in the compiler (e.g., override and overload + // resolution) that want to treat methods and properties the same. This + // abstract base class has the common parts. + // + // Changed to a ParentSymbol to allow generic methods to parent their type + // variables. + // ---------------------------------------------------------------------------- + + class MethodOrPropertySymbol : ParentSymbol + { + public uint modOptCount; // number of CMOD_OPTs in signature and return type + + public new bool isStatic; // Static member? + public bool isOverride; // Overrides an inherited member. Only valid if isVirtual is set. + // false implies that a new vtable slot is required for this method. + public bool useMethInstead; // Only valid iff isBogus == TRUE && IsPropertySymbol(). + // If this is true then tell the user to call the accessors directly. + public bool isOperator; // a user defined operator (or default indexed property) + public bool isParamArray; // new style varargs + public bool isHideByName; // this property hides all below it regardless of signature + public List ParameterNames { get; private set; } + private bool[] optionalParameterIndex; + private bool[] defaultParameterIndex; + private CONSTVAL[] defaultParameters; + private CType[] defaultParameterConstValTypes; + private bool[] dispatchConstantParameterIndex; + private bool[] unknownConstantParameterIndex; + private bool[] marshalAsIndex; + private UnmanagedType[] marshalAsBuffer; + + // This indicates the base member that this member overrides or implements. + // For an explicit interface member implementation, this is the interface member (and type) + // that the member implements. For an override member, this is the base member that is + // being overridden. This is not affected by implicit interface member implementation. + // If this symbol is a property and an explicit interface member implementation, the swtSlot + // may be an event. This is filled in during prepare. + public SymWithType swtSlot; + public ErrorType errExpImpl; // If name == NULL but swtExpImpl couldn't be resolved, this contains error information. + public CType RetType; // Return type. + + private TypeArray _Params; + public TypeArray Params + { + get + { + return _Params; + } + set + { + // Should only be set once! + _Params = value; + optionalParameterIndex = new bool[_Params.size]; + defaultParameterIndex = new bool[_Params.size]; + defaultParameters = new CONSTVAL[_Params.size]; + defaultParameterConstValTypes = new CType[_Params.size]; + dispatchConstantParameterIndex = new bool[_Params.size]; + unknownConstantParameterIndex = new bool[_Params.size]; + marshalAsIndex = new bool[_Params.size]; + marshalAsBuffer = new UnmanagedType[_Params.size]; + } + } // array of cParams parameter types. + public AggregateDeclaration declaration; // containing declaration + public int MetadataToken; + + public MethodOrPropertySymbol() + { + ParameterNames = new List(); + } + + ///////////////////////////////////////////////////////////////////////////////// + + public bool IsParameterOptional(int index) + { + Debug.Assert(index < Params.size); + + if (optionalParameterIndex == null) + { + return false; + } + return optionalParameterIndex[index]; + } + + public void SetOptionalParameter(int index) + { + Debug.Assert(optionalParameterIndex != null); + optionalParameterIndex[index] = true; + } + + public bool HasOptionalParameters() + { + if (optionalParameterIndex == null) + { + return false; + } + foreach (bool b in optionalParameterIndex) + { + if (b) + { + return true; + } + } + return false; + } + + public bool HasDefaultParameterValue(int index) + { + Debug.Assert(index < Params.size); + Debug.Assert(defaultParameterIndex != null); + return defaultParameterIndex[index]; + } + + public void SetDefaultParameterValue(int index, CType type, CONSTVAL cv) + { + Debug.Assert(defaultParameterIndex != null); + ConstValFactory factory = new ConstValFactory(); + defaultParameterIndex[index] = true; + defaultParameters[index] = factory.Copy(type.constValKind(), cv); + defaultParameterConstValTypes[index] = type; + } + + public CONSTVAL GetDefaultParameterValue(int index) + { + Debug.Assert(HasDefaultParameterValue(index)); + Debug.Assert(defaultParameterIndex != null); + return defaultParameters[index]; + } + + public CType GetDefaultParameterValueConstValType(int index) + { + Debug.Assert(HasDefaultParameterValue(index)); + return defaultParameterConstValTypes[index]; + } + + public bool IsMarshalAsParameter(int index) + { + return marshalAsIndex[index]; + } + + public void SetMarshalAsParameter(int index, UnmanagedType umt) + { + marshalAsIndex[index] = true; + marshalAsBuffer[index] = umt; + } + + public UnmanagedType GetMarshalAsParameterValue(int index) + { + Debug.Assert(IsMarshalAsParameter(index)); + return marshalAsBuffer[index]; + } + + public bool MarshalAsObject(int index) + { + UnmanagedType marshalAsType = default(UnmanagedType); + + if (IsMarshalAsParameter(index)) + { + marshalAsType = GetMarshalAsParameterValue(index); + } + +#if SILVERLIGHT && ! FEATURE_NETCORE + return marshalAsType == UnmanagedType.IUnknown; + +#else + return marshalAsType == UnmanagedType.Interface + || marshalAsType == UnmanagedType.IUnknown + || marshalAsType == UnmanagedType.IDispatch; +#endif + } + + public bool IsDispatchConstantParameter(int index) + { + return dispatchConstantParameterIndex[index]; + } + + public void SetDispatchConstantParameter(int index) + { + dispatchConstantParameterIndex[index] = true; + } + + public bool IsUnknownConstantParameter(int index) + { + return unknownConstantParameterIndex[index]; + } + + public void SetUnknownConstantParameter(int index) + { + unknownConstantParameterIndex[index] = true; + } + + public AggregateSymbol getClass() + { + return parent.AsAggregateSymbol(); + } + + public bool IsExpImpl() + { + return name == null; + } + + public AggregateDeclaration containingDeclaration() + { + return declaration; + } + } +} diff --git a/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/Symbols/MethodSymbol.cs b/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/Symbols/MethodSymbol.cs new file mode 100644 index 000000000..9370c5c7f --- /dev/null +++ b/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/Symbols/MethodSymbol.cs @@ -0,0 +1,219 @@ +// ==++== +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// ==--== + +using System.Collections.Generic; +using System.Diagnostics; +using System.Reflection; +using Microsoft.CSharp.RuntimeBinder.Syntax; + +namespace Microsoft.CSharp.RuntimeBinder.Semantics +{ + //////////////////////////////////////////////////////////////////////////////// + // + // MethodSymbol - a symbol representing a method. Parent is a struct, interface + // or class (aggregate). No children. + + class MethodSymbol : MethodOrPropertySymbol + { + private MethodKindEnum methKind; // An extra bit to prevent sign-extension + private bool inferenceMustFail; // Inference must fail if there are no type variables or if + private bool checkedInfMustFail; // there is a type variable used in no parameter. + + private MethodSymbol m_convNext; // For linked list of conversion operators. + private PropertySymbol m_prop; // For property accessors, this is the PropertySymbol. + private EventSymbol m_evt; // For event accessors, this is the EventSymbol. + + public bool isExtension; // is the method a extension method + public bool isExternal; // Has external definition. + public bool isVirtual; // Virtual member? + public bool isAbstract; // Abstract method? + public bool isVarargs; // has varargs + public MemberInfo AssociatedMemberInfo; + + public TypeArray typeVars; // All the type variables for a generic method, as declarations. + + // If there is a type variable in the method which is used in no parameter, + // then inference must fail. Since this is expensive to check, we cache + // the result of the first call. + + public bool InferenceMustFail() + { + if (checkedInfMustFail) + { + return inferenceMustFail; + } + Debug.Assert(!inferenceMustFail); + checkedInfMustFail = true; + for (int ivar = 0; ivar < typeVars.Size; ivar++) + { + TypeParameterType var = typeVars.ItemAsTypeParameterType(ivar); + // See if type var is used in a parameter. + for (int ipar = 0; ; ipar++) + { + if (ipar >= Params.Size) + { + // This type variable is not in any parameter. + inferenceMustFail = true; + return true; + } + if (TypeManager.TypeContainsType(Params.Item(ipar), var)) + { + break; + } + } + } + // All type variables are used in a parameter. + return false; + } + + public bool IsExtension() + { + return isExtension; + } + + public MethodKindEnum MethKind() + { + return methKind; + } + + public bool IsConstructor() + { + return methKind == MethodKindEnum.Constructor; + } + + public bool IsNullableConstructor() + { + return getClass().isPredefAgg(PredefinedType.PT_G_OPTIONAL) && + Params.Size == 1 && + Params.Item(0).IsGenericParameter && + IsConstructor(); + } + + public bool IsDestructor() // Is a destructor + { + return methKind == MethodKindEnum.Destructor; + } + + public bool isPropertyAccessor() // true if this method is a property set or get method + { + return methKind == MethodKindEnum.PropAccessor; + } + + public bool isEventAccessor() // true if this method is an event add/remove method + { + return methKind == MethodKindEnum.EventAccessor; + } + + public bool isExplicit() // is user defined explicit conversion operator + { + return methKind == MethodKindEnum.ExplicitConv; + } + + public bool isImplicit() // is user defined implicit conversion operator + { + return methKind == MethodKindEnum.ImplicitConv; + } + + public bool isInvoke() // Invoke method on a delegate - isn't user callable + { + return methKind == MethodKindEnum.Invoke; + } + + public void SetMethKind(MethodKindEnum mk) + { + methKind = mk; + } + + public MethodSymbol ConvNext() + { + Debug.Assert(isImplicit() || isExplicit()); + return m_convNext; + } + + public void SetConvNext(MethodSymbol conv) + { + Debug.Assert(isImplicit() || isExplicit()); + Debug.Assert(conv == null || conv.isImplicit() || conv.isExplicit()); + m_convNext = conv; + } + + public PropertySymbol getProperty() + { + Debug.Assert(isPropertyAccessor()); + return m_prop; + } + + public void SetProperty(PropertySymbol prop) + { + Debug.Assert(isPropertyAccessor()); + m_prop = prop; + } + + public EventSymbol getEvent() + { + Debug.Assert(isEventAccessor()); + return m_evt; + } + + public void SetEvent(EventSymbol evt) + { + Debug.Assert(isEventAccessor()); + m_evt = evt; + } + + public bool isConversionOperator() + { + return (isExplicit() || isImplicit()); + } + + public new bool isUserCallable() + { + return !isOperator && !isAnyAccessor(); + } + + public bool isAnyAccessor() + { + return isPropertyAccessor() || isEventAccessor(); + } + + /* + * returns true if this property is a set accessor + */ + public bool isSetAccessor() + { + if (!this.isPropertyAccessor()) + { + return false; + } + + PropertySymbol property = getProperty(); + + if (property == null) + { + Debug.Assert(false, "cannot find property for accessor"); + return false; + } + + return (this == property.methSet); + } + } + + // ---------------------------------------------------------------------------- + // + // InterfaceImplementationMethodSymbol + // + // an explicit method impl generated by the compiler + // used for CMOD_OPT interop + // ---------------------------------------------------------------------------- + + class InterfaceImplementationMethodSymbol : MethodSymbol + { + } + + class IteratorFinallyMethodSymbol : MethodSymbol + { + } +} diff --git a/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/Symbols/MiscellaneousSymbolFactory.cs b/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/Symbols/MiscellaneousSymbolFactory.cs new file mode 100644 index 000000000..93975db2d --- /dev/null +++ b/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/Symbols/MiscellaneousSymbolFactory.cs @@ -0,0 +1,53 @@ +// ==++== +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// ==--== + +using System.Diagnostics; +using Microsoft.CSharp.RuntimeBinder.Syntax; + +namespace Microsoft.CSharp.RuntimeBinder.Semantics +{ + using mdAssemblyRef = mdToken; + + internal class MiscSymFactory : SymFactoryBase + { + // Constructor. + + public MiscSymFactory(SYMTBL symtable) + : base(symtable, null) + { + } + + // Files + public InputFile CreateMDInfile(Name name, mdAssemblyRef idLocalAssembly) + { + InputFile sym = new InputFile(); + sym.isSource = false; + return sym; + } + + public Scope CreateScope(Scope parent) + { + Scope sym = newBasicSym(SYMKIND.SK_Scope, null, parent).AsScope(); + if (parent != null) + { + sym.nestingOrder = parent.nestingOrder + 1; + } + + return sym; + } + + public IndexerSymbol CreateIndexer(Name name, ParentSymbol parent, Name realName, AggregateDeclaration declaration) + { + IndexerSymbol sym = (IndexerSymbol)newBasicSym(SYMKIND.SK_IndexerSymbol, name, parent); + sym.setKind(SYMKIND.SK_PropertySymbol); + sym.isOperator = true; + sym.declaration = declaration; + + Debug.Assert(sym != null); + return sym; + } + } +} \ No newline at end of file diff --git a/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/Symbols/NamespaceOrAggregateSymbol.cs b/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/Symbols/NamespaceOrAggregateSymbol.cs new file mode 100644 index 000000000..42478f5f1 --- /dev/null +++ b/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/Symbols/NamespaceOrAggregateSymbol.cs @@ -0,0 +1,76 @@ +// ==++== +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// ==--== + +using System.Diagnostics; + +namespace Microsoft.CSharp.RuntimeBinder.Semantics +{ + // ---------------------------------------------------------------------------- + // + // NamespaceOrAggregateSymbol + // + // Base class for NamespaceSymbol and AggregateSymbol. Bags have DECLSYMs. + // Parent is another BAG. Children are other BAGs, members, type vars, etc. + // ---------------------------------------------------------------------------- + + abstract class NamespaceOrAggregateSymbol : ParentSymbol + { + Declaration declFirst; + Declaration declLast; + + public NamespaceOrAggregateSymbol() + { + } + + // ---------------------------------------------------------------------------- + // NamespaceOrAggregateSymbol + // ---------------------------------------------------------------------------- + + public Declaration DeclFirst() + { + return this.declFirst; + } + + // Compare to ParentSymbol::AddToChildList + public void AddDecl(Declaration decl) + { + Debug.Assert(decl != null); + Debug.Assert(this.IsNamespaceSymbol() || this.IsAggregateSymbol()); + Debug.Assert(decl.IsNamespaceDeclaration() || decl.IsAggregateDeclaration()); + Debug.Assert(!this.IsNamespaceSymbol() == !decl.IsNamespaceDeclaration()); + + // If parent is set it should be set to us! + Debug.Assert(decl.bag == null || decl.bag == this); + // There shouldn't be a declNext. + Debug.Assert(decl.declNext == null); + + if (this.declLast == null) + { + Debug.Assert(declFirst == null); + this.declFirst = this.declLast = decl; + } + else + { + this.declLast.declNext = decl; + this.declLast = decl; + +#if DEBUG + // Validate our chain. + Declaration pdecl; + for (pdecl = declFirst; pdecl != null && pdecl.declNext != null; pdecl = pdecl.declNext) + { } + Debug.Assert(pdecl == null || (pdecl == declLast && pdecl.declNext == null)); +#endif + } + + decl.declNext = null; + decl.bag = this; + + if (decl.IsNamespaceDeclaration()) + decl.AsNamespaceDeclaration().Bag().DeclAdded(decl.AsNamespaceDeclaration()); + } + } +} diff --git a/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/Symbols/NamespaceSymbol.cs b/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/Symbols/NamespaceSymbol.cs new file mode 100644 index 000000000..2d0e47d32 --- /dev/null +++ b/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/Symbols/NamespaceSymbol.cs @@ -0,0 +1,77 @@ +// ==++== +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// ==--== + +using System.Collections.Generic; +using System.Diagnostics; + +namespace Microsoft.CSharp.RuntimeBinder.Semantics +{ + // ---------------------------------------------------------------------------- + // + // NamespaceSymbol + // + // Namespaces, Namespace Declarations, and their members. + // + // + // The parent, child, nextChild relationships are overloaded for namespaces. + // The cause of all of this is that a namespace can be declared in multiple + // places. This would not be a problem except that the using clauses(which + // effect symbol lookup) are related to the namespace declaration not the + // namespace itself. The result is that each namespace needs lists of all of + // its declarations, and its members. Each namespace declaration needs a list + // the declarations and types declared within it. Each member of a namespace + // needs to access both the namespace it is contained in and the namespace + // declaration it is contained in. + // + // + // NamespaceSymbol - a symbol representing a name space. + // parent is the containing namespace. + // ---------------------------------------------------------------------------- + + class NamespaceSymbol : NamespaceOrAggregateSymbol + { + // Which assemblies and extern aliases contain this namespace. + private HashSet bsetFilter; + + public NamespaceSymbol() + { + bsetFilter = new HashSet(); + } + + public bool InAlias(KAID aid) + { + Debug.Assert(0 <= aid); + return bsetFilter.Contains(aid); + } + + public void DeclAdded(NamespaceDeclaration decl) + { + Debug.Assert(decl.Bag() == this); + //Debug.Assert(this.pdeclAttach == &decl.declNext); + + InputFile infile = decl.getInputFile(); + + if (infile.isSource) + { + bsetFilter.Add(KAID.kaidGlobal); + bsetFilter.Add(KAID.kaidThisAssembly); + } + else + { + infile.UnionAliasFilter(ref bsetFilter); + } + } + + public void AddAid(KAID aid) + { + if (aid == KAID.kaidThisAssembly) + { + bsetFilter.Add(KAID.kaidGlobal); + } + bsetFilter.Add(aid); + } + } +} \ No newline at end of file diff --git a/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/Symbols/ParentSymbol.cs b/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/Symbols/ParentSymbol.cs new file mode 100644 index 000000000..50ba8104f --- /dev/null +++ b/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/Symbols/ParentSymbol.cs @@ -0,0 +1,58 @@ +// ==++== +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// ==--== + +using System.Diagnostics; + +namespace Microsoft.CSharp.RuntimeBinder.Semantics +{ + // ---------------------------------------------------------------------------- + // + // ParentSymbol - a symbol that can contain other symbols as children. + // + // ---------------------------------------------------------------------------- + + class ParentSymbol : Symbol + { + public Symbol firstChild; // List of all children of this symbol + private Symbol lastChild; + + // This adds the sym to the child list but doesn't associate it + // in the symbol table. + + public void AddToChildList(Symbol sym) + { + Debug.Assert(sym != null /*&& this != null */); + + // If parent is set it should be set to us! + Debug.Assert(sym.parent == null || sym.parent == this); + // There shouldn't be a nextChild. + Debug.Assert(sym.nextChild == null); + + if (lastChild == null) + { + Debug.Assert(firstChild == null); + firstChild = lastChild = sym; + } + else + { + this.lastChild.nextChild = sym; + this.lastChild = sym; + sym.nextChild = null; + +#if DEBUG + // Validate our chain. + Symbol psym; + int count = 400; // Limited the length of chain that we'll run - so debug perf doesn't stink too badly. + for (psym = this.firstChild; psym != null && psym.nextChild != null && --count > 0; ) + psym = psym.nextChild; + Debug.Assert(this.lastChild == psym || count == 0); +#endif + } + + sym.parent = this; + } + } +} \ No newline at end of file diff --git a/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/Symbols/PropertySymbol.cs b/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/Symbols/PropertySymbol.cs new file mode 100644 index 000000000..cf22ee265 --- /dev/null +++ b/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/Symbols/PropertySymbol.cs @@ -0,0 +1,37 @@ +// ==++== +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// ==--== + +using System.Diagnostics; +using System.Reflection; + +namespace Microsoft.CSharp.RuntimeBinder.Semantics +{ + // ---------------------------------------------------------------------------- + // + // PropertySymbol + // + // PropertySymbol - a symbol representing a property. Parent is a struct, interface + // or class (aggregate). No children. + // ---------------------------------------------------------------------------- + + class PropertySymbol : MethodOrPropertySymbol + { + public MethodSymbol methGet; // Getter method (always has same parent) + public MethodSymbol methSet; // Setter method (always has same parent) + public PropertyInfo AssociatedPropertyInfo; + + public bool isIndexer() + { + return isOperator; + } + + public IndexerSymbol AsIndexerSymbol() + { + Debug.Assert(isIndexer()); + return (IndexerSymbol)this; + } + } +} \ No newline at end of file diff --git a/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/Symbols/Scope.cs b/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/Symbols/Scope.cs new file mode 100644 index 000000000..bb7478319 --- /dev/null +++ b/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/Symbols/Scope.cs @@ -0,0 +1,13 @@ +// ==++== +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// ==--== + +namespace Microsoft.CSharp.RuntimeBinder.Semantics +{ + class Scope : ParentSymbol + { + public uint nestingOrder; // the nesting order of this scopes. outermost == 0 + } +} diff --git a/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/Symbols/Symbol.cs b/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/Symbols/Symbol.cs new file mode 100644 index 000000000..361ff12ae --- /dev/null +++ b/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/Symbols/Symbol.cs @@ -0,0 +1,610 @@ +// ==++== +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// ==--== + +using System.Collections.Generic; +using System.Diagnostics; +using System.Reflection; +using Microsoft.CSharp.RuntimeBinder.Syntax; + +namespace Microsoft.CSharp.RuntimeBinder.Semantics +{ + // Alias ID's are indices into BitSets. + // 0 is reserved for the global namespace alias. + // 1 is reserved for this assembly. + // Start assigning at kaidStartAssigning. + enum KAID + { + kaidNil = -1, + + kaidGlobal = 0, + kaidErrorAssem, // NOTE: !CSEE only + kaidThisAssembly, + kaidUnresolved, + kaidStartAssigning, + + // Module id's are in their own range. + kaidMinModule = 0x10000000, + } + + /* + * Define the different access levels that symbols can have. + */ + enum ACCESS + { + ACC_UNKNOWN, // Not yet determined. + ACC_PRIVATE, + ACC_INTERNAL, + ACC_PROTECTED, + ACC_INTERNALPROTECTED, // internal OR protected + ACC_PUBLIC + } + + // The kinds of aggregates. + enum AggKindEnum + { + Unknown, + + Class, + Delegate, + Interface, + Struct, + Enum, + + Lim + } + + // The pseudo-methods uses for accessing arrays (except in + // the optimized 1-d case. + enum ARRAYMETHOD + { + ARRAYMETH_LOAD, + ARRAYMETH_LOADADDR, + ARRAYMETH_STORE, + ARRAYMETH_CTOR, + ARRAYMETH_GETAT, // Keep these in this order!!! + + ARRAYMETH_COUNT + }; + + ///////////////////////////////////////////////////////////////////////////////// + + // Special constraints. + enum SpecCons + { + None = 0x00, + + New = 0x01, + Ref = 0x02, + Val = 0x04 + } + + // ---------------------------------------------------------------------------- + // + // Symbol - the base symbol. + // + // ---------------------------------------------------------------------------- + + class Symbol + { + SYMKIND kind; // the symbol kind + bool isBogus; // can't be used in our language -- unsupported type(s) + bool checkedBogus; // Have we checked a method args/return for bogus types + ACCESS access; // access level + + // If this is true, then we had an error the first time so do not give an error the second time. + + public Name name; // name of the symbol + public ParentSymbol parent; // parent of the symbol + public Symbol nextChild; // next child of this parent + public Symbol nextSameName; // next child of this parent with same name. + + + public ACCESS GetAccess() + { + Debug.Assert(access != ACCESS.ACC_UNKNOWN); + return access; + } + + public void SetAccess(ACCESS access) + { + this.access = access; + } + + public SYMKIND getKind() + { + return this.kind; + } + + public void setKind(SYMKIND kind) + { + this.kind = kind; + } + + public symbmask_t mask() + { + return (symbmask_t)(1 << (int)kind); + } + + public bool checkBogus() + { + Debug.Assert(this.checkedBogus); + return this.isBogus; + } // if this Debug.Assert fires then call COMPILER_BASE::CheckBogus() instead + + public bool getBogus() + { + return this.isBogus; + } + + public bool hasBogus() + { + return this.checkedBogus; + } + + public void setBogus(bool isBogus) + { + this.isBogus = isBogus; + this.checkedBogus = true; + } + + public void initBogus() + { + this.isBogus = false; + this.checkedBogus = false; + } + + public bool computeCurrentBogusState() + { + if (this.hasBogus()) + { + return this.checkBogus(); + } + + bool fBogus = false; + + switch (this.getKind()) + { + case SYMKIND.SK_PropertySymbol: + case SYMKIND.SK_MethodSymbol: + { + MethodOrPropertySymbol meth = this.AsMethodOrPropertySymbol(); + + if (meth.RetType != null) + { + fBogus = meth.RetType.computeCurrentBogusState(); + } + if (meth.Params != null) + { + for (int i = 0; !fBogus && i < meth.Params.Size; i++) + { + fBogus |= meth.Params.Item(i).computeCurrentBogusState(); + } + } + } + break; + + /* + case SYMKIND.SK_ParameterModifierType: + case SYMKIND.SK_OptionalModifierType: + case SYMKIND.SK_PointerType: + case SYMKIND.SK_ArrayType: + case SYMKIND.SK_NullableType: + case SYMKIND.SK_PinnedType: + if (this.AsType().GetBaseOrParameterOrElementType() != null) + { + fBogus = this.AsType().GetBaseOrParameterOrElementType().computeCurrentBogusState(); + } + break; + */ + + case SYMKIND.SK_EventSymbol: + if (this.AsEventSymbol().type != null) + { + fBogus = this.AsEventSymbol().type.computeCurrentBogusState(); + } + break; + + case SYMKIND.SK_FieldSymbol: + if (this.AsFieldSymbol().GetType() != null) + { + fBogus = this.AsFieldSymbol().GetType().computeCurrentBogusState(); + } + break; + + /* + case SYMKIND.SK_ErrorType: + this.setBogus(false); + break; + + case SYMKIND.SK_AggregateType: + fBogus = this.AsAggregateType().getAggregate().computeCurrentBogusState(); + for (int i = 0; !fBogus && i < this.AsAggregateType().GetTypeArgsAll().size; i++) + { + fBogus |= this.AsAggregateType().GetTypeArgsAll().Item(i).computeCurrentBogusState(); + } + break; + */ + + case SYMKIND.SK_TypeParameterSymbol: + /* + case SYMKIND.SK_TypeParameterType: + case SYMKIND.SK_VoidType: + case SYMKIND.SK_NullType: + case SYMKIND.SK_OpenTypePlaceholderType: + case SYMKIND.SK_ArgumentListType: + case SYMKIND.SK_NaturalIntegerType: + */ + case SYMKIND.SK_LocalVariableSymbol: + this.setBogus(false); + break; + + case SYMKIND.SK_AggregateSymbol: + fBogus = this.hasBogus() && this.checkBogus(); + break; + + case SYMKIND.SK_Scope: + case SYMKIND.SK_LambdaScope: + case SYMKIND.SK_NamespaceSymbol: + case SYMKIND.SK_NamespaceDeclaration: + default: + Debug.Assert(false, "CheckBogus with invalid Symbol kind"); + this.setBogus(false); + break; + } + + if (fBogus) + { + // Only set this if at least 1 declared thing is bogus + this.setBogus(fBogus); + } + + return this.hasBogus() && this.checkBogus(); + } + + public bool IsNamespaceSymbol() { return this.kind == SYMKIND.SK_NamespaceSymbol; } + public bool IsNamespaceDeclaration() { return this.kind == SYMKIND.SK_NamespaceDeclaration; } + public bool IsAggregateSymbol() { return this.kind == SYMKIND.SK_AggregateSymbol; } + public bool IsAggregateDeclaration() { return this.kind == SYMKIND.SK_AggregateDeclaration; } + public bool IsFieldSymbol() { return this.kind == SYMKIND.SK_FieldSymbol; } + public bool IsLocalVariableSymbol() { return this.kind == SYMKIND.SK_LocalVariableSymbol; } + public bool IsMethodSymbol() { return this.kind == SYMKIND.SK_MethodSymbol; } + public bool IsPropertySymbol() { return this.kind == SYMKIND.SK_PropertySymbol; } + public bool IsTypeParameterSymbol() { return this.kind == SYMKIND.SK_TypeParameterSymbol; } + public bool IsEventSymbol() { return this.kind == SYMKIND.SK_EventSymbol; } + + public bool IsMethodOrPropertySymbol() + { + return this.IsMethodSymbol() || this.IsPropertySymbol(); + } + + public bool IsFMETHSYM() + { + return this.IsMethodSymbol(); + } + + public CType getType() + { + CType type = null; + if (IsMethodOrPropertySymbol()) + { + type = this.AsMethodOrPropertySymbol().RetType; + } + else if (IsFieldSymbol()) + { + type = this.AsFieldSymbol().GetType(); + } + else if (IsEventSymbol()) + { + type = this.AsEventSymbol().type; + } + return type; + } + + public bool isStatic + { + get + { + bool fStatic = false; + if (IsFieldSymbol()) + { + fStatic = this.AsFieldSymbol().isStatic; + } + else if (IsEventSymbol()) + { + fStatic = this.AsEventSymbol().isStatic; + } + else if (IsMethodOrPropertySymbol()) + { + fStatic = this.AsMethodOrPropertySymbol().isStatic; + } + else if (IsAggregateSymbol()) + { + fStatic = true; + } + return fStatic; + } + } + + public Assembly GetAssembly() + { + switch (this.kind) + { + case SYMKIND.SK_MethodSymbol: + case SYMKIND.SK_PropertySymbol: + case SYMKIND.SK_FieldSymbol: + case SYMKIND.SK_EventSymbol: + case SYMKIND.SK_TypeParameterSymbol: + return parent.AsAggregateSymbol().AssociatedAssembly; + + case SYMKIND.SK_AggregateDeclaration: + return this.AsAggregateDeclaration().GetAssembly(); + case SYMKIND.SK_AggregateSymbol: + return this.AsAggregateSymbol().AssociatedAssembly; + case SYMKIND.SK_NamespaceDeclaration: + case SYMKIND.SK_NamespaceSymbol: + case SYMKIND.SK_AssemblyQualifiedNamespaceSymbol: + default: + // Should never call this with any other kind. + Debug.Assert(false, "GetAssemblyID called on bad sym kind"); + return null; + } + } + + /* + * returns the assembly id for the declaration of this symbol + */ + public bool InternalsVisibleTo(Assembly assembly) + { + switch (this.kind) + { + case SYMKIND.SK_MethodSymbol: + case SYMKIND.SK_PropertySymbol: + case SYMKIND.SK_FieldSymbol: + case SYMKIND.SK_EventSymbol: + case SYMKIND.SK_TypeParameterSymbol: + return parent.AsAggregateSymbol().InternalsVisibleTo(assembly); + + case SYMKIND.SK_AggregateDeclaration: + return this.AsAggregateDeclaration().Agg().InternalsVisibleTo(assembly); + case SYMKIND.SK_AggregateSymbol: + return this.AsAggregateSymbol().InternalsVisibleTo(assembly); + case SYMKIND.SK_NamespaceDeclaration: + case SYMKIND.SK_ExternalAliasDefinitionSymbol: + case SYMKIND.SK_NamespaceSymbol: + case SYMKIND.SK_AssemblyQualifiedNamespaceSymbol: + default: + // Should never call this with any other kind. + Debug.Assert(false, "InternalsVisibleTo called on bad sym kind"); + return false; + } + } + + public bool SameAssemOrFriend(Symbol sym) + { + Assembly assem = GetAssembly(); + return assem == sym.GetAssembly() || sym.InternalsVisibleTo(assem); + } + + /* + * returns the inputfile where a symbol was declared. + * + * returns null for namespaces because they can be declared + * in multiple files. + */ + public InputFile getInputFile() + { + switch (kind) + { + case SYMKIND.SK_NamespaceSymbol: + case SYMKIND.SK_AssemblyQualifiedNamespaceSymbol: + // namespaces don't have input files + // call with a NamespaceDeclaration instead + Debug.Assert(false); + return null; + + case SYMKIND.SK_NamespaceDeclaration: + return null; + + case SYMKIND.SK_AggregateSymbol: + { +#if !CSEE + AggregateSymbol AggregateSymbol = this.AsAggregateSymbol(); + if (!AggregateSymbol.IsSource()) + return AggregateSymbol.DeclOnly().getInputFile(); + + // Because an AggregateSymbol that isn't metadata can be defined across multiple + // files, getInputFile isn't a reasonable operation. + Debug.Assert(false); + return null; +#endif + } + + /* + case SK_AggregateType: + return ((Symbol)this.AsAggregateType().getAggregate()).getInputFile(); + */ + + case SYMKIND.SK_AggregateDeclaration: + return this.AsAggregateDeclaration().getInputFile(); + + /* + case SK_TypeParameterType: + if (this.AsTypeParameterType().GetOwningSymbol().IsAggregateSymbol()) + { + ASSERT(0); + return null; + } + else + { + ASSERT(this.AsTypeParameterType().GetOwningSymbol().IsMethodSymbol()); + return AsTypeParameterType().GetOwningSymbol().AsMethodSymbol().getInputFile(); + } + */ + case SYMKIND.SK_TypeParameterSymbol: + if (this.parent.IsAggregateSymbol()) + { + // Because an AggregateSymbol that isn't metadata can be defined across multiple + // files, getInputFile isn't a reasonable operation. + Debug.Assert(false); + return null; + } + else if (this.parent.IsMethodSymbol()) + return this.parent.AsMethodSymbol().getInputFile(); + Debug.Assert(false); + break; + + case SYMKIND.SK_FieldSymbol: + return this.AsFieldSymbol().containingDeclaration().getInputFile(); + case SYMKIND.SK_MethodSymbol: + return this.AsMethodSymbol().containingDeclaration().getInputFile(); + case SYMKIND.SK_PropertySymbol: + return this.AsPropertySymbol().containingDeclaration().getInputFile(); + case SYMKIND.SK_EventSymbol: + return this.AsEventSymbol().containingDeclaration().getInputFile(); + + /* + case SK_PointerType: + case SK_NullableType: + case SK_ArrayType: + case SK_PinnedType: + case SK_ParameterModifierType: + case SK_OptionalModifierType: + return AsType().GetBaseOrParameterOrElementType().getInputFile(); + */ + + case SYMKIND.SK_GlobalAttributeDeclaration: + return parent.getInputFile(); + + /* + case SK_NullType: + case SK_VoidType: + return null; + */ + + default: + Debug.Assert(false); + break; + } + + return null; + } + + + /* Returns if the symbol is virtual. */ + public bool IsVirtual() + { + switch (kind) + { + case SYMKIND.SK_MethodSymbol: + return this.AsMethodSymbol().isVirtual; + case SYMKIND.SK_EventSymbol: + return this.AsEventSymbol().methAdd != null && this.AsEventSymbol().methAdd.isVirtual; + case SYMKIND.SK_PropertySymbol: + return (this.AsPropertySymbol().methGet != null && this.AsPropertySymbol().methGet.isVirtual) || + (this.AsPropertySymbol().methSet != null && this.AsPropertySymbol().methSet.isVirtual); + default: + return false; + } + } + + public bool IsOverride() + { + switch (kind) + { + case SYMKIND.SK_MethodSymbol: + case SYMKIND.SK_PropertySymbol: + return this.AsMethodOrPropertySymbol().isOverride; + case SYMKIND.SK_EventSymbol: + return this.AsEventSymbol().isOverride; + default: + return false; + } + } + + public bool IsHideByName() + { + switch (kind) + { + case SYMKIND.SK_MethodSymbol: + case SYMKIND.SK_PropertySymbol: + return this.AsMethodOrPropertySymbol().isHideByName; + case SYMKIND.SK_EventSymbol: + return this.AsEventSymbol().methAdd != null && this.AsEventSymbol().methAdd.isHideByName; + default: + return true; + } + } + + // Returns the virtual that this sym overrides (if IsOverride() is true), null otherwise. + public Symbol SymBaseVirtual() + { + switch (kind) + { + case SYMKIND.SK_MethodSymbol: + case SYMKIND.SK_PropertySymbol: + return this.AsMethodOrPropertySymbol().swtSlot.Sym; + case SYMKIND.SK_EventSymbol: + default: + return null; + } + } + + /* + * returns true if this symbol is a normal symbol visible to the user + */ + public bool isUserCallable() + { + switch (kind) + { + case SYMKIND.SK_MethodSymbol: + return this.AsMethodSymbol().isUserCallable(); + default: + break; + } + + return true; + } + } + + /* + * We have member functions here to do casts that, in DEBUG, check the + * symbol kind to make sure it is right. For example, the casting method + * for METHODSYM is called "asMETHODSYM". In retail builds, these + * methods optimize away to nothing. + */ + + internal static class SymbolExtensions + { + public static IEnumerable Children(this ParentSymbol symbol) + { + if (symbol == null) + yield break; + Symbol current = symbol.firstChild; + while (current != null) + { + yield return current; + current = current.nextChild; + } + } + + internal static MethodSymbol AsFMETHSYM(this Symbol symbol) { return symbol as MethodSymbol; } + + internal static NamespaceOrAggregateSymbol AsNamespaceOrAggregateSymbol(this Symbol symbol) { return symbol as NamespaceOrAggregateSymbol; } + internal static NamespaceSymbol AsNamespaceSymbol(this Symbol symbol) { return symbol as NamespaceSymbol; } + internal static AssemblyQualifiedNamespaceSymbol AsAssemblyQualifiedNamespaceSymbol(this Symbol symbol) { return symbol as AssemblyQualifiedNamespaceSymbol; } + internal static NamespaceDeclaration AsNamespaceDeclaration(this Symbol symbol) { return symbol as NamespaceDeclaration; } + internal static AggregateSymbol AsAggregateSymbol(this Symbol symbol) { return symbol as AggregateSymbol; } + internal static AggregateDeclaration AsAggregateDeclaration(this Symbol symbol) { return symbol as AggregateDeclaration; } + internal static FieldSymbol AsFieldSymbol(this Symbol symbol) { return symbol as FieldSymbol; } + internal static LocalVariableSymbol AsLocalVariableSymbol(this Symbol symbol) { return symbol as LocalVariableSymbol; } + internal static MethodSymbol AsMethodSymbol(this Symbol symbol) { return symbol as MethodSymbol; } + internal static PropertySymbol AsPropertySymbol(this Symbol symbol) { return symbol as PropertySymbol; } + internal static MethodOrPropertySymbol AsMethodOrPropertySymbol(this Symbol symbol) { return symbol as MethodOrPropertySymbol; } + internal static Scope AsScope(this Symbol symbol) { return symbol as Scope; } + internal static TypeParameterSymbol AsTypeParameterSymbol(this Symbol symbol) { return symbol as TypeParameterSymbol; } + internal static EventSymbol AsEventSymbol(this Symbol symbol) { return symbol as EventSymbol; } + } +} diff --git a/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/Symbols/SymbolFactory.cs b/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/Symbols/SymbolFactory.cs new file mode 100644 index 000000000..52292a033 --- /dev/null +++ b/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/Symbols/SymbolFactory.cs @@ -0,0 +1,177 @@ +// ==++== +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// ==--== + +using System.Diagnostics; +using Microsoft.CSharp.RuntimeBinder.Syntax; + +namespace Microsoft.CSharp.RuntimeBinder.Semantics +{ + internal class SymFactory : SymFactoryBase + { + public SymFactory( + SYMTBL symtable, + NameManager namemgr) : + base(symtable, namemgr) + { + } + + // Namespace + public NamespaceSymbol CreateNamespace(Name name, NamespaceSymbol parent) + { + NamespaceSymbol sym = newBasicSym(SYMKIND.SK_NamespaceSymbol, name, parent).AsNamespaceSymbol(); + sym.SetAccess(ACCESS.ACC_PUBLIC); + + return (sym); + } + + public AssemblyQualifiedNamespaceSymbol CreateNamespaceAid(Name name, ParentSymbol parent, KAID assemblyID) + { + Debug.Assert(name != null); + + AssemblyQualifiedNamespaceSymbol sym = newBasicSym(SYMKIND.SK_AssemblyQualifiedNamespaceSymbol, name, parent).AsAssemblyQualifiedNamespaceSymbol(); + + Debug.Assert(sym != null); + return sym; + } + + ///////////////////////////////////////////////////////////////////////////////// + public AggregateSymbol CreateAggregate(Name name, NamespaceOrAggregateSymbol parent, InputFile infile, TypeManager typeManager) + { + if (name == null || parent == null || infile == null || typeManager == null) + { + throw Error.InternalCompilerError(); + } + + AggregateSymbol sym = null; + if (infile.GetAssemblyID() == KAID.kaidUnresolved) + { + // Unresolved aggs need extra storage. + sym = CreateUnresolvedAggregate(name, parent, typeManager); + } + else + { + sym = newBasicSym(SYMKIND.SK_AggregateSymbol, name, parent).AsAggregateSymbol(); + sym.name = name; + sym.SetTypeManager(typeManager); + sym.SetSealed(false); + sym.SetAccess(ACCESS.ACC_UNKNOWN); + sym.initBogus(); + sym.SetIfaces(null); + sym.SetIfacesAll(null); + sym.SetTypeVars(null); + } + + sym.InitFromInfile(infile); + return sym; + } + + public AggregateDeclaration CreateAggregateDecl(AggregateSymbol agg, Declaration declOuter) + { + Debug.Assert(agg != null); + //Debug.Assert(declOuter == null || declOuter.Bag() == agg.Parent); + + // DECLSYMs are not parented like named symbols. + AggregateDeclaration sym = newBasicSym(SYMKIND.SK_AggregateDeclaration, agg.name, null).AsAggregateDeclaration(); + + if (declOuter != null) + { + declOuter.AddToChildList(sym); + } + agg.AddDecl(sym); + + Debug.Assert(sym != null); + return (sym); + } + + public AggregateSymbol CreateUnresolvedAggregate(Name name, ParentSymbol parent, TypeManager typeManager) + { + Debug.Assert(name != null); + + Symbol sym = newBasicSym(SYMKIND.SK_UnresolvedAggregateSymbol, name, parent); + AggregateSymbol AggregateSymbol = null; + + // Unresolved Aggs need extra storage, but are still considered Aggs. + + sym.setKind(SYMKIND.SK_AggregateSymbol); + AggregateSymbol = sym.AsAggregateSymbol(); + AggregateSymbol.SetTypeManager(typeManager); + + Debug.Assert(AggregateSymbol != null); + return (AggregateSymbol); + } + + // Members of aggs + public FieldSymbol CreateMemberVar(Name name, ParentSymbol parent, AggregateDeclaration declaration, int iIteratorLocal) + { + Debug.Assert(name != null); + + FieldSymbol sym = newBasicSym(SYMKIND.SK_FieldSymbol, name, parent).AsFieldSymbol(); + sym.declaration = declaration; + + Debug.Assert(sym != null); + return (sym); + } + + public LocalVariableSymbol CreateLocalVar(Name name, ParentSymbol parent, CType type) + { + LocalVariableSymbol sym = newBasicSym(SYMKIND.SK_LocalVariableSymbol, name, parent).AsLocalVariableSymbol(); + sym.SetType(type); + sym.SetAccess(ACCESS.ACC_UNKNOWN); // required for Symbol::hasExternalAccess which is used by refactoring + sym.wrap = null; + + return sym; + } + + public MethodSymbol CreateMethod(Name name, ParentSymbol parent, AggregateDeclaration declaration) + { + MethodSymbol sym = newBasicSym(SYMKIND.SK_MethodSymbol, name, parent).AsMethodSymbol(); + sym.declaration = declaration; + + return sym; + } + + public PropertySymbol CreateProperty(Name name, ParentSymbol parent, AggregateDeclaration declaration) + { + PropertySymbol sym = newBasicSym(SYMKIND.SK_PropertySymbol, name, parent).AsPropertySymbol(); + sym.declaration = declaration; + Debug.Assert(sym != null); + return (sym); + } + + public EventSymbol CreateEvent(Name name, ParentSymbol parent, AggregateDeclaration declaration) + { + EventSymbol sym = newBasicSym(SYMKIND.SK_EventSymbol, name, parent).AsEventSymbol(); + sym.declaration = declaration; + + Debug.Assert(sym != null); + return (sym); + } + + public TypeParameterSymbol CreateMethodTypeParameter(Name pName, MethodSymbol pParent, int index, int indexTotal) + { + TypeParameterSymbol pResult = newBasicSym(SYMKIND.SK_TypeParameterSymbol, pName, pParent).AsTypeParameterSymbol(); + pResult.SetIndexInOwnParameters(index); + pResult.SetIndexInTotalParameters(indexTotal); + + pResult.SetIsMethodTypeParameter(true); + pResult.SetAccess(ACCESS.ACC_PRIVATE); // Always private - not accessible anywhere except their own type. + + return pResult; + } + + public TypeParameterSymbol CreateClassTypeParameter(Name pName, AggregateSymbol pParent, int index, int indexTotal) + { + TypeParameterSymbol pResult = newBasicSym(SYMKIND.SK_TypeParameterSymbol, pName, pParent).AsTypeParameterSymbol(); + pResult.SetIndexInOwnParameters(index); + pResult.SetIndexInTotalParameters(indexTotal); + + pResult.SetIsMethodTypeParameter(false); + pResult.SetAccess(ACCESS.ACC_PRIVATE); // Always private - not accessible anywhere except their own type. + + return pResult; + } + } +} diff --git a/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/Symbols/SymbolFactoryBase.cs b/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/Symbols/SymbolFactoryBase.cs new file mode 100644 index 000000000..1dd498a04 --- /dev/null +++ b/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/Symbols/SymbolFactoryBase.cs @@ -0,0 +1,146 @@ +// ==++== +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// ==--== + +using Microsoft.CSharp.RuntimeBinder.Syntax; + +namespace Microsoft.CSharp.RuntimeBinder.Semantics +{ + // This class is the base class for the different symbol factories: the + // SymFactory, TypeFactory and MiscSymFactory. This provides a common + // way of creating syms that all three classes use. + + class SymFactoryBase + { + // Members. + protected SYMTBL m_pSymTable; + protected Name m_pMissingNameNode; + protected Name m_pMissingNameSym; + + protected Symbol newBasicSym( + SYMKIND kind, + Name name, + ParentSymbol parent) + { + // The parser creates names with PN_MISSING when attempting to recover from errors + // To prevent spurious errors, we create SYMs with a different name (PN_MISSINGSYM) + // so that they are never found when doing lookup. + if (name == m_pMissingNameNode) + { + name = m_pMissingNameSym; + } + + Symbol sym; + switch (kind) + { + case SYMKIND.SK_NamespaceSymbol: + sym = new NamespaceSymbol(); + sym.name = name; + break; + case SYMKIND.SK_NamespaceDeclaration: + sym = new NamespaceDeclaration(); + sym.name = name; + break; + case SYMKIND.SK_AssemblyQualifiedNamespaceSymbol: + sym = new AssemblyQualifiedNamespaceSymbol(); + sym.name = name; + break; + case SYMKIND.SK_AggregateSymbol: + sym = new AggregateSymbol(); + sym.name = name; + break; + case SYMKIND.SK_AggregateDeclaration: + sym = new AggregateDeclaration(); + sym.name = name; + break; + case SYMKIND.SK_TypeParameterSymbol: + sym = new TypeParameterSymbol(); + sym.name = name; + break; + case SYMKIND.SK_FieldSymbol: + sym = new FieldSymbol(); + sym.name = name; + break; + case SYMKIND.SK_LocalVariableSymbol: + sym = new LocalVariableSymbol(); + sym.name = name; + break; + case SYMKIND.SK_MethodSymbol: + sym = new MethodSymbol(); + sym.name = name; + break; + case SYMKIND.SK_PropertySymbol: + sym = new PropertySymbol(); + sym.name = name; + break; + case SYMKIND.SK_EventSymbol: + sym = new EventSymbol(); + sym.name = name; + break; + case SYMKIND.SK_TransparentIdentifierMemberSymbol: + sym = new TransparentIdentifierMemberSymbol(); + sym.name = name; + break; + case SYMKIND.SK_Scope: + sym = new Scope(); + sym.name = name; + break; + case SYMKIND.SK_LabelSymbol: + sym = new LabelSymbol(); + sym.name = name; + break; + case SYMKIND.SK_GlobalAttributeDeclaration: + sym = new GlobalAttributeDeclaration(); + sym.name = name; + break; + case SYMKIND.SK_UnresolvedAggregateSymbol: + sym = new UnresolvedAggregateSymbol(); + sym.name = name; + break; + case SYMKIND.SK_InterfaceImplementationMethodSymbol: + sym = new InterfaceImplementationMethodSymbol(); + sym.name = name; + break; + case SYMKIND.SK_IndexerSymbol: + sym = new IndexerSymbol(); + sym.name = name; + break; + case SYMKIND.SK_ParentSymbol: + sym = new ParentSymbol(); + sym.name = name; + break; + case SYMKIND.SK_IteratorFinallyMethodSymbol: + sym = new IteratorFinallyMethodSymbol(); + sym.name = name; + break; + default: + throw Error.InternalCompilerError(); + } + + sym.setKind(kind); + + if (parent != null) + { + // Set the parent element of the child symbol. + parent.AddToChildList(sym); + m_pSymTable.InsertChild(parent, sym); + } + + return (sym); + } + + // This class should never be created on its own. + protected SymFactoryBase(SYMTBL symtable, NameManager namemgr) + { + m_pSymTable = symtable; + + if (namemgr != null) + { + m_pMissingNameNode = namemgr.GetPredefName(PredefinedName.PN_MISSING); + m_pMissingNameSym = namemgr.GetPredefName(PredefinedName.PN_MISSINGSYM); + } + } + } +} diff --git a/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/Symbols/SymbolKind.cs b/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/Symbols/SymbolKind.cs new file mode 100644 index 000000000..467db0332 --- /dev/null +++ b/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/Symbols/SymbolKind.cs @@ -0,0 +1,49 @@ +// ==++== +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// ==--== + +namespace Microsoft.CSharp.RuntimeBinder.Semantics +{ + internal enum SYMKIND + { + SK_NamespaceSymbol, + SK_NamespaceDeclaration, + SK_AssemblyQualifiedNamespaceSymbol, + SK_AggregateSymbol, + SK_AggregateDeclaration, + SK_TypeParameterSymbol, + SK_FieldSymbol, + SK_LocalVariableSymbol, + SK_MethodSymbol, + SK_PropertySymbol, + SK_EventSymbol, + SK_TransparentIdentifierMemberSymbol, + SK_AliasSymbol, + SK_ExternalAliasDefinitionSymbol, + SK_Scope, + SK_CachedNameSymbol, + SK_LabelSymbol, + SK_GlobalAttributeDeclaration, + SK_LambdaScope, + SK_UnresolvedAggregateSymbol, + SK_InterfaceImplementationMethodSymbol, + SK_IndexerSymbol, + SK_ParentSymbol, + SK_IteratorFinallyMethodSymbol, + SK_LIM + } + + // The kinds of Synthesized aggregates + enum SynthAggKind + { + NotSynthesized, + + AnonymousMethodDisplayClass, + IteratorClass, + FixedBufferStruct, + + Lim + } +} diff --git a/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/Symbols/SymbolLoader.cs b/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/Symbols/SymbolLoader.cs new file mode 100644 index 000000000..cebbeaa04 --- /dev/null +++ b/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/Symbols/SymbolLoader.cs @@ -0,0 +1,861 @@ +// ==++== +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// ==--== + +using System.Collections.Generic; +using System.Diagnostics; +using Microsoft.CSharp.RuntimeBinder; +using Microsoft.CSharp.RuntimeBinder.Errors; +using Microsoft.CSharp.RuntimeBinder.Syntax; + +namespace Microsoft.CSharp.RuntimeBinder.Semantics +{ + internal class SymbolLoader + { + private NameManager m_nameManager; + + public PredefinedMembers PredefinedMembers { get; private set; } + public GlobalSymbolContext GlobalSymbolContext { get; private set; } + public ErrorHandling ErrorContext { get; private set; } + public SymbolTable RuntimeBinderSymbolTable { get; private set; } + + public SymbolLoader( + GlobalSymbolContext globalSymbols, + UserStringBuilder userStringBuilder, + ErrorHandling errorContext + ) + { + m_nameManager = globalSymbols.GetNameManager(); + PredefinedMembers = new PredefinedMembers(this); + ErrorContext = errorContext; + GlobalSymbolContext = globalSymbols; + Debug.Assert(GlobalSymbolContext != null); + } + + public ErrorHandling GetErrorContext() + { + return ErrorContext; + } + + public GlobalSymbolContext GetGlobalSymbolContext() + { + return GlobalSymbolContext; + } + + public MethodSymbol LookupInvokeMeth(AggregateSymbol pAggDel) + { + Debug.Assert(pAggDel.AggKind() == AggKindEnum.Delegate); + for (Symbol pSym = this.LookupAggMember(GetNameManager().GetPredefName(PredefinedName.PN_INVOKE), pAggDel, symbmask_t.MASK_ALL); + pSym != null; + pSym = this.LookupNextSym(pSym, pAggDel, symbmask_t.MASK_ALL)) + { + if (pSym.IsMethodSymbol() && pSym.AsMethodSymbol().isInvoke()) + { + return pSym.AsMethodSymbol(); + } + } + return null; + } + + public NameManager GetNameManager() + { + return m_nameManager; + } + + public PredefinedTypes getPredefTypes() + { + return GlobalSymbolContext.GetPredefTypes(); + } + + public TypeManager GetTypeManager() + { + return this.TypeManager; + } + + public TypeManager TypeManager + { + get { return this.GlobalSymbolContext.TypeManager; } + } + + public PredefinedMembers getPredefinedMembers() + { + return this.PredefinedMembers; + } + + public BSYMMGR getBSymmgr() + { + return this.GlobalSymbolContext.GetGlobalSymbols(); + } + + public SymFactory GetGlobalSymbolFactory() + { + return this.GlobalSymbolContext.GetGlobalSymbolFactory(); + } + + public MiscSymFactory GetGlobalMiscSymFactory() + { + return this.GlobalSymbolContext.GetGlobalMiscSymFactory(); + } + + public AggregateType GetReqPredefType(PredefinedType pt) + { + return GetReqPredefType(pt, true); + } + + public AggregateType GetReqPredefType(PredefinedType pt, bool fEnsureState) + { + AggregateSymbol agg = GetTypeManager().GetReqPredefAgg(pt); + if (agg == null) + { + Debug.Assert(false, "Required predef type missing"); + return null; + } + AggregateType ats = agg.getThisType(); + return ats; + } + + public AggregateSymbol GetOptPredefAgg(PredefinedType pt) + { + return GetOptPredefAgg(pt, true); + } + + public AggregateSymbol GetOptPredefAgg(PredefinedType pt, bool fEnsureState) + { + AggregateSymbol agg = GetTypeManager().GetOptPredefAgg(pt); + return agg; + } + + public AggregateType GetOptPredefType(PredefinedType pt) + { + return GetOptPredefType(pt, true); + } + + public AggregateType GetOptPredefType(PredefinedType pt, bool fEnsureState) + { + AggregateSymbol agg = GetTypeManager().GetOptPredefAgg(pt); + if (agg == null) + return null; + AggregateType ats = agg.getThisType(); + return ats; + } + + public AggregateType GetOptPredefTypeErr(PredefinedType pt, bool fEnsureState) + { + AggregateSymbol agg = GetTypeManager().GetOptPredefAgg(pt); + if (agg == null) + { + getPredefTypes().ReportMissingPredefTypeError(ErrorContext, pt); + return null; + } + + AggregateType ats = agg.getThisType(); + return ats; + } + + public Symbol LookupAggMember(Name name, AggregateSymbol agg, symbmask_t mask) + { + return getBSymmgr().LookupAggMember(name, agg, mask); + } + + public Symbol LookupNextSym(Symbol sym, ParentSymbol parent, symbmask_t kindmask) + { + return BSYMMGR.LookupNextSym(sym, parent, kindmask); + } + + public bool isManagedType(CType type) + { + return type.computeManagedType(this); + } + + // UNDONE: It would be nice to make this a virtual method on typeSym. + public AggregateType GetAggTypeSym(CType typeSym) + { + Debug.Assert(typeSym != null); + Debug.Assert(typeSym.IsAggregateType() || + typeSym.IsTypeParameterType() || + typeSym.IsArrayType() || + typeSym.IsNullableType()); + + switch (typeSym.GetTypeKind()) + { + case TypeKind.TK_AggregateType: + return typeSym.AsAggregateType(); + case TypeKind.TK_ArrayType: + return GetReqPredefType(PredefinedType.PT_ARRAY); + case TypeKind.TK_TypeParameterType: + return typeSym.AsTypeParameterType().GetEffectiveBaseClass(); + case TypeKind.TK_NullableType: + return typeSym.AsNullableType().GetAts(ErrorContext); + } + Debug.Assert(false, "Bad typeSym!"); + return null; + } + + public bool IsBaseInterface(CType pDerived, CType pBase) + { + Debug.Assert(pDerived != null); + Debug.Assert(pBase != null); + if (!pBase.isInterfaceType()) + { + return false; + } + if (!pDerived.IsAggregateType()) + { + return false; + } + AggregateType atsDer = pDerived.AsAggregateType(); + while (atsDer != null) + { + TypeArray ifacesAll = atsDer.GetIfacesAll(); + for (int i = 0; i < ifacesAll.Size; i++) + { + if (AreTypesEqualForConversion(ifacesAll.Item(i), pBase)) + { + return true; + } + } + atsDer = atsDer.GetBaseClass(); + } + return false; + } + + public bool IsBaseClassOfClass(CType pDerived, CType pBase) + { + Debug.Assert(pDerived != null); + Debug.Assert(pBase != null); + + // This checks to see whether derived is a class, and if so, + // if base is a base class of derived. + if (!pDerived.isClassType()) + { + return false; + } + return IsBaseClass(pDerived, pBase); + } + + public bool IsBaseClass(CType pDerived, CType pBase) + { + Debug.Assert(pDerived != null); + Debug.Assert(pBase != null); + // A base class has got to be a class. The derived type might be a struct. + + if (!pBase.isClassType()) + { + return false; + } + if (pDerived.IsNullableType()) + { + pDerived = pDerived.AsNullableType().GetAts(ErrorContext); + if (pDerived == null) + { + return false; + } + } + + if (!pDerived.IsAggregateType()) + { + return false; + } + + AggregateType atsDer = pDerived.AsAggregateType(); + AggregateType atsBase = pBase.AsAggregateType(); + AggregateType atsCur = atsDer.GetBaseClass(); + while (atsCur != null) + { + if (atsCur == atsBase) + { + return true; + } + atsCur = atsCur.GetBaseClass(); + } + return false; + } + + private bool HasCovariantArrayConversion(ArrayType pSource, ArrayType pDest) + { + Debug.Assert(pSource != null); + Debug.Assert(pDest != null); + // * S and T differ only in element type. In other words, S and T have the same number of dimensions. + // * Both SE and TE are reference types. + // * An implicit reference conversion exists from SE to TE. + return (pSource.rank == pDest.rank) && + HasImplicitReferenceConversion(pSource.GetElementType(), pDest.GetElementType()); + } + + public bool HasIdentityOrImplicitReferenceConversion(CType pSource, CType pDest) + { + Debug.Assert(pSource != null); + Debug.Assert(pDest != null); + + if (AreTypesEqualForConversion(pSource, pDest)) + { + return true; + } + return HasImplicitReferenceConversion(pSource, pDest); + } + + protected bool AreTypesEqualForConversion(CType pType1, CType pType2) + { + return pType1.Equals(pType2); + } + + private bool HasArrayConversionToInterface(ArrayType pSource, CType pDest) + { + Debug.Assert(pSource != null); + Debug.Assert(pDest != null); + if (pSource.rank != 1) + { + return false; + } + if (!pDest.isInterfaceType()) + { + return false; + } + + // * From a single-dimensional array type S[] to IList or IReadOnlyList and their base + // interfaces, provided that there is an implicit identity or reference + // conversion from S to T. + + // We only have six interfaces to check. IList, IReadOnlyList and their bases bases: + // * The base interface of IList is ICollection. + // * The base interface of ICollection is IEnumerable. + // * The base interface of IEnumerable is IEnumerable. + // * The base interface of IReadOnlyList is IReadOnlyCollection. + // * The base interface of IReadOnlyCollection is IEnumerable. + + if (pDest.isPredefType(PredefinedType.PT_IENUMERABLE)) + { + return true; + } + + AggregateType atsDest = pDest.AsAggregateType(); + AggregateSymbol aggDest = pDest.getAggregate(); + if (!aggDest.isPredefAgg(PredefinedType.PT_G_ILIST) && + !aggDest.isPredefAgg(PredefinedType.PT_G_ICOLLECTION) && + !aggDest.isPredefAgg(PredefinedType.PT_G_IENUMERABLE) && + !aggDest.isPredefAgg(PredefinedType.PT_G_IREADONLYCOLLECTION) && + !aggDest.isPredefAgg(PredefinedType.PT_G_IREADONLYLIST)) + { + return false; + } + + Debug.Assert(atsDest.GetTypeArgsAll().Size == 1); + + CType pSourceElement = pSource.GetElementType(); + CType pDestTypeArgument = atsDest.GetTypeArgsAll().Item(0); + return HasIdentityOrImplicitReferenceConversion(pSourceElement, pDestTypeArgument); + } + + public bool HasImplicitReferenceConversion(CType pSource, CType pDest) + { + Debug.Assert(pSource != null); + Debug.Assert(pDest != null); + + // The implicit reference conversions are: + // * From any reference type to Object. + if (pSource.IsRefType() && pDest.isPredefType(PredefinedType.PT_OBJECT)) + { + return true; + } + // * From any class type S to any class type T provided S is derived from T. + if (pSource.isClassType() && pDest.isClassType() && IsBaseClass(pSource, pDest)) + { + return true; + } + + // ORIGINAL RULES: + // // * From any class type S to any interface type T provided S implements T. + // if (pSource.isClassType() && pDest.isInterfaceType() && IsBaseInterface(pSource, pDest)) + // { + // return true; + // } + // // * from any interface type S to any interface type T, provided S is derived from T. + // if (pSource.isInterfaceType() && pDest.isInterfaceType() && IsBaseInterface(pSource, pDest)) + // { + // return true; + // } + + // VARIANCE EXTENSIONS: + // * From any class type S to any interface type T provided S implements an interface + // convertible to T. + // * From any interface type S to any interface type T provided S implements an interface + // convertible to T. + // * From any interface type S to any interface type T provided S is not T and S is + // an interface convertible to T. + + if (pSource.isClassType() && pDest.isInterfaceType() && HasAnyBaseInterfaceConversion(pSource, pDest)) + { + return true; + } + if (pSource.isInterfaceType() && pDest.isInterfaceType() && HasAnyBaseInterfaceConversion(pSource, pDest)) + { + return true; + } + if (pSource.isInterfaceType() && pDest.isInterfaceType() && pSource != pDest && + HasInterfaceConversion(pSource.AsAggregateType(), pDest.AsAggregateType())) + { + return true; + } + + // * From an array type S with an element type SE to an array type T with element type TE + // provided that all of the following are true: + // * S and T differ only in element type. In other words, S and T have the same number of dimensions. + // * Both SE and TE are reference types. + // * An implicit reference conversion exists from SE to TE. + if (pSource.IsArrayType() && pDest.IsArrayType() && + HasCovariantArrayConversion(pSource.AsArrayType(), pDest.AsArrayType())) + { + return true; + } + // * From any array type to System.Array or any interface implemented by System.Array. + if (pSource.IsArrayType() && (pDest.isPredefType(PredefinedType.PT_ARRAY) || + IsBaseInterface(GetReqPredefType(PredefinedType.PT_ARRAY, false), pDest))) + { + return true; + } + // * From a single-dimensional array type S[] to IList and its base + // interfaces, provided that there is an implicit identity or reference + // conversion from S to T. + if (pSource.IsArrayType() && HasArrayConversionToInterface(pSource.AsArrayType(), pDest)) + { + return true; + } + + // * From any delegate type to System.Delegate + // + // SPEC OMISSION: + // + // The spec should actually say + // + // * From any delegate type to System.Delegate + // * From any delegate type to System.MulticastDelegate + // * From any delegate type to any interface implemented by System.MulticastDelegate + if (pSource.isDelegateType() && + (pDest.isPredefType(PredefinedType.PT_MULTIDEL) || + pDest.isPredefType(PredefinedType.PT_DELEGATE) || + IsBaseInterface(GetReqPredefType(PredefinedType.PT_MULTIDEL, false), pDest))) + { + return true; + } + + // VARIANCE EXTENSION: + // * From any delegate type S to a delegate type T provided S is not T and + // S is a delegate convertible to T + + if (pSource.isDelegateType() && pDest.isDelegateType() && + HasDelegateConversion(pSource.AsAggregateType(), pDest.AsAggregateType())) + { + return true; + } + + // * From the null literal to any reference type + // NOTE: We extend the specification here. The C# 3.0 spec does not describe + // a "null type". Rather, it says that the null literal is typeless, and is + // convertible to any reference or nullable type. However, the C# 2.0 and 3.0 + // implementations have a "null type" which some expressions other than the + // null literal may have. (For example, (null??null), which is also an + // extension to the specification.) + if (pSource.IsNullType() && pDest.IsRefType()) + { + return true; + } + if (pSource.IsNullType() && pDest.IsNullableType()) + { + return true; + } + + // * Implicit conversions involving type parameters that are known to be reference types. + if (pSource.IsTypeParameterType() && + HasImplicitReferenceTypeParameterConversion(pSource.AsTypeParameterType(), pDest)) + { + return true; + } + + return false; + } + + private bool HasImplicitReferenceTypeParameterConversion( + TypeParameterType pSource, CType pDest) + { + Debug.Assert(pSource != null); + Debug.Assert(pDest != null); + + if (!pSource.IsRefType()) + { + // Not a reference conversion. + return false; + } + + // The following implicit conversions exist for a given type parameter T: + // + // * From T to its effective base class C. + AggregateType pEBC = pSource.GetEffectiveBaseClass(); + if (pDest == pEBC) + { + return true; + } + // * From T to any base class of C. + if (IsBaseClass(pEBC, pDest)) + { + return true; + } + // * From T to any interface implemented by C. + if (IsBaseInterface(pEBC, pDest)) + { + return true; + } + // * From T to any interface type I in T's effective interface set, and + // from T to any base interface of I. + TypeArray pInterfaces = pSource.GetInterfaceBounds(); + for (int i = 0; i < pInterfaces.Size; ++i) + { + if (pInterfaces.Item(i) == pDest) + { + return true; + } + } + // * From T to a type parameter U, provided T depends on U. + if (pDest.IsTypeParameterType() && pSource.DependsOn(pDest.AsTypeParameterType())) + { + return true; + } + return false; + } + + private bool HasAnyBaseInterfaceConversion(CType pDerived, CType pBase) + { + if (!pBase.isInterfaceType()) + { + return false; + } + if (!pDerived.IsAggregateType()) + { + return false; + } + AggregateType atsDer = pDerived.AsAggregateType(); + while (atsDer != null) + { + TypeArray ifacesAll = atsDer.GetIfacesAll(); + for (int i = 0; i < ifacesAll.size; i++) + { + if (HasInterfaceConversion(ifacesAll.Item(i).AsAggregateType(), pBase.AsAggregateType())) + { + return true; + } + + } + atsDer = atsDer.GetBaseClass(); + } + return false; + } + + //////////////////////////////////////////////////////////////////////////////// + // The rules for variant interface and delegate conversions are the same: + // + // An interface/delegate type S is convertible to an interface/delegate type T + // if and only if T is U and T is U such that for all + // parameters of U: + // + // * if the ith parameter of U is invariant then Si is exactly equal to Ti. + // * if the ith parameter of U is covariant then either Si is exactly equal + // to Ti, or there is an implicit reference conversion from Si to Ti. + // * if the ith parameter of U is contravariant then either Si is exactly + // equal to Ti, or there is an implicit reference conversion from Ti to Si. + + bool HasInterfaceConversion(AggregateType pSource, AggregateType pDest) + { + Debug.Assert(pSource != null && pSource.isInterfaceType()); + Debug.Assert(pDest != null && pDest.isInterfaceType()); + return HasVariantConversion(pSource, pDest); + } + + ////////////////////////////////////////////////////////////////////////////// + + bool HasDelegateConversion(AggregateType pSource, AggregateType pDest) + { + Debug.Assert(pSource != null && pSource.isDelegateType()); + Debug.Assert(pDest != null && pDest.isDelegateType()); + return HasVariantConversion(pSource, pDest); + } + + ////////////////////////////////////////////////////////////////////////////// + + bool HasVariantConversion(AggregateType pSource, AggregateType pDest) + { + Debug.Assert(pSource != null); + Debug.Assert(pDest != null); + if (pSource == pDest) + { + return true; + } + AggregateSymbol pAggSym = pSource.getAggregate(); + if (pAggSym != pDest.getAggregate()) + { + return false; + } + + TypeArray pTypeParams = pAggSym.GetTypeVarsAll(); + TypeArray pSourceArgs = pSource.GetTypeArgsAll(); + TypeArray pDestArgs = pDest.GetTypeArgsAll(); + + Debug.Assert(pTypeParams.size == pSourceArgs.size); + Debug.Assert(pTypeParams.size == pDestArgs.size); + + for (int iParam = 0; iParam < pTypeParams.size; ++iParam) + { + CType pSourceArg = pSourceArgs.Item(iParam); + CType pDestArg = pDestArgs.Item(iParam); + // If they're identical then this one is automatically good, so skip it. + if (pSourceArg == pDestArg) + { + continue; + } + TypeParameterType pParam = pTypeParams.Item(iParam).AsTypeParameterType(); + if (pParam.Invariant) + { + return false; + } + if (pParam.Covariant) + { + if (!HasImplicitReferenceConversion(pSourceArg, pDestArg)) + { + return false; + } + } + if (pParam.Contravariant) + { + if (!HasImplicitReferenceConversion(pDestArg, pSourceArg)) + { + return false; + } + } + } + return true; + } + + + private bool HasImplicitBoxingTypeParameterConversion( + TypeParameterType pSource, CType pDest) + { + Debug.Assert(pSource != null); + Debug.Assert(pDest != null); + + if (pSource.IsRefType()) + { + // Not a boxing conversion; both source and destination are references. + return false; + } + + // The following implicit conversions exist for a given type parameter T: + // + // * From T to its effective base class C. + AggregateType pEBC = pSource.GetEffectiveBaseClass(); + if (pDest == pEBC) + { + return true; + } + // * From T to any base class of C. + if (IsBaseClass(pEBC, pDest)) + { + return true; + } + // * From T to any interface implemented by C. + if (IsBaseInterface(pEBC, pDest)) + { + return true; + } + // * From T to any interface type I in T's effective interface set, and + // from T to any base interface of I. + TypeArray pInterfaces = pSource.GetInterfaceBounds(); + for (int i = 0; i < pInterfaces.Size; ++i) + { + if (pInterfaces.Item(i) == pDest) + { + return true; + } + } + // * The conversion from T to a type parameter U, provided T depends on U, is not + // classified as a boxing conversion because it is not guaranteed to box. + // (If both T and U are value types then it is an identity conversion.) + + return false; + } + + private bool HasImplicitTypeParameterBaseConversion( + TypeParameterType pSource, CType pDest) + { + Debug.Assert(pSource != null); + Debug.Assert(pDest != null); + + if (HasImplicitReferenceTypeParameterConversion(pSource, pDest)) + { + return true; + } + if (HasImplicitBoxingTypeParameterConversion(pSource, pDest)) + { + return true; + } + if (pDest.IsTypeParameterType() && pSource.DependsOn(pDest.AsTypeParameterType())) + { + return true; + } + return false; + } + + public bool HasImplicitBoxingConversion(CType pSource, CType pDest) + { + Debug.Assert(pSource != null); + Debug.Assert(pDest != null); + + // Certain type parameter conversions are classified as boxing conversions. + + if (pSource.IsTypeParameterType() && + HasImplicitBoxingTypeParameterConversion(pSource.AsTypeParameterType(), pDest)) + { + return true; + } + + // The rest of the boxing conversions only operate when going from a value type + // to a reference type. + + if (!pSource.IsValType() || !pDest.IsRefType()) + { + return false; + } + + // A boxing conversion exists from a nullable type to a reference type + // if and only if a boxing conversion exists from the underlying type. + + if (pSource.IsNullableType()) + { + return HasImplicitBoxingConversion(pSource.AsNullableType().GetUnderlyingType(), pDest); + } + + // A boxing conversion exists from any non-nullable value type to object, + // to System.ValueType, and to any interface type implemented by the + // non-nullable value type. Futhermore, an enum type can be converted + // to the type System.Enum. + + // We set the base class of the structs to System.ValueType, System.Enum, etc, + // so we can just check here. + + if (IsBaseClass(pSource, pDest)) + { + return true; + } + if (HasAnyBaseInterfaceConversion(pSource, pDest)) + { + return true; + } + return false; + } + + public bool HasBaseConversion(CType pSource, CType pDest) + { + // By a "base conversion" we mean: + // + // * an identity conversion + // * an implicit reference conversion + // * an implicit boxing conversion + // * an implicit type parameter conversion + // + // In other words, these are conversions that can be made to a base + // class, base interface or co/contravariant type without any change in + // representation other than boxing. A conversion from, say, int to double, + // is NOT a "base conversion", because representation is changed. A conversion + // from, say, lambda to expression tree is not a "base conversion" because + // do not have a type. + // + // The existence of a base conversion depends solely upon the source and + // destination types, not the source expression. + // + // This notion is not found in the spec but it is useful in the implementation. + + if (pSource.IsAggregateType() && pDest.isPredefType(PredefinedType.PT_OBJECT)) + { + // If we are going from any aggregate type (class, struct, interface, enum or delegate) + // to object, we immediately return true. This may seem like a mere optimization -- + // after all, if we have an aggregate then we have some kind of implicit conversion + // to object. + // + // However, it is not a mere optimization; this introduces a control flow change + // in error reporting scenarios for unresolved type forwarders. If a type forwarder + // cannot be resolved then the resulting type symbol will be an aggregate, but + // we will not be able to classify it into class, struct, etc. + // + // We know that we will have an error in this case; we do not wish to compound + // that error by giving a spurious "you cannot convert this thing to object" + // error, which, after all, will go away when the type forwarding problem is + // fixed. + return true; + } + + if (HasIdentityOrImplicitReferenceConversion(pSource, pDest)) + { + return true; + } + if (HasImplicitBoxingConversion(pSource, pDest)) + { + return true; + } + if (pSource.IsTypeParameterType() && + HasImplicitTypeParameterBaseConversion(pSource.AsTypeParameterType(), pDest)) + { + return true; + } + return false; + } + + public bool FCanLift() + { + return null != GetOptPredefAgg(PredefinedType.PT_G_OPTIONAL, false); + } + + public bool IsBaseAggregate(AggregateSymbol derived, AggregateSymbol @base) + { + Debug.Assert(!derived.IsEnum() && !@base.IsEnum()); + + if (derived == @base) + return true; // identity. + + // refactoring error tolerance: structs and delegates can be base classes in error scenarios so + // we cannot filter on whether or not the base is marked as sealed. + + if (@base.IsInterface()) + { + // Search the direct and indirect interfaces via ifacesAll, going up the base chain... + + while (derived != null) + { + for (int i = 0; i < derived.GetIfacesAll().Size; i++) + { + AggregateType iface = derived.GetIfacesAll().Item(i).AsAggregateType(); + if (iface.getAggregate() == @base) + return true; + } + derived = derived.GetBaseAgg(); + } + + return false; + } + + // base is a class. Just go up the base class chain to look for it. + + while (derived.GetBaseClass() != null) + { + derived = derived.GetBaseClass().getAggregate(); + if (derived == @base) + return true; + } + return false; + } + + internal void SetSymbolTable(SymbolTable symbolTable) + { + RuntimeBinderSymbolTable = symbolTable; + } + } +} + diff --git a/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/Symbols/SymbolManagerBase.cs b/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/Symbols/SymbolManagerBase.cs new file mode 100644 index 000000000..6af0c3d2e --- /dev/null +++ b/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/Symbols/SymbolManagerBase.cs @@ -0,0 +1,425 @@ +// ==++== +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// ==--== + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Globalization; +using Microsoft.CSharp.RuntimeBinder.Syntax; + +namespace Microsoft.CSharp.RuntimeBinder.Semantics +{ + struct AidContainer + { + internal static readonly AidContainer NullAidContainer = default(AidContainer); + + enum Kind + { + None = 0, + File, + ExternAlias + } + + object m_value; + + public AidContainer(FileRecord file) + { + m_value = file; + } + } + + class BSYMMGR + { + internal HashSet bsetGlobalAssemblies; // Assemblies in the global alias. + + // Special nullable members. + public PropertySymbol propNubValue; + public MethodSymbol methNubCtor; + + SymFactory symFactory; + MiscSymFactory miscSymFactory; + + NamespaceSymbol rootNS; // The "root" (unnamed) namespace. + + // Map from aids to INFILESYMs and EXTERNALIASSYMs + protected List ssetAssembly; + // Map from aids to MODULESYMs and OUTFILESYMs + + protected NameManager m_nameTable; + protected SYMTBL tableGlobal; + + // The hash table for type arrays. + protected Dictionary tableTypeArrays; + + private InputFile m_infileUnres; + + const int LOG2_SYMTBL_INITIAL_BUCKET_CNT = 13; // Initial local size: 8192 buckets. + + private static readonly TypeArray taEmpty = new TypeArray(new CType[] { }); + + public BSYMMGR(NameManager nameMgr, TypeManager typeManager) + { + this.m_nameTable = nameMgr; + this.tableGlobal = new SYMTBL(); + this.symFactory = new SymFactory(this.tableGlobal, this.m_nameTable); + this.miscSymFactory = new MiscSymFactory(this.tableGlobal); + + this.ssetAssembly = new List(); + + this.m_infileUnres = new InputFile(); + this.m_infileUnres.isSource = false; + this.m_infileUnres.SetAssemblyID(KAID.kaidUnresolved); + + this.ssetAssembly.Add(new AidContainer(m_infileUnres)); + this.bsetGlobalAssemblies = new HashSet(); + this.bsetGlobalAssemblies.Add(KAID.kaidThisAssembly); + this.tableTypeArrays = new Dictionary(); + this.rootNS = symFactory.CreateNamespace(m_nameTable.Add(""), null); + GetNsAid(rootNS, KAID.kaidGlobal); + } + + public void Init() + { + /* + tableTypeArrays.Init(&this->GetPageHeap(), this->getAlloc()); + tableNameToSym.Init(this); + nsToExtensionMethods.Init(this); + + // Some root symbols. + Name* emptyName = m_nameTable->AddString(L""); + rootNS = symFactory.CreateNamespace(emptyName, NULL); // Root namespace + nsaGlobal = GetNsAid(rootNS, kaidGlobal); + + m_infileUnres.name = emptyName; + m_infileUnres.isSource = false; + m_infileUnres.idLocalAssembly = mdTokenNil; + m_infileUnres.SetAssemblyID(kaidUnresolved, allocGlobal); + + size_t isym; + isym = ssetAssembly.Add(&m_infileUnres); + ASSERT(isym == 0); + */ + + InitPreLoad(); + } + + + public NameManager GetNameManager() + { + return m_nameTable; + } + + public SYMTBL GetSymbolTable() + { + return tableGlobal; + } + + public static TypeArray EmptyTypeArray() + { + return taEmpty; + } + + public AssemblyQualifiedNamespaceSymbol GetRootNsAid(KAID aid) + { + return GetNsAid(rootNS, aid); + } + + public NamespaceSymbol GetRootNS() + { + return rootNS; + } + + public KAID AidAlloc(InputFile sym) + { + ssetAssembly.Add(new AidContainer(sym)); + return (KAID)(ssetAssembly.Count - 1 + KAID.kaidUnresolved); + } + + public BetterType CompareTypes(TypeArray ta1, TypeArray ta2) + { + // IF YOU CHANGE THIS METHOD BE SURE TO UPDATE CMethodMemberRef::CompareSignature IN THE LANGAUGE SERVICE!!! + + if (ta1 == ta2) + { + return BetterType.Same; + } + if (ta1.Size != ta2.Size) + { + // The one with more parameters is more specific. + return ta1.Size > ta2.Size ? BetterType.Left : BetterType.Right; + } + + BetterType nTot = BetterType.Neither; + + for (int i = 0; i < ta1.Size; i++) + { + CType type1 = ta1.Item(i); + CType type2 = ta2.Item(i); + BetterType nParam = BetterType.Neither; + + LAgain: + if (type1.GetTypeKind() != type2.GetTypeKind()) + { + if (type1.IsTypeParameterType()) + { + nParam = BetterType.Right; + } + else if (type2.IsTypeParameterType()) + { + nParam = BetterType.Left; + } + } + else + { + switch (type1.GetTypeKind()) + { + default: + Debug.Assert(false, "Bad kind in CompareTypes"); + break; + case TypeKind.TK_TypeParameterType: + case TypeKind.TK_ErrorType: + break; + + case TypeKind.TK_PointerType: + case TypeKind.TK_ParameterModifierType: + case TypeKind.TK_ArrayType: + case TypeKind.TK_NullableType: + type1 = type1.GetBaseOrParameterOrElementType(); + type2 = type2.GetBaseOrParameterOrElementType(); + goto LAgain; + + case TypeKind.TK_AggregateType: + nParam = CompareTypes(type1.AsAggregateType().GetTypeArgsAll(), type2.AsAggregateType().GetTypeArgsAll()); + break; + } + } + + if (nParam == BetterType.Right || nParam == BetterType.Left) + { + if (nTot == BetterType.Same || nTot == BetterType.Neither) + { + nTot = nParam; + } + else if (nParam != nTot) + { + return BetterType.Neither; + } + } + } + + return nTot; + + // IF YOU CHANGE THIS METHOD BE SURE TO UPDATE CMethodMemberRef::CompareSignature IN THE LANGAUGE SERVICE!!! + } + + public SymFactory GetSymFactory() + { + return this.symFactory; + } + + public MiscSymFactory GetMiscSymFactory() + { + return this.miscSymFactory; + } + + //////////////////////////////////////////////////////////////////////////////// + // Build the data structures needed to make FPreLoad fast. Make sure the + // namespaces are created. Compute and sort hashes of the NamespaceSymbol * value and type + // name (sans arity indicator). + + void InitPreLoad() + { + for (int i = 0; i < (int)PredefinedType.PT_COUNT; ++i) + { + NamespaceSymbol ns = GetRootNS(); + string name = PredefinedTypeFacts.GetName((PredefinedType)i); + int start = 0; + while (start < name.Length) + { + int iDot = name.IndexOf('.', start); + if (iDot == -1) break; + string sub = (iDot > start) ? name.Substring(start, iDot - start) : name.Substring(start); + Name nm = this.GetNameManager().Add(sub); + NamespaceSymbol sym = this.LookupGlobalSymCore(nm, ns, symbmask_t.MASK_NamespaceSymbol).AsNamespaceSymbol(); + if (sym == null) + { + ns = this.symFactory.CreateNamespace(nm, ns); + } + else + { + ns = sym; + } + start += sub.Length + 1; + } + } + } + + public Symbol LookupGlobalSymCore(Name name, ParentSymbol parent, symbmask_t kindmask) + { + return tableGlobal.LookupSym(name, parent, kindmask); + } + + public Symbol LookupAggMember(Name name, AggregateSymbol agg, symbmask_t mask) + { + return tableGlobal.LookupSym(name, agg, mask); + } + + public static Symbol LookupNextSym(Symbol sym, ParentSymbol parent, symbmask_t kindmask) + { + Debug.Assert(sym.parent == parent); + + sym = sym.nextSameName; + Debug.Assert(sym == null || sym.parent == parent); + + // Keep traversing the list of symbols with same name and parent. + while (sym != null) + { + if ((kindmask & sym.mask()) > 0) + return sym; + + sym = sym.nextSameName; + Debug.Assert(sym == null || sym.parent == parent); + } + + return null; + } + + public Name GetNameFromPtrs(object u1, object u2) + { + // Note: this won't produce the same names as the native logic + if (u2 != null) + { + return this.m_nameTable.Add(string.Format(CultureInfo.InvariantCulture, "{0:X}-{1:X}", u1.GetHashCode(), u2.GetHashCode())); + } + else + { + return this.m_nameTable.Add(string.Format(CultureInfo.InvariantCulture, "{0:X}", u1.GetHashCode())); + } + } + + public AssemblyQualifiedNamespaceSymbol GetNsAid(NamespaceSymbol ns, KAID aid) + { + Name name = GetNameFromPtrs(aid, 0); + Debug.Assert(name != null); + + AssemblyQualifiedNamespaceSymbol nsa = LookupGlobalSymCore(name, ns, symbmask_t.MASK_AssemblyQualifiedNamespaceSymbol).AsAssemblyQualifiedNamespaceSymbol(); + if (nsa == null) + { + // Create a new one. + nsa = symFactory.CreateNamespaceAid(name, ns, aid); + } + + Debug.Assert(nsa.GetNS() == ns); + + return nsa; + } + + //////////////////////////////////////////////////////////////////////////////// + // Allocate a type array; used to represent a parameter list. + // We use a hash table to make sure that allocating the same type array twice + // returns the same value. This does two things: + // + // 1) Save a lot of memory. + // 2) Make it so parameter lists can be compared by a simple pointer comparison + // 3) Allow us to associate a token with each signature for faster metadata emit + + protected struct TypeArrayKey : IEquatable + { + CType[] types; + int hashCode; + + public TypeArrayKey(CType[] types) + { + this.types = types; + this.hashCode = 0; + for (int i = 0, n = types.Length; i < n; i++) + { + this.hashCode ^= types[i].GetHashCode(); + } + } + + public bool Equals(TypeArrayKey other) + { + if (other.types == this.types) return true; + if (other.types.Length != this.types.Length) return false; + if (other.hashCode != this.hashCode) return false; + for (int i = 0, n = this.types.Length; i < n; i++) + { + if (!this.types[i].Equals(other.types[i])) + return false; + } + return true; + } + + public override bool Equals(object obj) + { + if (obj is TypeArrayKey) + { + return this.Equals((TypeArrayKey)obj); + } + return false; + } + + public override int GetHashCode() + { + return this.hashCode; + } + } + + public TypeArray AllocParams(int ctype, CType[] prgtype) + { + if (ctype == 0) + { + return taEmpty; + } + Debug.Assert(ctype == prgtype.Length); + return AllocParams(prgtype); + } + + // TODO: define this method + public TypeArray AllocParams(int ctype, TypeArray array, int offset) + { + CType[] types = array.ToArray(); + CType[] newTypes = new CType[ctype]; +#if SILVERLIGHT + Array.Copy(types, offset, newTypes, 0, ctype); +#else + Array.ConstrainedCopy(types, offset, newTypes, 0, ctype); +#endif + return AllocParams(newTypes); + } + + public TypeArray AllocParams(params CType[] types) + { + if (types == null || types.Length == 0) + { + return taEmpty; + } + TypeArrayKey key = new TypeArrayKey(types); + TypeArray result; + if (!tableTypeArrays.TryGetValue(key, out result)) + { + result = new TypeArray(types); + tableTypeArrays.Add(key, result); + } + return result; + } + + public TypeArray ConcatParams(CType[] prgtype1, CType[] prgtype2) + { + CType[] combined = new CType[prgtype1.Length + prgtype2.Length]; + Array.Copy(prgtype1, combined, prgtype1.Length); + Array.Copy(prgtype2, 0, combined, prgtype1.Length, prgtype2.Length); + return AllocParams(combined); + } + + public TypeArray ConcatParams(TypeArray pta1, TypeArray pta2) + { + return ConcatParams(pta1.ToArray(), pta2.ToArray()); + } + } +} + diff --git a/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/Symbols/SymbolMask.cs b/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/Symbols/SymbolMask.cs new file mode 100644 index 000000000..dd4b405a8 --- /dev/null +++ b/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/Symbols/SymbolMask.cs @@ -0,0 +1,34 @@ +// ==++== +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// ==--== + +using System; + +namespace Microsoft.CSharp.RuntimeBinder.Semantics +{ + [Flags] + internal enum symbmask_t : long + { + MASK_NamespaceSymbol = 1 << (int)SYMKIND.SK_NamespaceSymbol, + MASK_NamespaceDeclaration = 1 << (int)SYMKIND.SK_NamespaceDeclaration, + MASK_AssemblyQualifiedNamespaceSymbol = 1 << (int)SYMKIND.SK_AssemblyQualifiedNamespaceSymbol, + MASK_AggregateSymbol = 1 << (int)SYMKIND.SK_AggregateSymbol, + MASK_AggregateDeclaration = 1 << (int)SYMKIND.SK_AggregateDeclaration, + MASK_TypeParameterSymbol = 1 << (int)SYMKIND.SK_TypeParameterSymbol, + MASK_FieldSymbol = 1 << (int)SYMKIND.SK_FieldSymbol, + MASK_LocalVariableSymbol = 1 << (int)SYMKIND.SK_LocalVariableSymbol, + MASK_MethodSymbol = 1 << (int)SYMKIND.SK_MethodSymbol, + MASK_PropertySymbol = 1 << (int)SYMKIND.SK_PropertySymbol, + MASK_EventSymbol = 1 << (int)SYMKIND.SK_EventSymbol, + MASK_TransparentIdentifierMemberSymbol = 1 << (int)SYMKIND.SK_TransparentIdentifierMemberSymbol, + MASK_Scope = 1 << (int)SYMKIND.SK_Scope, + MASK_CachedNameSymbol = 1 << (int)SYMKIND.SK_CachedNameSymbol, + MASK_LabelSymbol = 1 << (int)SYMKIND.SK_LabelSymbol, + MASK_GlobalAttributeDeclaration = 1 << (int)SYMKIND.SK_GlobalAttributeDeclaration, + MASK_LambdaScope = 1 << (int)SYMKIND.SK_LambdaScope, + MASK_ALL = ~ 0, + LOOKUPMASK = (MASK_AssemblyQualifiedNamespaceSymbol | MASK_FieldSymbol | MASK_LocalVariableSymbol | MASK_MethodSymbol | MASK_PropertySymbol) + } +} diff --git a/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/Symbols/SymbolTable.cs b/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/Symbols/SymbolTable.cs new file mode 100644 index 000000000..0e4ca117e --- /dev/null +++ b/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/Symbols/SymbolTable.cs @@ -0,0 +1,111 @@ +// ==++== +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// ==--== + +using System.Collections.Generic; +using System.Diagnostics; +using Microsoft.CSharp.RuntimeBinder.Syntax; + +namespace Microsoft.CSharp.RuntimeBinder.Semantics +{ + // A symbol table is a helper class used by the symbol manager. There are + // two symbol tables; a global and a local. + + class SYMTBL + { + ///////////////////////////////////////////////////////////////////////////////// + // Public + + public SYMTBL() + { + dictionary = new Dictionary(); + } + + public Symbol LookupSym(Name name, ParentSymbol parent, symbmask_t kindmask) + { + Key k = new Key(name, parent); + Symbol sym; + + if (dictionary.TryGetValue(k, out sym)) + { + return FindCorrectKind(sym, kindmask); + } + + return null; + } + + public void InsertChild(ParentSymbol parent, Symbol child) + { + Debug.Assert(child.nextSameName == null); + Debug.Assert(child.parent == null || child.parent == parent); + child.parent = parent; + + // Place the child into the hash table. + InsertChildNoGrow(child); + } + + private void InsertChildNoGrow(Symbol child) + { + Key k = new Key(child.name, child.parent); + Symbol sym; + + if (dictionary.TryGetValue(k, out sym)) + { + // Link onto the end of the symbol chain here. + while (sym != null && sym.nextSameName != null) + { + sym = sym.nextSameName; + } + + Debug.Assert(sym != null && sym.nextSameName == null); + sym.nextSameName = child; + return; + } + else + { + dictionary.Add(k, child); + } + } + + private static Symbol FindCorrectKind(Symbol sym, symbmask_t kindmask) + { + do + { + if ((kindmask & sym.mask()) != 0) + { + return sym; + } + sym = sym.nextSameName; + } while (sym != null); + + return null; + } + + private Dictionary dictionary; + + sealed class Key + { + private readonly Name name; + private readonly ParentSymbol parent; + + public Key(Name name, ParentSymbol parent) + { + this.name = name; + this.parent = parent; + } + + public override bool Equals(object obj) + { + Key k = obj as Key; + return k != null && name.Equals(k.name) && parent.Equals(k.parent); + } + + public override int GetHashCode() + { + return name.GetHashCode() ^ parent.GetHashCode(); + } + } + } +} diff --git a/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/Symbols/TransparentIdentifierMemberSymbol.cs b/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/Symbols/TransparentIdentifierMemberSymbol.cs new file mode 100644 index 000000000..1e429bd1e --- /dev/null +++ b/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/Symbols/TransparentIdentifierMemberSymbol.cs @@ -0,0 +1,12 @@ +// ==++== +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// ==--== + +namespace Microsoft.CSharp.RuntimeBinder.Semantics +{ + class TransparentIdentifierMemberSymbol : Symbol + { + } +} diff --git a/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/Symbols/TypeParameterSymbol.cs b/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/Symbols/TypeParameterSymbol.cs new file mode 100644 index 000000000..a40b61a41 --- /dev/null +++ b/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/Symbols/TypeParameterSymbol.cs @@ -0,0 +1,134 @@ +// ==++== +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// ==--== + +using System.Diagnostics; + +namespace Microsoft.CSharp.RuntimeBinder.Semantics +{ + class TypeParameterSymbol : Symbol + { + private bool m_bIsMethodTypeParameter; + private bool m_bHasRefBound; + private bool m_bHasValBound; + private SpecCons m_constraints; + + private TypeParameterType m_pTypeParameterType; + + private int m_nIndexInOwnParameters; + private int m_nIndexInTotalParameters; + + private TypeArray m_pBounds; + private TypeArray m_pInterfaceBounds; + + private AggregateType m_pEffectiveBaseClass; + private CType m_pDeducedBaseClass; // This may be a NullableType or an ArrayType etc, for error reporting. + + public bool Covariant; + public bool Invariant { get { return !Covariant && !Contravariant; } } + public bool Contravariant; + + public void SetTypeParameterType(TypeParameterType pType) + { + m_pTypeParameterType = pType; + } + + public TypeParameterType GetTypeParameterType() + { + return m_pTypeParameterType; + } + + public bool IsMethodTypeParameter() + { + return m_bIsMethodTypeParameter; + } + + public void SetIsMethodTypeParameter(bool b) + { + m_bIsMethodTypeParameter = b; + } + + public int GetIndexInOwnParameters() + { + return m_nIndexInOwnParameters; + } + + public void SetIndexInOwnParameters(int index) + { + m_nIndexInOwnParameters = index; + } + + public int GetIndexInTotalParameters() + { + return m_nIndexInTotalParameters; + } + + public void SetIndexInTotalParameters(int index) + { + Debug.Assert(index >= m_nIndexInOwnParameters); + m_nIndexInTotalParameters = index; + } + + public TypeArray GetInterfaceBounds() + { + return m_pInterfaceBounds; + } + + public void SetBounds(TypeArray pBounds) + { + m_pBounds = pBounds; + m_pInterfaceBounds = null; + m_pEffectiveBaseClass = null; + m_pDeducedBaseClass = null; + m_bHasRefBound = false; + m_bHasValBound = false; + } + + public TypeArray GetBounds() + { + return m_pBounds; + } + + public void SetConstraints(SpecCons constraints) + { + m_constraints = constraints; + } + + public AggregateType GetEffectiveBaseClass() + { + return m_pEffectiveBaseClass; + } + + public bool IsValueType() + { + return (m_constraints & SpecCons.Val) > 0 || m_bHasValBound; + } + + public bool IsReferenceType() + { + return (m_constraints & SpecCons.Ref) > 0 || m_bHasRefBound; + } + + public bool IsNonNullableValueType() + { + return (m_constraints & SpecCons.Val) > 0 || m_bHasValBound && !m_pDeducedBaseClass.IsNullableType(); + } + + public bool HasNewConstraint() + { + return (m_constraints & SpecCons.New) > 0; + } + + public bool HasRefConstraint() + { + return (m_constraints & SpecCons.Ref) > 0; + } + + public bool HasValConstraint() + { + return (m_constraints & SpecCons.Val) > 0; + } + } +} diff --git a/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/Symbols/UnresolvedAggregateSymbol.cs b/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/Symbols/UnresolvedAggregateSymbol.cs new file mode 100644 index 000000000..cd1de3309 --- /dev/null +++ b/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/Symbols/UnresolvedAggregateSymbol.cs @@ -0,0 +1,22 @@ +// ==++== +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// ==--== + +namespace Microsoft.CSharp.RuntimeBinder.Semantics +{ + // ---------------------------------------------------------------------------- + // + // UnresolvedAggregateSymbol + // + // A fabricated AggregateSymbol to represent an imported type that we couldn't resolve. + // Used for error reporting. + // In the EE this is used as a place holder until the real AggregateSymbol is created. + // + // ---------------------------------------------------------------------------- + + class UnresolvedAggregateSymbol : AggregateSymbol + { + } +} \ No newline at end of file diff --git a/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/Symbols/VariableSymbol.cs b/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/Symbols/VariableSymbol.cs new file mode 100644 index 000000000..b48e7f93b --- /dev/null +++ b/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/Symbols/VariableSymbol.cs @@ -0,0 +1,22 @@ +// ==++== +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// ==--== + +namespace Microsoft.CSharp.RuntimeBinder.Semantics +{ + // ---------------------------------------------------------------------------- + // + // VariableSymbol + // + // VariableSymbol - a symbol representing a variable. Specific subclasses are + // used - FieldSymbol for member variables, LocalVariableSymbol for local variables + // and formal parameters, + // ---------------------------------------------------------------------------- + + class VariableSymbol : Symbol + { + protected CType type; // CType of the field. + } +} \ No newline at end of file diff --git a/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/Tree/ArrayIndex.cs b/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/Tree/ArrayIndex.cs new file mode 100644 index 000000000..dd41a279c --- /dev/null +++ b/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/Tree/ArrayIndex.cs @@ -0,0 +1,19 @@ +// ==++== +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// ==--== + +namespace Microsoft.CSharp.RuntimeBinder.Semantics +{ + internal class EXPRARRAYINDEX : EXPR + { + private EXPR Array; + public EXPR GetArray() { return Array; } + public void SetArray(EXPR value) { Array = value; } + + private EXPR Index; + public EXPR GetIndex() { return Index; } + public void SetIndex(EXPR value) { Index = value; } + } +} diff --git a/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/Tree/ArrayInitialization.cs b/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/Tree/ArrayInitialization.cs new file mode 100644 index 000000000..06b274b15 --- /dev/null +++ b/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/Tree/ArrayInitialization.cs @@ -0,0 +1,25 @@ +// ==++== +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// ==--== + +namespace Microsoft.CSharp.RuntimeBinder.Semantics +{ + internal class EXPRARRINIT : EXPR + { + private EXPR OptionalArguments; + public EXPR GetOptionalArguments() { return OptionalArguments; } + public void SetOptionalArguments(EXPR value) { OptionalArguments = value; } + + private EXPR OptionalArgumentDimensions; + public EXPR GetOptionalArgumentDimensions() { return OptionalArgumentDimensions; } + public void SetOptionalArgumentDimensions(EXPR value) { OptionalArgumentDimensions = value; } + + // The EXPRs bound as the size of the array. + public int[] dimSizes; + public int dimSize; + public bool GeneratedForParamArray; + } + +} diff --git a/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/Tree/ArrayLength.cs b/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/Tree/ArrayLength.cs new file mode 100644 index 000000000..d86e4dc4c --- /dev/null +++ b/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/Tree/ArrayLength.cs @@ -0,0 +1,15 @@ +// ==++== +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// ==--== + +namespace Microsoft.CSharp.RuntimeBinder.Semantics +{ + internal class EXPRARRAYLENGTH : EXPR + { + private EXPR Array; + public EXPR GetArray() { return Array; } + public void SetArray(EXPR value) { Array = value; } + } +} diff --git a/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/Tree/Assignment.cs b/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/Tree/Assignment.cs new file mode 100644 index 000000000..687034aaf --- /dev/null +++ b/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/Tree/Assignment.cs @@ -0,0 +1,19 @@ +// ==++== +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// ==--== + +namespace Microsoft.CSharp.RuntimeBinder.Semantics +{ + internal class EXPRASSIGNMENT : EXPR + { + private EXPR LHS; + public EXPR GetLHS() { return LHS; } + public void SetLHS(EXPR value) { LHS = value; } + + private EXPR RHS; + public EXPR GetRHS() { return RHS; } + public void SetRHS(EXPR value) { RHS = value; } + } +} diff --git a/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/Tree/BinaryOperator.cs b/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/Tree/BinaryOperator.cs new file mode 100644 index 000000000..07b4c958a --- /dev/null +++ b/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/Tree/BinaryOperator.cs @@ -0,0 +1,30 @@ +// ==++== +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// ==--== + +namespace Microsoft.CSharp.RuntimeBinder.Semantics +{ + internal class EXPRBINOP : EXPR + { + private EXPR OptionalLeftChild; + public EXPR GetOptionalLeftChild() { return OptionalLeftChild; } + public void SetOptionalLeftChild(EXPR value) { OptionalLeftChild = value; } + + private EXPR OptionalRightChild; + public EXPR GetOptionalRightChild() { return OptionalRightChild; } + public void SetOptionalRightChild(EXPR value) { OptionalRightChild = value; } + + private EXPR OptionalUserDefinedCall; + public EXPR GetOptionalUserDefinedCall() { return OptionalUserDefinedCall; } + public void SetOptionalUserDefinedCall(EXPR value) { OptionalUserDefinedCall = value; } + + public MethWithInst predefinedMethodToCall; + public bool isLifted; + + private MethPropWithInst UserDefinedCallMethod; + public MethPropWithInst GetUserDefinedCallMethod() { return UserDefinedCallMethod; } + public void SetUserDefinedCallMethod(MethPropWithInst value) { UserDefinedCallMethod = value; } + } +} diff --git a/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/Tree/Block.cs b/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/Tree/Block.cs new file mode 100644 index 000000000..c9f7cfeed --- /dev/null +++ b/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/Tree/Block.cs @@ -0,0 +1,17 @@ +// ==++== +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// ==--== + +namespace Microsoft.CSharp.RuntimeBinder.Semantics +{ + internal class EXPRBLOCK : EXPRSTMT + { + private EXPRSTMT OptionalStatements; + public EXPRSTMT GetOptionalStatements() { return OptionalStatements; } + public void SetOptionalStatements(EXPRSTMT value) { OptionalStatements = value; } + + public Scope OptionalScopeSymbol; + } +} diff --git a/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/Tree/BoundAnonymousFunction.cs b/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/Tree/BoundAnonymousFunction.cs new file mode 100644 index 000000000..09b044cf3 --- /dev/null +++ b/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/Tree/BoundAnonymousFunction.cs @@ -0,0 +1,37 @@ +// ==++== +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// ==--== + +using System.Diagnostics; +using Microsoft.CSharp.RuntimeBinder.Syntax; + +namespace Microsoft.CSharp.RuntimeBinder.Semantics +{ + // UNDONE: Rename BoundAnonymousFunction + internal class EXPRBOUNDLAMBDA : EXPR + { + public EXPRBLOCK OptionalBody; + private Scope argumentScope; // The scope containing the names of the parameters + // The scope that will hold this anonymous function. This starts off as the outer scope and is then + // ratcheted down to the correct scope after the containing method is fully bound. + + public void Initialize(Scope argScope) + { + Debug.Assert(argScope != null); + argumentScope = argScope; + } + + public AggregateType DelegateType() + { + return type.AsAggregateType(); + } + + public Scope ArgumentScope() + { + Debug.Assert(argumentScope != null); + return argumentScope; + } + } +} diff --git a/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/Tree/Call.cs b/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/Tree/Call.cs new file mode 100644 index 000000000..5e9004002 --- /dev/null +++ b/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/Tree/Call.cs @@ -0,0 +1,27 @@ +// ==++== +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// ==--== + +namespace Microsoft.CSharp.RuntimeBinder.Semantics +{ + internal class EXPRCALL : EXPR + { + private EXPR OptionalArguments; + public EXPR GetOptionalArguments() { return OptionalArguments; } + public void SetOptionalArguments(EXPR value) { OptionalArguments = value; } + + private EXPRMEMGRP MemberGroup; + public EXPRMEMGRP GetMemberGroup() { return MemberGroup; } + public void SetMemberGroup(EXPRMEMGRP value) { MemberGroup = value; } + + public MethWithInst mwi; + + public PREDEFMETH PredefinedMethod; + + public NullableCallLiftKind nubLiftKind; + public EXPR pConversions; + public EXPR castOfNonLiftedResultToLiftedType; + } +} diff --git a/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/Tree/Cast.cs b/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/Tree/Cast.cs new file mode 100644 index 000000000..6a85bc0f7 --- /dev/null +++ b/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/Tree/Cast.cs @@ -0,0 +1,19 @@ +// ==++== +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// ==--== + +namespace Microsoft.CSharp.RuntimeBinder.Semantics +{ + internal class EXPRCAST : EXPR + { + public EXPR Argument; + public EXPR GetArgument() { return Argument; } + public void SetArgument(EXPR expr) { Argument = expr; } + public EXPRTYPEORNAMESPACE DestinationType; + public EXPRTYPEORNAMESPACE GetDestinationType() { return DestinationType; } + public void SetDestinationType(EXPRTYPEORNAMESPACE expr) { DestinationType = expr; } + public bool IsBoxingCast() { return (flags & (EXPRFLAG.EXF_BOX | EXPRFLAG.EXF_FORCE_BOX)) != 0; } + } +} diff --git a/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/Tree/Class.cs b/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/Tree/Class.cs new file mode 100644 index 000000000..2e56bb166 --- /dev/null +++ b/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/Tree/Class.cs @@ -0,0 +1,12 @@ +// ==++== +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// ==--== + +namespace Microsoft.CSharp.RuntimeBinder.Semantics +{ + internal class EXPRCLASS : EXPRTYPEORNAMESPACE + { + } +} diff --git a/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/Tree/CompoundOperator.cs b/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/Tree/CompoundOperator.cs new file mode 100644 index 000000000..ed9e92a0b --- /dev/null +++ b/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/Tree/CompoundOperator.cs @@ -0,0 +1,25 @@ +// ==++== +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// ==--== + +namespace Microsoft.CSharp.RuntimeBinder.Semantics +{ + internal class EXPRMULTIGET : EXPR + { + public EXPRMULTI OptionalMulti; + public EXPRMULTI GetOptionalMulti() { return OptionalMulti; } + public void SetOptionalMulti(EXPRMULTI value) { OptionalMulti = value; } + } + + internal class EXPRMULTI : EXPR + { + public EXPR Left; + public EXPR GetLeft() { return Left; } + public void SetLeft(EXPR value) { Left = value; } + public EXPR Operator; + public EXPR GetOperator() { return Operator; } + public void SetOperator(EXPR value) { Operator = value; } + } +} diff --git a/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/Tree/Concatenate.cs b/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/Tree/Concatenate.cs new file mode 100644 index 000000000..8372e62ae --- /dev/null +++ b/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/Tree/Concatenate.cs @@ -0,0 +1,19 @@ +// ==++== +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// ==--== + +namespace Microsoft.CSharp.RuntimeBinder.Semantics +{ + // UNDONE: This should probably be made into just another EXPRBINOP flavour + internal class EXPRCONCAT : EXPR + { + public EXPR FirstArgument; + public EXPR GetFirstArgument() { return FirstArgument; } + public void SetFirstArgument(EXPR value) { FirstArgument = value; } + public EXPR SecondArgument; + public EXPR GetSecondArgument() { return SecondArgument; } + public void SetSecondArgument(EXPR value) { SecondArgument = value; } + } +} diff --git a/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/Tree/ConditionalOperator.cs b/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/Tree/ConditionalOperator.cs new file mode 100644 index 000000000..70efcf082 --- /dev/null +++ b/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/Tree/ConditionalOperator.cs @@ -0,0 +1,21 @@ +// ==++== +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// ==--== + +namespace Microsoft.CSharp.RuntimeBinder.Semantics +{ + internal class EXPRQUESTIONMARK : EXPR + { + // UNDONE: Refactor so that the condition, consequence and alternative + // UNDONE: are all in one place. This business of representing the colon + // UNDONE: as a binop is goofy. + public EXPR TestExpression; + public EXPR GetTestExpression() { return TestExpression; } + public void SetTestExpression(EXPR value) { TestExpression = value; } + public EXPRBINOP Consequence; + public EXPRBINOP GetConsequence() { return Consequence; } + public void SetConsequence(EXPRBINOP value) { Consequence = value; } + } +} diff --git a/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/Tree/Constant.cs b/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/Tree/Constant.cs new file mode 100644 index 000000000..ccd4dd095 --- /dev/null +++ b/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/Tree/Constant.cs @@ -0,0 +1,70 @@ +// ==++== +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// ==--== + +using System.Diagnostics; +using System.Text; + +namespace Microsoft.CSharp.RuntimeBinder.Semantics +{ + internal class EXPRCONSTANT : EXPR + { + public EXPR OptionalConstructorCall; + public EXPR GetOptionalConstructorCall() { return OptionalConstructorCall; } + public void SetOptionalConstructorCall(EXPR value) { OptionalConstructorCall = value; } + + private CONSTVAL val; + + public bool IsZero + { + get + { + return Val.IsZero(this.type.constValKind()); + } + } + public bool isZero() { return IsZero; } + public CONSTVAL getVal() { return Val; } + public void setVal(CONSTVAL newValue) { Val = newValue; } + public CONSTVAL Val + { + get + { + return val; + } + set + { + val = value; + } + } + + public ulong getU64Value() { return val.ulongVal; } + public long getI64Value() { return I64Value; } + public long I64Value + { + get + { + FUNDTYPE ft = type.fundType(); + switch (ft) + { + case FUNDTYPE.FT_I8: + case FUNDTYPE.FT_U8: + return val.longVal; + case FUNDTYPE.FT_U4: + return val.uiVal; + case FUNDTYPE.FT_I1: + case FUNDTYPE.FT_I2: + case FUNDTYPE.FT_I4: + case FUNDTYPE.FT_U1: + case FUNDTYPE.FT_U2: + return val.iVal; + default: + Debug.Assert(false, "Bad fundType in getI64Value"); + return 0; + } + } + } + } + +} diff --git a/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/Tree/EXPR.cs b/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/Tree/EXPR.cs new file mode 100644 index 000000000..93406c307 --- /dev/null +++ b/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/Tree/EXPR.cs @@ -0,0 +1,195 @@ +// ==++== +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// ==--== + +using System.Diagnostics; + +namespace Microsoft.CSharp.RuntimeBinder.Semantics +{ + abstract class EXPR + { + protected static void RETAILVERIFY(bool f) + { + //if (!f) + //Debug.Assert(false, "panic!"); + } + + internal object RuntimeObject; + internal CType RuntimeObjectActualType; + + public ExpressionKind kind; + public EXPRFLAG flags; + public bool IsError; + public bool IsOptionalArgument; + public void SetInaccessibleBit() + { + IsError = true; + } + + public void SetMismatchedStaticBit() + { + switch (kind) + { + case ExpressionKind.EK_CALL: + if (this.asCALL().GetMemberGroup() != null) + this.asCALL().GetMemberGroup().SetMismatchedStaticBit(); + break; + } + IsError = true; + } + + public string errorString; + public CType type; + public void setType(CType t) + { + this.type = t; + } + + public void setAssignment() + { + Debug.Assert(!this.isSTMT()); + this.flags |= EXPRFLAG.EXF_ASSGOP; + } + + public bool isOK() + { + return !HasError(); + } + + // REFACTOR: This is redundant. + public bool HasError() + { + return IsError; + } + public void SetError() + { + IsError = true; + } + + public bool HasObject() + { + switch (kind) + { + case ExpressionKind.EK_FIELD: + case ExpressionKind.EK_PROP: + case ExpressionKind.EK_CALL: + case ExpressionKind.EK_EVENT: + case ExpressionKind.EK_MEMGRP: + case ExpressionKind.EK_FUNCPTR: + return true; + } + return false; + } + + public EXPR getArgs() + { + RETAILVERIFY(this.isCALL() || this.isPROP() || this.isFIELD() || this.isARRAYINDEX()); + if (this.isFIELD()) + return null; + switch (kind) + { + case ExpressionKind.EK_CALL: + return this.asCALL().GetOptionalArguments(); + + case ExpressionKind.EK_PROP: + return this.asPROP().GetOptionalArguments(); + + case ExpressionKind.EK_ARRAYINDEX: + return this.asARRAYINDEX().GetIndex(); + } + Debug.Assert(false, "Shouldn't get here without a CALL, PROP, FIELD or ARRINDEX"); + return null; + } + + public void setArgs(EXPR args) + { + RETAILVERIFY(this.isCALL() || this.isPROP() || this.isFIELD() || this.isARRAYINDEX()); + if (this.isFIELD()) + { + Debug.Assert(false, "Setting arguments on a field."); + return; + } + switch (kind) + { + case ExpressionKind.EK_CALL: + this.asCALL().SetOptionalArguments(args); + return; + + case ExpressionKind.EK_PROP: + this.asPROP().SetOptionalArguments(args); + return; + + case ExpressionKind.EK_ARRAYINDEX: + this.asARRAYINDEX().SetIndex(args); + return; + } + Debug.Assert(false, "Shouldn't get here without a CALL, PROP, FIELD or ARRINDEX"); + } + + public EXPR getObject() + { + RETAILVERIFY(this.HasObject()); + switch (kind) + { + case ExpressionKind.EK_FIELD: + return this.asFIELD().OptionalObject; + case ExpressionKind.EK_PROP: + return this.asPROP().GetMemberGroup().OptionalObject; + case ExpressionKind.EK_CALL: + return this.asCALL().GetMemberGroup().OptionalObject; + case ExpressionKind.EK_MEMGRP: + return this.asMEMGRP().OptionalObject; + case ExpressionKind.EK_EVENT: + return this.asEVENT().OptionalObject; + case ExpressionKind.EK_FUNCPTR: + return this.asFUNCPTR().OptionalObject; + } + return null; + } + public void SetObject(EXPR pExpr) + { + RETAILVERIFY(HasObject()); + switch (kind) + { + case ExpressionKind.EK_FIELD: + this.asFIELD().OptionalObject = pExpr; + break; + case ExpressionKind.EK_PROP: + this.asPROP().GetMemberGroup().OptionalObject = pExpr; + break; + case ExpressionKind.EK_CALL: + this.asCALL().GetMemberGroup().OptionalObject = pExpr; + break; + case ExpressionKind.EK_MEMGRP: + this.asMEMGRP().OptionalObject = pExpr; + break; + case ExpressionKind.EK_EVENT: + this.asEVENT().OptionalObject = pExpr; + break; + case ExpressionKind.EK_FUNCPTR: + this.asFUNCPTR().OptionalObject = pExpr; + break; + } + } + + public SymWithType GetSymWithType() + { + switch (kind) + { + default: + Debug.Assert(false, "Bad expr kind in GetSymWithType"); + return ((EXPRCALL)this).mwi; + case ExpressionKind.EK_CALL: + return ((EXPRCALL)this).mwi; + case ExpressionKind.EK_PROP: + return ((EXPRPROP)this).pwtSlot; + case ExpressionKind.EK_FIELD: + return ((EXPRFIELD)this).fwt; + case ExpressionKind.EK_EVENT: + return ((EXPREVENT)this).ewt; + } + } + } +} diff --git a/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/Tree/Event.cs b/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/Tree/Event.cs new file mode 100644 index 000000000..d09c4b0ac --- /dev/null +++ b/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/Tree/Event.cs @@ -0,0 +1,14 @@ +// ==++== +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// ==--== + +namespace Microsoft.CSharp.RuntimeBinder.Semantics +{ + internal class EXPREVENT : EXPR + { + public EXPR OptionalObject; + public EventWithType ewt; + } +} diff --git a/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/Tree/ExpressionIterator.cs b/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/Tree/ExpressionIterator.cs new file mode 100644 index 000000000..4c5d5ecf8 --- /dev/null +++ b/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/Tree/ExpressionIterator.cs @@ -0,0 +1,100 @@ +// ==++== +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// ==--== + +namespace Microsoft.CSharp.RuntimeBinder.Semantics +{ + // ExpressionIterator is an iterator for EXPRLISTs, designed to be used + // in the following way: + // + // for (ExpressionIterator it(list); !it.AtEnd(); it.MoveNext()) + // { + // EXPR expr = it.Current(); + // ... + // ... // work with expr + // } + // + // The constructor takes an EXPR which can point to either an EXPRLIST + // or a non-list EXPR, or nothing. + // + // Upon construction, the iterator's current element is the first element + // in the list, which is to say it's not necessary to call MoveNext + // before usage. AtEnd is true iff the iterator's current element is + // beyond the last element of the list. These semantics differ from + // IEnumerator in the framework, but are natural for usage in the C++ + // for-loop as above. + // + // Outside of a for loop, usage might look like this: + // + // ExpressionIterator it(list); + // EXPR expr1 = it.Current(); + // it.MoveNext(); + // EXPR expr2 = it.Current(); + // it.MoveNext(); + // EXPR expr3 = it.Current(); + // it.MoveNext(); + // + // This would get the first three elements in a list. Also available is the + // static Count: + // + // int n = ExpressionIterator::Count(list); + + internal class ExpressionIterator + { + public ExpressionIterator(EXPR pExpr) { Init(pExpr); } + + public bool AtEnd() { return m_pCurrent == null && m_pList == null; } + + public EXPR Current() { return m_pCurrent; } + + public void MoveNext() + { + if (AtEnd()) + { + return; + } + else if (m_pList == null) + { + m_pCurrent = null; + } + else + { + Init(m_pList.GetOptionalNextListNode()); + } + } + + public static int Count(EXPR pExpr) + { + int c = 0; + for (ExpressionIterator it = new ExpressionIterator(pExpr); !it.AtEnd(); it.MoveNext()) + { + ++c; + } + return c; + } + + private EXPRLIST m_pList; + private EXPR m_pCurrent; + + private void Init(EXPR pExpr) + { + if (pExpr == null) + { + m_pList = null; + m_pCurrent = null; + } + else if (pExpr.isLIST()) + { + m_pList = pExpr.asLIST(); + m_pCurrent = m_pList.GetOptionalElement(); + } + else + { + m_pList = null; + m_pCurrent = pExpr; + } + } + } +} diff --git a/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/Tree/Field.cs b/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/Tree/Field.cs new file mode 100644 index 000000000..4a6c62951 --- /dev/null +++ b/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/Tree/Field.cs @@ -0,0 +1,16 @@ +// ==++== +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// ==--== + +namespace Microsoft.CSharp.RuntimeBinder.Semantics +{ + internal class EXPRFIELD : EXPR + { + public EXPR OptionalObject; + public EXPR GetOptionalObject() { return OptionalObject; } + public void SetOptionalObject(EXPR value) { OptionalObject = value; } + public FieldWithType fwt; + } +} diff --git a/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/Tree/FieldInfo.cs b/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/Tree/FieldInfo.cs new file mode 100644 index 000000000..1ef411762 --- /dev/null +++ b/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/Tree/FieldInfo.cs @@ -0,0 +1,27 @@ +// ==++== +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// ==--== + +namespace Microsoft.CSharp.RuntimeBinder.Semantics +{ + internal class EXPRFIELDINFO : EXPR + { + public FieldSymbol Field() + { + return field; + } + public AggregateType FieldType() + { + return fieldType; + } + public void Init(FieldSymbol f, AggregateType ft) + { + field = f; + fieldType = ft; + } + private FieldSymbol field; + private AggregateType fieldType; + } +} diff --git a/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/Tree/HoistedLocal.cs b/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/Tree/HoistedLocal.cs new file mode 100644 index 000000000..a5889bef7 --- /dev/null +++ b/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/Tree/HoistedLocal.cs @@ -0,0 +1,12 @@ +// ==++== +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// ==--== + +namespace Microsoft.CSharp.RuntimeBinder.Semantics +{ + internal class EXPRHOISTEDLOCALEXPR : EXPR + { + } +} diff --git a/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/Tree/List.cs b/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/Tree/List.cs new file mode 100644 index 000000000..9977125f1 --- /dev/null +++ b/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/Tree/List.cs @@ -0,0 +1,21 @@ +// ==++== +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// ==--== + +namespace Microsoft.CSharp.RuntimeBinder.Semantics +{ + internal class EXPRLIST : EXPR + { + // UNDONE: DevDivBugs 49559 - Make the EXPRLIST actually store an ATLList instead + // of using this funny chaining thing. Note that the very last LIST node will have its + // NextListNode be the last element. + public EXPR OptionalElement; + public EXPR GetOptionalElement() { return OptionalElement; } + public void SetOptionalElement(EXPR value) { OptionalElement = value; } + public EXPR OptionalNextListNode; + public EXPR GetOptionalNextListNode() { return OptionalNextListNode; } + public void SetOptionalNextListNode(EXPR value) { OptionalNextListNode = value; } + } +} diff --git a/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/Tree/LocalVariable.cs b/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/Tree/LocalVariable.cs new file mode 100644 index 000000000..c8e5b0d91 --- /dev/null +++ b/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/Tree/LocalVariable.cs @@ -0,0 +1,13 @@ +// ==++== +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// ==--== + +namespace Microsoft.CSharp.RuntimeBinder.Semantics +{ + internal class EXPRLOCAL : EXPR + { + public LocalVariableSymbol local; + } +} diff --git a/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/Tree/MemberGroup.cs b/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/Tree/MemberGroup.cs new file mode 100644 index 000000000..2cc48fbc4 --- /dev/null +++ b/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/Tree/MemberGroup.cs @@ -0,0 +1,43 @@ +// ==++== +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// ==--== + +using Microsoft.CSharp.RuntimeBinder.Syntax; + +namespace Microsoft.CSharp.RuntimeBinder.Semantics +{ + internal class EXPRMEMGRP : EXPR + { + public Name name; + public TypeArray typeArgs; + public SYMKIND sk; + // The type containing the members. This may be a TypeParameterType or an AggregateType. + // This may be NULL (if types is not NULL). + + // This is nomally NULL - meaning the particular member + // is unknown. For the invoke method on a delegate it is typically known. + // After binding the group to arguments, this is set to the meth sym that is bound. + // The object expression. NULL for a static invocation. + public EXPR OptionalObject; + public EXPR GetOptionalObject() { return OptionalObject; } + public void SetOptionalObject(EXPR value) { OptionalObject = value; } + // The lhs that was bound to resolve this invocation. Set for static invocations only. + // We dont use the regular defines because we dont want to visit this guy in regular visitors, + // just in the visitors that create the node maps for LAF and refactoring. + public EXPR OptionalLHS; + public EXPR GetOptionalLHS() { return OptionalLHS; } + public void SetOptionalLHS(EXPR lhs) { OptionalLHS = lhs; } + // The owning call of this member group. NEVER visit this thing. + // The list of the methods that the memgroup is binding to. This list is formulated after binding + // the name of the method. When we've attempted to bind the arguments, we populate the MethPropWithInst list. + public CMemberLookupResults MemberLookupResults; + public CMemberLookupResults GetMemberLookupResults() { return MemberLookupResults; } + public void SetMemberLookupResults(CMemberLookupResults results) { MemberLookupResults = results; } + public CType ParentType; + public CType GetParentType() { return ParentType; } + public void SetParentType(CType type) { ParentType = type; } + public bool isDelegate() { return (flags & EXPRFLAG.EXF_DELEGATE) != 0; } + } +} diff --git a/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/Tree/MethodInfo.cs b/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/Tree/MethodInfo.cs new file mode 100644 index 000000000..a5c214379 --- /dev/null +++ b/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/Tree/MethodInfo.cs @@ -0,0 +1,13 @@ +// ==++== +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// ==--== + +namespace Microsoft.CSharp.RuntimeBinder.Semantics +{ + internal class EXPRMETHODINFO : EXPR + { + public MethWithInst Method; + } +} diff --git a/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/Tree/MethodReference.cs b/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/Tree/MethodReference.cs new file mode 100644 index 000000000..d08ff6902 --- /dev/null +++ b/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/Tree/MethodReference.cs @@ -0,0 +1,15 @@ +// ==++== +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// ==--== + +namespace Microsoft.CSharp.RuntimeBinder.Semantics +{ + internal class EXPRFUNCPTR : EXPR + { + public MethWithInst mwi; + public EXPR OptionalObject; + public void SetOptionalObject(EXPR value) { OptionalObject = value; } + } +} diff --git a/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/Tree/NamedArgumentSpecification.cs b/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/Tree/NamedArgumentSpecification.cs new file mode 100644 index 000000000..5ecfa6bc2 --- /dev/null +++ b/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/Tree/NamedArgumentSpecification.cs @@ -0,0 +1,14 @@ +// ==++== +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// ==--== + +namespace Microsoft.CSharp.RuntimeBinder.Semantics +{ + internal class EXPRNamedArgumentSpecification : EXPR + { + public Microsoft.CSharp.RuntimeBinder.Syntax.Name Name; + public EXPR Value; + } +} diff --git a/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/Tree/Property.cs b/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/Tree/Property.cs new file mode 100644 index 000000000..c7200b5b4 --- /dev/null +++ b/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/Tree/Property.cs @@ -0,0 +1,33 @@ +// ==++== +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// ==--== + +namespace Microsoft.CSharp.RuntimeBinder.Semantics +{ + internal class EXPRPROP : EXPR + { + // If we have this.prop = 123, but the implementation of the property is in the + // base class, then the object is of the base class type. Note that to get + // the object, we must go through the MEMGRP. + // + // "throughObject" is + // of the type we are actually calling through. (We need to know the + // "through" type to ensure that protected semantics are correctly enforced.) + + public EXPR OptionalArguments; + public EXPR GetOptionalArguments() { return OptionalArguments; } + public void SetOptionalArguments(EXPR value) { OptionalArguments = value; } + public EXPRMEMGRP MemberGroup; + public EXPRMEMGRP GetMemberGroup() { return MemberGroup; } + public void SetMemberGroup(EXPRMEMGRP value) { MemberGroup = value; } + public EXPR OptionalObjectThrough; + public EXPR GetOptionalObjectThrough() { return OptionalObjectThrough; } + public void SetOptionalObjectThrough(EXPR value) { OptionalObjectThrough = value; } + + public PropWithType pwtSlot; + public MethWithType mwtSet; + public bool isBaseCall() { return 0 != (flags & EXPRFLAG.EXF_BASECALL); } + } +} diff --git a/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/Tree/PropertyInfo.cs b/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/Tree/PropertyInfo.cs new file mode 100644 index 000000000..ec0daec5d --- /dev/null +++ b/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/Tree/PropertyInfo.cs @@ -0,0 +1,13 @@ +// ==++== +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// ==--== + +namespace Microsoft.CSharp.RuntimeBinder.Semantics +{ + internal class EXPRPropertyInfo : EXPR + { + public PropWithType Property; + } +} diff --git a/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/Tree/Return.cs b/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/Tree/Return.cs new file mode 100644 index 000000000..0caafa2dd --- /dev/null +++ b/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/Tree/Return.cs @@ -0,0 +1,16 @@ +// ==++== +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// ==--== + +namespace Microsoft.CSharp.RuntimeBinder.Semantics +{ + internal class EXPRRETURN : EXPRSTMT + { + // Return object is optional because of void returns. + public EXPR OptionalObject; + public EXPR GetOptionalObject() { return OptionalObject; } + public void SetOptionalObject(EXPR value) { OptionalObject = value; } + } +} diff --git a/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/Tree/Statement.cs b/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/Tree/Statement.cs new file mode 100644 index 000000000..798a69a08 --- /dev/null +++ b/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/Tree/Statement.cs @@ -0,0 +1,21 @@ +// ==++== +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// ==--== + +namespace Microsoft.CSharp.RuntimeBinder.Semantics +{ + internal abstract class EXPRSTMT : EXPR + { + private EXPRSTMT NextStatement; + public EXPRSTMT GetOptionalNextStatement() + { + return NextStatement; + } + public void SetOptionalNextStatement(EXPRSTMT nextStatement) + { + NextStatement = nextStatement; + } + } +} diff --git a/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/Tree/Temporary.cs b/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/Tree/Temporary.cs new file mode 100644 index 000000000..fc0843c54 --- /dev/null +++ b/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/Tree/Temporary.cs @@ -0,0 +1,15 @@ +// ==++== +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// ==--== + +namespace Microsoft.CSharp.RuntimeBinder.Semantics +{ + internal class EXPRWRAP : EXPR + { + public EXPR OptionalExpression; + public EXPR GetOptionalExpression() { return OptionalExpression; } + public void SetOptionalExpression(EXPR value) { OptionalExpression = value; } + } +} diff --git a/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/Tree/This.cs b/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/Tree/This.cs new file mode 100644 index 000000000..fb95defce --- /dev/null +++ b/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/Tree/This.cs @@ -0,0 +1,12 @@ +// ==++== +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// ==--== + +namespace Microsoft.CSharp.RuntimeBinder.Semantics +{ + internal class EXPRTHISPOINTER : EXPRLOCAL + { + } +} diff --git a/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/Tree/TypeArguments.cs b/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/Tree/TypeArguments.cs new file mode 100644 index 000000000..7f2364ec9 --- /dev/null +++ b/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/Tree/TypeArguments.cs @@ -0,0 +1,20 @@ +// ==++== +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// ==--== + +namespace Microsoft.CSharp.RuntimeBinder.Semantics +{ + /************************************************************************************************* + This wraps the type arguments for a class. It contains the TypeArray which is + associated with the AggregateType for the instantiation of the class. + *************************************************************************************************/ + + internal class EXPRTYPEARGUMENTS : EXPR + { + public EXPR OptionalElements; + public EXPR GetOptionalElements() { return OptionalElements; } + public void SetOptionalElements(EXPR value) { OptionalElements = value; } + } +} diff --git a/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/Tree/TypeOf.cs b/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/Tree/TypeOf.cs new file mode 100644 index 000000000..3b94af138 --- /dev/null +++ b/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/Tree/TypeOf.cs @@ -0,0 +1,15 @@ +// ==++== +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// ==--== + +namespace Microsoft.CSharp.RuntimeBinder.Semantics +{ + internal class EXPRTYPEOF : EXPR + { + public EXPRTYPEORNAMESPACE SourceType; + public EXPRTYPEORNAMESPACE GetSourceType() { return SourceType; } + public void SetSourceType(EXPRTYPEORNAMESPACE value) { SourceType = value; } + } +} diff --git a/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/Tree/TypeOrNamespace.cs b/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/Tree/TypeOrNamespace.cs new file mode 100644 index 000000000..b7cb90c0a --- /dev/null +++ b/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/Tree/TypeOrNamespace.cs @@ -0,0 +1,18 @@ +// ==++== +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// ==--== + +namespace Microsoft.CSharp.RuntimeBinder.Semantics +{ + /************************************************************************************************* + This is the base class for this set of EXPRs. When binding a type, the result + must be a type or a namespace. This EXPR encapsulates that fact. The lhs member is the EXPR + tree that was bound to resolve the type or namespace. + *************************************************************************************************/ + internal class EXPRTYPEORNAMESPACE : EXPR + { + public ITypeOrNamespace TypeOrNamespace; + } +} diff --git a/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/Tree/UnaryOperator.cs b/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/Tree/UnaryOperator.cs new file mode 100644 index 000000000..dfe65a64e --- /dev/null +++ b/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/Tree/UnaryOperator.cs @@ -0,0 +1,16 @@ +// ==++== +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// ==--== + +namespace Microsoft.CSharp.RuntimeBinder.Semantics +{ + internal class EXPRUNARYOP : EXPR + { + public EXPR Child; + public EXPR OptionalUserDefinedCall; + public MethWithInst predefinedMethodToCall; + public MethPropWithInst UserDefinedCallMethod; + } +} diff --git a/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/Tree/UnboundAnonymousFunction.cs b/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/Tree/UnboundAnonymousFunction.cs new file mode 100644 index 000000000..cfcbf4c4e --- /dev/null +++ b/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/Tree/UnboundAnonymousFunction.cs @@ -0,0 +1,12 @@ +// ==++== +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// ==--== + +namespace Microsoft.CSharp.RuntimeBinder.Semantics +{ + internal class EXPRUNBOUNDLAMBDA : EXPR + { + } +} diff --git a/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/Tree/UserDefinedConversion.cs b/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/Tree/UserDefinedConversion.cs new file mode 100644 index 000000000..02296880b --- /dev/null +++ b/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/Tree/UserDefinedConversion.cs @@ -0,0 +1,15 @@ +// ==++== +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// ==--== + +namespace Microsoft.CSharp.RuntimeBinder.Semantics +{ + internal class EXPRUSERDEFINEDCONVERSION : EXPR + { + public EXPR Argument; + public EXPR UserDefinedCall; + public MethWithInst UserDefinedCallMethod; + } +} diff --git a/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/Tree/UserDefinedLogicalOperator.cs b/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/Tree/UserDefinedLogicalOperator.cs new file mode 100644 index 000000000..51d11da99 --- /dev/null +++ b/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/Tree/UserDefinedLogicalOperator.cs @@ -0,0 +1,15 @@ +// ==++== +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// ==--== + +namespace Microsoft.CSharp.RuntimeBinder.Semantics +{ + internal class EXPRUSERLOGOP : EXPR + { + public EXPR TrueFalseCall; + public EXPRCALL OperatorCall; + public EXPR FirstOperandToExamine; + } +} diff --git a/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/Tree/Visitors/ExprVisitorBase.cs b/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/Tree/Visitors/ExprVisitorBase.cs new file mode 100644 index 000000000..5a444a97f --- /dev/null +++ b/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/Tree/Visitors/ExprVisitorBase.cs @@ -0,0 +1,1033 @@ +// ==++== +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// ==--== + +using System.Diagnostics; + +namespace Microsoft.CSharp.RuntimeBinder.Semantics +{ + class ExprVisitorBase + { + public EXPR Visit(EXPR pExpr) + { + if (pExpr == null) + { + return null; + } + + EXPR pResult; + if (IsCachedExpr(pExpr, out pResult)) + { + return pResult; + } + + if (pExpr.isSTMT()) + { + return CacheExprMapping(pExpr, DispatchStatementList(pExpr.asSTMT())); + } + + return CacheExprMapping(pExpr, Dispatch(pExpr)); + } + + ///////////////////////////////////////////////////////////////////////////////// + + protected EXPRSTMT DispatchStatementList(EXPRSTMT expr) + { + Debug.Assert(expr != null); + + EXPRSTMT first = expr; + EXPRSTMT pexpr = first; + + while (pexpr != null) + { + // If the processor replaces the statement -- potentially with + // null, another statement, or a list of statements -- then we + // make sure that the statement list is hooked together correctly. + + EXPRSTMT next = pexpr.GetOptionalNextStatement(); + EXPRSTMT old = pexpr; + + // Unhook the next one. + pexpr.SetOptionalNextStatement(null); + + EXPR result = Dispatch(pexpr); + Debug.Assert(result == null || result.isSTMT()); + + if (pexpr == first) + { + first = (result == null) ? null : result.asSTMT(); + } + else + { + pexpr.SetOptionalNextStatement((result == null) ? null : result.asSTMT()); + } + + // A transformation may return back a list of statements (or + // if the statements have been determined to be unnecessary, + // perhaps it has simply returned null.) + // + // Skip visiting the new list, then hook the tail of the old list + // up to the end of the new list. + + while (pexpr.GetOptionalNextStatement() != null) + { + pexpr = pexpr.GetOptionalNextStatement(); + } + + // Re-hook the next pointer. + pexpr.SetOptionalNextStatement(next); + } + return first; + } + + ///////////////////////////////////////////////////////////////////////////////// + + protected bool IsCachedExpr(EXPR pExpr, out EXPR pTransformedExpr) + { + pTransformedExpr = null; + return false; + } + + ///////////////////////////////////////////////////////////////////////////////// + + protected EXPR CacheExprMapping(EXPR pExpr, EXPR pTransformedExpr) + { + return pTransformedExpr; + } + + protected virtual EXPR Dispatch(EXPR pExpr) + { + switch (pExpr.kind) + { + case ExpressionKind.EK_BLOCK: + return VisitBLOCK(pExpr as EXPRBLOCK); + case ExpressionKind.EK_RETURN: + return VisitRETURN(pExpr as EXPRRETURN); + case ExpressionKind.EK_BINOP: + return VisitBINOP(pExpr as EXPRBINOP); + case ExpressionKind.EK_UNARYOP: + return VisitUNARYOP(pExpr as EXPRUNARYOP); + case ExpressionKind.EK_ASSIGNMENT: + return VisitASSIGNMENT(pExpr as EXPRASSIGNMENT); + case ExpressionKind.EK_LIST: + return VisitLIST(pExpr as EXPRLIST); + case ExpressionKind.EK_QUESTIONMARK: + return VisitQUESTIONMARK(pExpr as EXPRQUESTIONMARK); + case ExpressionKind.EK_ARRAYINDEX: + return VisitARRAYINDEX(pExpr as EXPRARRAYINDEX); + case ExpressionKind.EK_ARRAYLENGTH: + return VisitARRAYLENGTH(pExpr as EXPRARRAYLENGTH); + case ExpressionKind.EK_CALL: + return VisitCALL(pExpr as EXPRCALL); + case ExpressionKind.EK_EVENT: + return VisitEVENT(pExpr as EXPREVENT); + case ExpressionKind.EK_FIELD: + return VisitFIELD(pExpr as EXPRFIELD); + case ExpressionKind.EK_LOCAL: + return VisitLOCAL(pExpr as EXPRLOCAL); + case ExpressionKind.EK_THISPOINTER: + return VisitTHISPOINTER(pExpr as EXPRTHISPOINTER); + case ExpressionKind.EK_CONSTANT: + return VisitCONSTANT(pExpr as EXPRCONSTANT); + case ExpressionKind.EK_TYPEARGUMENTS: + return VisitTYPEARGUMENTS(pExpr as EXPRTYPEARGUMENTS); + case ExpressionKind.EK_TYPEORNAMESPACE: + return VisitTYPEORNAMESPACE(pExpr as EXPRTYPEORNAMESPACE); + case ExpressionKind.EK_CLASS: + return VisitCLASS(pExpr as EXPRCLASS); + case ExpressionKind.EK_FUNCPTR: + return VisitFUNCPTR(pExpr as EXPRFUNCPTR); + case ExpressionKind.EK_PROP: + return VisitPROP(pExpr as EXPRPROP); + case ExpressionKind.EK_MULTI: + return VisitMULTI(pExpr as EXPRMULTI); + case ExpressionKind.EK_MULTIGET: + return VisitMULTIGET(pExpr as EXPRMULTIGET); + case ExpressionKind.EK_WRAP: + return VisitWRAP(pExpr as EXPRWRAP); + case ExpressionKind.EK_CONCAT: + return VisitCONCAT(pExpr as EXPRCONCAT); + case ExpressionKind.EK_ARRINIT: + return VisitARRINIT(pExpr as EXPRARRINIT); + case ExpressionKind.EK_CAST: + return VisitCAST(pExpr as EXPRCAST); + case ExpressionKind.EK_USERDEFINEDCONVERSION: + return VisitUSERDEFINEDCONVERSION(pExpr as EXPRUSERDEFINEDCONVERSION); + case ExpressionKind.EK_TYPEOF: + return VisitTYPEOF(pExpr as EXPRTYPEOF); + case ExpressionKind.EK_ZEROINIT: + return VisitZEROINIT(pExpr as EXPRZEROINIT); + case ExpressionKind.EK_USERLOGOP: + return VisitUSERLOGOP(pExpr as EXPRUSERLOGOP); + case ExpressionKind.EK_MEMGRP: + return VisitMEMGRP(pExpr as EXPRMEMGRP); + case ExpressionKind.EK_BOUNDLAMBDA: + return VisitBOUNDLAMBDA(pExpr as EXPRBOUNDLAMBDA); + case ExpressionKind.EK_UNBOUNDLAMBDA: + return VisitUNBOUNDLAMBDA(pExpr as EXPRUNBOUNDLAMBDA); + case ExpressionKind.EK_HOISTEDLOCALEXPR: + return VisitHOISTEDLOCALEXPR(pExpr as EXPRHOISTEDLOCALEXPR); + case ExpressionKind.EK_FIELDINFO: + return VisitFIELDINFO(pExpr as EXPRFIELDINFO); + case ExpressionKind.EK_METHODINFO: + return VisitMETHODINFO(pExpr as EXPRMETHODINFO); + + // Binary operators + case ExpressionKind.EK_EQUALS: + return VisitEQUALS(pExpr.asBIN()); + case ExpressionKind.EK_COMPARE: + return VisitCOMPARE(pExpr.asBIN()); + case ExpressionKind.EK_NE: + return VisitNE(pExpr.asBIN()); + case ExpressionKind.EK_LT: + return VisitLT(pExpr.asBIN()); + case ExpressionKind.EK_LE: + return VisitLE(pExpr.asBIN()); + case ExpressionKind.EK_GT: + return VisitGT(pExpr.asBIN()); + case ExpressionKind.EK_GE: + return VisitGE(pExpr.asBIN()); + case ExpressionKind.EK_ADD: + return VisitADD(pExpr.asBIN()); + case ExpressionKind.EK_SUB: + return VisitSUB(pExpr.asBIN()); + case ExpressionKind.EK_MUL: + return VisitMUL(pExpr.asBIN()); + case ExpressionKind.EK_DIV: + return VisitDIV(pExpr.asBIN()); + case ExpressionKind.EK_MOD: + return VisitMOD(pExpr.asBIN()); + case ExpressionKind.EK_BITAND: + return VisitBITAND(pExpr.asBIN()); + case ExpressionKind.EK_BITOR: + return VisitBITOR(pExpr.asBIN()); + case ExpressionKind.EK_BITXOR: + return VisitBITXOR(pExpr.asBIN()); + case ExpressionKind.EK_LSHIFT: + return VisitLSHIFT(pExpr.asBIN()); + case ExpressionKind.EK_RSHIFT: + return VisitRSHIFT(pExpr.asBIN()); + case ExpressionKind.EK_LOGAND: + return VisitLOGAND(pExpr.asBIN()); + case ExpressionKind.EK_LOGOR: + return VisitLOGOR(pExpr.asBIN()); + case ExpressionKind.EK_SEQUENCE: + return VisitSEQUENCE(pExpr.asBIN()); + case ExpressionKind.EK_SEQREV: + return VisitSEQREV(pExpr.asBIN()); + case ExpressionKind.EK_SAVE: + return VisitSAVE(pExpr.asBIN()); + case ExpressionKind.EK_SWAP: + return VisitSWAP(pExpr.asBIN()); + case ExpressionKind.EK_INDIR: + return VisitINDIR(pExpr.asBIN()); + case ExpressionKind.EK_STRINGEQ: + return VisitSTRINGEQ(pExpr.asBIN()); + case ExpressionKind.EK_STRINGNE: + return VisitSTRINGNE(pExpr.asBIN()); + case ExpressionKind.EK_DELEGATEEQ: + return VisitDELEGATEEQ(pExpr.asBIN()); + case ExpressionKind.EK_DELEGATENE: + return VisitDELEGATENE(pExpr.asBIN()); + case ExpressionKind.EK_DELEGATEADD: + return VisitDELEGATEADD(pExpr.asBIN()); + case ExpressionKind.EK_DELEGATESUB: + return VisitDELEGATESUB(pExpr.asBIN()); + case ExpressionKind.EK_EQ: + return VisitEQ(pExpr.asBIN()); + + // Unary operators + case ExpressionKind.EK_TRUE: + return VisitTRUE(pExpr.asUnaryOperator()); + case ExpressionKind.EK_FALSE: + return VisitFALSE(pExpr.asUnaryOperator()); + case ExpressionKind.EK_INC: + return VisitINC(pExpr.asUnaryOperator()); + case ExpressionKind.EK_DEC: + return VisitDEC(pExpr.asUnaryOperator()); + case ExpressionKind.EK_LOGNOT: + return VisitLOGNOT(pExpr.asUnaryOperator()); + case ExpressionKind.EK_NEG: + return VisitNEG(pExpr.asUnaryOperator()); + case ExpressionKind.EK_UPLUS: + return VisitUPLUS(pExpr.asUnaryOperator()); + case ExpressionKind.EK_BITNOT: + return VisitBITNOT(pExpr.asUnaryOperator()); + case ExpressionKind.EK_ADDR: + return VisitADDR(pExpr.asUnaryOperator()); + case ExpressionKind.EK_DECIMALNEG: + return VisitDECIMALNEG(pExpr.asUnaryOperator()); + case ExpressionKind.EK_DECIMALINC: + return VisitDECIMALINC(pExpr.asUnaryOperator()); + case ExpressionKind.EK_DECIMALDEC: + return VisitDECIMALDEC(pExpr.asUnaryOperator()); + default: + throw Error.InternalCompilerError(); + } + } + protected void VisitChildren(EXPR pExpr) + { + Debug.Assert(pExpr != null); + + EXPR exprRet = null; + + // Lists are a special case. We treat a list not as a + // binary node but rather as a node with n children. + if (pExpr.isLIST()) + { + EXPRLIST list = pExpr.asLIST(); + while (true) + { + list.SetOptionalElement(Visit(list.GetOptionalElement())); + if (list.GetOptionalNextListNode() == null) + { + return; + } + if (!list.GetOptionalNextListNode().isLIST()) + { + list.SetOptionalNextListNode(Visit(list.GetOptionalNextListNode())); + return; + } + list = list.GetOptionalNextListNode().asLIST(); + } + } + + switch (pExpr.kind) + { + default: + if (pExpr.isUnaryOperator()) + { + goto VISIT_EXPRUNARYOP; + } + Debug.Assert(pExpr.isBIN()); + goto VISIT_EXPRBINOP; + + VISIT_EXPR: + break; + VISIT_BASE_EXPRSTMT: + goto VISIT_EXPR; + VISIT_EXPRSTMT: + goto VISIT_BASE_EXPRSTMT; + + case ExpressionKind.EK_BINOP: + goto VISIT_EXPRBINOP; + VISIT_BASE_EXPRBINOP: + goto VISIT_EXPR; + VISIT_EXPRBINOP: + exprRet = Visit((pExpr as EXPRBINOP).GetOptionalLeftChild()); + (pExpr as EXPRBINOP).SetOptionalLeftChild(exprRet as EXPR); + exprRet = Visit((pExpr as EXPRBINOP).GetOptionalRightChild()); + (pExpr as EXPRBINOP).SetOptionalRightChild(exprRet as EXPR); + goto VISIT_BASE_EXPRBINOP; + + case ExpressionKind.EK_LIST: + goto VISIT_EXPRLIST; + VISIT_BASE_EXPRLIST: + goto VISIT_EXPR; + VISIT_EXPRLIST: + exprRet = Visit((pExpr as EXPRLIST).GetOptionalElement()); + (pExpr as EXPRLIST).SetOptionalElement(exprRet as EXPR); + exprRet = Visit((pExpr as EXPRLIST).GetOptionalNextListNode()); + (pExpr as EXPRLIST).SetOptionalNextListNode(exprRet as EXPR); + goto VISIT_BASE_EXPRLIST; + + case ExpressionKind.EK_ASSIGNMENT: + goto VISIT_EXPRASSIGNMENT; + VISIT_BASE_EXPRASSIGNMENT: + goto VISIT_EXPR; + VISIT_EXPRASSIGNMENT: + exprRet = Visit((pExpr as EXPRASSIGNMENT).GetLHS()); + Debug.Assert(exprRet != null); + (pExpr as EXPRASSIGNMENT).SetLHS(exprRet as EXPR); + exprRet = Visit((pExpr as EXPRASSIGNMENT).GetRHS()); + Debug.Assert(exprRet != null); + (pExpr as EXPRASSIGNMENT).SetRHS(exprRet as EXPR); + goto VISIT_BASE_EXPRASSIGNMENT; + + case ExpressionKind.EK_QUESTIONMARK: + goto VISIT_EXPRQUESTIONMARK; + VISIT_BASE_EXPRQUESTIONMARK: + goto VISIT_EXPR; + VISIT_EXPRQUESTIONMARK: + exprRet = Visit((pExpr as EXPRQUESTIONMARK).GetTestExpression()); + Debug.Assert(exprRet != null); + (pExpr as EXPRQUESTIONMARK).SetTestExpression(exprRet as EXPR); + exprRet = Visit((pExpr as EXPRQUESTIONMARK).GetConsequence()); + Debug.Assert(exprRet != null); + (pExpr as EXPRQUESTIONMARK).SetConsequence(exprRet as EXPRBINOP); + goto VISIT_BASE_EXPRQUESTIONMARK; + + case ExpressionKind.EK_ARRAYINDEX: + goto VISIT_EXPRARRAYINDEX; + VISIT_BASE_EXPRARRAYINDEX: + goto VISIT_EXPR; + VISIT_EXPRARRAYINDEX: + exprRet = Visit((pExpr as EXPRARRAYINDEX).GetArray()); + Debug.Assert(exprRet != null); + (pExpr as EXPRARRAYINDEX).SetArray(exprRet as EXPR); + exprRet = Visit((pExpr as EXPRARRAYINDEX).GetIndex()); + Debug.Assert(exprRet != null); + (pExpr as EXPRARRAYINDEX).SetIndex(exprRet as EXPR); + goto VISIT_BASE_EXPRARRAYINDEX; + + case ExpressionKind.EK_ARRAYLENGTH: + goto VISIT_EXPRARRAYLENGTH; + VISIT_BASE_EXPRARRAYLENGTH: + goto VISIT_EXPR; + VISIT_EXPRARRAYLENGTH: + exprRet = Visit((pExpr as EXPRARRAYLENGTH).GetArray()); + Debug.Assert(exprRet != null); + (pExpr as EXPRARRAYLENGTH).SetArray(exprRet as EXPR); + goto VISIT_BASE_EXPRARRAYLENGTH; + + case ExpressionKind.EK_UNARYOP: + goto VISIT_EXPRUNARYOP; + VISIT_BASE_EXPRUNARYOP: + goto VISIT_EXPR; + VISIT_EXPRUNARYOP: + exprRet = Visit((pExpr as EXPRUNARYOP).Child); + Debug.Assert(exprRet != null); + (pExpr as EXPRUNARYOP).Child = exprRet as EXPR; + goto VISIT_BASE_EXPRUNARYOP; + + case ExpressionKind.EK_USERLOGOP: + goto VISIT_EXPRUSERLOGOP; + VISIT_BASE_EXPRUSERLOGOP: + goto VISIT_EXPR; + VISIT_EXPRUSERLOGOP: + exprRet = Visit((pExpr as EXPRUSERLOGOP).TrueFalseCall); + Debug.Assert(exprRet != null); + (pExpr as EXPRUSERLOGOP).TrueFalseCall = exprRet as EXPR; + exprRet = Visit((pExpr as EXPRUSERLOGOP).OperatorCall); + Debug.Assert(exprRet != null); + (pExpr as EXPRUSERLOGOP).OperatorCall = exprRet as EXPRCALL; + exprRet = Visit((pExpr as EXPRUSERLOGOP).FirstOperandToExamine); + Debug.Assert(exprRet != null); + (pExpr as EXPRUSERLOGOP).FirstOperandToExamine = exprRet as EXPR; + goto VISIT_BASE_EXPRUSERLOGOP; + + case ExpressionKind.EK_TYPEOF: + goto VISIT_EXPRTYPEOF; + VISIT_BASE_EXPRTYPEOF: + goto VISIT_EXPR; + VISIT_EXPRTYPEOF: + exprRet = Visit((pExpr as EXPRTYPEOF).GetSourceType()); + (pExpr as EXPRTYPEOF).SetSourceType(exprRet as EXPRTYPEORNAMESPACE); + goto VISIT_BASE_EXPRTYPEOF; + + case ExpressionKind.EK_CAST: + goto VISIT_EXPRCAST; + VISIT_BASE_EXPRCAST: + goto VISIT_EXPR; + VISIT_EXPRCAST: + exprRet = Visit((pExpr as EXPRCAST).GetArgument()); + Debug.Assert(exprRet != null); + (pExpr as EXPRCAST).SetArgument(exprRet as EXPR); + exprRet = Visit((pExpr as EXPRCAST).GetDestinationType()); + (pExpr as EXPRCAST).SetDestinationType(exprRet as EXPRTYPEORNAMESPACE); + goto VISIT_BASE_EXPRCAST; + + case ExpressionKind.EK_USERDEFINEDCONVERSION: + goto VISIT_EXPRUSERDEFINEDCONVERSION; + VISIT_BASE_EXPRUSERDEFINEDCONVERSION: + goto VISIT_EXPR; + VISIT_EXPRUSERDEFINEDCONVERSION: + exprRet = Visit((pExpr as EXPRUSERDEFINEDCONVERSION).UserDefinedCall); + Debug.Assert(exprRet != null); + (pExpr as EXPRUSERDEFINEDCONVERSION).UserDefinedCall = exprRet as EXPR; + goto VISIT_BASE_EXPRUSERDEFINEDCONVERSION; + + case ExpressionKind.EK_ZEROINIT: + goto VISIT_EXPRZEROINIT; + VISIT_BASE_EXPRZEROINIT: + goto VISIT_EXPR; + VISIT_EXPRZEROINIT: + exprRet = Visit((pExpr as EXPRZEROINIT).OptionalArgument); + (pExpr as EXPRZEROINIT).OptionalArgument = exprRet as EXPR; + // Used for when we zeroinit 0 parameter constructors for structs/enums. + exprRet = Visit((pExpr as EXPRZEROINIT).OptionalConstructorCall); + (pExpr as EXPRZEROINIT).OptionalConstructorCall = exprRet as EXPR; + goto VISIT_BASE_EXPRZEROINIT; + + case ExpressionKind.EK_BLOCK: + goto VISIT_EXPRBLOCK; + VISIT_BASE_EXPRBLOCK: + goto VISIT_EXPRSTMT; + VISIT_EXPRBLOCK: + exprRet = Visit((pExpr as EXPRBLOCK).GetOptionalStatements()); + (pExpr as EXPRBLOCK).SetOptionalStatements(exprRet as EXPRSTMT); + goto VISIT_BASE_EXPRBLOCK; + + case ExpressionKind.EK_MEMGRP: + goto VISIT_EXPRMEMGRP; + VISIT_BASE_EXPRMEMGRP: + goto VISIT_EXPR; + VISIT_EXPRMEMGRP: + // The object expression. NULL for a static invocation. + exprRet = Visit((pExpr as EXPRMEMGRP).GetOptionalObject()); + (pExpr as EXPRMEMGRP).SetOptionalObject(exprRet as EXPR); + goto VISIT_BASE_EXPRMEMGRP; + + case ExpressionKind.EK_CALL: + goto VISIT_EXPRCALL; + VISIT_BASE_EXPRCALL: + goto VISIT_EXPR; + VISIT_EXPRCALL: + exprRet = Visit((pExpr as EXPRCALL).GetOptionalArguments()); + (pExpr as EXPRCALL).SetOptionalArguments(exprRet as EXPR); + exprRet = Visit((pExpr as EXPRCALL).GetMemberGroup()); + Debug.Assert(exprRet != null); + (pExpr as EXPRCALL).SetMemberGroup(exprRet as EXPRMEMGRP); + goto VISIT_BASE_EXPRCALL; + + + case ExpressionKind.EK_PROP: + goto VISIT_EXPRPROP; + VISIT_BASE_EXPRPROP: + goto VISIT_EXPR; + VISIT_EXPRPROP: + exprRet = Visit((pExpr as EXPRPROP).GetOptionalArguments()); + (pExpr as EXPRPROP).SetOptionalArguments(exprRet as EXPR); + exprRet = Visit((pExpr as EXPRPROP).GetMemberGroup()); + Debug.Assert(exprRet != null); + (pExpr as EXPRPROP).SetMemberGroup(exprRet as EXPRMEMGRP); + goto VISIT_BASE_EXPRPROP; + + case ExpressionKind.EK_FIELD: + goto VISIT_EXPRFIELD; + VISIT_BASE_EXPRFIELD: + goto VISIT_EXPR; + VISIT_EXPRFIELD: + exprRet = Visit((pExpr as EXPRFIELD).GetOptionalObject()); + (pExpr as EXPRFIELD).SetOptionalObject(exprRet as EXPR); + goto VISIT_BASE_EXPRFIELD; + + case ExpressionKind.EK_EVENT: + goto VISIT_EXPREVENT; + VISIT_BASE_EXPREVENT: + goto VISIT_EXPR; + VISIT_EXPREVENT: + exprRet = Visit((pExpr as EXPREVENT).OptionalObject); + (pExpr as EXPREVENT).OptionalObject = exprRet as EXPR; + goto VISIT_BASE_EXPREVENT; + + case ExpressionKind.EK_LOCAL: + goto VISIT_EXPRLOCAL; + VISIT_BASE_EXPRLOCAL: + goto VISIT_EXPR; + VISIT_EXPRLOCAL: + goto VISIT_BASE_EXPRLOCAL; + + case ExpressionKind.EK_THISPOINTER: + goto VISIT_EXPRTHISPOINTER; + VISIT_BASE_EXPRTHISPOINTER: + goto VISIT_EXPRLOCAL; + VISIT_EXPRTHISPOINTER: + goto VISIT_BASE_EXPRTHISPOINTER; + + case ExpressionKind.EK_RETURN: + goto VISIT_EXPRRETURN; + VISIT_BASE_EXPRRETURN: + goto VISIT_EXPRSTMT; + VISIT_EXPRRETURN: + exprRet = Visit((pExpr as EXPRRETURN).GetOptionalObject()); + (pExpr as EXPRRETURN).SetOptionalObject(exprRet as EXPR); + goto VISIT_BASE_EXPRRETURN; + + case ExpressionKind.EK_CONSTANT: + goto VISIT_EXPRCONSTANT; + VISIT_BASE_EXPRCONSTANT: + goto VISIT_EXPR; + VISIT_EXPRCONSTANT: + // Used for when we zeroinit 0 parameter constructors for structs/enums. + exprRet = Visit((pExpr as EXPRCONSTANT).GetOptionalConstructorCall()); + (pExpr as EXPRCONSTANT).SetOptionalConstructorCall(exprRet as EXPR); + goto VISIT_BASE_EXPRCONSTANT; + + /************************************************************************************************* + TYPEEXPRs defined: + + The following exprs are used to represent the results of type binding, and are defined as follows: + + TYPEARGUMENTS - This wraps the type arguments for a class. It contains the TypeArray* which is + associated with the AggregateType for the instantiation of the class. + + TYPEORNAMESPACE - This is the base class for this set of EXPRs. When binding a type, the result + must be a type or a namespace. This EXPR encapsulates that fact. The lhs member is the EXPR + tree that was bound to resolve the type or namespace. + + TYPEORNAMESPACEERROR - This is the error class for the type or namespace exprs when we dont know + what to bind it to. + + The following three exprs all have a TYPEORNAMESPACE child, which is their fundamental type: + POINTERTYPE - This wraps the sym for the pointer type. + NULLABLETYPE - This wraps the sym for the nullable type. + + CLASS - This represents an instantiation of a class. + + NSPACE - This represents a namespace, which is the intermediate step when attempting to bind + a qualified name. + + ALIAS - This represents an alias + + *************************************************************************************************/ + + case ExpressionKind.EK_TYPEARGUMENTS: + goto VISIT_EXPRTYPEARGUMENTS; + VISIT_BASE_EXPRTYPEARGUMENTS: + goto VISIT_EXPR; + VISIT_EXPRTYPEARGUMENTS: + exprRet = Visit((pExpr as EXPRTYPEARGUMENTS).GetOptionalElements()); + (pExpr as EXPRTYPEARGUMENTS).SetOptionalElements(exprRet as EXPR); + goto VISIT_BASE_EXPRTYPEARGUMENTS; + + case ExpressionKind.EK_TYPEORNAMESPACE: + goto VISIT_EXPRTYPEORNAMESPACE; + VISIT_BASE_EXPRTYPEORNAMESPACE: + goto VISIT_EXPR; + VISIT_EXPRTYPEORNAMESPACE: + goto VISIT_BASE_EXPRTYPEORNAMESPACE; + + case ExpressionKind.EK_CLASS: + goto VISIT_EXPRCLASS; + VISIT_BASE_EXPRCLASS: + goto VISIT_EXPRTYPEORNAMESPACE; + VISIT_EXPRCLASS: + goto VISIT_BASE_EXPRCLASS; + + case ExpressionKind.EK_FUNCPTR: + goto VISIT_EXPRFUNCPTR; + VISIT_BASE_EXPRFUNCPTR: + goto VISIT_EXPR; + VISIT_EXPRFUNCPTR: + goto VISIT_BASE_EXPRFUNCPTR; + + case ExpressionKind.EK_MULTIGET: + goto VISIT_EXPRMULTIGET; + VISIT_BASE_EXPRMULTIGET: + goto VISIT_EXPR; + VISIT_EXPRMULTIGET: + goto VISIT_BASE_EXPRMULTIGET; + + case ExpressionKind.EK_MULTI: + goto VISIT_EXPRMULTI; + VISIT_BASE_EXPRMULTI: + goto VISIT_EXPR; + VISIT_EXPRMULTI: + exprRet = Visit((pExpr as EXPRMULTI).GetLeft()); + Debug.Assert(exprRet != null); + (pExpr as EXPRMULTI).SetLeft(exprRet as EXPR); + exprRet = Visit((pExpr as EXPRMULTI).GetOperator()); + Debug.Assert(exprRet != null); + (pExpr as EXPRMULTI).SetOperator(exprRet as EXPR); + goto VISIT_BASE_EXPRMULTI; + + case ExpressionKind.EK_WRAP: + goto VISIT_EXPRWRAP; + VISIT_BASE_EXPRWRAP: + goto VISIT_EXPR; + VISIT_EXPRWRAP: + goto VISIT_BASE_EXPRWRAP; + + case ExpressionKind.EK_CONCAT: + goto VISIT_EXPRCONCAT; + VISIT_BASE_EXPRCONCAT: + goto VISIT_EXPR; + VISIT_EXPRCONCAT: + exprRet = Visit((pExpr as EXPRCONCAT).GetFirstArgument()); + Debug.Assert(exprRet != null); + (pExpr as EXPRCONCAT).SetFirstArgument(exprRet as EXPR); + exprRet = Visit((pExpr as EXPRCONCAT).GetSecondArgument()); + Debug.Assert(exprRet != null); + (pExpr as EXPRCONCAT).SetSecondArgument(exprRet as EXPR); + goto VISIT_BASE_EXPRCONCAT; + + case ExpressionKind.EK_ARRINIT: + goto VISIT_EXPRARRINIT; + VISIT_BASE_EXPRARRINIT: + goto VISIT_EXPR; + VISIT_EXPRARRINIT: + exprRet = Visit((pExpr as EXPRARRINIT).GetOptionalArguments()); + (pExpr as EXPRARRINIT).SetOptionalArguments(exprRet as EXPR); + exprRet = Visit((pExpr as EXPRARRINIT).GetOptionalArgumentDimensions()); + (pExpr as EXPRARRINIT).SetOptionalArgumentDimensions(exprRet as EXPR); + goto VISIT_BASE_EXPRARRINIT; + + case ExpressionKind.EK_NOOP: + goto VISIT_EXPRNOOP; + VISIT_BASE_EXPRNOOP: + goto VISIT_EXPRSTMT; + VISIT_EXPRNOOP: + goto VISIT_BASE_EXPRNOOP; + + case ExpressionKind.EK_BOUNDLAMBDA: + goto VISIT_EXPRBOUNDLAMBDA; + VISIT_BASE_EXPRBOUNDLAMBDA: + goto VISIT_EXPR; + VISIT_EXPRBOUNDLAMBDA: + exprRet = Visit((pExpr as EXPRBOUNDLAMBDA).OptionalBody); + (pExpr as EXPRBOUNDLAMBDA).OptionalBody = exprRet as EXPRBLOCK; + goto VISIT_BASE_EXPRBOUNDLAMBDA; + + case ExpressionKind.EK_UNBOUNDLAMBDA: + goto VISIT_EXPRUNBOUNDLAMBDA; + VISIT_BASE_EXPRUNBOUNDLAMBDA: + goto VISIT_EXPR; + VISIT_EXPRUNBOUNDLAMBDA: + goto VISIT_BASE_EXPRUNBOUNDLAMBDA; + + case ExpressionKind.EK_HOISTEDLOCALEXPR: + goto VISIT_EXPRHOISTEDLOCALEXPR; + VISIT_BASE_EXPRHOISTEDLOCALEXPR: + goto VISIT_EXPR; + VISIT_EXPRHOISTEDLOCALEXPR: + goto VISIT_BASE_EXPRHOISTEDLOCALEXPR; + + case ExpressionKind.EK_FIELDINFO: + goto VISIT_EXPRFIELDINFO; + VISIT_BASE_EXPRFIELDINFO: + goto VISIT_EXPR; + VISIT_EXPRFIELDINFO: + goto VISIT_BASE_EXPRFIELDINFO; + + case ExpressionKind.EK_METHODINFO: + goto VISIT_EXPRMETHODINFO; + VISIT_BASE_EXPRMETHODINFO: + goto VISIT_EXPR; + VISIT_EXPRMETHODINFO: + goto VISIT_BASE_EXPRMETHODINFO; + } + } + protected virtual EXPR VisitEXPR(EXPR pExpr) + { + VisitChildren(pExpr); + return pExpr; + } + protected virtual EXPR VisitBLOCK(EXPRBLOCK pExpr) + { + return VisitSTMT(pExpr); + } + protected virtual EXPR VisitTHISPOINTER(EXPRTHISPOINTER pExpr) + { + return VisitLOCAL(pExpr); + } + protected virtual EXPR VisitRETURN(EXPRRETURN pExpr) + { + return VisitSTMT(pExpr); + } + protected virtual EXPR VisitCLASS(EXPRCLASS pExpr) + { + return VisitTYPEORNAMESPACE(pExpr); + } + protected virtual EXPR VisitSTMT(EXPRSTMT pExpr) + { + return VisitEXPR(pExpr); + } + protected virtual EXPR VisitBINOP(EXPRBINOP pExpr) + { + return VisitEXPR(pExpr); + } + protected virtual EXPR VisitLIST(EXPRLIST pExpr) + { + return VisitEXPR(pExpr); + } + protected virtual EXPR VisitASSIGNMENT(EXPRASSIGNMENT pExpr) + { + return VisitEXPR(pExpr); + } + protected virtual EXPR VisitQUESTIONMARK(EXPRQUESTIONMARK pExpr) + { + return VisitEXPR(pExpr); + } + protected virtual EXPR VisitARRAYINDEX(EXPRARRAYINDEX pExpr) + { + return VisitEXPR(pExpr); + } + protected virtual EXPR VisitARRAYLENGTH(EXPRARRAYLENGTH pExpr) + { + return VisitEXPR(pExpr); + } + protected virtual EXPR VisitUNARYOP(EXPRUNARYOP pExpr) + { + return VisitEXPR(pExpr); + } + protected virtual EXPR VisitUSERLOGOP(EXPRUSERLOGOP pExpr) + { + return VisitEXPR(pExpr); + } + protected virtual EXPR VisitTYPEOF(EXPRTYPEOF pExpr) + { + return VisitEXPR(pExpr); + } + protected virtual EXPR VisitCAST(EXPRCAST pExpr) + { + return VisitEXPR(pExpr); + } + protected virtual EXPR VisitUSERDEFINEDCONVERSION(EXPRUSERDEFINEDCONVERSION pExpr) + { + return VisitEXPR(pExpr); + } + protected virtual EXPR VisitZEROINIT(EXPRZEROINIT pExpr) + { + return VisitEXPR(pExpr); + } + protected virtual EXPR VisitMEMGRP(EXPRMEMGRP pExpr) + { + return VisitEXPR(pExpr); + } + protected virtual EXPR VisitCALL(EXPRCALL pExpr) + { + return VisitEXPR(pExpr); + } + protected virtual EXPR VisitPROP(EXPRPROP pExpr) + { + return VisitEXPR(pExpr); + } + protected virtual EXPR VisitFIELD(EXPRFIELD pExpr) + { + return VisitEXPR(pExpr); + } + protected virtual EXPR VisitEVENT(EXPREVENT pExpr) + { + return VisitEXPR(pExpr); + } + protected virtual EXPR VisitLOCAL(EXPRLOCAL pExpr) + { + return VisitEXPR(pExpr); + } + protected virtual EXPR VisitCONSTANT(EXPRCONSTANT pExpr) + { + return VisitEXPR(pExpr); + } + protected virtual EXPR VisitTYPEARGUMENTS(EXPRTYPEARGUMENTS pExpr) + { + return VisitEXPR(pExpr); + } + protected virtual EXPR VisitTYPEORNAMESPACE(EXPRTYPEORNAMESPACE pExpr) + { + return VisitEXPR(pExpr); + } + protected virtual EXPR VisitFUNCPTR(EXPRFUNCPTR pExpr) + { + return VisitEXPR(pExpr); + } + protected virtual EXPR VisitMULTIGET(EXPRMULTIGET pExpr) + { + return VisitEXPR(pExpr); + } + protected virtual EXPR VisitMULTI(EXPRMULTI pExpr) + { + return VisitEXPR(pExpr); + } + protected virtual EXPR VisitWRAP(EXPRWRAP pExpr) + { + return VisitEXPR(pExpr); + } + protected virtual EXPR VisitCONCAT(EXPRCONCAT pExpr) + { + return VisitEXPR(pExpr); + } + protected virtual EXPR VisitARRINIT(EXPRARRINIT pExpr) + { + return VisitEXPR(pExpr); + } + protected virtual EXPR VisitBOUNDLAMBDA(EXPRBOUNDLAMBDA pExpr) + { + return VisitEXPR(pExpr); + } + protected virtual EXPR VisitUNBOUNDLAMBDA(EXPRUNBOUNDLAMBDA pExpr) + { + return VisitEXPR(pExpr); + } + protected virtual EXPR VisitHOISTEDLOCALEXPR(EXPRHOISTEDLOCALEXPR pExpr) + { + return VisitEXPR(pExpr); + } + protected virtual EXPR VisitFIELDINFO(EXPRFIELDINFO pExpr) + { + return VisitEXPR(pExpr); + } + protected virtual EXPR VisitMETHODINFO(EXPRMETHODINFO pExpr) + { + return VisitEXPR(pExpr); + } + protected virtual EXPR VisitEQUALS(EXPRBINOP pExpr) + { + return VisitBINOP(pExpr); + } + protected virtual EXPR VisitCOMPARE(EXPRBINOP pExpr) + { + return VisitBINOP(pExpr); + } + protected virtual EXPR VisitEQ(EXPRBINOP pExpr) + { + return VisitBINOP(pExpr); + } + protected virtual EXPR VisitNE(EXPRBINOP pExpr) + { + return VisitBINOP(pExpr); + } + protected virtual EXPR VisitLE(EXPRBINOP pExpr) + { + return VisitBINOP(pExpr); + } + protected virtual EXPR VisitGE(EXPRBINOP pExpr) + { + return VisitBINOP(pExpr); + } + protected virtual EXPR VisitADD(EXPRBINOP pExpr) + { + return VisitBINOP(pExpr); + } + protected virtual EXPR VisitSUB(EXPRBINOP pExpr) + { + return VisitBINOP(pExpr); + } + protected virtual EXPR VisitDIV(EXPRBINOP pExpr) + { + return VisitBINOP(pExpr); + } + protected virtual EXPR VisitBITAND(EXPRBINOP pExpr) + { + return VisitBINOP(pExpr); + } + protected virtual EXPR VisitBITOR(EXPRBINOP pExpr) + { + return VisitBINOP(pExpr); + } + protected virtual EXPR VisitLSHIFT(EXPRBINOP pExpr) + { + return VisitBINOP(pExpr); + } + protected virtual EXPR VisitLOGAND(EXPRBINOP pExpr) + { + return VisitBINOP(pExpr); + } + protected virtual EXPR VisitSEQUENCE(EXPRBINOP pExpr) + { + return VisitBINOP(pExpr); + } + protected virtual EXPR VisitSAVE(EXPRBINOP pExpr) + { + return VisitBINOP(pExpr); + } + protected virtual EXPR VisitINDIR(EXPRBINOP pExpr) + { + return VisitBINOP(pExpr); + } + protected virtual EXPR VisitSTRINGEQ(EXPRBINOP pExpr) + { + return VisitBINOP(pExpr); + } + protected virtual EXPR VisitDELEGATEEQ(EXPRBINOP pExpr) + { + return VisitBINOP(pExpr); + } + protected virtual EXPR VisitDELEGATEADD(EXPRBINOP pExpr) + { + return VisitBINOP(pExpr); + } + protected virtual EXPR VisitRANGE(EXPRBINOP pExpr) + { + return VisitBINOP(pExpr); + } + protected virtual EXPR VisitLT(EXPRBINOP pExpr) + { + return VisitBINOP(pExpr); + } + protected virtual EXPR VisitMUL(EXPRBINOP pExpr) + { + return VisitBINOP(pExpr); + } + protected virtual EXPR VisitBITXOR(EXPRBINOP pExpr) + { + return VisitBINOP(pExpr); + } + protected virtual EXPR VisitRSHIFT(EXPRBINOP pExpr) + { + return VisitBINOP(pExpr); + } + protected virtual EXPR VisitLOGOR(EXPRBINOP pExpr) + { + return VisitBINOP(pExpr); + } + protected virtual EXPR VisitSEQREV(EXPRBINOP pExpr) + { + return VisitBINOP(pExpr); + } + protected virtual EXPR VisitSTRINGNE(EXPRBINOP pExpr) + { + return VisitBINOP(pExpr); + } + protected virtual EXPR VisitDELEGATENE(EXPRBINOP pExpr) + { + return VisitBINOP(pExpr); + } + protected virtual EXPR VisitGT(EXPRBINOP pExpr) + { + return VisitBINOP(pExpr); + } + protected virtual EXPR VisitMOD(EXPRBINOP pExpr) + { + return VisitBINOP(pExpr); + } + protected virtual EXPR VisitSWAP(EXPRBINOP pExpr) + { + return VisitBINOP(pExpr); + } + protected virtual EXPR VisitDELEGATESUB(EXPRBINOP pExpr) + { + return VisitBINOP(pExpr); + } + protected virtual EXPR VisitTRUE(EXPRUNARYOP pExpr) + { + return VisitUNARYOP(pExpr); + } + protected virtual EXPR VisitINC(EXPRUNARYOP pExpr) + { + return VisitUNARYOP(pExpr); + } + protected virtual EXPR VisitLOGNOT(EXPRUNARYOP pExpr) + { + return VisitUNARYOP(pExpr); + } + protected virtual EXPR VisitNEG(EXPRUNARYOP pExpr) + { + return VisitUNARYOP(pExpr); + } + protected virtual EXPR VisitBITNOT(EXPRUNARYOP pExpr) + { + return VisitUNARYOP(pExpr); + } + protected virtual EXPR VisitADDR(EXPRUNARYOP pExpr) + { + return VisitUNARYOP(pExpr); + } + protected virtual EXPR VisitDECIMALNEG(EXPRUNARYOP pExpr) + { + return VisitUNARYOP(pExpr); + } + protected virtual EXPR VisitDECIMALDEC(EXPRUNARYOP pExpr) + { + return VisitUNARYOP(pExpr); + } + protected virtual EXPR VisitFALSE(EXPRUNARYOP pExpr) + { + return VisitUNARYOP(pExpr); + } + protected virtual EXPR VisitDEC(EXPRUNARYOP pExpr) + { + return VisitUNARYOP(pExpr); + } + protected virtual EXPR VisitUPLUS(EXPRUNARYOP pExpr) + { + return VisitUNARYOP(pExpr); + } + protected virtual EXPR VisitDECIMALINC(EXPRUNARYOP pExpr) + { + return VisitUNARYOP(pExpr); + } + } +} diff --git a/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/Tree/Visitors/ExpressionTreeRewriter.cs b/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/Tree/Visitors/ExpressionTreeRewriter.cs new file mode 100644 index 000000000..e9016e236 --- /dev/null +++ b/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/Tree/Visitors/ExpressionTreeRewriter.cs @@ -0,0 +1,1266 @@ +// ==++== +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// ==--== + +using System.Diagnostics; +using Microsoft.CSharp.RuntimeBinder.Syntax; + +namespace Microsoft.CSharp.RuntimeBinder.Semantics +{ + internal class ExpressionTreeRewriter : ExprVisitorBase + { + public static EXPR Rewrite(EXPR expr, ExprFactory expressionFactory, SymbolLoader symbolLoader) + { + ExpressionTreeRewriter rewriter = new ExpressionTreeRewriter(expressionFactory, symbolLoader); + rewriter.alwaysRewrite = true; + return rewriter.Visit(expr); + } + + protected ExprFactory expressionFactory; + protected SymbolLoader symbolLoader; + protected EXPRBOUNDLAMBDA currentAnonMeth; + protected bool alwaysRewrite; + + protected ExprFactory GetExprFactory() { return expressionFactory; } + protected SymbolLoader GetSymbolLoader() { return symbolLoader; } + + protected ExpressionTreeRewriter(ExprFactory expressionFactory, SymbolLoader symbolLoader) + { + this.expressionFactory = expressionFactory; + this.symbolLoader = symbolLoader; + this.alwaysRewrite = false; + } + + protected override EXPR Dispatch(EXPR expr) + { + Debug.Assert(expr != null); + + EXPR result; + result = base.Dispatch(expr); + if (result == expr) + { + throw Error.InternalCompilerError(); + } + return result; + } + + ///////////////////////////////////////////////////////////////////////////////// + // Statement types. + protected override EXPR VisitASSIGNMENT(EXPRASSIGNMENT assignment) + { + Debug.Assert(assignment != null); + Debug.Assert(alwaysRewrite || currentAnonMeth != null); + + // For assignments, we either have a member assignment or an indexed assignment. + //Debug.Assert(assignment.GetLHS().isPROP() || assignment.GetLHS().isFIELD() || assignment.GetLHS().isARRAYINDEX() || assignment.GetLHS().isLOCAL()); + EXPR lhs; + if (assignment.GetLHS().isPROP()) + { + EXPRPROP prop = assignment.GetLHS().asPROP(); + + if (prop.GetOptionalArguments() == null) + { + // Regular property. + lhs = Visit(prop); + } + else + { + // Indexed assignment. Here we need to find the instance of the object, create the + // PropInfo for the thing, and get the array of expressions that make up the index arguments. + // + // The LHS becomes Expression.Property(instance, indexerInfo, arguments). + EXPR instance = Visit(prop.GetMemberGroup().GetOptionalObject()); + EXPR propInfo = GetExprFactory().CreatePropertyInfo(prop.pwtSlot.Prop(), prop.pwtSlot.Ats); + EXPR arguments = GenerateParamsArray( + GenerateArgsList(prop.GetOptionalArguments()), + PredefinedType.PT_EXPRESSION); + + lhs = GenerateCall(PREDEFMETH.PM_EXPRESSION_PROPERTY, instance, propInfo, arguments); + } + } + else + { + lhs = Visit(assignment.GetLHS()); + } + + EXPR rhs = Visit(assignment.GetRHS()); + return GenerateCall(PREDEFMETH.PM_EXPRESSION_ASSIGN, lhs, rhs); + } + protected override EXPR VisitMULTIGET(EXPRMULTIGET pExpr) + { + return Visit(pExpr.GetOptionalMulti().Left); + } + protected override EXPR VisitMULTI(EXPRMULTI pExpr) + { + EXPR rhs = Visit(pExpr.Operator); + EXPR lhs = Visit(pExpr.Left); + return GenerateCall(PREDEFMETH.PM_EXPRESSION_ASSIGN, lhs, rhs); + } + + ///////////////////////////////////////////////////////////////////////////////// + // Expression types. + + protected override EXPR VisitBOUNDLAMBDA(EXPRBOUNDLAMBDA anonmeth) + { + Debug.Assert(anonmeth != null); + + EXPRBOUNDLAMBDA prevAnonMeth = currentAnonMeth; + currentAnonMeth = anonmeth; + MethodSymbol lambdaMethod = GetPreDefMethod(PREDEFMETH.PM_EXPRESSION_LAMBDA); + + CType delegateType = anonmeth.DelegateType(); + TypeArray lambdaTypeParams = GetSymbolLoader().getBSymmgr().AllocParams(1, new CType[] { delegateType }); + AggregateType expressionType = GetSymbolLoader().GetOptPredefTypeErr(PredefinedType.PT_EXPRESSION, true); + MethWithInst mwi = new MethWithInst(lambdaMethod, expressionType, lambdaTypeParams); + EXPR createParameters = CreateWraps(anonmeth); + EXPR body = RewriteLambdaBody(anonmeth); + EXPR parameters = RewriteLambdaParameters(anonmeth); + EXPR args = GetExprFactory().CreateList(body, parameters); + CType typeRet = GetSymbolLoader().GetTypeManager().SubstType(mwi.Meth().RetType, mwi.GetType(), mwi.TypeArgs); + EXPRMEMGRP pMemGroup = GetExprFactory().CreateMemGroup(null, mwi); + EXPR callLambda = GetExprFactory().CreateCall(0, typeRet, args, pMemGroup, mwi); + + callLambda.asCALL().PredefinedMethod = PREDEFMETH.PM_EXPRESSION_LAMBDA; + + currentAnonMeth = prevAnonMeth; + if (createParameters != null) + { + callLambda = GetExprFactory().CreateSequence(createParameters, callLambda); + } + EXPR expr = DestroyWraps(anonmeth, callLambda); + // If we are already inside an expression tree rewrite and this is an expression tree lambda + // then it needs to be quoted. + if (currentAnonMeth != null) + { + expr = GenerateCall(PREDEFMETH.PM_EXPRESSION_QUOTE, expr); + } + return expr; + } + protected override EXPR VisitCONSTANT(EXPRCONSTANT expr) + { + Debug.Assert(expr != null); + Debug.Assert(alwaysRewrite || currentAnonMeth != null); + return GenerateConstant(expr); + } + protected override EXPR VisitLOCAL(EXPRLOCAL local) + { + Debug.Assert(local != null); + Debug.Assert(alwaysRewrite || currentAnonMeth != null); + Debug.Assert(!local.local.isThis); + // this is true for all parameters of an expression lambda + if (local.local.wrap != null) + { + return local.local.wrap; + } + Debug.Assert(local.local.fUsedInAnonMeth); + return GetExprFactory().CreateHoistedLocalInExpression(local); + } + protected override EXPR VisitTHISPOINTER(EXPRTHISPOINTER expr) + { + Debug.Assert(expr != null); + Debug.Assert(alwaysRewrite || currentAnonMeth != null); + Debug.Assert(expr.local.isThis); + return GenerateConstant(expr); + } + protected override EXPR VisitFIELD(EXPRFIELD expr) + { + Debug.Assert(expr != null); + EXPR pObject; + if (expr.GetOptionalObject() == null) + { + pObject = GetExprFactory().CreateNull(); + } + else + { + pObject = Visit(expr.GetOptionalObject()); + } + EXPRFIELDINFO pFieldInfo = GetExprFactory().CreateFieldInfo(expr.fwt.Field(), expr.fwt.GetType()); + return GenerateCall(PREDEFMETH.PM_EXPRESSION_FIELD, pObject, pFieldInfo); + } + protected override EXPR VisitUSERDEFINEDCONVERSION(EXPRUSERDEFINEDCONVERSION expr) + { + Debug.Assert(expr != null); + Debug.Assert(alwaysRewrite || currentAnonMeth != null); + return GenerateUserDefinedConversion(expr, expr.Argument); + } + protected override EXPR VisitCAST(EXPRCAST pExpr) + { + Debug.Assert(pExpr != null); + Debug.Assert(alwaysRewrite || currentAnonMeth != null); + + EXPR pArgument = pExpr.GetArgument(); + + // If we have generated an identity cast or reference cast to a base class + // we can omit the cast. + if (pArgument.type == pExpr.type || + GetSymbolLoader().IsBaseClassOfClass(pArgument.type, pExpr.type) || + CConversions.FImpRefConv(GetSymbolLoader(), pArgument.type, pExpr.type)) + { + return Visit(pArgument); + } + + // If we have a cast to PredefinedType.PT_G_EXPRESSION and the thing that we're casting is + // a EXPRBOUNDLAMBDA that is an expression tree, then just visit the expression tree. + if (pExpr.type != null && + pExpr.type.isPredefType(PredefinedType.PT_G_EXPRESSION) && + pArgument.isBOUNDLAMBDA()) + { + return Visit(pArgument); + } + + EXPR result = GenerateConversion(pArgument, pExpr.type, pExpr.isChecked()); + if ((pExpr.flags & EXPRFLAG.EXF_UNBOXRUNTIME) != 0) + { + // Propagate the unbox flag to the call for the ExpressionTreeCallRewriter. + result.flags |= EXPRFLAG.EXF_UNBOXRUNTIME; + } + return result; + } + protected override EXPR VisitCONCAT(EXPRCONCAT expr) + { + Debug.Assert(expr != null); + Debug.Assert(alwaysRewrite || currentAnonMeth != null); + PREDEFMETH pdm; + if (expr.GetFirstArgument().type.isPredefType(PredefinedType.PT_STRING) && expr.GetSecondArgument().type.isPredefType(PredefinedType.PT_STRING)) + { + pdm = PREDEFMETH.PM_STRING_CONCAT_STRING_2; + } + else + { + pdm = PREDEFMETH.PM_STRING_CONCAT_OBJECT_2; + } + EXPR p1 = Visit(expr.GetFirstArgument()); + EXPR p2 = Visit(expr.GetSecondArgument()); + MethodSymbol method = GetPreDefMethod(pdm); + EXPR methodInfo = GetExprFactory().CreateMethodInfo(method, GetSymbolLoader().GetReqPredefType(PredefinedType.PT_STRING), null); + return GenerateCall(PREDEFMETH.PM_EXPRESSION_ADD_USER_DEFINED, p1, p2, methodInfo); + } + protected override EXPR VisitBINOP(EXPRBINOP expr) + { + Debug.Assert(expr != null); + Debug.Assert(alwaysRewrite || currentAnonMeth != null); + if (expr.GetUserDefinedCallMethod() != null) + { + return GenerateUserDefinedBinaryOperator(expr); + } + else + { + return GenerateBuiltInBinaryOperator(expr); + } + } + protected override EXPR VisitUNARYOP(EXPRUNARYOP pExpr) + { + Debug.Assert(pExpr != null); + Debug.Assert(alwaysRewrite || currentAnonMeth != null); + if (pExpr.UserDefinedCallMethod != null) + { + return GenerateUserDefinedUnaryOperator(pExpr); + } + else + { + return GenerateBuiltInUnaryOperator(pExpr); + } + } + protected override EXPR VisitARRAYINDEX(EXPRARRAYINDEX pExpr) + { + Debug.Assert(pExpr != null); + Debug.Assert(alwaysRewrite || currentAnonMeth != null); + EXPR arr = Visit(pExpr.GetArray()); + EXPR args = GenerateIndexList(pExpr.GetIndex()); + if (args.isLIST()) + { + EXPR Params = GenerateParamsArray(args, PredefinedType.PT_EXPRESSION); + return GenerateCall(PREDEFMETH.PM_EXPRESSION_ARRAYINDEX2, arr, Params); + } + return GenerateCall(PREDEFMETH.PM_EXPRESSION_ARRAYINDEX, arr, args); + } + protected override EXPR VisitARRAYLENGTH(EXPRARRAYLENGTH pExpr) + { + return GenerateBuiltInUnaryOperator(PREDEFMETH.PM_EXPRESSION_ARRAYLENGTH, pExpr.GetArray(), pExpr); + } + protected override EXPR VisitQUESTIONMARK(EXPRQUESTIONMARK pExpr) + { + Debug.Assert(pExpr != null); + Debug.Assert(alwaysRewrite || currentAnonMeth != null); + EXPR p1 = Visit(pExpr.GetTestExpression()); + EXPR p2 = GenerateQuestionMarkOperand(pExpr.GetConsequence().asBINOP().GetOptionalLeftChild()); + EXPR p3 = GenerateQuestionMarkOperand(pExpr.GetConsequence().asBINOP().GetOptionalRightChild()); + return GenerateCall(PREDEFMETH.PM_EXPRESSION_CONDITION, p1, p2, p3); + } + protected override EXPR VisitCALL(EXPRCALL expr) + { + Debug.Assert(expr != null); + Debug.Assert(alwaysRewrite || currentAnonMeth != null); + + switch (expr.nubLiftKind) + { + default: + break; + case NullableCallLiftKind.NullableIntermediateConversion: + case NullableCallLiftKind.NullableConversion: + case NullableCallLiftKind.NullableConversionConstructor: + return GenerateConversion(expr.GetOptionalArguments(), expr.type, expr.isChecked()); + case NullableCallLiftKind.NotLiftedIntermediateConversion: + case NullableCallLiftKind.UserDefinedConversion: + return GenerateUserDefinedConversion(expr.GetOptionalArguments(), expr.type, expr.mwi); + } + + if (expr.mwi.Meth().IsConstructor()) + { + return GenerateConstructor(expr); + } + + EXPRMEMGRP memberGroup = expr.GetMemberGroup(); + if (memberGroup.isDelegate()) + { + return GenerateDelegateInvoke(expr); + } + + EXPR pObject; + if (expr.mwi.Meth().isStatic || expr.GetMemberGroup().GetOptionalObject() == null) + { + pObject = GetExprFactory().CreateNull(); + } + else + { + pObject = expr.GetMemberGroup().GetOptionalObject(); + + // If we have, say, an int? which is the object of a call to ToString + // then we do NOT want to generate ((object)i).ToString() because that + // will convert a null-valued int? to a null object. Rather what we want + // to do is box it to a ValueType and call ValueType.ToString. + // + // To implement this we say that if the object of the call is an implicit boxing cast + // then just generate the object, not the cast. If the cast is explicit in the + // source code then it will be an EXPLICITCAST and we will visit it normally. + // + // CONSIDER: It might be better to rewrite the expression tree API so that it + // can handle in the general case all implicit boxing conversions. Right now it + // requires that all arguments to a call that need to be boxed be explicitly boxed. + + if (pObject != null && pObject.isCAST() && pObject.asCAST().IsBoxingCast()) + { + pObject = pObject.asCAST().GetArgument(); + } + pObject = Visit(pObject); + } + EXPR methodInfo = GetExprFactory().CreateMethodInfo(expr.mwi); + EXPR args = GenerateArgsList(expr.GetOptionalArguments()); + EXPR Params = GenerateParamsArray(args, PredefinedType.PT_EXPRESSION); + PREDEFMETH pdm = PREDEFMETH.PM_EXPRESSION_CALL; + Debug.Assert(!expr.mwi.Meth().isVirtual || expr.GetMemberGroup().GetOptionalObject() != null); + + return GenerateCall(pdm, pObject, methodInfo, Params); + } + protected override EXPR VisitPROP(EXPRPROP expr) + { + Debug.Assert(expr != null); + Debug.Assert(alwaysRewrite || currentAnonMeth != null); + EXPR pObject; + if (expr.pwtSlot.Prop().isStatic || expr.GetMemberGroup().GetOptionalObject() == null) + { + pObject = GetExprFactory().CreateNull(); + } + else + { + pObject = Visit(expr.GetMemberGroup().GetOptionalObject()); + } + EXPR propInfo = GetExprFactory().CreatePropertyInfo(expr.pwtSlot.Prop(), expr.pwtSlot.GetType()); + if (expr.GetOptionalArguments() != null) + { + // It is an indexer property. Turn it into a virtual method call. + EXPR args = GenerateArgsList(expr.GetOptionalArguments()); + EXPR Params = GenerateParamsArray(args, PredefinedType.PT_EXPRESSION); + return GenerateCall(PREDEFMETH.PM_EXPRESSION_PROPERTY, pObject, propInfo, Params); + } + return GenerateCall(PREDEFMETH.PM_EXPRESSION_PROPERTY, pObject, propInfo); + } + protected override EXPR VisitARRINIT(EXPRARRINIT expr) + { + Debug.Assert(expr != null); + Debug.Assert(alwaysRewrite || currentAnonMeth != null); + // POSSIBLE ERROR: Multi-d should be an error? + EXPR pTypeOf = CreateTypeOf(expr.type.AsArrayType().GetElementType()); + EXPR args = GenerateArgsList(expr.GetOptionalArguments()); + EXPR Params = GenerateParamsArray(args, PredefinedType.PT_EXPRESSION); + return GenerateCall(PREDEFMETH.PM_EXPRESSION_NEWARRAYINIT, pTypeOf, Params); + } + protected override EXPR VisitZEROINIT(EXPRZEROINIT expr) + { + Debug.Assert(expr != null); + Debug.Assert(alwaysRewrite || currentAnonMeth != null); + Debug.Assert(expr.OptionalArgument == null); + + if (expr.IsConstructor) + { + // We have a parameterless "new MyStruct()" which has been realized as a zero init. + // CONSIDER: Move this realization out of the initial binding and into a later pass? + EXPRTYPEOF pTypeOf = CreateTypeOf(expr.type); + return GenerateCall(PREDEFMETH.PM_EXPRESSION_NEW_TYPE, pTypeOf); + } + return GenerateConstant(expr); + } + protected override EXPR VisitTYPEOF(EXPRTYPEOF expr) + { + Debug.Assert(expr != null); + Debug.Assert(alwaysRewrite || currentAnonMeth != null); + return GenerateConstant(expr); + } + + protected virtual EXPR GenerateQuestionMarkOperand(EXPR pExpr) + { + Debug.Assert(pExpr != null); + // We must not optimize away compiler-generated reference casts because + // the expression tree API insists that the CType of both sides be identical. + if (pExpr.isCAST()) + { + return GenerateConversion(pExpr.asCAST().GetArgument(), pExpr.type, pExpr.isChecked()); + } + return Visit(pExpr); + } + protected virtual EXPR GenerateDelegateInvoke(EXPRCALL expr) + { + Debug.Assert(expr != null); + EXPRMEMGRP memberGroup = expr.GetMemberGroup(); + Debug.Assert(memberGroup.isDelegate()); + EXPR oldObject = memberGroup.GetOptionalObject(); + Debug.Assert(oldObject != null); + EXPR pObject = Visit(oldObject); + EXPR args = GenerateArgsList(expr.GetOptionalArguments()); + EXPR Params = GenerateParamsArray(args, PredefinedType.PT_EXPRESSION); + return GenerateCall(PREDEFMETH.PM_EXPRESSION_INVOKE, pObject, Params); + } + protected virtual EXPR GenerateBuiltInBinaryOperator(EXPRBINOP expr) + { + Debug.Assert(expr != null); + Debug.Assert(alwaysRewrite || currentAnonMeth != null); + PREDEFMETH pdm; + + switch (expr.kind) + { + case ExpressionKind.EK_LSHIFT: pdm = PREDEFMETH.PM_EXPRESSION_LEFTSHIFT; break; + case ExpressionKind.EK_RSHIFT: pdm = PREDEFMETH.PM_EXPRESSION_RIGHTSHIFT; break; + case ExpressionKind.EK_BITXOR: pdm = PREDEFMETH.PM_EXPRESSION_EXCLUSIVEOR; break; + case ExpressionKind.EK_BITOR: pdm = PREDEFMETH.PM_EXPRESSION_OR; break; + case ExpressionKind.EK_BITAND: pdm = PREDEFMETH.PM_EXPRESSION_AND; break; + case ExpressionKind.EK_LOGAND: pdm = PREDEFMETH.PM_EXPRESSION_ANDALSO; break; + case ExpressionKind.EK_LOGOR: pdm = PREDEFMETH.PM_EXPRESSION_ORELSE; break; + case ExpressionKind.EK_STRINGEQ: pdm = PREDEFMETH.PM_EXPRESSION_EQUAL; break; + case ExpressionKind.EK_EQ: pdm = PREDEFMETH.PM_EXPRESSION_EQUAL; break; + case ExpressionKind.EK_STRINGNE: pdm = PREDEFMETH.PM_EXPRESSION_NOTEQUAL; break; + case ExpressionKind.EK_NE: pdm = PREDEFMETH.PM_EXPRESSION_NOTEQUAL; break; + case ExpressionKind.EK_GE: pdm = PREDEFMETH.PM_EXPRESSION_GREATERTHANOREQUAL; break; + case ExpressionKind.EK_LE: pdm = PREDEFMETH.PM_EXPRESSION_LESSTHANOREQUAL; break; + case ExpressionKind.EK_LT: pdm = PREDEFMETH.PM_EXPRESSION_LESSTHAN; break; + case ExpressionKind.EK_GT: pdm = PREDEFMETH.PM_EXPRESSION_GREATERTHAN; break; + case ExpressionKind.EK_MOD: pdm = PREDEFMETH.PM_EXPRESSION_MODULO; break; + case ExpressionKind.EK_DIV: pdm = PREDEFMETH.PM_EXPRESSION_DIVIDE; break; + // UNDONE: Tracked by DDBugs 109559 + // UNDONE: DivideChecked + case ExpressionKind.EK_MUL: + pdm = expr.isChecked() ? PREDEFMETH.PM_EXPRESSION_MULTIPLYCHECKED : PREDEFMETH.PM_EXPRESSION_MULTIPLY; + break; + case ExpressionKind.EK_SUB: + pdm = expr.isChecked() ? PREDEFMETH.PM_EXPRESSION_SUBTRACTCHECKED : PREDEFMETH.PM_EXPRESSION_SUBTRACT; + break; + case ExpressionKind.EK_ADD: + pdm = expr.isChecked() ? PREDEFMETH.PM_EXPRESSION_ADDCHECKED : PREDEFMETH.PM_EXPRESSION_ADD; + break; + + default: + throw Error.InternalCompilerError(); + } + EXPR origL = expr.GetOptionalLeftChild(); + EXPR origR = expr.GetOptionalRightChild(); + Debug.Assert(origL != null); + Debug.Assert(origR != null); + CType typeL = origL.type; + CType typeR = origR.type; + + EXPR newL = Visit(origL); + EXPR newR = Visit(origR); + + bool didEnumConversion = false; + CType convertL = null; + CType convertR = null; + + if (typeL.isEnumType()) + { + // We have already inserted casts if not lifted, so we should never see an enum. + Debug.Assert(expr.isLifted); + convertL = GetSymbolLoader().GetTypeManager().GetNullable(typeL.underlyingEnumType()); + typeL = convertL; + didEnumConversion = true; + } + else if (typeL.IsNullableType() && typeL.StripNubs().isEnumType()) + { + Debug.Assert(expr.isLifted); + convertL = GetSymbolLoader().GetTypeManager().GetNullable(typeL.StripNubs().underlyingEnumType()); + typeL = convertL; + didEnumConversion = true; + } + if (typeR.isEnumType()) + { + Debug.Assert(expr.isLifted); + convertR = GetSymbolLoader().GetTypeManager().GetNullable(typeR.underlyingEnumType()); + typeR = convertR; + didEnumConversion = true; + } + else if (typeR.IsNullableType() && typeR.StripNubs().isEnumType()) + { + Debug.Assert(expr.isLifted); + convertR = GetSymbolLoader().GetTypeManager().GetNullable(typeR.StripNubs().underlyingEnumType()); + typeR = convertR; + didEnumConversion = true; + } + if (typeL.IsNullableType() && typeL.StripNubs() == typeR) + { + convertR = typeL; + } + if (typeR.IsNullableType() && typeR.StripNubs() == typeL) + { + convertL = typeR; + } + + if (convertL != null) + { + newL = GenerateCall(PREDEFMETH.PM_EXPRESSION_CONVERT, newL, CreateTypeOf(convertL)); + } + if (convertR != null) + { + newR = GenerateCall(PREDEFMETH.PM_EXPRESSION_CONVERT, newR, CreateTypeOf(convertR)); + } + + EXPR call = GenerateCall(pdm, newL, newR); + + if (didEnumConversion && expr.type.StripNubs().isEnumType()) + { + call = GenerateCall(PREDEFMETH.PM_EXPRESSION_CONVERT, call, CreateTypeOf(expr.type)); + } + + return call; + + } + protected virtual EXPR GenerateBuiltInUnaryOperator(EXPRUNARYOP expr) + { + Debug.Assert(expr != null); + Debug.Assert(alwaysRewrite || currentAnonMeth != null); + PREDEFMETH pdm; + switch (expr.kind) + { + case ExpressionKind.EK_UPLUS: + return Visit(expr.Child); + case ExpressionKind.EK_BITNOT: pdm = PREDEFMETH.PM_EXPRESSION_NOT; break; + case ExpressionKind.EK_LOGNOT: pdm = PREDEFMETH.PM_EXPRESSION_NOT; break; + case ExpressionKind.EK_NEG: + pdm = expr.isChecked() ? PREDEFMETH.PM_EXPRESSION_NEGATECHECKED : PREDEFMETH.PM_EXPRESSION_NEGATE; + break; + default: + throw Error.InternalCompilerError(); + } + EXPR origOp = expr.Child; + + return GenerateBuiltInUnaryOperator(pdm, origOp, expr); + } + protected virtual EXPR GenerateBuiltInUnaryOperator(PREDEFMETH pdm, EXPR pOriginalOperator, EXPR pOperator) + { + EXPR op = Visit(pOriginalOperator); + if (pOriginalOperator.type.IsNullableType() && pOriginalOperator.type.StripNubs().isEnumType()) + { + Debug.Assert(pOperator.kind == ExpressionKind.EK_BITNOT); // The only built-in unary operator defined on nullable enum. + CType underlyingType = pOriginalOperator.type.StripNubs().underlyingEnumType(); + CType nullableType = GetSymbolLoader().GetTypeManager().GetNullable(underlyingType); + op = GenerateCall(PREDEFMETH.PM_EXPRESSION_CONVERT, op, CreateTypeOf(nullableType)); + } + EXPR call = GenerateCall(pdm, op); + if (pOriginalOperator.type.IsNullableType() && pOriginalOperator.type.StripNubs().isEnumType()) + { + call = GenerateCall(PREDEFMETH.PM_EXPRESSION_CONVERT, call, CreateTypeOf(pOperator.type)); + } + + return call; + } + protected virtual EXPR GenerateUserDefinedBinaryOperator(EXPRBINOP expr) + { + Debug.Assert(expr != null); + Debug.Assert(alwaysRewrite || currentAnonMeth != null); + PREDEFMETH pdm; + + switch (expr.kind) + { + case ExpressionKind.EK_LOGOR: pdm = PREDEFMETH.PM_EXPRESSION_ORELSE_USER_DEFINED; break; + case ExpressionKind.EK_LOGAND: pdm = PREDEFMETH.PM_EXPRESSION_ANDALSO_USER_DEFINED; break; + case ExpressionKind.EK_LSHIFT: pdm = PREDEFMETH.PM_EXPRESSION_LEFTSHIFT_USER_DEFINED; break; + case ExpressionKind.EK_RSHIFT: pdm = PREDEFMETH.PM_EXPRESSION_RIGHTSHIFT_USER_DEFINED; break; + case ExpressionKind.EK_BITXOR: pdm = PREDEFMETH.PM_EXPRESSION_EXCLUSIVEOR_USER_DEFINED; break; + case ExpressionKind.EK_BITOR: pdm = PREDEFMETH.PM_EXPRESSION_OR_USER_DEFINED; break; + case ExpressionKind.EK_BITAND: pdm = PREDEFMETH.PM_EXPRESSION_AND_USER_DEFINED; break; + case ExpressionKind.EK_MOD: pdm = PREDEFMETH.PM_EXPRESSION_MODULO_USER_DEFINED; break; + case ExpressionKind.EK_DIV: pdm = PREDEFMETH.PM_EXPRESSION_DIVIDE_USER_DEFINED; break; + // UNDONE: Tracked by DDBugs 109559 + // UNDONE: DivideChecked + case ExpressionKind.EK_STRINGEQ: + case ExpressionKind.EK_STRINGNE: + case ExpressionKind.EK_DELEGATEEQ: + case ExpressionKind.EK_DELEGATENE: + case ExpressionKind.EK_EQ: + case ExpressionKind.EK_NE: + case ExpressionKind.EK_GE: + case ExpressionKind.EK_GT: + case ExpressionKind.EK_LE: + case ExpressionKind.EK_LT: + return GenerateUserDefinedComparisonOperator(expr); + case ExpressionKind.EK_DELEGATESUB: + case ExpressionKind.EK_SUB: + pdm = expr.isChecked() ? PREDEFMETH.PM_EXPRESSION_SUBTRACTCHECKED_USER_DEFINED : PREDEFMETH.PM_EXPRESSION_SUBTRACT_USER_DEFINED; + break; + case ExpressionKind.EK_DELEGATEADD: + case ExpressionKind.EK_ADD: + pdm = expr.isChecked() ? PREDEFMETH.PM_EXPRESSION_ADDCHECKED_USER_DEFINED : PREDEFMETH.PM_EXPRESSION_ADD_USER_DEFINED; + break; + case ExpressionKind.EK_MUL: + pdm = expr.isChecked() ? PREDEFMETH.PM_EXPRESSION_MULTIPLYCHECKED_USER_DEFINED : PREDEFMETH.PM_EXPRESSION_MULTIPLY_USER_DEFINED; + break; + default: + throw Error.InternalCompilerError(); + } + EXPR p1 = expr.GetOptionalLeftChild(); + EXPR p2 = expr.GetOptionalRightChild(); + EXPR udcall = expr.GetOptionalUserDefinedCall(); + if (udcall != null) + { + Debug.Assert(udcall.kind == ExpressionKind.EK_CALL || udcall.kind == ExpressionKind.EK_USERLOGOP); + if (udcall.kind == ExpressionKind.EK_CALL) + { + EXPRLIST args = udcall.asCALL().GetOptionalArguments().asLIST(); + Debug.Assert(args.GetOptionalNextListNode().kind != ExpressionKind.EK_LIST); + p1 = args.GetOptionalElement(); + p2 = args.GetOptionalNextListNode(); + } + else + { + EXPRLIST args = udcall.asUSERLOGOP().OperatorCall.GetOptionalArguments().asLIST(); + Debug.Assert(args.GetOptionalNextListNode().kind != ExpressionKind.EK_LIST); + p1 = args.GetOptionalElement().asWRAP().GetOptionalExpression(); + p2 = args.GetOptionalNextListNode(); + } + } + p1 = Visit(p1); + p2 = Visit(p2); + FixLiftedUserDefinedBinaryOperators(expr, ref p1, ref p2); + EXPR methodInfo = GetExprFactory().CreateMethodInfo(expr.GetUserDefinedCallMethod()); + EXPR call = GenerateCall(pdm, p1, p2, methodInfo); + // Delegate add/subtract generates a call to Combine/Remove, which returns System.Delegate, + // not the operand delegate CType. We must cast to the delegate CType. + if (expr.kind == ExpressionKind.EK_DELEGATESUB || expr.kind == ExpressionKind.EK_DELEGATEADD) + { + EXPR pTypeOf = CreateTypeOf(expr.type); + return GenerateCall(PREDEFMETH.PM_EXPRESSION_CONVERT, call, pTypeOf); + } + return call; + } + protected virtual EXPR GenerateUserDefinedUnaryOperator(EXPRUNARYOP expr) + { + Debug.Assert(expr != null); + Debug.Assert(alwaysRewrite || currentAnonMeth != null); + PREDEFMETH pdm; + EXPR arg = expr.Child; + EXPRCALL call = expr.OptionalUserDefinedCall.asCALL(); + if (call != null) + { + // Use the actual argument of the call; it may contain user-defined + // conversions or be a bound lambda, and that will not be in the original + // argument stashed away in the left child of the operator. + arg = call.GetOptionalArguments(); + } + Debug.Assert(arg != null && arg.kind != ExpressionKind.EK_LIST); + switch (expr.kind) + { + case ExpressionKind.EK_TRUE: + case ExpressionKind.EK_FALSE: + return Visit(call); + case ExpressionKind.EK_UPLUS: + pdm = PREDEFMETH.PM_EXPRESSION_UNARYPLUS_USER_DEFINED; + break; + case ExpressionKind.EK_BITNOT: pdm = PREDEFMETH.PM_EXPRESSION_NOT_USER_DEFINED; break; + case ExpressionKind.EK_LOGNOT: pdm = PREDEFMETH.PM_EXPRESSION_NOT_USER_DEFINED; break; + // CONSIDER: Now that we have unary operators with methods in them, can we just make this an ExpressionKind.EK_NEG? + case ExpressionKind.EK_DECIMALNEG: + case ExpressionKind.EK_NEG: + pdm = expr.isChecked() ? PREDEFMETH.PM_EXPRESSION_NEGATECHECKED_USER_DEFINED : PREDEFMETH.PM_EXPRESSION_NEGATE_USER_DEFINED; + break; + + case ExpressionKind.EK_INC: + case ExpressionKind.EK_DEC: + case ExpressionKind.EK_DECIMALINC: + case ExpressionKind.EK_DECIMALDEC: + pdm = PREDEFMETH.PM_EXPRESSION_CALL; + break; + + default: + throw Error.InternalCompilerError(); + } + EXPR op = Visit(arg); + EXPR methodInfo = GetExprFactory().CreateMethodInfo(expr.UserDefinedCallMethod); + + if (expr.kind == ExpressionKind.EK_INC || expr.kind == ExpressionKind.EK_DEC || + expr.kind == ExpressionKind.EK_DECIMALINC || expr.kind == ExpressionKind.EK_DECIMALDEC) + { + return GenerateCall(pdm, null, methodInfo, GenerateParamsArray(op, PredefinedType.PT_EXPRESSION)); + } + return GenerateCall(pdm, op, methodInfo); + } + protected virtual EXPR GenerateUserDefinedComparisonOperator(EXPRBINOP expr) + { + Debug.Assert(expr != null); + Debug.Assert(alwaysRewrite || currentAnonMeth != null); + PREDEFMETH pdm; + + switch (expr.kind) + { + case ExpressionKind.EK_STRINGEQ: pdm = PREDEFMETH.PM_EXPRESSION_EQUAL_USER_DEFINED; break; + case ExpressionKind.EK_STRINGNE: pdm = PREDEFMETH.PM_EXPRESSION_NOTEQUAL_USER_DEFINED; break; + case ExpressionKind.EK_DELEGATEEQ: pdm = PREDEFMETH.PM_EXPRESSION_EQUAL_USER_DEFINED; break; + case ExpressionKind.EK_DELEGATENE: pdm = PREDEFMETH.PM_EXPRESSION_NOTEQUAL_USER_DEFINED; break; + case ExpressionKind.EK_EQ: pdm = PREDEFMETH.PM_EXPRESSION_EQUAL_USER_DEFINED; break; + case ExpressionKind.EK_NE: pdm = PREDEFMETH.PM_EXPRESSION_NOTEQUAL_USER_DEFINED; break; + case ExpressionKind.EK_LE: pdm = PREDEFMETH.PM_EXPRESSION_LESSTHANOREQUAL_USER_DEFINED; break; + case ExpressionKind.EK_LT: pdm = PREDEFMETH.PM_EXPRESSION_LESSTHAN_USER_DEFINED; break; + case ExpressionKind.EK_GE: pdm = PREDEFMETH.PM_EXPRESSION_GREATERTHANOREQUAL_USER_DEFINED; break; + case ExpressionKind.EK_GT: pdm = PREDEFMETH.PM_EXPRESSION_GREATERTHAN_USER_DEFINED; break; + default: + throw Error.InternalCompilerError(); + } + EXPR p1 = expr.GetOptionalLeftChild(); + EXPR p2 = expr.GetOptionalRightChild(); + if (expr.GetOptionalUserDefinedCall() != null) + { + EXPRCALL udcall = expr.GetOptionalUserDefinedCall().asCALL(); + EXPRLIST args = udcall.GetOptionalArguments().asLIST(); + Debug.Assert(args.GetOptionalNextListNode().kind != ExpressionKind.EK_LIST); + + p1 = args.GetOptionalElement(); + p2 = args.GetOptionalNextListNode(); + } + p1 = Visit(p1); + p2 = Visit(p2); + FixLiftedUserDefinedBinaryOperators(expr, ref p1, ref p2); + EXPR lift = GetExprFactory().CreateBoolConstant(false); // We never lift to null in C#. + EXPR methodInfo = GetExprFactory().CreateMethodInfo(expr.GetUserDefinedCallMethod()); + return GenerateCall(pdm, p1, p2, lift, methodInfo); + } + protected EXPR RewriteLambdaBody(EXPRBOUNDLAMBDA anonmeth) + { + Debug.Assert(anonmeth != null); + Debug.Assert(anonmeth.OptionalBody != null); + Debug.Assert(anonmeth.OptionalBody.GetOptionalStatements() != null); + // There ought to be no way to get an empty statement block successfully converted into an expression tree. + Debug.Assert(anonmeth.OptionalBody.GetOptionalStatements().GetOptionalNextStatement() == null); + + EXPRBLOCK body = anonmeth.OptionalBody; + + // The most likely case: + if (body.GetOptionalStatements().isRETURN()) + { + Debug.Assert(body.GetOptionalStatements().asRETURN().GetOptionalObject() != null); + return Visit(body.GetOptionalStatements().asRETURN().GetOptionalObject()); + } + // This can only if it is a void delegate and this is a void expression, such as a call to a void method + // or something like Expression> e = (Foo f) => f.MyEvent += MyDelegate; + + throw Error.InternalCompilerError(); + } + protected EXPR RewriteLambdaParameters(EXPRBOUNDLAMBDA anonmeth) + { + Debug.Assert(anonmeth != null); + + // new ParameterExpression[2] {Parameter(typeof(type1), name1), Parameter(typeof(type2), name2)} + + EXPR paramArrayInitializerArgs = null; + EXPR paramArrayInitializerArgsTail = paramArrayInitializerArgs; + + for (Symbol sym = anonmeth.ArgumentScope(); sym != null; sym = sym.nextChild) + { + if (!sym.IsLocalVariableSymbol()) + { + continue; + } + LocalVariableSymbol local = sym.AsLocalVariableSymbol(); + if (local.isThis) + { + continue; + } + GetExprFactory().AppendItemToList(local.wrap, ref paramArrayInitializerArgs, ref paramArrayInitializerArgsTail); + } + + return GenerateParamsArray(paramArrayInitializerArgs, PredefinedType.PT_PARAMETEREXPRESSION); + } + protected virtual EXPR GenerateConversion(EXPR arg, CType CType, bool bChecked) + { + return GenerateConversionWithSource(Visit(arg), CType, bChecked || arg.isChecked()); + } + protected virtual EXPR GenerateConversionWithSource(EXPR pTarget, CType pType, bool bChecked) + { + PREDEFMETH pdm = bChecked ? PREDEFMETH.PM_EXPRESSION_CONVERTCHECKED : PREDEFMETH.PM_EXPRESSION_CONVERT; + EXPR pTypeOf = CreateTypeOf(pType); + return GenerateCall(pdm, pTarget, pTypeOf); + } + protected virtual EXPR GenerateValueAccessConversion(EXPR pArgument) + { + Debug.Assert(pArgument != null); + CType pStrippedTypeOfArgument = pArgument.type.StripNubs(); + EXPR pStrippedTypeExpr = CreateTypeOf(pStrippedTypeOfArgument); + return GenerateCall(PREDEFMETH.PM_EXPRESSION_CONVERT, Visit(pArgument), pStrippedTypeExpr); + } + protected virtual EXPR GenerateUserDefinedConversion(EXPR arg, CType type, MethWithInst method) + { + EXPR target = Visit(arg); + return GenerateUserDefinedConversion(arg, type, target, method); + } + protected virtual EXPR GenerateUserDefinedConversion(EXPR arg, CType CType, EXPR target, MethWithInst method) + { + // The user-defined explicit conversion from enum? to decimal or decimal? requires + // that we convert the enum? to its nullable underlying CType. + if (isEnumToDecimalConversion(arg.type, CType)) + { + // Special case: If we have enum? to decimal? then we need to emit + // a conversion from enum? to its nullable underlying CType first. + // This is unfortunate; we ought to reorganize how conversions are + // represented in the EXPR tree so that this is more transparent. + + // converting an enum to its underlying CType never fails, so no need to check it. + CType underlyingType = arg.type.StripNubs().underlyingEnumType(); + CType nullableType = GetSymbolLoader().GetTypeManager().GetNullable(underlyingType); + EXPR typeofNubEnum = CreateTypeOf(nullableType); + target = GenerateCall(PREDEFMETH.PM_EXPRESSION_CONVERT, target, typeofNubEnum); + } + + // If the methodinfo does not return the target CType AND this is not a lifted conversion + // from one value CType to another, then we need to wrap the whole thing in another conversion, + // e.g. if we have a user-defined conversion from int to S? and we have (S)myint, then we need to generate + // Convert(Convert(myint, typeof(S?), op_implicit), typeof(S)) + + CType pMethodReturnType = GetSymbolLoader().GetTypeManager().SubstType(method.Meth().RetType, + method.GetType(), method.TypeArgs); + bool fDontLiftReturnType = (pMethodReturnType == CType || (IsNullableValueType(arg.type) && IsNullableValueType(CType))); + + EXPR typeofInner = CreateTypeOf(fDontLiftReturnType ? CType : pMethodReturnType); + EXPR methodInfo = GetExprFactory().CreateMethodInfo(method); + PREDEFMETH pdmInner = arg.isChecked() ? PREDEFMETH.PM_EXPRESSION_CONVERTCHECKED_USER_DEFINED : PREDEFMETH.PM_EXPRESSION_CONVERT_USER_DEFINED; + EXPR callUserDefinedConversion = GenerateCall(pdmInner, target, typeofInner, methodInfo); + + if (fDontLiftReturnType) + { + return callUserDefinedConversion; + } + + PREDEFMETH pdmOuter = arg.isChecked() ? PREDEFMETH.PM_EXPRESSION_CONVERTCHECKED : PREDEFMETH.PM_EXPRESSION_CONVERT; + EXPR typeofOuter = CreateTypeOf(CType); + return GenerateCall(pdmOuter, callUserDefinedConversion, typeofOuter); + } + protected virtual EXPR GenerateUserDefinedConversion(EXPRUSERDEFINEDCONVERSION pExpr, EXPR pArgument) + { + EXPR pCastCall = pExpr.UserDefinedCall; + EXPR pCastArgument = pExpr.Argument; + EXPR pConversionSource = null; + + if (!isEnumToDecimalConversion(pArgument.type, pExpr.type) && IsNullableValueAccess(pCastArgument, pArgument)) + { + // We have an implicit conversion of nullable CType to the value CType, generate a convert node for it. + pConversionSource = GenerateValueAccessConversion(pArgument); + } + else if (pCastCall.isCALL() && pCastCall.asCALL().pConversions != null) + { + EXPR pUDConversion = pCastCall.asCALL().pConversions; + if (pUDConversion.isCALL()) + { + EXPR pUDConversionArgument = pUDConversion.asCALL().GetOptionalArguments(); + if (IsNullableValueAccess(pUDConversionArgument, pArgument)) + { + pConversionSource = GenerateValueAccessConversion(pArgument); + } + else + { + pConversionSource = Visit(pUDConversionArgument); + } + return GenerateConversionWithSource(pConversionSource, pCastCall.type, pCastCall.asCALL().isChecked()); + } + else + { + // This can happen if we have a UD conversion from C to, say, int, + // and we have an explicit cast to decimal?. The conversion should + // then be bound as two chained user-defined conversions. + Debug.Assert(pUDConversion.isUSERDEFINEDCONVERSION()); + // Just recurse. + return GenerateUserDefinedConversion(pUDConversion.asUSERDEFINEDCONVERSION(), pArgument); + } + } + else + { + pConversionSource = Visit(pCastArgument); + } + return GenerateUserDefinedConversion(pCastArgument, pExpr.type, pConversionSource, pExpr.UserDefinedCallMethod); + } + protected virtual EXPR GenerateParameter(string name, CType CType) + { + GetSymbolLoader().GetReqPredefType(PredefinedType.PT_STRING); // force an ensure state + EXPRCONSTANT nameString = GetExprFactory().CreateStringConstant(name); + EXPRTYPEOF pTypeOf = CreateTypeOf(CType); + return GenerateCall(PREDEFMETH.PM_EXPRESSION_PARAMETER, pTypeOf, nameString); + } + protected MethodSymbol GetPreDefMethod(PREDEFMETH pdm) + { + return GetSymbolLoader().getPredefinedMembers().GetMethod(pdm); + } + protected EXPRTYPEOF CreateTypeOf(CType CType) + { + return GetExprFactory().CreateTypeOf(CType); + } + protected EXPR CreateWraps(EXPRBOUNDLAMBDA anonmeth) + { + EXPR sequence = null; + for (Symbol sym = anonmeth.ArgumentScope().firstChild; sym != null; sym = sym.nextChild) + { + if (!sym.IsLocalVariableSymbol()) + { + continue; + } + LocalVariableSymbol local = sym.AsLocalVariableSymbol(); + if (local.isThis) + { + continue; + } + Debug.Assert(anonmeth.OptionalBody != null); + EXPR create = GenerateParameter(local.name.Text, local.GetType()); + local.wrap = GetExprFactory().CreateWrapNoAutoFree(anonmeth.OptionalBody.OptionalScopeSymbol, create); + EXPR save = GetExprFactory().CreateSave(local.wrap); + if (sequence == null) + { + sequence = save; + } + else + { + sequence = GetExprFactory().CreateSequence(sequence, save); + } + } + + return sequence; + } + protected EXPR DestroyWraps(EXPRBOUNDLAMBDA anonmeth, EXPR sequence) + { + for (Symbol sym = anonmeth.ArgumentScope(); sym != null; sym = sym.nextChild) + { + if (!sym.IsLocalVariableSymbol()) + { + continue; + } + LocalVariableSymbol local = sym.AsLocalVariableSymbol(); + if (local.isThis) + { + continue; + } + Debug.Assert(local.wrap != null); + Debug.Assert(anonmeth.OptionalBody != null); + EXPR freeWrap = GetExprFactory().CreateWrap(anonmeth.OptionalBody.OptionalScopeSymbol, local.wrap); + sequence = GetExprFactory().CreateReverseSequence(sequence, freeWrap); + } + return sequence; + } + protected virtual EXPR GenerateConstructor(EXPRCALL expr) + { + Debug.Assert(expr != null); + Debug.Assert(expr.mwi.Meth().IsConstructor()); + + // Realize a call to new DELEGATE(obj, FUNCPTR) as though it actually was + // (DELEGATE)CreateDelegate(typeof(DELEGATE), obj, GetMethInfoFromHandle(FUNCPTR)) + + if (IsDelegateConstructorCall(expr)) + { + return GenerateDelegateConstructor(expr); + } + EXPR constructorInfo = GetExprFactory().CreateMethodInfo(expr.mwi); + EXPR args = GenerateArgsList(expr.GetOptionalArguments()); + EXPR Params = GenerateParamsArray(args, PredefinedType.PT_EXPRESSION); + if (expr.type.IsAggregateType() && expr.type.AsAggregateType().getAggregate().IsAnonymousType()) + { + EXPR members = GenerateMembersArray(expr.type.AsAggregateType(), PredefinedType.PT_METHODINFO); + return GenerateCall(PREDEFMETH.PM_EXPRESSION_NEW_MEMBERS, constructorInfo, Params, members); + } + else + { + return GenerateCall(PREDEFMETH.PM_EXPRESSION_NEW, constructorInfo, Params); + } + } + protected virtual EXPR GenerateDelegateConstructor(EXPRCALL expr) + { + // In: + // + // new DELEGATE(obj, &FUNC) + // + // Out: + // + // Cast( + // Call( + // null, + // (MethodInfo)GetMethodFromHandle(&CreateDelegate), + // new Expression[3]{ + // Constant(typeof(DELEGATE)), + // transformed-object, + // Constant((MethodInfo)GetMethodFromHandle(&FUNC)}), + // typeof(DELEGATE)) + // + + Debug.Assert(expr != null); + Debug.Assert(expr.mwi.Meth().IsConstructor()); + Debug.Assert(expr.type.isDelegateType()); + Debug.Assert(expr.GetOptionalArguments() != null); + Debug.Assert(expr.GetOptionalArguments().isLIST()); + EXPRLIST origArgs = expr.GetOptionalArguments().asLIST(); + EXPR target = origArgs.GetOptionalElement(); + Debug.Assert(origArgs.GetOptionalNextListNode().kind == ExpressionKind.EK_FUNCPTR); + EXPRFUNCPTR funcptr = origArgs.GetOptionalNextListNode().asFUNCPTR(); + MethodSymbol createDelegateMethod = GetPreDefMethod(PREDEFMETH.PM_DELEGATE_CREATEDELEGATE_TYPE_OBJ_METHINFO); + AggregateType delegateType = GetSymbolLoader().GetOptPredefTypeErr(PredefinedType.PT_DELEGATE, true); + MethWithInst mwi = new MethWithInst(createDelegateMethod, delegateType); + + EXPR exprnull = GetExprFactory().CreateNull(); + EXPR methinfo = GetExprFactory().CreateMethodInfo(mwi); + EXPR param1 = GenerateConstant(CreateTypeOf(expr.type)); + EXPR param2 = Visit(target); + EXPR param3 = GenerateConstant(GetExprFactory().CreateMethodInfo(funcptr.mwi)); + EXPR paramsList = GetExprFactory().CreateList(param1, param2, param3); + EXPR Params = GenerateParamsArray(paramsList, PredefinedType.PT_EXPRESSION); + EXPR call = GenerateCall(PREDEFMETH.PM_EXPRESSION_CALL, exprnull, methinfo, Params); + EXPR pTypeOf = CreateTypeOf(expr.type); + return GenerateCall(PREDEFMETH.PM_EXPRESSION_CONVERT, call, pTypeOf); + } + protected virtual EXPR GenerateArgsList(EXPR oldArgs) + { + EXPR newArgs = null; + EXPR newArgsTail = newArgs; + for (ExpressionIterator it = new ExpressionIterator(oldArgs); !it.AtEnd(); it.MoveNext()) + { + EXPR oldArg = it.Current(); + GetExprFactory().AppendItemToList(Visit(oldArg), ref newArgs, ref newArgsTail); + } + return newArgs; + } + protected virtual EXPR GenerateIndexList(EXPR oldIndices) + { + CType intType = symbolLoader.GetReqPredefType(PredefinedType.PT_INT, true); + + EXPR newIndices = null; + EXPR newIndicesTail = newIndices; + for (ExpressionIterator it = new ExpressionIterator(oldIndices); !it.AtEnd(); it.MoveNext()) + { + EXPR newIndex = it.Current(); + if (newIndex.type != intType) + { + EXPRCLASS exprType = expressionFactory.CreateClass(intType, null, null); + newIndex = expressionFactory.CreateCast(EXPRFLAG.EXF_INDEXEXPR, exprType, newIndex); + newIndex.flags |= EXPRFLAG.EXF_CHECKOVERFLOW; + } + EXPR rewrittenIndex = Visit(newIndex); + expressionFactory.AppendItemToList(rewrittenIndex, ref newIndices, ref newIndicesTail); + } + return newIndices; + } + protected virtual EXPR GenerateConstant(EXPR expr) + { + EXPRFLAG flags = 0; + + AggregateType pObject = GetSymbolLoader().GetReqPredefType(PredefinedType.PT_OBJECT, true); + + if (expr.type.IsNullType()) + { + EXPRTYPEOF pTypeOf = CreateTypeOf(pObject); + return GenerateCall(PREDEFMETH.PM_EXPRESSION_CONSTANT_OBJECT_TYPE, expr, pTypeOf); + } + + AggregateType stringType = GetSymbolLoader().GetReqPredefType(PredefinedType.PT_STRING, true); + if (expr.type != stringType) + { + flags = EXPRFLAG.EXF_BOX; + } + + EXPRCLASS objectType = GetExprFactory().MakeClass(pObject); + EXPRCAST cast = GetExprFactory().CreateCast(flags, objectType, expr); + EXPRTYPEOF pTypeOf2 = CreateTypeOf(expr.type); + + return GenerateCall(PREDEFMETH.PM_EXPRESSION_CONSTANT_OBJECT_TYPE, cast, pTypeOf2); + } + protected EXPRCALL GenerateCall(PREDEFMETH pdm, EXPR arg1) + { + MethodSymbol method = GetPreDefMethod(pdm); + // this should be enforced in an earlier pass and the tranform pass should not + // be handeling this error + if (method == null) + return null; + AggregateType expressionType = GetSymbolLoader().GetOptPredefTypeErr(PredefinedType.PT_EXPRESSION, true); + MethWithInst mwi = new MethWithInst(method, expressionType); + EXPRMEMGRP pMemGroup = GetExprFactory().CreateMemGroup(null, mwi); + EXPRCALL call = GetExprFactory().CreateCall(0, mwi.Meth().RetType, arg1, pMemGroup, mwi); + call.PredefinedMethod = pdm; + return call; + } + protected EXPRCALL GenerateCall(PREDEFMETH pdm, EXPR arg1, EXPR arg2) + { + MethodSymbol method = GetPreDefMethod(pdm); + if (method == null) + return null; + AggregateType expressionType = GetSymbolLoader().GetOptPredefTypeErr(PredefinedType.PT_EXPRESSION, true); + EXPR args = GetExprFactory().CreateList(arg1, arg2); + MethWithInst mwi = new MethWithInst(method, expressionType); + EXPRMEMGRP pMemGroup = GetExprFactory().CreateMemGroup(null, mwi); + EXPRCALL call = GetExprFactory().CreateCall(0, mwi.Meth().RetType, args, pMemGroup, mwi); + call.PredefinedMethod = pdm; + return call; + } + protected EXPRCALL GenerateCall(PREDEFMETH pdm, EXPR arg1, EXPR arg2, EXPR arg3) + { + MethodSymbol method = GetPreDefMethod(pdm); + if (method == null) + return null; + AggregateType expressionType = GetSymbolLoader().GetOptPredefTypeErr(PredefinedType.PT_EXPRESSION, true); + EXPR args = GetExprFactory().CreateList(arg1, arg2, arg3); + MethWithInst mwi = new MethWithInst(method, expressionType); + EXPRMEMGRP pMemGroup = GetExprFactory().CreateMemGroup(null, mwi); + EXPRCALL call = GetExprFactory().CreateCall(0, mwi.Meth().RetType, args, pMemGroup, mwi); + call.PredefinedMethod = pdm; + return call; + } + protected EXPRCALL GenerateCall(PREDEFMETH pdm, EXPR arg1, EXPR arg2, EXPR arg3, EXPR arg4) + { + MethodSymbol method = GetPreDefMethod(pdm); + if (method == null) + return null; + AggregateType expressionType = GetSymbolLoader().GetOptPredefTypeErr(PredefinedType.PT_EXPRESSION, true); + EXPR args = GetExprFactory().CreateList(arg1, arg2, arg3, arg4); + MethWithInst mwi = new MethWithInst(method, expressionType); + EXPRMEMGRP pMemGroup = GetExprFactory().CreateMemGroup(null, mwi); + EXPRCALL call = GetExprFactory().CreateCall(0, mwi.Meth().RetType, args, pMemGroup, mwi); + call.PredefinedMethod = pdm; + return call; + } + protected virtual EXPRARRINIT GenerateParamsArray(EXPR args, PredefinedType pt) + { + int parameterCount = ExpressionIterator.Count(args); + AggregateType paramsArrayElementType = GetSymbolLoader().GetOptPredefTypeErr(pt, true); + ArrayType paramsArrayType = GetSymbolLoader().GetTypeManager().GetArray(paramsArrayElementType, 1); + EXPRCONSTANT paramsArrayArg = GetExprFactory().CreateIntegerConstant(parameterCount); + EXPRARRINIT arrayInit = GetExprFactory().CreateArrayInit(EXPRFLAG.EXF_CANTBENULL, paramsArrayType, args, paramsArrayArg, null); + arrayInit.dimSize = parameterCount; + arrayInit.dimSizes = new int[] { arrayInit.dimSize }; // CLEANUP: Why isn't this done by the factory? + return arrayInit; + } + protected virtual EXPRARRINIT GenerateMembersArray(AggregateType anonymousType, PredefinedType pt) + { + EXPR newArgs = null; + EXPR newArgsTail = newArgs; + int methodCount = 0; + AggregateSymbol aggSym = anonymousType.getAggregate(); + + for (Symbol member = aggSym.firstChild; member != null; member = member.nextChild) + { + if (member.IsMethodSymbol()) + { + MethodSymbol method = member.AsMethodSymbol(); + if (method.MethKind() == MethodKindEnum.PropAccessor) + { + EXPRMETHODINFO methodInfo = GetExprFactory().CreateMethodInfo(method, anonymousType, method.Params); + GetExprFactory().AppendItemToList(methodInfo, ref newArgs, ref newArgsTail); + methodCount++; + } + } + } + + AggregateType paramsArrayElementType = GetSymbolLoader().GetOptPredefTypeErr(pt, true); + ArrayType paramsArrayType = GetSymbolLoader().GetTypeManager().GetArray(paramsArrayElementType, 1); + EXPRCONSTANT paramsArrayArg = GetExprFactory().CreateIntegerConstant(methodCount); + EXPRARRINIT arrayInit = GetExprFactory().CreateArrayInit(EXPRFLAG.EXF_CANTBENULL, paramsArrayType, newArgs, paramsArrayArg, null); + arrayInit.dimSize = methodCount; + arrayInit.dimSizes = new int[] { arrayInit.dimSize }; // CLEANUP: Why isn't this done by the factory? + return arrayInit; + } + protected void FixLiftedUserDefinedBinaryOperators(EXPRBINOP expr, ref EXPR pp1, ref EXPR pp2) + { + // If we have lifted T1 op T2 to T1? op T2?, and we have an expression T1 op T2? or T1? op T2 then + // we need to ensure that the unlifted actual arguments are promoted to their nullable CType. + Debug.Assert(expr != null); + Debug.Assert(pp1 != null); + Debug.Assert(pp1 != null); + Debug.Assert(pp2 != null); + Debug.Assert(pp2 != null); + MethodSymbol method = expr.GetUserDefinedCallMethod().Meth(); + EXPR orig1 = expr.GetOptionalLeftChild(); + EXPR orig2 = expr.GetOptionalRightChild(); + Debug.Assert(orig1 != null && orig2 != null); + EXPR new1 = pp1; + EXPR new2 = pp2; + CType fptype1 = method.Params.Item(0); + CType fptype2 = method.Params.Item(1); + CType aatype1 = orig1.type; + CType aatype2 = orig2.type; + // Is the operator even a candidate for lifting? + if (fptype1.IsNullableType() || fptype2.IsNullableType() || + !fptype1.IsAggregateType() || !fptype2.IsAggregateType() || + !fptype1.AsAggregateType().getAggregate().IsValueType() || + !fptype2.AsAggregateType().getAggregate().IsValueType()) + { + return; + } + CType nubfptype1 = GetSymbolLoader().GetTypeManager().GetNullable(fptype1); + CType nubfptype2 = GetSymbolLoader().GetTypeManager().GetNullable(fptype2); + // If we have null op X, or T1 op T2?, or T1 op null, lift first arg to T1? + if (aatype1.IsNullType() || aatype1 == fptype1 && (aatype2 == nubfptype2 || aatype2.IsNullType())) + { + new1 = GenerateCall(PREDEFMETH.PM_EXPRESSION_CONVERT, new1, CreateTypeOf(nubfptype1)); + } + + // If we have X op null, or T1? op T2, or null op T2, lift second arg to T2? + if (aatype2.IsNullType() || aatype2 == fptype2 && (aatype1 == nubfptype1 || aatype1.IsNullType())) + { + new2 = GenerateCall(PREDEFMETH.PM_EXPRESSION_CONVERT, new2, CreateTypeOf(nubfptype2)); + } + pp1 = new1; + pp2 = new2; + } + protected bool IsNullableValueType(CType pType) + { + if (pType.IsNullableType()) + { + CType pStrippedType = pType.StripNubs(); + return pStrippedType.IsAggregateType() && pStrippedType.AsAggregateType().getAggregate().IsValueType(); + } + return false; + } + protected bool IsNullableValueAccess(EXPR pExpr, EXPR pObject) + { + Debug.Assert(pExpr != null); + return pExpr.isPROP() && (pExpr.asPROP().GetMemberGroup().GetOptionalObject() == pObject) && pObject.type.IsNullableType(); + } + protected bool IsDelegateConstructorCall(EXPR pExpr) + { + Debug.Assert(pExpr != null); + if (!pExpr.isCALL()) + { + return false; + } + EXPRCALL pCall = pExpr.asCALL(); + return pCall.mwi.Meth() != null && + pCall.mwi.Meth().IsConstructor() && + pCall.type.isDelegateType() && + pCall.GetOptionalArguments() != null && + pCall.GetOptionalArguments().isLIST() && + pCall.GetOptionalArguments().asLIST().GetOptionalNextListNode().kind == ExpressionKind.EK_FUNCPTR; + } + private static bool isEnumToDecimalConversion(CType argtype, CType desttype) + { + CType strippedArgType = argtype.IsNullableType() ? argtype.StripNubs() : argtype; + CType strippedDestType = desttype.IsNullableType() ? desttype.StripNubs() : desttype; + return strippedArgType.isEnumType() && strippedDestType.isPredefType(PredefinedType.PT_DECIMAL); + } + } +} diff --git a/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/Tree/ZeroInitialize.cs b/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/Tree/ZeroInitialize.cs new file mode 100644 index 000000000..b953a0d73 --- /dev/null +++ b/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/Tree/ZeroInitialize.cs @@ -0,0 +1,15 @@ +// ==++== +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// ==--== + +namespace Microsoft.CSharp.RuntimeBinder.Semantics +{ + internal class EXPRZEROINIT : EXPR + { + public EXPR OptionalArgument; + public EXPR OptionalConstructorCall; + public bool IsConstructor; + } +} diff --git a/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/TypeBind.cs b/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/TypeBind.cs new file mode 100644 index 000000000..a113ccf8c --- /dev/null +++ b/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/TypeBind.cs @@ -0,0 +1,404 @@ +// ==++== +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// ==--== + +using System.Diagnostics; +using Microsoft.CSharp.RuntimeBinder.Errors; +using Microsoft.CSharp.RuntimeBinder.Syntax; + +namespace Microsoft.CSharp.RuntimeBinder.Semantics +{ + ///////////////////////////////////////////////////////////////////////////////// + // Defines the structure used when binding types + + // CheckConstraints options. + + internal enum CheckConstraintsFlags + { + None = 0x00, + Outer = 0x01, + NoDupErrors = 0x02, + NoErrors = 0x04, + } + + ///////////////////////////////////////////////////////////////////////////////// + // TypeBind has static methods to accomplish most tasks. + // + // For some of these tasks there are also instance methods. The instance + // method versions don't report not found errors (they may report others) but + // instead record error information in the TypeBind instance. Call the + // ReportErrors method to report recorded errors. + + internal static class TypeBind + { + // Check the constraints of any type arguments in the given Type. + public static bool CheckConstraints(CSemanticChecker checker, ErrorHandling errHandling, CType type, CheckConstraintsFlags flags) + { + type = type.GetNakedType(false); + + if (type.IsNullableType()) + { + CType typeT = type.AsNullableType().GetAts(checker.GetErrorContext()); + if (typeT != null) + type = typeT; + else + type = type.GetNakedType(true); + } + + if (!type.IsAggregateType()) + return true; + + AggregateType ats = type.AsAggregateType(); + + if (ats.GetTypeArgsAll().size == 0) + { + // Common case: there are no type vars, so there are no constraints. + ats.fConstraintsChecked = true; + ats.fConstraintError = false; + return true; + } + + if (ats.fConstraintsChecked) + { + // Already checked. + if (!ats.fConstraintError || (flags & CheckConstraintsFlags.NoDupErrors) != 0) + { + // No errors or no need to report errors again. + return !ats.fConstraintError; + } + } + + TypeArray typeVars = ats.getAggregate().GetTypeVars(); + TypeArray typeArgsThis = ats.GetTypeArgsThis(); + TypeArray typeArgsAll = ats.GetTypeArgsAll(); + + Debug.Assert(typeVars.size == typeArgsThis.size); + + if (!ats.fConstraintsChecked) + { + ats.fConstraintsChecked = true; + ats.fConstraintError = false; + } + + // Check the outer type first. If CheckConstraintsFlags.Outer is not specified and the + // outer type has already been checked then don't bother checking it. + if (ats.outerType != null && ((flags & CheckConstraintsFlags.Outer) != 0 || !ats.outerType.fConstraintsChecked)) + { + CheckConstraints(checker, errHandling, ats.outerType, flags); + ats.fConstraintError |= ats.outerType.fConstraintError; + } + + if (typeVars.size > 0) + ats.fConstraintError |= !CheckConstraintsCore(checker, errHandling, ats.getAggregate(), typeVars, typeArgsThis, typeArgsAll, null, (flags & CheckConstraintsFlags.NoErrors)); + + // Now check type args themselves. + for (int i = 0; i < typeArgsThis.size; i++) + { + CType arg = typeArgsThis.Item(i).GetNakedType(true); + if (arg.IsAggregateType() && !arg.AsAggregateType().fConstraintsChecked) + { + CheckConstraints(checker, errHandling, arg.AsAggregateType(), flags | CheckConstraintsFlags.Outer); + if (arg.AsAggregateType().fConstraintError) + ats.fConstraintError = true; + } + } + return !ats.fConstraintError; + } + + //////////////////////////////////////////////////////////////////////////////// + // Check the constraints on the method instantiation. + public static void CheckMethConstraints(CSemanticChecker checker, ErrorHandling errCtx, MethWithInst mwi) + { + Debug.Assert(mwi.Meth() != null && mwi.GetType() != null && mwi.TypeArgs != null); + Debug.Assert(mwi.Meth().typeVars.size == mwi.TypeArgs.size); + Debug.Assert(mwi.GetType().getAggregate() == mwi.Meth().getClass()); + + if (mwi.TypeArgs.size > 0) + { + CheckConstraintsCore(checker, errCtx, mwi.Meth(), mwi.Meth().typeVars, mwi.TypeArgs, mwi.GetType().GetTypeArgsAll(), mwi.TypeArgs, CheckConstraintsFlags.None); + } + } + //////////////////////////////////////////////////////////////////////////////// + // Check whether typeArgs satisfies the constraints of typeVars. The + // typeArgsCls and typeArgsMeth are used for substitution on the bounds. The + // tree and symErr are used for error reporting. + + private static bool CheckConstraintsCore(CSemanticChecker checker, ErrorHandling errHandling, Symbol symErr, TypeArray typeVars, TypeArray typeArgs, TypeArray typeArgsCls, TypeArray typeArgsMeth, CheckConstraintsFlags flags) + { + Debug.Assert(typeVars.size == typeArgs.size); + Debug.Assert(typeVars.size > 0); + Debug.Assert(flags == CheckConstraintsFlags.None || flags == CheckConstraintsFlags.NoErrors); + + bool fError = false; + + for (int i = 0; i < typeVars.size; i++) + { + // Empty bounds should be set to object. + TypeParameterType var = typeVars.ItemAsTypeParameterType(i); + CType arg = typeArgs.Item(i); + + bool fOK = CheckSingleConstraint(checker, errHandling, symErr, var, arg, typeArgsCls, typeArgsMeth, flags); + fError |= !fOK; + } + + return !fError; + } + + private static bool CheckSingleConstraint(CSemanticChecker checker, ErrorHandling errHandling, Symbol symErr, TypeParameterType var, CType arg, TypeArray typeArgsCls, TypeArray typeArgsMeth, CheckConstraintsFlags flags) + { + bool fReportErrors = 0 == (flags & CheckConstraintsFlags.NoErrors); + + if (arg.IsOpenTypePlaceholderType()) + { + return true; + } + + if (arg.IsErrorType()) + { + // Error should have been reported previously. + return false; + } + + if (checker.CheckBogus(arg)) + { + if (fReportErrors) + { + errHandling.ErrorRef(ErrorCode.ERR_BogusType, arg); + } + + return false; + } + + if (arg.IsPointerType() || arg.isSpecialByRefType()) + { + if (fReportErrors) + { + errHandling.Error(ErrorCode.ERR_BadTypeArgument, arg); + } + + return false; + } + + if (arg.isStaticClass()) + { + if (fReportErrors) + { + checker.ReportStaticClassError(null, arg, ErrorCode.ERR_GenericArgIsStaticClass); + } + + return false; + } + + bool fError = false; + if (var.HasRefConstraint() && !arg.IsRefType()) + { + if (fReportErrors) + { + errHandling.ErrorRef(ErrorCode.ERR_RefConstraintNotSatisfied, symErr, new ErrArgNoRef(var), arg); + } + + fError = true; + } + + TypeArray bnds = checker.GetSymbolLoader().GetTypeManager().SubstTypeArray(var.GetBounds(), typeArgsCls, typeArgsMeth); + int itypeMin = 0; + + if (var.HasValConstraint()) + { + // If we have a type variable that is constrained to a value type, then we + // want to check if its a nullable type, so that we can report the + // constraint error below. In order to do this however, we need to check + // that either the type arg is not a value type, or it is a nullable type. + // + // To check whether or not its a nullable type, we need to get the resolved + // bound from the type argument and check against that. + + bool bIsValueType = arg.IsValType(); + bool bIsNullable = arg.IsNullableType(); + if (bIsValueType && arg.IsTypeParameterType()) + { + TypeArray pArgBnds = arg.AsTypeParameterType().GetBounds(); + if (pArgBnds.size > 0) + { + bIsNullable = pArgBnds.Item(0).IsNullableType(); + } + } + + if (!bIsValueType || bIsNullable) + { + if (fReportErrors) + { + errHandling.ErrorRef(ErrorCode.ERR_ValConstraintNotSatisfied, symErr, new ErrArgNoRef(var), arg); + } + + fError = true; + } + + // Since FValCon() is set it is redundant to check System.ValueType as well. + if (bnds.size != 0 && bnds.Item(0).isPredefType(PredefinedType.PT_VALUE)) + { + itypeMin = 1; + } + } + + for (int j = itypeMin; j < bnds.size; j++) + { + CType typeBnd = bnds.Item(j); + if (!SatisfiesBound(checker, arg, typeBnd)) + { + if (fReportErrors) + { + // The bound isn't satisfied because of a constaint type. Explain to the user why not. + // There are 4 main cases, based on the type of the supplied type argument: + // - reference type, or type parameter known to be a reference type + // - nullable type, from which there is a boxing conversion to the constraint type(see below for details) + // - type varaiable + // - value type + // These cases are broken out because: a) The sets of conversions which can be used + // for constraint satisfaction is different based on the type argument supplied, + // and b) Nullable is one funky type, and user's can use all the help they can get + // when using it. + ErrorCode error; + if (arg.IsRefType()) + { + // A reference type can only satisfy bounds to types + // to which they have an implicit reference conversion + error = ErrorCode.ERR_GenericConstraintNotSatisfiedRefType; + } + else if (arg.IsNullableType() && checker.GetSymbolLoader().HasBaseConversion(arg.AsNullableType().GetUnderlyingType(), typeBnd)) // UNDONE: this is inlining FBoxingConv + { + // nullable types do not satisfy bounds to every type that they are boxable to + // They only satisfy bounds of object and ValueType + if (typeBnd.isPredefType(PredefinedType.PT_ENUM) || arg.AsNullableType().GetUnderlyingType() == typeBnd) + { + // Nullable types don't satisfy bounds of EnumType, or the underlying type of the enum + // even though the conversion from Nullable to these types is a boxing conversion + // This is a rare case, because these bounds can never be directly stated ... + // These bounds can only occur when one type paramter is constrained to a second type parameter + // and the second type parameter is instantiated with Enum or the underlying type of the first type + // parameter + error = ErrorCode.ERR_GenericConstraintNotSatisfiedNullableEnum; + } + else + { + // Nullable types don't satisfy the bounds of any interface type + // even when there is a boxing conversion from the Nullable type to + // the interface type. This will be a relatively common scenario + // so we cal it out separately from the previous case. + Debug.Assert(typeBnd.isInterfaceType()); + error = ErrorCode.ERR_GenericConstraintNotSatisfiedNullableInterface; + } + } + else if (arg.IsTypeParameterType()) + { + // Type variables can satisfy bounds through boxing and type variable conversions + Debug.Assert(!arg.IsRefType()); + error = ErrorCode.ERR_GenericConstraintNotSatisfiedTyVar; + } + else + { + // Value types can only satisfy bounds through boxing conversions. + // Note that the exceptional case of Nullable types and boxing is handled above. + error = ErrorCode.ERR_GenericConstraintNotSatisfiedValType; + } + errHandling.Error(error, new ErrArgRef(symErr), new ErrArg(typeBnd, ErrArgFlags.Unique), var, new ErrArgRef(arg, ErrArgFlags.Unique)); + } + fError = true; + } + } + + // Check the newable constraint. + if (!var.HasNewConstraint() || arg.IsValType()) + { + return !fError; + } + + if (arg.isClassType()) + { + AggregateSymbol agg = arg.AsAggregateType().getAggregate(); + + // DevDivBugs : 133313 + // due to late binding nature of IDE created symbols, the AggregateSymbol might not + // have all the information necessary yet, if it is not fully bound. + // by calling LookupAggMember, it will ensure that we will update all the + // information necessary at least for the given method. + checker.GetSymbolLoader().LookupAggMember(checker.GetNameManager().GetPredefName(PredefinedName.PN_CTOR), agg, symbmask_t.MASK_ALL); + + if (agg.HasPubNoArgCtor() && !agg.IsAbstract()) + { + return !fError; + } + } + else if (arg.IsTypeParameterType() && arg.AsTypeParameterType().HasNewConstraint()) + { + return !fError; + } + + if (fReportErrors) + { + errHandling.ErrorRef(ErrorCode.ERR_NewConstraintNotSatisfied, symErr, new ErrArgNoRef(var), arg); + } + + return false; + } + + //////////////////////////////////////////////////////////////////////////////// + // Determine whether the arg type satisfies the typeBnd constraint. Note that + // typeBnd could be just about any type (since we added naked type parameter + // constraints). + + private static bool SatisfiesBound(CSemanticChecker checker, CType arg, CType typeBnd) + { + if (typeBnd == arg) + return true; + + switch (typeBnd.GetTypeKind()) + { + default: + Debug.Assert(false, "Unexpected type."); + return false; + + case TypeKind.TK_VoidType: + case TypeKind.TK_PointerType: + case TypeKind.TK_ErrorType: + return false; + + case TypeKind.TK_ArrayType: + case TypeKind.TK_TypeParameterType: + break; + + case TypeKind.TK_NullableType: + typeBnd = typeBnd.AsNullableType().GetAts(checker.GetErrorContext()); + if (null == typeBnd) + return true; + break; + + case TypeKind.TK_AggregateType: + break; + } + + Debug.Assert(typeBnd.IsAggregateType() || typeBnd.IsTypeParameterType() || typeBnd.IsArrayType()); + + switch (arg.GetTypeKind()) + { + default: + return false; + case TypeKind.TK_ErrorType: + case TypeKind.TK_PointerType: + return false; + case TypeKind.TK_NullableType: + arg = arg.AsNullableType().GetAts(checker.GetErrorContext()); + if (null == arg) + return true; + // Fall through. + goto case TypeKind.TK_TypeParameterType; + case TypeKind.TK_TypeParameterType: + case TypeKind.TK_ArrayType: + case TypeKind.TK_AggregateType: + return checker.GetSymbolLoader().HasBaseConversion(arg, typeBnd); + } + } + } +} diff --git a/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/Types/AggregateType.cs b/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/Types/AggregateType.cs new file mode 100644 index 000000000..c8d89718b --- /dev/null +++ b/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/Types/AggregateType.cs @@ -0,0 +1,226 @@ +// ==++== +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// ==--== + +using System.Diagnostics; + +namespace Microsoft.CSharp.RuntimeBinder.Semantics +{ + // ---------------------------------------------------------------------------- + // + // AggregateType + // + // Represents a genericructed (or instantiated) type. Parent is the AggregateSymbol. + // ---------------------------------------------------------------------------- + + partial class AggregateType : CType + { + private TypeArray m_pTypeArgsThis; + private TypeArray m_pTypeArgsAll; // includes args from outer types + private AggregateSymbol m_pOwningAggregate; + +#if ! CSEE // The EE can't cache these since the AGGSYMs may change as things are imported. + private AggregateType baseType; // This is the result of calling SubstTypeArray on the aggregate's baseClass. + private TypeArray ifacesAll; // This is the result of calling SubstTypeArray on the aggregate's ifacesAll. + private TypeArray winrtifacesAll; //This is the list of collection interfaces implemented by a WinRT object. +#else // !CSEE + + public short proxyOID ; // oid for the managed proxy in the host running inside the debugee + public short typeConverterID ; +#endif // !CSEE + + public bool fConstraintsChecked; // Have theraints been checked yet? + public bool fConstraintError; // Did theraints check produce an error? + + // These two flags are used to track hiding within interfaces. + // Their use and validity is always localized. See e.g. MemberLookup::LookupInInterfaces. + public bool fAllHidden; // All members are hidden by a derived interface member. + public bool fDiffHidden; // Members other than a specific kind are hidden by a derived interface member or class member. + + public AggregateType outerType; // the outer type if this is a nested type + + public void SetOwningAggregate(AggregateSymbol agg) + { + m_pOwningAggregate = agg; + } + + public AggregateSymbol GetOwningAggregate() + { + return m_pOwningAggregate; + } + + public AggregateType GetBaseClass() + { +#if CSEE + AggregateType atsBase = getAggregate().GetBaseClass(); + if (!atsBase || GetTypeArgsAll().size == 0 || atsBase.GetTypeArgsAll().size == 0) + return atsBase; + + return getAggregate().GetTypeManager().SubstType(atsBase, GetTypeArgsAll()).AsAggregateType(); +#else // !CSEE + + if (baseType == null) + { + baseType = getAggregate().GetTypeManager().SubstType(getAggregate().GetBaseClass(), GetTypeArgsAll()) as AggregateType; + } + + return baseType; +#endif // !CSEE + } + + public void SetTypeArgsThis(TypeArray pTypeArgsThis) + { + TypeArray pOuterTypeArgs; + if (outerType != null) + { + Debug.Assert(outerType.GetTypeArgsThis() != null); + Debug.Assert(outerType.GetTypeArgsAll() != null); + + pOuterTypeArgs = outerType.GetTypeArgsAll(); + } + else + { + pOuterTypeArgs = BSYMMGR.EmptyTypeArray(); + } + + Debug.Assert(pTypeArgsThis != null); + m_pTypeArgsThis = pTypeArgsThis; + SetTypeArgsAll(pOuterTypeArgs); + } + + public void SetTypeArgsAll(TypeArray outerTypeArgs) + { + Debug.Assert(m_pTypeArgsThis != null); + + // Here we need to check our current type args. If we have an open placeholder, + // then we need to have all open placeholders, and we want to flush + // our outer type args so that they're open placeholders. + // + // This is because of the following scenario: + // + // class B + // { + // class C + // { + // } + // class D + // { + // void Foo() + // { + // Type T = typeof(C<>); + // } + // } + // } + // + // The outer type will be B, but the inner type will be C<>. However, + // this will eventually be represented in IL as B<>.C<>. As such, we should + // keep our data structures clear - if we have one open type argument, then + // all of them must be open type arguments. + // + // Ensure that invariant here. + + TypeArray pCheckedOuterTypeArgs = outerTypeArgs; + TypeManager pTypeManager = getAggregate().GetTypeManager(); + + if (m_pTypeArgsThis.Size > 0 && AreAllTypeArgumentsUnitTypes(m_pTypeArgsThis)) + { + if (outerTypeArgs.Size > 0 && !AreAllTypeArgumentsUnitTypes(outerTypeArgs)) + { + // We have open placeholder types in our type, but not the parent. + pCheckedOuterTypeArgs = pTypeManager.CreateArrayOfUnitTypes(outerTypeArgs.Size); + } + } + m_pTypeArgsAll = pTypeManager.ConcatenateTypeArrays(pCheckedOuterTypeArgs, m_pTypeArgsThis); + } + + public bool AreAllTypeArgumentsUnitTypes(TypeArray typeArray) + { + if (typeArray.Size == 0) + { + return true; + } + + for (int i = 0; i < typeArray.size; i++) + { + Debug.Assert(typeArray.Item(i) != null); + if (!typeArray.Item(i).IsOpenTypePlaceholderType()) + { + return false; + } + } + return true; + } + + public TypeArray GetTypeArgsThis() + { + return m_pTypeArgsThis; + } + + public TypeArray GetTypeArgsAll() + { + return m_pTypeArgsAll; + } + + public TypeArray GetIfacesAll() + { + if (ifacesAll == null) + { + ifacesAll = getAggregate().GetTypeManager().SubstTypeArray(getAggregate().GetIfacesAll(), GetTypeArgsAll()); + } + return ifacesAll; + } + + public TypeArray GetWinRTCollectionIfacesAll(SymbolLoader pSymbolLoader) + { + if (winrtifacesAll == null) + { + TypeArray ifaces = GetIfacesAll(); + System.Collections.Generic.List typeList = new System.Collections.Generic.List(); + + for (int i = 0; i < ifaces.size; i++) + { + AggregateType type = ifaces.Item(i).AsAggregateType(); + Debug.Assert(type.isInterfaceType()); + + if (type.IsCollectionType()) + { + typeList.Add(type); + } + } + winrtifacesAll = pSymbolLoader.getBSymmgr().AllocParams(typeList.Count, typeList.ToArray()); + } + return winrtifacesAll; + } + + // UNDONE: Can we redo the implementation of this so that it does not + // UNDONE: use the global symbol loader? Iterate over the type children + // UNDONE: to look for the Invoke method. + + public TypeArray GetDelegateParameters(SymbolLoader pSymbolLoader) + { + Debug.Assert(isDelegateType()); + MethodSymbol invoke = pSymbolLoader.LookupInvokeMeth(this.getAggregate()); + if (invoke == null || !invoke.isInvoke()) + { + // This can happen if the delegate is internal to another assembly. + return null; + } + return this.getAggregate().GetTypeManager().SubstTypeArray(invoke.Params, this); + } + + public CType GetDelegateReturnType(SymbolLoader pSymbolLoader) + { + Debug.Assert(isDelegateType()); + MethodSymbol invoke = pSymbolLoader.LookupInvokeMeth(this.getAggregate()); + if (invoke == null || !invoke.isInvoke()) + { + // This can happen if the delegate is internal to another assembly. + return null; + } + return this.getAggregate().GetTypeManager().SubstType(invoke.RetType, this); + } + } +} + diff --git a/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/Types/ArgumentListType.cs b/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/Types/ArgumentListType.cs new file mode 100644 index 000000000..7367f1460 --- /dev/null +++ b/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/Types/ArgumentListType.cs @@ -0,0 +1,16 @@ +// ==++== +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// ==--== + +namespace Microsoft.CSharp.RuntimeBinder.Semantics +{ + // ---------------------------------------------------------------------------- + // ArgumentListType - a placeholder typesym used only as the type of a C-style varargs. + // There is exactly one of these. + // ---------------------------------------------------------------------------- + + class ArgumentListType : CType + { }; +} diff --git a/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/Types/ArrayType.cs b/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/Types/ArrayType.cs new file mode 100644 index 000000000..72211db42 --- /dev/null +++ b/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/Types/ArrayType.cs @@ -0,0 +1,32 @@ +// ==++== +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// ==--== + +namespace Microsoft.CSharp.RuntimeBinder.Semantics +{ + // ---------------------------------------------------------------------------- + // ArrayType - a symbol representing an array. + // ---------------------------------------------------------------------------- + + class ArrayType : CType + { + // rank of the array. zero means unknown rank int [?]. + public int rank; + + public CType GetElementType() { return m_pElementType; } + public void SetElementType(CType pType) { m_pElementType = pType; } + + // Returns the first non-array type in the parent chain. + public CType GetBaseElementType() + { + CType type; + for (type = GetElementType(); type.IsArrayType(); type = type.AsArrayType().GetElementType()) ; + return type; + } + + private CType m_pElementType; + } +} + diff --git a/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/Types/BoundLambdaType.cs b/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/Types/BoundLambdaType.cs new file mode 100644 index 000000000..9fa29e21f --- /dev/null +++ b/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/Types/BoundLambdaType.cs @@ -0,0 +1,16 @@ +// ==++== +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// ==--== + +namespace Microsoft.CSharp.RuntimeBinder.Semantics +{ + // ---------------------------------------------------------------------------- + // BoundLambdaType - a placeholder typesym used only as the type of an anonymous + // method expression. There is exactly one of these. + // ---------------------------------------------------------------------------- + + class BoundLambdaType : CType + { }; +} diff --git a/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/Types/ErrorType.cs b/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/Types/ErrorType.cs new file mode 100644 index 000000000..e45e56e87 --- /dev/null +++ b/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/Types/ErrorType.cs @@ -0,0 +1,36 @@ +// ==++== +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// ==--== + +using Microsoft.CSharp.RuntimeBinder.Syntax; + +namespace Microsoft.CSharp.RuntimeBinder.Semantics +{ + // ---------------------------------------------------------------------------- + // + // ErrorType + // + // ErrorType - a symbol representing an error that has been reported. + // ---------------------------------------------------------------------------- + + class ErrorType : CType + { + public Name nameText; + public TypeArray typeArgs; + + public bool HasParent() { return m_pParentType != null || m_pParentNS != null; } + + public bool HasTypeParent() { return m_pParentType != null; } + public CType GetTypeParent() { return m_pParentType; } + public void SetTypeParent(CType pType) { m_pParentType = pType; } + + public bool HasNSParent() { return m_pParentNS != null; } + public AssemblyQualifiedNamespaceSymbol GetNSParent() { return m_pParentNS; } + public void SetNSParent(AssemblyQualifiedNamespaceSymbol pNS) { m_pParentNS = pNS; } + + private CType m_pParentType; + private AssemblyQualifiedNamespaceSymbol m_pParentNS; + } +} diff --git a/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/Types/MethodGroupType.cs b/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/Types/MethodGroupType.cs new file mode 100644 index 000000000..bc6c288c5 --- /dev/null +++ b/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/Types/MethodGroupType.cs @@ -0,0 +1,16 @@ +// ==++== +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// ==--== + +namespace Microsoft.CSharp.RuntimeBinder.Semantics +{ + // ---------------------------------------------------------------------------- + // MethodGroupType - a placeholder typesym used only as the type of an method + // groupe expression. There is exactly one of these. + // ---------------------------------------------------------------------------- + + class MethodGroupType : CType + { }; +} diff --git a/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/Types/NullType.cs b/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/Types/NullType.cs new file mode 100644 index 000000000..5fb2a20e0 --- /dev/null +++ b/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/Types/NullType.cs @@ -0,0 +1,16 @@ +// ==++== +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// ==--== + +namespace Microsoft.CSharp.RuntimeBinder.Semantics +{ + // ---------------------------------------------------------------------------- + // NullType - represents the null type -- the type of the "null constant". + // ---------------------------------------------------------------------------- + + class NullType : CType + { + } +} diff --git a/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/Types/NullableType.cs b/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/Types/NullableType.cs new file mode 100644 index 000000000..fa6afdfdb --- /dev/null +++ b/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/Types/NullableType.cs @@ -0,0 +1,54 @@ +// ==++== +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// ==--== + +using Microsoft.CSharp.RuntimeBinder.Errors; +using Microsoft.CSharp.RuntimeBinder.Syntax; + +namespace Microsoft.CSharp.RuntimeBinder.Semantics +{ + // ---------------------------------------------------------------------------- + // + // NullableType + // + // A "derived" type representing Nullable. The base type T is the parent. + // + // ---------------------------------------------------------------------------- + + class NullableType : CType + { + public AggregateType ats; + public BSYMMGR symmgr; + public TypeManager typeManager; + + public AggregateType GetAts(ErrorHandling errorContext) + { + AggregateSymbol aggNullable = typeManager.GetNullable(); + if (aggNullable == null) + { + throw Error.InternalCompilerError(); + } + + if (ats == null) + { + if (aggNullable == null) + { + typeManager.ReportMissingPredefTypeError(errorContext, PredefinedType.PT_G_OPTIONAL); + return null; + } + + CType typePar = GetUnderlyingType(); + CType[] typeParArray = new CType[] { typePar }; + TypeArray ta = symmgr.AllocParams(1, typeParArray); + ats = typeManager.GetAggregate(aggNullable, ta); + } + return ats; + } + public CType GetUnderlyingType() { return UnderlyingType; } + public void SetUnderlyingType(CType pType) { UnderlyingType = pType; } + + public CType UnderlyingType; + } +} diff --git a/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/Types/OpenTypePlaceholderType.cs b/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/Types/OpenTypePlaceholderType.cs new file mode 100644 index 000000000..e1976799d --- /dev/null +++ b/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/Types/OpenTypePlaceholderType.cs @@ -0,0 +1,17 @@ +// ==++== +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// ==--== + +namespace Microsoft.CSharp.RuntimeBinder.Semantics +{ + + // ---------------------------------------------------------------------------- + // OpenTypePlaceholderType - a placeholder typesym used only in type argument lists for open + // types. There is exactly one of these. + // ---------------------------------------------------------------------------- + + class OpenTypePlaceholderType : CType + { } +} diff --git a/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/Types/ParameterModifierType.cs b/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/Types/ParameterModifierType.cs new file mode 100644 index 000000000..1f609819d --- /dev/null +++ b/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/Types/ParameterModifierType.cs @@ -0,0 +1,27 @@ +// ==++== +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// ==--== + +namespace Microsoft.CSharp.RuntimeBinder.Semantics +{ + // ---------------------------------------------------------------------------- + // + // ParameterModifierType + // + // ParameterModifierType - a symbol representing parameter modifier -- either + // out or ref. + // + // ---------------------------------------------------------------------------- + + class ParameterModifierType : CType + { + public bool isOut; // True for out parameter, false for ref parameter. + + public CType GetParameterType() { return m_pParameterType; } + public void SetParameterType(CType pType) { m_pParameterType = pType; } + + private CType m_pParameterType; + } +} diff --git a/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/Types/PointerType.cs b/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/Types/PointerType.cs new file mode 100644 index 000000000..61e429713 --- /dev/null +++ b/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/Types/PointerType.cs @@ -0,0 +1,15 @@ +// ==++== +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// ==--== + +namespace Microsoft.CSharp.RuntimeBinder.Semantics +{ + class PointerType : CType + { + public CType GetReferentType() { return m_pReferentType; } + public void SetReferentType(CType pType) { m_pReferentType = pType; } + private CType m_pReferentType; + } +} diff --git a/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/Types/PredefinedTypes.cs b/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/Types/PredefinedTypes.cs new file mode 100644 index 000000000..7968cbc8b --- /dev/null +++ b/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/Types/PredefinedTypes.cs @@ -0,0 +1,607 @@ +// ==++== +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// ==--== + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using Microsoft.CSharp.RuntimeBinder.Errors; +using Microsoft.CSharp.RuntimeBinder.Syntax; + +namespace Microsoft.CSharp.RuntimeBinder.Semantics +{ + // TODO: make sure this is the correct declarations + internal enum CorElementType + { + ELEMENT_TYPE_U1, + ELEMENT_TYPE_I2, + ELEMENT_TYPE_I4, + ELEMENT_TYPE_I8, + ELEMENT_TYPE_R4, + ELEMENT_TYPE_R8, + ELEMENT_TYPE_CHAR, + ELEMENT_TYPE_BOOLEAN, + ELEMENT_TYPE_I1, + ELEMENT_TYPE_U2, + ELEMENT_TYPE_U4, + ELEMENT_TYPE_U8, + ELEMENT_TYPE_I, + ELEMENT_TYPE_U, + ELEMENT_TYPE_OBJECT, + ELEMENT_TYPE_STRING, + ELEMENT_TYPE_TYPEDBYREF, + ELEMENT_TYPE_CLASS, + ELEMENT_TYPE_VALUETYPE, + ELEMENT_TYPE_END + } + + + class PredefinedTypes + { + SymbolTable runtimeBinderSymbolTable; + BSYMMGR pBSymmgr; + AggregateSymbol[] predefSyms; // array of predefined symbol types. + KAID aidMsCorLib; // The assembly ID for all predefined types. + + public PredefinedTypes(BSYMMGR pBSymmgr) + { + this.pBSymmgr = pBSymmgr; + this.aidMsCorLib = KAID.kaidNil; + this.runtimeBinderSymbolTable = null; + } + + // We want to delay load the predef syms as needed. + private AggregateSymbol DelayLoadPredefSym(PredefinedType pt) + { + CType type = runtimeBinderSymbolTable.GetCTypeFromType(PredefinedTypeFacts.GetAssociatedSystemType(pt)); + AggregateSymbol sym = type.getAggregate(); + + // If we failed to load this thing, we have problems. + if (sym == null) + { + return null; + } + return PredefinedTypes.InitializePredefinedType(sym, pt); + } + + internal static AggregateSymbol InitializePredefinedType(AggregateSymbol sym, PredefinedType pt) + { + sym.SetPredefined(true); + sym.SetPredefType(pt); + sym.SetSkipUDOps(pt <= PredefinedType.PT_ENUM && pt != PredefinedType.PT_INTPTR && pt != PredefinedType.PT_UINTPTR && pt != PredefinedType.PT_TYPE); + + return sym; + } + + public bool Init(ErrorHandling errorContext, SymbolTable symtable) + { + runtimeBinderSymbolTable = symtable; + Debug.Assert(pBSymmgr != null); + +#if !CSEE + Debug.Assert(predefSyms == null); +#else // CSEE + Debug.Assert(predefSyms == null || aidMsCorLib != KAID.kaidNil); +#endif // CSEE + + if (aidMsCorLib == KAID.kaidNil) + { + // If we haven't found mscorlib yet, first look for System.Object. Then use its assembly as + // the location for all other pre-defined types. + AggregateSymbol aggObj = FindPredefinedType(errorContext, PredefinedTypeFacts.GetName(PredefinedType.PT_OBJECT), KAID.kaidGlobal, AggKindEnum.Class, 0, true); + if (aggObj == null) + return false; + aidMsCorLib = aggObj.GetAssemblyID(); + } + + predefSyms = new AggregateSymbol[(int)PredefinedType.PT_COUNT]; + Debug.Assert(predefSyms != null); + + return true; + } + + //////////////////////////////////////////////////////////////////////////////// + // finds an existing declaration for a predefined type. + // returns null on failure. If isRequired is true, an error message is also + // given. + + private static readonly char[] nameSeparators = new char[] { '.' }; + + private AggregateSymbol FindPredefinedType(ErrorHandling errorContext, string pszType, KAID aid, AggKindEnum aggKind, int arity, bool isRequired) + { + Debug.Assert(!string.IsNullOrEmpty(pszType)); // Shouldn't be the empty string! + + NamespaceOrAggregateSymbol bagCur = pBSymmgr.GetRootNS(); + Name name = null; + + string[] nameParts = pszType.Split(nameSeparators); + for (int i = 0, n = nameParts.Length; i < n; i++) + { + name = pBSymmgr.GetNameManager().Add(nameParts[i]); + + if (i == n - 1) + { + // This is the last component. Handle it special below. + break; + } + + // first search for an outer type which is also predefined + // this must be first because we always create a namespace for + // outer names, even for nested types + AggregateSymbol aggNext = pBSymmgr.LookupGlobalSymCore(name, bagCur, symbmask_t.MASK_AggregateSymbol).AsAggregateSymbol(); + if (aggNext != null && aggNext.InAlias(aid) && aggNext.IsPredefined()) + { + bagCur = aggNext; + } + else + { + // ... if no outer type, then search for namespaces + NamespaceSymbol nsNext = pBSymmgr.LookupGlobalSymCore(name, bagCur, symbmask_t.MASK_NamespaceSymbol).AsNamespaceSymbol(); + bool bIsInAlias = true; + if (nsNext == null) + { + bIsInAlias = false; + } + else + { + bIsInAlias = nsNext.InAlias(aid); + } + if (!bIsInAlias) + { + // Didn't find the namespace in this aid. + if (isRequired) + { + errorContext.Error(ErrorCode.ERR_PredefinedTypeNotFound, pszType); + } + return null; + } + bagCur = nsNext; + } + } + + AggregateSymbol aggAmbig; + AggregateSymbol aggBad; + AggregateSymbol aggFound = FindPredefinedTypeCore(name, bagCur, aid, aggKind, arity, out aggAmbig, out aggBad); + + if (aggFound == null) + { + // Didn't find the AggregateSymbol. + if (aggBad != null && (isRequired || aid == KAID.kaidGlobal && aggBad.IsSource())) + errorContext.ErrorRef(ErrorCode.ERR_PredefinedTypeBadType, aggBad); + else if (isRequired) + errorContext.Error(ErrorCode.ERR_PredefinedTypeNotFound, pszType); + return null; + } + + if (aggAmbig == null && aid != KAID.kaidGlobal) + { + // Look in kaidGlobal to make sure there isn't a conflicting one. + AggregateSymbol tmp; + AggregateSymbol agg2 = FindPredefinedTypeCore(name, bagCur, KAID.kaidGlobal, aggKind, arity, out aggAmbig, out tmp); + Debug.Assert(agg2 != null); + if (agg2 != aggFound) + aggAmbig = agg2; + } + + return aggFound; + } + + AggregateSymbol FindPredefinedTypeCore(Name name, NamespaceOrAggregateSymbol bag, KAID aid, AggKindEnum aggKind, int arity, + out AggregateSymbol paggAmbig, out AggregateSymbol paggBad) + { + AggregateSymbol aggFound = null; + paggAmbig = null; + paggBad = null; + + for (AggregateSymbol aggCur = pBSymmgr.LookupGlobalSymCore(name, bag, symbmask_t.MASK_AggregateSymbol).AsAggregateSymbol(); + aggCur != null; + aggCur = BSYMMGR.LookupNextSym(aggCur, bag, symbmask_t.MASK_AggregateSymbol).AsAggregateSymbol()) + { + if (!aggCur.InAlias(aid) || aggCur.GetTypeVarsAll().size != arity) + { + continue; + } + if (aggCur.AggKind() != aggKind) + { + if (paggBad == null) + { + paggBad = aggCur; + } + continue; + } + if (aggFound != null) + { + Debug.Assert(paggAmbig == null); + paggAmbig = aggCur; + break; + } + aggFound = aggCur; + if (paggAmbig == null) + { + break; + } + } + + return aggFound; + } + + public void ReportMissingPredefTypeError(ErrorHandling errorContext, PredefinedType pt) + { + Debug.Assert(pBSymmgr != null); + Debug.Assert(predefSyms != null); + Debug.Assert((PredefinedType)0 <= pt && pt < PredefinedType.PT_COUNT && predefSyms[(int)pt] == null); + + // We do not assert that !predefTypeInfo[pt].isRequired because if the user is defining + // their own MSCorLib and is defining a required PredefType, they'll run into this error + // and we need to allow it to go through. + + errorContext.Error(ErrorCode.ERR_PredefinedTypeNotFound, PredefinedTypeFacts.GetName(pt)); + } + + public AggregateSymbol GetReqPredefAgg(PredefinedType pt) + { + if (!PredefinedTypeFacts.IsRequired(pt)) throw Error.InternalCompilerError(); + if (predefSyms[(int)pt] == null) + { + // Delay load this thing. + predefSyms[(int)pt] = DelayLoadPredefSym(pt); + } + return predefSyms[(int)pt]; + } + + public AggregateSymbol GetOptPredefAgg(PredefinedType pt) + { + if (predefSyms[(int)pt] == null) + { + // Delay load this thing. + predefSyms[(int)pt] = DelayLoadPredefSym(pt); + } + + Debug.Assert(predefSyms != null); + return predefSyms[(int)pt]; + } + + //////////////////////////////////////////////////////////////////////////////// + // Some of the predefined types have built-in names, like "int" or "string" or + // "object". This return the nice name if one exists; otherwise null is + // returned. + + public static string GetNiceName(PredefinedType pt) + { + return PredefinedTypeFacts.GetNiceName(pt); + } + + public static string GetNiceName(AggregateSymbol type) + { + if (type.IsPredefined()) + return GetNiceName(type.GetPredefType()); + else + return null; + } + + public static string GetFullName(PredefinedType pt) + { + return PredefinedTypeFacts.GetName(pt); + } + + public static bool isRequired(PredefinedType pt) + { + return PredefinedTypeFacts.IsRequired(pt); + } + } + + internal static class PredefinedTypeFacts + { + internal static string GetName(PredefinedType type) + { + return pdTypes[(int)type].name; + } + + internal static bool IsRequired(PredefinedType type) + { + return pdTypes[(int)type].required; + } + + internal static FUNDTYPE GetFundType(PredefinedType type) + { + return pdTypes[(int)type].fundType; + } + + internal static Type GetAssociatedSystemType(PredefinedType type) + { + return pdTypes[(int)type].AssociatedSystemType; + } + + internal static bool IsSimpleType(PredefinedType type) + { + switch (type) + { + case PredefinedType.PT_BYTE: + case PredefinedType.PT_SHORT: + case PredefinedType.PT_INT: + case PredefinedType.PT_LONG: + case PredefinedType.PT_FLOAT: + case PredefinedType.PT_DOUBLE: + case PredefinedType.PT_DECIMAL: + case PredefinedType.PT_CHAR: + case PredefinedType.PT_BOOL: + case PredefinedType.PT_SBYTE: + case PredefinedType.PT_USHORT: + case PredefinedType.PT_UINT: + case PredefinedType.PT_ULONG: + return true; + default: + return false; + } + } + + internal static bool IsNumericType(PredefinedType type) + { + switch (type) + { + case PredefinedType.PT_BYTE: + case PredefinedType.PT_SHORT: + case PredefinedType.PT_INT: + case PredefinedType.PT_LONG: + case PredefinedType.PT_FLOAT: + case PredefinedType.PT_DOUBLE: + case PredefinedType.PT_DECIMAL: + case PredefinedType.PT_SBYTE: + case PredefinedType.PT_USHORT: + case PredefinedType.PT_UINT: + case PredefinedType.PT_ULONG: + return true; + default: + return false; + } + } + + internal static string GetNiceName(PredefinedType type) + { + switch (type) + { + case PredefinedType.PT_BYTE: + return "byte"; + case PredefinedType.PT_SHORT: + return "short"; + case PredefinedType.PT_INT: + return "int"; + case PredefinedType.PT_LONG: + return "long"; + case PredefinedType.PT_FLOAT: + return "float"; + case PredefinedType.PT_DOUBLE: + return "double"; + case PredefinedType.PT_DECIMAL: + return "decimal"; + case PredefinedType.PT_CHAR: + return "char"; + case PredefinedType.PT_BOOL: + return "bool"; + case PredefinedType.PT_SBYTE: + return "sbyte"; + case PredefinedType.PT_USHORT: + return "ushort"; + case PredefinedType.PT_UINT: + return "uint"; + case PredefinedType.PT_ULONG: + return "ulong"; + case PredefinedType.PT_OBJECT: + return "object"; + case PredefinedType.PT_STRING: + return "string"; + default: + return null; + } + } + + internal static bool IsPredefinedType(string name) + { + return pdTypeNames.ContainsKey(name); + } + + internal static PredefinedType GetPredefTypeIndex(string name) + { + return pdTypeNames[name]; + } + + class PredefinedTypeInfo + { + internal PredefinedType type; + internal string name; + internal bool required; + internal FUNDTYPE fundType; + internal Type AssociatedSystemType; + + internal PredefinedTypeInfo(PredefinedType type, Type associatedSystemType, string name, bool required, int arity, AggKindEnum aggKind, FUNDTYPE fundType, bool inMscorlib) + { + this.type = type; + this.name = name; + this.required = required; + this.fundType = fundType; + this.AssociatedSystemType = associatedSystemType; + } + + internal PredefinedTypeInfo(PredefinedType type, Type associatedSystemType, string name, bool required, int arity, bool inMscorlib) + : this(type, associatedSystemType, name, required, arity, AggKindEnum.Class, FUNDTYPE.FT_REF, inMscorlib) + { + } + } + + static PredefinedTypeFacts() + { +#if DEBUG + for (int i = 0; i < (int)PredefinedType.PT_COUNT; i++) + { + System.Diagnostics.Debug.Assert(pdTypes[i].type == (PredefinedType)i); + } +#endif + for (int i = 0; i < (int)PredefinedType.PT_COUNT; i++) + { + pdTypeNames.Add(pdTypes[i].AssociatedSystemType.FullName, (PredefinedType)i); + } + } + + static readonly Dictionary pdTypeNames = new Dictionary(); + + static readonly PredefinedTypeInfo[] pdTypes = new PredefinedTypeInfo[] { + new PredefinedTypeInfo(PredefinedType.PT_BYTE, typeof(System.Byte), "System.Byte", true, 0, AggKindEnum.Struct, FUNDTYPE.FT_U1, true), + new PredefinedTypeInfo(PredefinedType.PT_SHORT, typeof(System.Int16), "System.Int16", true, 0, AggKindEnum.Struct, FUNDTYPE.FT_I2, true), + new PredefinedTypeInfo(PredefinedType.PT_INT, typeof(System.Int32), "System.Int32", true, 0, AggKindEnum.Struct, FUNDTYPE.FT_I4, true), + new PredefinedTypeInfo(PredefinedType.PT_LONG, typeof(System.Int64), "System.Int64", true, 0, AggKindEnum.Struct, FUNDTYPE.FT_I8, true), + new PredefinedTypeInfo(PredefinedType.PT_FLOAT, typeof(System.Single), "System.Single", true, 0, AggKindEnum.Struct, FUNDTYPE.FT_R4, true), + new PredefinedTypeInfo(PredefinedType.PT_DOUBLE, typeof(System.Double), "System.Double", true, 0, AggKindEnum.Struct, FUNDTYPE.FT_R8, true), + new PredefinedTypeInfo(PredefinedType.PT_DECIMAL, typeof(System.Decimal), "System.Decimal", false, 0, AggKindEnum.Struct, FUNDTYPE.FT_STRUCT, true), + new PredefinedTypeInfo(PredefinedType.PT_CHAR, typeof(System.Char), "System.Char", true, 0, AggKindEnum.Struct, FUNDTYPE.FT_U2, true), + new PredefinedTypeInfo(PredefinedType.PT_BOOL, typeof(System.Boolean), "System.Boolean", true, 0, AggKindEnum.Struct, FUNDTYPE.FT_I1, true), + new PredefinedTypeInfo(PredefinedType.PT_SBYTE, typeof(System.SByte), "System.SByte", true, 0, AggKindEnum.Struct, FUNDTYPE.FT_I1, true), + new PredefinedTypeInfo(PredefinedType.PT_USHORT, typeof(System.UInt16), "System.UInt16", true, 0, AggKindEnum.Struct, FUNDTYPE.FT_U2, true), + new PredefinedTypeInfo(PredefinedType.PT_UINT, typeof(System.UInt32), "System.UInt32", true, 0, AggKindEnum.Struct, FUNDTYPE.FT_U4, true), + new PredefinedTypeInfo(PredefinedType.PT_ULONG, typeof(System.UInt64), "System.UInt64", true, 0, AggKindEnum.Struct, FUNDTYPE.FT_U8, true), + new PredefinedTypeInfo(PredefinedType.PT_INTPTR, typeof(System.IntPtr), "System.IntPtr", true, 0, AggKindEnum.Struct, FUNDTYPE.FT_STRUCT, true), + new PredefinedTypeInfo(PredefinedType.PT_UINTPTR, typeof(System.UIntPtr), "System.UIntPtr", true, 0, AggKindEnum.Struct, FUNDTYPE.FT_STRUCT, true), + new PredefinedTypeInfo(PredefinedType.PT_OBJECT, typeof(System.Object), "System.Object", true, 0, true), + new PredefinedTypeInfo(PredefinedType.PT_STRING, typeof(System.String), "System.String", true, 0, true), + new PredefinedTypeInfo(PredefinedType.PT_DELEGATE, typeof(System.Delegate), "System.Delegate", true, 0, true), + new PredefinedTypeInfo(PredefinedType.PT_MULTIDEL, typeof(System.MulticastDelegate), "System.MulticastDelegate", true, 0, true), + new PredefinedTypeInfo(PredefinedType.PT_ARRAY, typeof(System.Array), "System.Array", true, 0, true), + new PredefinedTypeInfo(PredefinedType.PT_EXCEPTION, typeof(System.Exception), "System.Exception", true, 0, true), + new PredefinedTypeInfo(PredefinedType.PT_TYPE, typeof(System.Type), "System.Type", true, 0, true), + new PredefinedTypeInfo(PredefinedType.PT_MONITOR, typeof(System.Threading.Monitor), "System.Threading.Monitor", true, 0, true), + new PredefinedTypeInfo(PredefinedType.PT_VALUE, typeof(System.ValueType), "System.ValueType", true, 0, true), + new PredefinedTypeInfo(PredefinedType.PT_ENUM, typeof(System.Enum), "System.Enum", true, 0, true), + new PredefinedTypeInfo(PredefinedType.PT_DATETIME, typeof(System.DateTime), "System.DateTime", true, 0, AggKindEnum.Struct, FUNDTYPE.FT_STRUCT, true), +#pragma warning disable 0618 // To avoid deprecation warning + new PredefinedTypeInfo(PredefinedType.PT_SECURITYATTRIBUTE, typeof(System.Security.Permissions.CodeAccessSecurityAttribute), "System.Security.Permissions.CodeAccessSecurityAttribute", false, 0, true), + new PredefinedTypeInfo(PredefinedType.PT_SECURITYPERMATTRIBUTE, typeof(System.Security.Permissions.SecurityPermissionAttribute), "System.Security.Permissions.SecurityPermissionAttribute", false, 0, true), +#pragma warning restore 0618 + new PredefinedTypeInfo(PredefinedType.PT_UNVERIFCODEATTRIBUTE, typeof(System.Security.UnverifiableCodeAttribute), "System.Security.UnverifiableCodeAttribute", false, 0, true), + new PredefinedTypeInfo(PredefinedType.PT_DEBUGGABLEATTRIBUTE, typeof(System.Diagnostics.DebuggableAttribute), "System.Diagnostics.DebuggableAttribute", false, 0, true), + new PredefinedTypeInfo(PredefinedType.PT_DEBUGGABLEATTRIBUTE_DEBUGGINGMODES, typeof(System.Diagnostics.DebuggableAttribute.DebuggingModes), "System.Diagnostics.DebuggableAttribute.DebuggingModes", false, 0, true), +#if !SILVERLIGHT + new PredefinedTypeInfo(PredefinedType.PT_MARSHALBYREF, typeof(System.MarshalByRefObject), "System.MarshalByRefObject", false, 0, true), + new PredefinedTypeInfo(PredefinedType.PT_CONTEXTBOUND, typeof(System.ContextBoundObject), "System.ContextBoundObject", false, 0, true), +#endif + new PredefinedTypeInfo(PredefinedType.PT_IN, typeof(System.Runtime.InteropServices.InAttribute), "System.Runtime.InteropServices.InAttribute", false, 0, true), + new PredefinedTypeInfo(PredefinedType.PT_OUT, typeof(System.Runtime.InteropServices.OutAttribute), "System.Runtime.InteropServices.OutAttribute", false, 0, true), + new PredefinedTypeInfo(PredefinedType.PT_ATTRIBUTE, typeof(System.Attribute), "System.Attribute", true, 0, true), + new PredefinedTypeInfo(PredefinedType.PT_ATTRIBUTEUSAGE, typeof(System.AttributeUsageAttribute), "System.AttributeUsageAttribute", false, 0, true), + new PredefinedTypeInfo(PredefinedType.PT_ATTRIBUTETARGETS, typeof(System.AttributeTargets), "System.AttributeTargets", false, 0, AggKindEnum.Enum, FUNDTYPE.FT_STRUCT, true), + new PredefinedTypeInfo(PredefinedType.PT_OBSOLETE, typeof(System.ObsoleteAttribute), "System.ObsoleteAttribute", false, 0, true), + new PredefinedTypeInfo(PredefinedType.PT_CONDITIONAL, typeof(System.Diagnostics.ConditionalAttribute), "System.Diagnostics.ConditionalAttribute", false, 0, true), + new PredefinedTypeInfo(PredefinedType.PT_CLSCOMPLIANT, typeof(System.CLSCompliantAttribute), "System.CLSCompliantAttribute", false, 0, true), + new PredefinedTypeInfo(PredefinedType.PT_GUID, typeof(System.Runtime.InteropServices.GuidAttribute), "System.Runtime.InteropServices.GuidAttribute", false, 0, true), + new PredefinedTypeInfo(PredefinedType.PT_DEFAULTMEMBER, typeof(System.Reflection.DefaultMemberAttribute), "System.Reflection.DefaultMemberAttribute", true, 0, true), + new PredefinedTypeInfo(PredefinedType.PT_PARAMS, typeof(System.ParamArrayAttribute), "System.ParamArrayAttribute", true, 0, true), + new PredefinedTypeInfo(PredefinedType.PT_COMIMPORT, typeof(System.Runtime.InteropServices.ComImportAttribute), "System.Runtime.InteropServices.ComImportAttribute", false, 0, true), + new PredefinedTypeInfo(PredefinedType.PT_FIELDOFFSET, typeof(System.Runtime.InteropServices.FieldOffsetAttribute), "System.Runtime.InteropServices.FieldOffsetAttribute", false, 0, true), + new PredefinedTypeInfo(PredefinedType.PT_STRUCTLAYOUT, typeof(System.Runtime.InteropServices.StructLayoutAttribute), "System.Runtime.InteropServices.StructLayoutAttribute", false, 0, true), + new PredefinedTypeInfo(PredefinedType.PT_LAYOUTKIND, typeof(System.Runtime.InteropServices.LayoutKind), "System.Runtime.InteropServices.LayoutKind", false, 0, AggKindEnum.Enum, FUNDTYPE.FT_STRUCT, true), + new PredefinedTypeInfo(PredefinedType.PT_MARSHALAS, typeof(System.Runtime.InteropServices.MarshalAsAttribute), "System.Runtime.InteropServices.MarshalAsAttribute", false, 0, true), + new PredefinedTypeInfo(PredefinedType.PT_DLLIMPORT, typeof(System.Runtime.InteropServices.DllImportAttribute), "System.Runtime.InteropServices.DllImportAttribute", false, 0, true), + new PredefinedTypeInfo(PredefinedType.PT_INDEXERNAME, typeof(System.Runtime.CompilerServices.IndexerNameAttribute), "System.Runtime.CompilerServices.IndexerNameAttribute", false, 0, true), + new PredefinedTypeInfo(PredefinedType.PT_DECIMALCONSTANT, typeof(System.Runtime.CompilerServices.DecimalConstantAttribute), "System.Runtime.CompilerServices.DecimalConstantAttribute", false, 0, true), +#if !SILVERLIGHT + new PredefinedTypeInfo(PredefinedType.PT_REQUIRED, typeof(System.Runtime.CompilerServices.RequiredAttributeAttribute), "System.Runtime.CompilerServices.RequiredAttributeAttribute", false, 0, true), + new PredefinedTypeInfo(PredefinedType.PT_DEFAULTVALUE, typeof(System.Runtime.InteropServices.DefaultParameterValueAttribute), "System.Runtime.InteropServices.DefaultParameterValueAttribute", false, 0, true), +#endif + new PredefinedTypeInfo(PredefinedType.PT_UNMANAGEDFUNCTIONPOINTER, typeof(System.Runtime.InteropServices.UnmanagedFunctionPointerAttribute), "System.Runtime.InteropServices.UnmanagedFunctionPointerAttribute", false, 0, true), + new PredefinedTypeInfo(PredefinedType.PT_CALLINGCONVENTION, typeof(System.Runtime.InteropServices.CallingConvention), "System.Runtime.InteropServices.CallingConvention", false, 0, AggKindEnum.Enum, FUNDTYPE.FT_I4, true), + new PredefinedTypeInfo(PredefinedType.PT_CHARSET, typeof(System.Runtime.InteropServices.CharSet), "System.Runtime.InteropServices.CharSet", false, 0, AggKindEnum.Enum, FUNDTYPE.FT_STRUCT, true), + new PredefinedTypeInfo(PredefinedType.PT_REFANY, typeof(System.TypedReference), "System.TypedReference", false, 0, AggKindEnum.Struct, FUNDTYPE.FT_STRUCT, true), +#if !SILVERLIGHT + new PredefinedTypeInfo(PredefinedType.PT_ARGITERATOR, typeof(System.ArgIterator), "System.ArgIterator", false, 0, AggKindEnum.Struct, FUNDTYPE.FT_STRUCT, true), +#endif + new PredefinedTypeInfo(PredefinedType.PT_TYPEHANDLE, typeof(System.RuntimeTypeHandle), "System.RuntimeTypeHandle", true, 0, AggKindEnum.Struct, FUNDTYPE.FT_STRUCT, true), + new PredefinedTypeInfo(PredefinedType.PT_FIELDHANDLE, typeof(System.RuntimeFieldHandle), "System.RuntimeFieldHandle", true, 0, AggKindEnum.Struct, FUNDTYPE.FT_STRUCT, true), + new PredefinedTypeInfo(PredefinedType.PT_METHODHANDLE, typeof(System.RuntimeMethodHandle), "System.RuntimeMethodHandle", false, 0, AggKindEnum.Struct, FUNDTYPE.FT_STRUCT, true), + new PredefinedTypeInfo(PredefinedType.PT_ARGUMENTHANDLE, typeof(System.RuntimeArgumentHandle), "System.RuntimeArgumentHandle", false, 0, AggKindEnum.Struct, FUNDTYPE.FT_STRUCT, true), +#if !SILVERLIGHT + new PredefinedTypeInfo(PredefinedType.PT_HASHTABLE, typeof(System.Collections.Hashtable), "System.Collections.Hashtable", false, 0, true), +#endif + new PredefinedTypeInfo(PredefinedType.PT_G_DICTIONARY, typeof(System.Collections.Generic.Dictionary<,>), "System.Collections.Generic.Dictionary", false, 2, true), + new PredefinedTypeInfo(PredefinedType.PT_IASYNCRESULT, typeof(System.IAsyncResult), "System.IAsyncResult", false, 0, AggKindEnum.Interface, FUNDTYPE.FT_REF, true), + new PredefinedTypeInfo(PredefinedType.PT_ASYNCCBDEL, typeof(System.AsyncCallback), "System.AsyncCallback", false, 0, AggKindEnum.Delegate, FUNDTYPE.FT_REF, true), +#pragma warning disable 0618 // To avoid deprecation warning + new PredefinedTypeInfo(PredefinedType.PT_SECURITYACTION, typeof(System.Security.Permissions.SecurityAction), "System.Security.Permissions.SecurityAction", false, 0, AggKindEnum.Enum, FUNDTYPE.FT_I4, true), +#pragma warning restore 0618 + new PredefinedTypeInfo(PredefinedType.PT_IDISPOSABLE, typeof(System.IDisposable), "System.IDisposable", true, 0, AggKindEnum.Interface, FUNDTYPE.FT_REF, true), + new PredefinedTypeInfo(PredefinedType.PT_IENUMERABLE, typeof(System.Collections.IEnumerable), "System.Collections.IEnumerable", true, 0, AggKindEnum.Interface, FUNDTYPE.FT_REF, true), + new PredefinedTypeInfo(PredefinedType.PT_IENUMERATOR, typeof(System.Collections.IEnumerator), "System.Collections.IEnumerator", true, 0, AggKindEnum.Interface, FUNDTYPE.FT_REF, true), + new PredefinedTypeInfo(PredefinedType.PT_SYSTEMVOID, typeof(void), "System.Void", true, 0, AggKindEnum.Struct, FUNDTYPE.FT_STRUCT, true), + new PredefinedTypeInfo(PredefinedType.PT_RUNTIMEHELPERS, typeof(System.Runtime.CompilerServices.RuntimeHelpers), "System.Runtime.CompilerServices.RuntimeHelpers", false, 0, true), + new PredefinedTypeInfo(PredefinedType.PT_VOLATILEMOD, typeof(System.Runtime.CompilerServices.IsVolatile), "System.Runtime.CompilerServices.IsVolatile", false, 0, true), + new PredefinedTypeInfo(PredefinedType.PT_COCLASS, typeof(System.Runtime.InteropServices.CoClassAttribute), "System.Runtime.InteropServices.CoClassAttribute", false, 0, true), + new PredefinedTypeInfo(PredefinedType.PT_ACTIVATOR, typeof(System.Activator), "System.Activator", false, 0, true), + new PredefinedTypeInfo(PredefinedType.PT_G_IENUMERABLE, typeof(System.Collections.Generic.IEnumerable<>), "System.Collections.Generic.IEnumerable", false, 1, AggKindEnum.Interface, FUNDTYPE.FT_REF, true), + new PredefinedTypeInfo(PredefinedType.PT_G_IENUMERATOR, typeof(System.Collections.Generic.IEnumerator<>), "System.Collections.Generic.IEnumerator", false, 1, AggKindEnum.Interface, FUNDTYPE.FT_REF, true), + new PredefinedTypeInfo(PredefinedType.PT_G_OPTIONAL, typeof(System.Nullable<>), "System.Nullable", false, 1, AggKindEnum.Struct, FUNDTYPE.FT_STRUCT, true), + new PredefinedTypeInfo(PredefinedType.PT_FIXEDBUFFER, typeof(System.Runtime.CompilerServices.FixedBufferAttribute), "System.Runtime.CompilerServices.FixedBufferAttribute", false, 0, true), + new PredefinedTypeInfo(PredefinedType.PT_DEFAULTCHARSET, typeof(System.Runtime.InteropServices.DefaultCharSetAttribute), "System.Runtime.InteropServices.DefaultCharSetAttribute", false, 0, true), + new PredefinedTypeInfo(PredefinedType.PT_COMPILATIONRELAXATIONS, typeof(System.Runtime.CompilerServices.CompilationRelaxationsAttribute), "System.Runtime.CompilerServices.CompilationRelaxationsAttribute", false, 0, true), + new PredefinedTypeInfo(PredefinedType.PT_RUNTIMECOMPATIBILITY, typeof(System.Runtime.CompilerServices.RuntimeCompatibilityAttribute), "System.Runtime.CompilerServices.RuntimeCompatibilityAttribute", false, 0, true), + new PredefinedTypeInfo(PredefinedType.PT_FRIENDASSEMBLY, typeof(System.Runtime.CompilerServices.InternalsVisibleToAttribute), "System.Runtime.CompilerServices.InternalsVisibleToAttribute", false, 0, true), + new PredefinedTypeInfo(PredefinedType.PT_DEBUGGERHIDDEN, typeof(System.Diagnostics.DebuggerHiddenAttribute), "System.Diagnostics.DebuggerHiddenAttribute", false, 0, true), + new PredefinedTypeInfo(PredefinedType.PT_TYPEFORWARDER, typeof(System.Runtime.CompilerServices.TypeForwardedToAttribute), "System.Runtime.CompilerServices.TypeForwardedToAttribute", false, 0, true), + new PredefinedTypeInfo(PredefinedType.PT_KEYFILE, typeof(System.Reflection.AssemblyKeyFileAttribute), "System.Reflection.AssemblyKeyFileAttribute", false, 0, true), + new PredefinedTypeInfo(PredefinedType.PT_KEYNAME, typeof(System.Reflection.AssemblyKeyNameAttribute), "System.Reflection.AssemblyKeyNameAttribute", false, 0, true), + new PredefinedTypeInfo(PredefinedType.PT_DELAYSIGN, typeof(System.Reflection.AssemblyDelaySignAttribute), "System.Reflection.AssemblyDelaySignAttribute", false, 0, true), + new PredefinedTypeInfo(PredefinedType.PT_NOTSUPPORTEDEXCEPTION, typeof(System.NotSupportedException), "System.NotSupportedException", false, 0, true), + new PredefinedTypeInfo(PredefinedType.PT_THREAD, typeof(System.Threading.Thread), "System.Threading.Thread", false, 0, true), + new PredefinedTypeInfo(PredefinedType.PT_COMPILERGENERATED, typeof(System.Runtime.CompilerServices.CompilerGeneratedAttribute), "System.Runtime.CompilerServices.CompilerGeneratedAttribute", false, 0, true), + new PredefinedTypeInfo(PredefinedType.PT_UNSAFEVALUETYPE, typeof(System.Runtime.CompilerServices.UnsafeValueTypeAttribute), "System.Runtime.CompilerServices.UnsafeValueTypeAttribute", false, 0, true), + new PredefinedTypeInfo(PredefinedType.PT_ASSEMBLYFLAGS, typeof(System.Reflection.AssemblyFlagsAttribute), "System.Reflection.AssemblyFlagsAttribute", false, 0, true), + new PredefinedTypeInfo(PredefinedType.PT_ASSEMBLYVERSION, typeof(System.Reflection.AssemblyVersionAttribute), "System.Reflection.AssemblyVersionAttribute", false, 0, true), + new PredefinedTypeInfo(PredefinedType.PT_ASSEMBLYCULTURE, typeof(System.Reflection.AssemblyCultureAttribute), "System.Reflection.AssemblyCultureAttribute", false, 0, true), + // LINQ + new PredefinedTypeInfo(PredefinedType.PT_G_IQUERYABLE, typeof(System.Linq.IQueryable<>), "System.Linq.IQueryable`1", false, 1, AggKindEnum.Interface, FUNDTYPE.FT_REF, false), + new PredefinedTypeInfo(PredefinedType.PT_IQUERYABLE, typeof(System.Linq.IQueryable), "System.Linq.IQueryable", false, 0, AggKindEnum.Interface, FUNDTYPE.FT_REF, false), + new PredefinedTypeInfo(PredefinedType.PT_STRINGBUILDER, typeof(System.Text.StringBuilder), "System.Text.StringBuilder", false, 0, true), + new PredefinedTypeInfo(PredefinedType.PT_G_ICOLLECTION, typeof(System.Collections.Generic.ICollection<>), "System.Collections.Generic.ICollection", false, 1, AggKindEnum.Interface, FUNDTYPE.FT_REF, true), + new PredefinedTypeInfo(PredefinedType.PT_G_ILIST, typeof(System.Collections.Generic.IList<>), "System.Collections.Generic.IList", false, 1, AggKindEnum.Interface, FUNDTYPE.FT_REF, true), + new PredefinedTypeInfo(PredefinedType.PT_EXTENSION, typeof(System.Runtime.CompilerServices.ExtensionAttribute), "System.Runtime.CompilerServices.ExtensionAttribute", false, 0, false), + new PredefinedTypeInfo(PredefinedType.PT_G_EXPRESSION, typeof(System.Linq.Expressions.Expression<>), "System.Linq.Expressions.Expression", false, 1, false), + new PredefinedTypeInfo(PredefinedType.PT_EXPRESSION, typeof(System.Linq.Expressions.Expression), "System.Linq.Expressions.Expression", false, 0, false), + new PredefinedTypeInfo(PredefinedType.PT_LAMBDAEXPRESSION, typeof(System.Linq.Expressions.LambdaExpression), "System.Linq.Expressions.LambdaExpression", false, 0, false), + new PredefinedTypeInfo(PredefinedType.PT_BINARYEXPRESSION, typeof(System.Linq.Expressions.BinaryExpression), "System.Linq.Expressions.BinaryExpression", false, 0, false), + new PredefinedTypeInfo(PredefinedType.PT_UNARYEXPRESSION, typeof(System.Linq.Expressions.UnaryExpression), "System.Linq.Expressions.UnaryExpression", false, 0, false), + new PredefinedTypeInfo(PredefinedType.PT_CONDITIONALEXPRESSION, typeof(System.Linq.Expressions.ConditionalExpression), "System.Linq.Expressions.ConditionalExpression", false, 0, false), + new PredefinedTypeInfo(PredefinedType.PT_CONSTANTEXPRESSION, typeof(System.Linq.Expressions.ConstantExpression), "System.Linq.Expressions.ConstantExpression", false, 0, false), + new PredefinedTypeInfo(PredefinedType.PT_PARAMETEREXPRESSION, typeof(System.Linq.Expressions.ParameterExpression), "System.Linq.Expressions.ParameterExpression", false, 0, false), + new PredefinedTypeInfo(PredefinedType.PT_MEMBEREXPRESSION, typeof(System.Linq.Expressions.MemberExpression), "System.Linq.Expressions.MemberExpression", false, 0, false), + new PredefinedTypeInfo(PredefinedType.PT_METHODCALLEXPRESSION, typeof(System.Linq.Expressions.MethodCallExpression), "System.Linq.Expressions.MethodCallExpression", false, 0, false), + new PredefinedTypeInfo(PredefinedType.PT_NEWEXPRESSION, typeof(System.Linq.Expressions.NewExpression), "System.Linq.Expressions.NewExpression", false, 0, false), + new PredefinedTypeInfo(PredefinedType.PT_BINDING, typeof(System.Linq.Expressions.MemberBinding), "System.Linq.Expressions.MemberBinding", false, 0, false), + new PredefinedTypeInfo(PredefinedType.PT_MEMBERINITEXPRESSION, typeof(System.Linq.Expressions.MemberInitExpression), "System.Linq.Expressions.MemberInitExpression", false, 0, false), + new PredefinedTypeInfo(PredefinedType.PT_LISTINITEXPRESSION, typeof(System.Linq.Expressions.ListInitExpression), "System.Linq.Expressions.ListInitExpression", false, 0, false), + new PredefinedTypeInfo(PredefinedType.PT_TYPEBINARYEXPRESSION, typeof(System.Linq.Expressions.TypeBinaryExpression), "System.Linq.Expressions.TypeBinaryExpression", false, 0, false), + new PredefinedTypeInfo(PredefinedType.PT_NEWARRAYEXPRESSION, typeof(System.Linq.Expressions.NewArrayExpression), "System.Linq.Expressions.NewArrayExpression", false, 0, false), + new PredefinedTypeInfo(PredefinedType.PT_MEMBERASSIGNMENT, typeof(System.Linq.Expressions.MemberAssignment), "System.Linq.Expressions.MemberAssignment", false, 0, false), + new PredefinedTypeInfo(PredefinedType.PT_MEMBERLISTBINDING, typeof(System.Linq.Expressions.MemberListBinding), "System.Linq.Expressions.MemberListBinding", false, 0, false), + new PredefinedTypeInfo(PredefinedType.PT_MEMBERMEMBERBINDING, typeof(System.Linq.Expressions.MemberMemberBinding), "System.Linq.Expressions.MemberMemberBinding", false, 0, false), + new PredefinedTypeInfo(PredefinedType.PT_INVOCATIONEXPRESSION, typeof(System.Linq.Expressions.InvocationExpression), "System.Linq.Expressions.InvocationExpression", false, 0, false), + new PredefinedTypeInfo(PredefinedType.PT_FIELDINFO, typeof(System.Reflection.FieldInfo), "System.Reflection.FieldInfo", false, 0, true), + new PredefinedTypeInfo(PredefinedType.PT_METHODINFO, typeof(System.Reflection.MethodInfo), "System.Reflection.MethodInfo", false, 0, true), + new PredefinedTypeInfo(PredefinedType.PT_CONSTRUCTORINFO, typeof(System.Reflection.ConstructorInfo), "System.Reflection.ConstructorInfo", false, 0, true), + new PredefinedTypeInfo(PredefinedType.PT_PROPERTYINFO, typeof(System.Reflection.PropertyInfo), "System.Reflection.PropertyInfo", false, 0, true), + new PredefinedTypeInfo(PredefinedType.PT_METHODBASE, typeof(System.Reflection.MethodBase), "System.Reflection.MethodBase", false, 0, true), + new PredefinedTypeInfo(PredefinedType.PT_MEMBERINFO, typeof(System.Reflection.MemberInfo), "System.Reflection.MemberInfo", false, 0, true), + new PredefinedTypeInfo(PredefinedType.PT_DEBUGGERDISPLAY, typeof(System.Diagnostics.DebuggerDisplayAttribute), "System.Diagnostics.DebuggerDisplayAttribute", false, 0, true), + new PredefinedTypeInfo(PredefinedType.PT_DEBUGGERBROWSABLE, typeof(System.Diagnostics.DebuggerBrowsableAttribute), "System.Diagnostics.DebuggerBrowsableAttribute", false, 0, true), + new PredefinedTypeInfo(PredefinedType.PT_DEBUGGERBROWSABLESTATE, typeof(System.Diagnostics.DebuggerBrowsableState), "System.Diagnostics.DebuggerBrowsableState", false, 0, AggKindEnum.Enum, FUNDTYPE.FT_I4, true), + new PredefinedTypeInfo(PredefinedType.PT_G_EQUALITYCOMPARER, typeof(System.Collections.Generic.EqualityComparer<>), "System.Collections.Generic.EqualityComparer", false, 1, true), + new PredefinedTypeInfo(PredefinedType.PT_ELEMENTINITIALIZER, typeof(System.Linq.Expressions.ElementInit), "System.Linq.Expressions.ElementInit", false, 0, false), + +#if !SILVERLIGHT + new PredefinedTypeInfo(PredefinedType.PT_UNKNOWNWRAPPER, typeof(System.Runtime.InteropServices.UnknownWrapper), "System.Runtime.InteropServices.UnknownWrapper", false, 0, true), + new PredefinedTypeInfo(PredefinedType.PT_DISPATCHWRAPPER, typeof(System.Runtime.InteropServices.DispatchWrapper), "System.Runtime.InteropServices.DispatchWrapper", false, 0, true), +#endif + new PredefinedTypeInfo(PredefinedType.PT_MISSING, typeof(System.Reflection.Missing), "System.Reflection.Missing", false, 0, true), + new PredefinedTypeInfo(PredefinedType.PT_G_IREADONLYLIST, typeof(System.Collections.Generic.IReadOnlyList<>), "System.Collections.Generic.IReadOnlyList", false, 1, AggKindEnum.Interface, FUNDTYPE.FT_REF, false), + new PredefinedTypeInfo(PredefinedType.PT_G_IREADONLYCOLLECTION, typeof(System.Collections.Generic.IReadOnlyCollection<>), "System.Collections.Generic.IReadOnlyCollection", false, 1, AggKindEnum.Interface, FUNDTYPE.FT_REF, false), + }; + } + +} \ No newline at end of file diff --git a/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/Types/Type.cs b/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/Types/Type.cs new file mode 100644 index 000000000..7b8dca794 --- /dev/null +++ b/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/Types/Type.cs @@ -0,0 +1,781 @@ +// ==++== +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// ==--== + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Reflection; +using Microsoft.CSharp.RuntimeBinder.Syntax; + +namespace Microsoft.CSharp.RuntimeBinder.Semantics +{ + class CType : ITypeOrNamespace + { + private TypeKind m_typeKind; + private Name m_pName; + + private bool fHasErrors; // Whether anyituents have errors. This is immutable. + private bool fUnres; // Whether anyituents are unresolved. This is immutable. + private bool isBogus; // can't be used in our language -- unsupported type(s) + private bool checkedBogus; // Have we checked a method args/return for bogus types + + // Is and As methods. + public AggregateType AsAggregateType() { return this as AggregateType; } + public ErrorType AsErrorType() { return this as ErrorType; } + public ArrayType AsArrayType() { return this as ArrayType; } + public PointerType AsPointerType() { return this as PointerType; } + public ParameterModifierType AsParameterModifierType() { return this as ParameterModifierType; } + public NullableType AsNullableType() { return this as NullableType; } + public TypeParameterType AsTypeParameterType() { return this as TypeParameterType; } + + public bool IsAggregateType() { return this is AggregateType; } + public bool IsVoidType() { return this is VoidType; } + public bool IsNullType() { return this is NullType; } + public bool IsOpenTypePlaceholderType() { return this is OpenTypePlaceholderType; } + public bool IsBoundLambdaType() { return this is BoundLambdaType; } + public bool IsMethodGroupType() { return this is MethodGroupType; } + public bool IsErrorType() { return this is ErrorType; } + public bool IsArrayType() { return this is ArrayType; } + public bool IsPointerType() { return this is PointerType; } + public bool IsParameterModifierType() { return this is ParameterModifierType; } + public bool IsNullableType() { return this is NullableType; } + public bool IsTypeParameterType() { return this is TypeParameterType; } + + public bool IsWindowsRuntimeType() + { + return this.AssociatedSystemType.Attributes.HasFlag(System.Reflection.TypeAttributes.WindowsRuntime); + } + + public bool IsCollectionType() + { + if ((this.AssociatedSystemType.IsGenericType && + (this.AssociatedSystemType.GetGenericTypeDefinition() == typeof(System.Collections.Generic.IList<>) || + this.AssociatedSystemType.GetGenericTypeDefinition() == typeof(System.Collections.Generic.ICollection<>) || + this.AssociatedSystemType.GetGenericTypeDefinition() == typeof(System.Collections.Generic.IEnumerable<>) || + this.AssociatedSystemType.GetGenericTypeDefinition() == typeof(System.Collections.Generic.IReadOnlyList<>) || + this.AssociatedSystemType.GetGenericTypeDefinition() == typeof(System.Collections.Generic.IReadOnlyCollection<>) || + this.AssociatedSystemType.GetGenericTypeDefinition() == typeof(System.Collections.Generic.IDictionary<,>) || + this.AssociatedSystemType.GetGenericTypeDefinition() == typeof(System.Collections.Generic.IReadOnlyDictionary<,>))) || + this.AssociatedSystemType == typeof(System.Collections.IList) || + this.AssociatedSystemType == typeof(System.Collections.ICollection) || + this.AssociatedSystemType == typeof(System.Collections.IEnumerable) || + this.AssociatedSystemType == typeof(System.Collections.Specialized.INotifyCollectionChanged) || + this.AssociatedSystemType == typeof(System.ComponentModel.INotifyPropertyChanged)) + { + return true; + } + return false; + } + + // API similar to System.Type + public bool IsGenericParameter + { + get { return this.IsTypeParameterType(); } + } + + private Type _associatedSystemType; + public Type AssociatedSystemType + { + get + { + if (_associatedSystemType == null) + { + _associatedSystemType = CalculateAssociatedSystemType(this); + } + + return _associatedSystemType; + } + } + + private static Type CalculateAssociatedSystemType(CType src) + { + Type result = null; + + switch (src.GetTypeKind()) + { + case TypeKind.TK_ArrayType: + ArrayType a = src.AsArrayType(); + Type elementType = a.GetElementType().AssociatedSystemType; + if (a.rank == 1) + { + result = elementType.MakeArrayType(); + } + else + { + result = elementType.MakeArrayType(a.rank); + } + break; + + case TypeKind.TK_NullableType: + NullableType n = src.AsNullableType(); + Type underlyingType = n.GetUnderlyingType().AssociatedSystemType; + result = typeof(Nullable<>).MakeGenericType(underlyingType); + break; + + case TypeKind.TK_PointerType: + PointerType p = src.AsPointerType(); + Type referentType = p.GetReferentType().AssociatedSystemType; + result = referentType.MakePointerType(); + break; + + case TypeKind.TK_ParameterModifierType: + ParameterModifierType r = src.AsParameterModifierType(); + Type parameterType = r.GetParameterType().AssociatedSystemType; + result = parameterType.MakeByRefType(); + break; + + case TypeKind.TK_AggregateType: + result = CalculateAssociatedSystemTypeForAggregate(src.AsAggregateType()); + break; + + case TypeKind.TK_TypeParameterType: + TypeParameterType t = src.AsTypeParameterType(); + Type parentType = null; + if (t.IsMethodTypeParameter()) + { + MethodInfo meth = t.GetOwningSymbol().AsMethodSymbol().AssociatedMemberInfo as MethodInfo; + result = meth.GetGenericArguments()[t.GetIndexInOwnParameters()]; + } + else + { + parentType = t.GetOwningSymbol().AsAggregateSymbol().AssociatedSystemType; + result = parentType.GetGenericArguments()[t.GetIndexInOwnParameters()]; + } + break; + + case TypeKind.TK_ArgumentListType: + case TypeKind.TK_BoundLambdaType: + case TypeKind.TK_ErrorType: + case TypeKind.TK_MethodGroupType: + case TypeKind.TK_NaturalIntegerType: + case TypeKind.TK_NullType: + case TypeKind.TK_OpenTypePlaceholderType: + case TypeKind.TK_UnboundLambdaType: + case TypeKind.TK_VoidType: + + default: + break; + } + + Debug.Assert(result != null || src.GetTypeKind() == TypeKind.TK_AggregateType); + return result; + } + + private static Type CalculateAssociatedSystemTypeForAggregate(AggregateType aggtype) + { + AggregateSymbol agg = aggtype.GetOwningAggregate(); + TypeArray typeArgs = aggtype.GetTypeArgsAll(); + + List list = new List(); + + // Get each type arg. + for (int i = 0; i < typeArgs.size; i++) + { + // Unnamed type parameter types are just placeholders. + if (typeArgs.Item(i).IsTypeParameterType() && typeArgs.Item(i).AsTypeParameterType().GetTypeParameterSymbol().name == null) + { + return null; + } + list.Add(typeArgs.Item(i).AssociatedSystemType); + } + + Type[] systemTypeArgs = list.ToArray(); + Type uninstantiatedType = agg.AssociatedSystemType; + + if (uninstantiatedType.IsGenericType) + { + try + { + return uninstantiatedType.MakeGenericType(systemTypeArgs); + } + catch (ArgumentException) + { + // If the constraints don't work, just return the type without substituting it. + return uninstantiatedType; + } + } + return uninstantiatedType; + } + + // ITypeOrNamespace + public bool IsType() { return true; } + public bool IsNamespace() { return false; } + public AssemblyQualifiedNamespaceSymbol AsNamespace() { throw Error.InternalCompilerError(); } + public CType AsType() { return this; } + + public TypeKind GetTypeKind() { return m_typeKind; } + public void SetTypeKind(TypeKind kind) { m_typeKind = kind; } + + public Name GetName() { return m_pName; } + public void SetName(Name pName) { m_pName = pName; } + + public bool checkBogus() { return isBogus; } + public bool getBogus() { return isBogus; } + public bool hasBogus() { return checkedBogus; } + public void setBogus(bool isBogus) + { + this.isBogus = isBogus; + checkedBogus = true; + } + public bool computeCurrentBogusState() + { + if (hasBogus()) + { + return checkBogus(); + } + + bool fBogus = false; + + switch (GetTypeKind()) + { + case TypeKind.TK_ParameterModifierType: + case TypeKind.TK_PointerType: + case TypeKind.TK_ArrayType: + case TypeKind.TK_NullableType: + if (GetBaseOrParameterOrElementType() != null) + { + fBogus = GetBaseOrParameterOrElementType().computeCurrentBogusState(); + } + break; + + case TypeKind.TK_ErrorType: + setBogus(false); + break; + + case TypeKind.TK_AggregateType: + fBogus = AsAggregateType().getAggregate().computeCurrentBogusState(); + for (int i = 0; !fBogus && i < AsAggregateType().GetTypeArgsAll().size; i++) + { + fBogus |= AsAggregateType().GetTypeArgsAll().Item(i).computeCurrentBogusState(); + } + break; + + case TypeKind.TK_TypeParameterType: + case TypeKind.TK_VoidType: + case TypeKind.TK_NullType: + case TypeKind.TK_OpenTypePlaceholderType: + case TypeKind.TK_ArgumentListType: + case TypeKind.TK_NaturalIntegerType: + setBogus(false); + break; + + default: + throw Error.InternalCompilerError(); + //setBogus(false); + //break; + } + + if (fBogus) + { + // Only set this if at least 1 declared thing is bogus + setBogus(fBogus); + } + + return hasBogus() && checkBogus(); + } + + // This call switches on the kind and dispatches accordingly. This should really only be + // used when dereferencing TypeArrays. We should consider refactoring our code to not + // need this type of thing - strongly typed handling of TypeArrays would be much better. + public CType GetBaseOrParameterOrElementType() + { + switch (GetTypeKind()) + { + case TypeKind.TK_ArrayType: + return AsArrayType().GetElementType(); + + case TypeKind.TK_PointerType: + return AsPointerType().GetReferentType(); + + case TypeKind.TK_ParameterModifierType: + return AsParameterModifierType().GetParameterType(); + + case TypeKind.TK_NullableType: + return AsNullableType().GetUnderlyingType(); + + default: + return null; + } + } + + public void InitFromParent() + { + Debug.Assert(!IsAggregateType()); + CType typePar = null; + + if (IsErrorType()) + { + typePar = AsErrorType().GetTypeParent(); + } + else + { + typePar = GetBaseOrParameterOrElementType(); + } + + this.fHasErrors = typePar.HasErrors(); + this.fUnres = typePar.IsUnresolved(); +#if CSEE + + this.typeRes = this; + if (!this.fUnres) + this.tsRes = ktsImportMax; + this.fDirty = typePar.fDirty; + this.tsDirty = typePar.tsDirty; +#endif // CSEE + } + + public bool HasErrors() + { + return fHasErrors; + } + public void SetErrors(bool fHasErrors) + { + this.fHasErrors = fHasErrors; + } + public bool IsUnresolved() + { + return fUnres; + } + public void SetUnresolved(bool fUnres) + { + this.fUnres = fUnres; + } + + //////////////////////////////////////////////////////////////////////////////// + // Given a symbol, determine its fundemental type. This is the type that + // indicate how the item is stored and what instructions are used to reference + // if. The fundemental types are: + // one of the integral/float types (includes enums with that underlying type) + // reference type + // struct/value type + public FUNDTYPE fundType() + { + switch (this.GetTypeKind()) + { + + case TypeKind.TK_AggregateType: + { + AggregateSymbol sym = this.AsAggregateType().getAggregate(); + + // Treat enums like their underlying types. + if (sym.IsEnum()) + { + sym = sym.GetUnderlyingType().getAggregate(); + } + + if (sym.IsStruct()) + { + // Struct type could be predefined (int, long, etc.) or some other struct. + if (sym.IsPredefined()) + return PredefinedTypeFacts.GetFundType(sym.GetPredefType()); + return FUNDTYPE.FT_STRUCT; + } + return FUNDTYPE.FT_REF; // Interfaces, classes, delegates are reference types. + } + + case TypeKind.TK_TypeParameterType: + return FUNDTYPE.FT_VAR; + + case TypeKind.TK_ArrayType: + case TypeKind.TK_NullType: + return FUNDTYPE.FT_REF; + + case TypeKind.TK_PointerType: + return FUNDTYPE.FT_PTR; + + case TypeKind.TK_NullableType: + return FUNDTYPE.FT_STRUCT; + + default: + return FUNDTYPE.FT_NONE; + } + } + public ConstValKind constValKind() + { + if (this.isPointerLike()) + { + return ConstValKind.IntPtr; + } + + switch (this.fundType()) + { + case FUNDTYPE.FT_I8: + case FUNDTYPE.FT_U8: + return ConstValKind.Long; + case FUNDTYPE.FT_STRUCT: + // Here we can either have a decimal type, or an enum + // whose fundamental type is decimal. + Debug.Assert((this.getAggregate().IsEnum() && this.getAggregate().GetUnderlyingType().getPredefType() == PredefinedType.PT_DECIMAL) + || (this.isPredefined() && this.getPredefType() == PredefinedType.PT_DATETIME) + || (this.isPredefined() && this.getPredefType() == PredefinedType.PT_DECIMAL)); + + if (isPredefined() && getPredefType() == PredefinedType.PT_DATETIME) + { + return ConstValKind.Long; + } + return ConstValKind.Decimal; + + case FUNDTYPE.FT_REF: + if (this.isPredefined() && this.getPredefType() == PredefinedType.PT_STRING) + { + return ConstValKind.String; + } + else + { + return ConstValKind.IntPtr; + } + case FUNDTYPE.FT_R4: + return ConstValKind.Float; + case FUNDTYPE.FT_R8: + return ConstValKind.Double; + case FUNDTYPE.FT_I1: + return ConstValKind.Boolean; + default: + return ConstValKind.Int; + } + } + public CType underlyingType() + { + if (this.IsAggregateType() && getAggregate().IsEnum()) + return getAggregate().GetUnderlyingType(); + return this; + } + + //////////////////////////////////////////////////////////////////////////////// + // Strips off ArrayType, ParameterModifierType, PointerType, PinnedType and optionally NullableType + // and returns the result. + public CType GetNakedType(bool fStripNub) + { + if (this == null) + return null; + + for (CType type = this; ; ) + { + switch (type.GetTypeKind()) + { + default: + return type; + + case TypeKind.TK_NullableType: + if (!fStripNub) + return type; + type = type.GetBaseOrParameterOrElementType(); + break; + case TypeKind.TK_ArrayType: + case TypeKind.TK_ParameterModifierType: + case TypeKind.TK_PointerType: + type = type.GetBaseOrParameterOrElementType(); + break; + } + } + } + public AggregateSymbol GetNakedAgg() + { + return GetNakedAgg(false); + } + public AggregateSymbol GetNakedAgg(bool fStripNub) + { + CType type = GetNakedType(fStripNub); + if (type != null && type.IsAggregateType()) + return type.AsAggregateType().getAggregate(); + return null; + } + public AggregateSymbol getAggregate() + { + Debug.Assert(IsAggregateType()); + return AsAggregateType().GetOwningAggregate(); + } + + public CType StripNubs() + { + if (this == null) + return null; + CType type; + for (type = this; type.IsNullableType(); type = type.AsNullableType().GetUnderlyingType()) + ; + return type; + } + public CType StripNubs(out int pcnub) + { + pcnub = 0; + if (this == null) + return null; + CType type; + for (type = this; type.IsNullableType(); type = type.AsNullableType().GetUnderlyingType()) + (pcnub)++; + return type; + } + + public bool isDelegateType() + { + return (this.IsAggregateType() && this.getAggregate().IsDelegate()); + } + + //////////////////////////////////////////////////////////////////////////////// + // A few types are considered "simple" types for purposes of conversions and so + // on. They are the fundemental types the compiler knows about for operators and + // conversions. + public bool isSimpleType() + { + return (this.isPredefined() && + PredefinedTypeFacts.IsSimpleType(this.getPredefType())); + } + public bool isSimpleOrEnum() + { + return isSimpleType() || isEnumType(); + } + public bool isSimpleOrEnumOrString() + { + return isSimpleType() || isPredefType(PredefinedType.PT_STRING) || isEnumType(); + } + + public bool isPointerLike() + { + return IsPointerType() || this.isPredefType(PredefinedType.PT_INTPTR) || this.isPredefType(PredefinedType.PT_UINTPTR); + } + + //////////////////////////////////////////////////////////////////////////////// + // A few types are considered "numeric" types. They are the fundemental number + // types the compiler knows about for operators and conversions. + public bool isNumericType() + { + return (this.isPredefined() && + PredefinedTypeFacts.IsNumericType(this.getPredefType())); + } + public bool isStructOrEnum() + { + return (IsAggregateType() && (getAggregate().IsStruct() || getAggregate().IsEnum())) || IsNullableType(); + } + public bool isStructType() + { + return this.IsAggregateType() && this.getAggregate().IsStruct() || this.IsNullableType(); + } + public bool isEnumType() + { + return (IsAggregateType() && getAggregate().IsEnum()); + } + public bool isInterfaceType() + { + return (this.IsAggregateType() && this.getAggregate().IsInterface()); + } + public bool isClassType() + { + return (this.IsAggregateType() && this.getAggregate().IsClass()); + } + public AggregateType underlyingEnumType() + { + Debug.Assert(isEnumType()); + return getAggregate().GetUnderlyingType(); + } + public bool isUnsigned() + { + if (this.IsAggregateType()) + { + AggregateType sym = this.AsAggregateType(); + if (sym.isEnumType()) + { + sym = sym.underlyingEnumType(); + } + if (sym.isPredefined()) + { + PredefinedType pt = sym.getPredefType(); + return pt == PredefinedType.PT_UINTPTR || pt == PredefinedType.PT_BYTE || (pt >= PredefinedType.PT_USHORT && pt <= PredefinedType.PT_ULONG); + } + else + { + return false; + } + } + else + { + return this.IsPointerType(); + } + } + public bool isUnsafe() + { + // Pointer types are the only unsafe types. + // Note that generics may not be instantiated with pointer types + return (this != null && (this.IsPointerType() || (this.IsArrayType() && this.AsArrayType().GetElementType().isUnsafe()))); + } + public bool isPredefType(PredefinedType pt) + { + if (this == null) + return false; + if (this.IsAggregateType()) + return this.AsAggregateType().getAggregate().IsPredefined() && this.AsAggregateType().getAggregate().GetPredefType() == pt; + return (this.IsVoidType() && pt == PredefinedType.PT_VOID); + } + public bool isPredefined() + { + return this.IsAggregateType() && this.getAggregate().IsPredefined(); + } + public PredefinedType getPredefType() + { + //ASSERT(isPredefined()); + return this.getAggregate().GetPredefType(); + } + + //////////////////////////////////////////////////////////////////////////////// + // Is this type System.TypedReference or System.ArgIterator? + // (used for errors becase these types can't go certain places) + + public bool isSpecialByRefType() + { + if (this == null) + return false; + else if (this.isPredefined()) + return this.getPredefType() == PredefinedType.PT_REFANY +#if !SILVERLIGHT + || this.getPredefType() == PredefinedType.PT_ARGITERATOR +#endif + || this.getPredefType() == PredefinedType.PT_ARGUMENTHANDLE; + else + return false; + } + public bool isStaticClass() + { + if (this == null) + return false; + + AggregateSymbol agg = this.GetNakedAgg(false); + if (agg == null) + return false; + + if (!agg.IsStatic()) + return false; + + return true; + } + public bool computeManagedType(SymbolLoader symbolLoader) + { + if (this.IsVoidType()) + return false; + + switch (this.fundType()) + { + case FUNDTYPE.FT_NONE: + case FUNDTYPE.FT_REF: + case FUNDTYPE.FT_VAR: + return true; + + case FUNDTYPE.FT_STRUCT: + if (this.IsNullableType()) + { + return true; + } + else + { + AggregateSymbol aggT = this.getAggregate(); + + // See if we already know. + if (aggT.IsKnownManagedStructStatus()) + { + return aggT.IsManagedStruct(); + } + + // Generics are always managed. + if (aggT.GetTypeVarsAll().size > 0) + { + aggT.SetManagedStruct(true); + return true; + } + + // If the struct layout has an error, dont recurse its children. + if (aggT.IsLayoutError()) + { + aggT.SetUnmanagedStruct(true); + return false; + } + + // at this point we can only determine the managed status + // if we have members defined, otherwise we don't know the result + if (symbolLoader != null) + { + for (Symbol ps = aggT.firstChild; ps != null; ps = ps.nextChild) + { + if (ps.IsFieldSymbol() && !ps.AsFieldSymbol().isStatic) + { + CType type = ps.AsFieldSymbol().GetType(); + if (type.computeManagedType(symbolLoader)) + { + aggT.SetManagedStruct(true); + return true; + } + } + } + + aggT.SetUnmanagedStruct(true); + } + + return false; + } + default: + return false; + } + } + public CType GetDelegateTypeOfPossibleExpression() + { + if (isPredefType(PredefinedType.PT_G_EXPRESSION)) + { + return this.AsAggregateType().GetTypeArgsThis().Item(0); + } + + return this; + } + + // These check for AGGTYPESYMs, TYVARSYMs and others as appropriate. + public bool IsValType() + { + switch (this.GetTypeKind()) + { + case TypeKind.TK_TypeParameterType: + return this.AsTypeParameterType().IsValueType(); + case TypeKind.TK_AggregateType: + return this.AsAggregateType().getAggregate().IsValueType(); + case TypeKind.TK_NullableType: + return true; + default: + return false; + } + } + public bool IsNonNubValType() + { + switch (this.GetTypeKind()) + { + case TypeKind.TK_TypeParameterType: + return this.AsTypeParameterType().IsNonNullableValueType(); + case TypeKind.TK_AggregateType: + return this.AsAggregateType().getAggregate().IsValueType(); + case TypeKind.TK_NullableType: + return false; + default: + return false; + } + } + public bool IsRefType() + { + switch (this.GetTypeKind()) + { + case TypeKind.TK_ArrayType: + case TypeKind.TK_NullType: + return true; + case TypeKind.TK_TypeParameterType: + return this.AsTypeParameterType().IsReferenceType(); + case TypeKind.TK_AggregateType: + return this.AsAggregateType().getAggregate().IsRefType(); + default: + return false; + } + } + + // A few types can be the same pointer value and not actually + // be equivalent or convertible (like ANONMETHSYMs) + public bool IsNeverSameType() + { + return IsBoundLambdaType() || IsMethodGroupType() || (IsErrorType() && !AsErrorType().HasParent()); + } + } +} diff --git a/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/Types/TypeArray.cs b/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/Types/TypeArray.cs new file mode 100644 index 000000000..91118359d --- /dev/null +++ b/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/Types/TypeArray.cs @@ -0,0 +1,68 @@ +// ==++== +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// ==--== + +using System.Diagnostics; +using System.Linq; + +namespace Microsoft.CSharp.RuntimeBinder.Semantics +{ + ///////////////////////////////////////////////////////////////////////////////// + // Encapsulates a type list, including its size and metadata token. + + class TypeArray + { + private CType[] items; + + public TypeArray(CType[] types) + { + items = types; + if (items == null) + { + items = new CType[0]; + } + } + + public int Size { get { return this.items.Length; } } + public int size { get { return Size; } } + + public bool HasErrors() { return false; } + public CType Item(int i) { return items[i]; } + public TypeParameterType ItemAsTypeParameterType(int i) { return items[i].AsTypeParameterType(); } + + public CType[] ToArray() { return this.items.ToArray(); } + + [System.Runtime.CompilerServices.IndexerName("EyeTim")] + public CType this[int i] + { + get { return this.items[i]; } + } + + public int Count + { + get { return this.items.Length; } + } + +#if DEBUG + public void AssertValid() + { + Debug.Assert(size >= 0); + for (int i = 0; i < size; i++) + { + Debug.Assert(items[i] != null); + } + } +#endif + + public void CopyItems(int i, int c, CType[] dest) + { + for (int j = 0; j < c; ++j) + { + dest[j] = items[i + j]; + } + } + + } +} diff --git a/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/Types/TypeFactory.cs b/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/Types/TypeFactory.cs new file mode 100644 index 000000000..353ba9c5f --- /dev/null +++ b/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/Types/TypeFactory.cs @@ -0,0 +1,166 @@ +// ==++== +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// ==--== + +using System.Diagnostics; +using Microsoft.CSharp.RuntimeBinder.Syntax; + +namespace Microsoft.CSharp.RuntimeBinder.Semantics +{ + class TypeFactory + { + // Constructor. + public TypeFactory() + { + } + + // Aggregate + public AggregateType CreateAggregateType( + Name name, + AggregateSymbol parent, + TypeArray typeArgsThis, + AggregateType outerType) + { + AggregateType type = new AggregateType(); + + type.outerType = outerType; + type.SetOwningAggregate(parent); + type.SetTypeArgsThis(typeArgsThis); + type.SetName(name); + + type.SetTypeKind(TypeKind.TK_AggregateType); + return type; + } + + // TypeParameter + public TypeParameterType CreateTypeParameter(TypeParameterSymbol pSymbol) + { + TypeParameterType type = new TypeParameterType(); + type.SetTypeParameterSymbol(pSymbol); + type.SetUnresolved(pSymbol.parent != null && pSymbol.parent.IsAggregateSymbol() && pSymbol.parent.AsAggregateSymbol().IsUnresolved()); + type.SetName(pSymbol.name); + +#if CSEE + type.typeRes = type; + if (!type.IsUnresolved()) + { + type.tsRes = ktsImportMax; + } +#endif // CSEE + + Debug.Assert(pSymbol.GetTypeParameterType() == null); + pSymbol.SetTypeParameterType(type); + + type.SetTypeKind(TypeKind.TK_TypeParameterType); + return type; + } + + // Primitives + public VoidType CreateVoid() + { + VoidType type = new VoidType(); + type.SetTypeKind(TypeKind.TK_VoidType); + return type; + } + + public NullType CreateNull() + { + NullType type = new NullType(); + type.SetTypeKind(TypeKind.TK_NullType); + return type; + } + + public OpenTypePlaceholderType CreateUnit() + { + OpenTypePlaceholderType type = new OpenTypePlaceholderType(); + type.SetTypeKind(TypeKind.TK_OpenTypePlaceholderType); + return type; + } + + public BoundLambdaType CreateAnonMethod() + { + BoundLambdaType type = new BoundLambdaType(); + type.SetTypeKind(TypeKind.TK_BoundLambdaType); + return type; + } + + public MethodGroupType CreateMethodGroup() + { + MethodGroupType type = new MethodGroupType(); + type.SetTypeKind(TypeKind.TK_MethodGroupType); + return type; + } + + public ArgumentListType CreateArgList() + { + ArgumentListType type = new ArgumentListType(); + type.SetTypeKind(TypeKind.TK_ArgumentListType); + return type; + } + + public ErrorType CreateError( + Name name, + CType parent, + AssemblyQualifiedNamespaceSymbol pParentNS, + Name nameText, + TypeArray typeArgs) + { + ErrorType e = new ErrorType(); + e.SetName(name); + e.nameText = nameText; + e.typeArgs = typeArgs; + e.SetTypeParent(parent); + e.SetNSParent(pParentNS); + + e.SetTypeKind(TypeKind.TK_ErrorType); + return e; + } + + // Derived types - parent is base type + public ArrayType CreateArray(Name name, CType pElementType, int rank) + { + ArrayType type = new ArrayType(); + + type.SetName(name); + type.rank = rank; + type.SetElementType(pElementType); + + type.SetTypeKind(TypeKind.TK_ArrayType); + return type; + } + + public PointerType CreatePointer(Name name, CType pReferentType) + { + PointerType type = new PointerType(); + type.SetName(name); + type.SetReferentType(pReferentType); + + type.SetTypeKind(TypeKind.TK_PointerType); + return type; + } + + public ParameterModifierType CreateParameterModifier(Name name, CType pParameterType) + { + ParameterModifierType type = new ParameterModifierType(); + type.SetName(name); + type.SetParameterType(pParameterType); + + type.SetTypeKind(TypeKind.TK_ParameterModifierType); + return type; + } + + public NullableType CreateNullable(Name name, CType pUnderlyingType, BSYMMGR symmgr, TypeManager typeManager) + { + NullableType type = new NullableType(); + type.SetName(name); + type.SetUnderlyingType(pUnderlyingType); + type.symmgr = symmgr; + type.typeManager = typeManager; + + type.SetTypeKind(TypeKind.TK_NullableType); + return type; + } + } +} diff --git a/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/Types/TypeKind.cs b/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/Types/TypeKind.cs new file mode 100644 index 000000000..f8581e704 --- /dev/null +++ b/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/Types/TypeKind.cs @@ -0,0 +1,27 @@ +// ==++== +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// ==--== + +namespace Microsoft.CSharp.RuntimeBinder.Semantics +{ + internal enum TypeKind + { + TK_AggregateType, + TK_VoidType, + TK_NullType, + TK_OpenTypePlaceholderType, + TK_BoundLambdaType, + TK_UnboundLambdaType, + TK_MethodGroupType, + TK_ErrorType, + TK_NaturalIntegerType, + TK_ArgumentListType, + TK_ArrayType, + TK_PointerType, + TK_ParameterModifierType, + TK_NullableType, + TK_TypeParameterType + } +} diff --git a/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/Types/TypeManager.cs b/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/Types/TypeManager.cs new file mode 100644 index 000000000..5644aaef7 --- /dev/null +++ b/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/Types/TypeManager.cs @@ -0,0 +1,1352 @@ +// ==++== +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// ==--== + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; +using System.Reflection; +using System.Runtime.CompilerServices; +using Microsoft.CSharp.RuntimeBinder; +using Microsoft.CSharp.RuntimeBinder.Errors; +using Microsoft.CSharp.RuntimeBinder.Syntax; + +#if FEATURE_NETCORE +using System.Security; +#endif +namespace Microsoft.CSharp.RuntimeBinder.Semantics +{ + internal class TypeManager + { + BSYMMGR m_BSymmgr; + PredefinedTypes m_predefTypes; + + TypeFactory m_typeFactory; + TypeTable m_typeTable; + SymbolTable m_symbolTable; + + // Special types + VoidType voidType; + NullType nullType; + OpenTypePlaceholderType typeUnit; + BoundLambdaType typeAnonMeth; + MethodGroupType typeMethGrp; + ArgumentListType argListType; + ErrorType errorType; + + StdTypeVarColl stvcMethod; + StdTypeVarColl stvcClass; + + public TypeManager() + { + this.m_predefTypes = null; // Initialized via the Init call. + this.m_BSymmgr = null; // Initialized via the Init call. + this.m_typeFactory = new TypeFactory(); + this.m_typeTable = new TypeTable(); + + // special types with their own symbol kind. + errorType = m_typeFactory.CreateError(null, null, null, null, null); + voidType = m_typeFactory.CreateVoid(); + nullType = m_typeFactory.CreateNull(); + typeUnit = m_typeFactory.CreateUnit(); + typeAnonMeth = m_typeFactory.CreateAnonMethod(); + typeMethGrp = m_typeFactory.CreateMethodGroup(); + argListType = m_typeFactory.CreateArgList(); + + InitType(errorType); + errorType.SetErrors(true); + + InitType(voidType); + InitType(nullType); + InitType(typeUnit); + InitType(typeAnonMeth); + InitType(typeMethGrp); + + stvcMethod = new StdTypeVarColl(); + stvcClass = new StdTypeVarColl(); + } + + public void InitTypeFactory(SymbolTable table) + { + m_symbolTable = table; + } + + private void InitType(CType at) + { + } + + public static bool TypeContainsAnonymousTypes(CType type) + { + CType ctype = (CType)type; + + LRecurse: // Label used for "tail" recursion. + switch (ctype.GetTypeKind()) + { + default: + Debug.Assert(false, "Bad Symbol kind in TypeContainsAnonymousTypes"); + return false; + + case TypeKind.TK_NullType: + case TypeKind.TK_VoidType: + case TypeKind.TK_NullableType: + case TypeKind.TK_TypeParameterType: + case TypeKind.TK_UnboundLambdaType: + case TypeKind.TK_MethodGroupType: + return false; + + case TypeKind.TK_ArrayType: + case TypeKind.TK_ParameterModifierType: + case TypeKind.TK_PointerType: + ctype = (CType)ctype.GetBaseOrParameterOrElementType(); + goto LRecurse; + + case TypeKind.TK_AggregateType: + if (ctype.AsAggregateType().getAggregate().IsAnonymousType()) + { + return true; + } + + TypeArray typeArgsAll = ctype.AsAggregateType().GetTypeArgsAll(); + for (int i = 0; i < typeArgsAll.size; i++) + { + CType typeArg = typeArgsAll.Item(i); + + if (TypeContainsAnonymousTypes(typeArg)) + { + return true; + } + } + return false; + + case TypeKind.TK_ErrorType: + if (ctype.AsErrorType().HasTypeParent()) + { + ctype = ctype.AsErrorType().GetTypeParent(); + goto LRecurse; + } + return false; + } + } + + class StdTypeVarColl + { + public List prgptvs; + + public StdTypeVarColl() + { + prgptvs = new List(); + } + + //////////////////////////////////////////////////////////////////////////////// + // Get the standard type variable (eg, !0, !1, or !!0, !!1). + // + // iv is the index. + // pbsm is the containing symbol manager + // fMeth designates whether this is a method type var or class type var + // + // The standard class type variables are useful during emit, but not for type + // comparison when binding. The standard method type variables are useful during + // binding for signature comparison. + + public TypeParameterType GetTypeVarSym(int iv, TypeManager pTypeManager, bool fMeth) + { + Debug.Assert(iv >= 0); + + TypeParameterType tpt = null; + if (iv >= this.prgptvs.Count) + { + TypeParameterSymbol pTypeParameter = new TypeParameterSymbol(); + pTypeParameter.SetIsMethodTypeParameter(fMeth); + pTypeParameter.SetIndexInOwnParameters(iv); + pTypeParameter.SetIndexInTotalParameters(iv); + pTypeParameter.SetAccess(ACCESS.ACC_PRIVATE); + tpt = pTypeManager.GetTypeParameter(pTypeParameter); + this.prgptvs.Add(tpt); + } + else + { + tpt = this.prgptvs[iv]; + } + Debug.Assert(tpt != null); + return tpt; + } + } + + public ArrayType GetArray(CType elementType, int args) + { + Name name; + ArrayType pArray; + + Debug.Assert(args > 0 && args < 32767); + + switch (args) + { + case 1: + case 2: + name = m_BSymmgr.GetNameManager().GetPredefinedName(PredefinedName.PN_ARRAY0 + args); + break; + default: + name = m_BSymmgr.GetNameManager().Add("[X" + args + 1); + break; + } + + // See if we already have an array type of this element type and rank. + pArray = m_typeTable.LookupArray(name, elementType); + if (pArray == null) + { + // No existing array symbol. Create a new one. + pArray = m_typeFactory.CreateArray(name, elementType, args); + pArray.InitFromParent(); + + m_typeTable.InsertArray(name, elementType, pArray); + } + else + { + Debug.Assert(pArray.HasErrors() == elementType.HasErrors()); + Debug.Assert(pArray.IsUnresolved() == elementType.IsUnresolved()); + } + + Debug.Assert(pArray.rank == args); + Debug.Assert(pArray.GetElementType() == elementType); + + return pArray; + } + + public AggregateType GetAggregate(AggregateSymbol agg, AggregateType atsOuter, TypeArray typeArgs) + { + Debug.Assert(agg.GetTypeManager() == this); + Debug.Assert(atsOuter == null || atsOuter.getAggregate() == agg.Parent, ""); + + if (typeArgs == null) + { + typeArgs = BSYMMGR.EmptyTypeArray(); + } + + Debug.Assert(agg.GetTypeVars().Size == typeArgs.Size); + + Name name = m_BSymmgr.GetNameFromPtrs(typeArgs, atsOuter); + Debug.Assert(name != null); + + AggregateType pAggregate = m_typeTable.LookupAggregate(name, agg); + if (pAggregate == null) + { + pAggregate = m_typeFactory.CreateAggregateType( + name, + agg, + typeArgs, + atsOuter + ); + + Debug.Assert(!pAggregate.fConstraintsChecked && !pAggregate.fConstraintError); + + pAggregate.SetErrors(pAggregate.GetTypeArgsAll().HasErrors()); +#if CSEE + + SpecializedSymbolCreationEE* pSymCreate = static_cast(m_BSymmgr.GetSymFactory().m_pSpecializedSymbolCreation); + AggregateSymbolExtra* pExtra = pSymCreate.GetHashTable().GetElement(agg).AsAggregateSymbolExtra(); + pAggregate.typeRes = pAggregate; + if (!pAggregate.IsUnresolved()) + { + pAggregate.tsRes = ktsImportMax; + } + pAggregate.fDirty = pExtra.IsDirty() || pAggregate.IsUnresolved(); + pAggregate.tsDirty = pExtra.GetLastComputedDirtyBit(); +#endif // CSEE + + m_typeTable.InsertAggregate(name, agg, pAggregate); + + // If we have a generic type definition, then we need to set the + // base class to be our current base type, and use that to calculate + // our agg type and its base, then set it to be the generic version of the + // base type. This is because: + // + // Suppose we have Foo : IFoo + // + // Initially, the BaseType will be IFoo, which gives us the substitution + // that we want to use for our agg type's base type. However, in the Symbol chain, + // we want the base type to be IFoo. Thats why we need to do this little trick. + // + // If we dont have a generic type definition, then we just need to set our base + // class. This is so that if we have a base type that's generic, we'll be + // getting the correctly instantiated base type. + + if (pAggregate.AssociatedSystemType != null && + pAggregate.AssociatedSystemType.BaseType != null) + { + // Store the old base class. + + AggregateType oldBaseType = agg.GetBaseClass(); + agg.SetBaseClass(m_symbolTable.GetCTypeFromType(pAggregate.AssociatedSystemType.BaseType).AsAggregateType()); + pAggregate.GetBaseClass(); // Get the base type for the new agg type we're making. + + agg.SetBaseClass(oldBaseType); + } + } + else + { + Debug.Assert(pAggregate.HasErrors() == pAggregate.GetTypeArgsAll().HasErrors()); + } + + Debug.Assert(pAggregate.getAggregate() == agg); + Debug.Assert(pAggregate.GetTypeArgsThis() != null && pAggregate.GetTypeArgsAll() != null); + Debug.Assert(pAggregate.GetTypeArgsThis() == typeArgs); + + return pAggregate; + } + + public AggregateType GetAggregate(AggregateSymbol agg, TypeArray typeArgsAll) + { + Debug.Assert(typeArgsAll != null && typeArgsAll.Size == agg.GetTypeVarsAll().Size); + + if (typeArgsAll.size == 0) + return agg.getThisType(); + + AggregateSymbol aggOuter = agg.GetOuterAgg(); + + if (aggOuter == null) + return GetAggregate(agg, null, typeArgsAll); + + int cvarOuter = aggOuter.GetTypeVarsAll().Size; + Debug.Assert(cvarOuter <= typeArgsAll.Size); + + TypeArray typeArgsOuter = m_BSymmgr.AllocParams(cvarOuter, typeArgsAll, 0); + TypeArray typeArgsInner = m_BSymmgr.AllocParams(agg.GetTypeVars().Size, typeArgsAll, cvarOuter); + AggregateType atsOuter = GetAggregate(aggOuter, typeArgsOuter); + + return GetAggregate(agg, atsOuter, typeArgsInner); + } + + public PointerType GetPointer(CType baseType) + { + PointerType pPointer = m_typeTable.LookupPointer(baseType); + if (pPointer == null) + { + // No existing type. Create a new one. + Name namePtr = m_BSymmgr.GetNameManager().GetPredefName(PredefinedName.PN_PTR); + + pPointer = m_typeFactory.CreatePointer(namePtr, baseType); + pPointer.InitFromParent(); + + m_typeTable.InsertPointer(baseType, pPointer); + } + else + { + Debug.Assert(pPointer.HasErrors() == baseType.HasErrors()); + Debug.Assert(pPointer.IsUnresolved() == baseType.IsUnresolved()); + } + + Debug.Assert(pPointer.GetReferentType() == baseType); + + return pPointer; + } + + public NullableType GetNullable(CType pUnderlyingType) + { + NullableType pNullableType = m_typeTable.LookupNullable(pUnderlyingType); + if (pNullableType == null) + { + Name pName = m_BSymmgr.GetNameManager().GetPredefName(PredefinedName.PN_NUB); + + pNullableType = m_typeFactory.CreateNullable(pName, pUnderlyingType, m_BSymmgr, this); + pNullableType.InitFromParent(); + + m_typeTable.InsertNullable(pUnderlyingType, pNullableType); + } + + return pNullableType; + } + + public NullableType GetNubFromNullable(AggregateType ats) + { + Debug.Assert(ats.isPredefType(PredefinedType.PT_G_OPTIONAL)); + return GetNullable(ats.GetTypeArgsAll().Item(0)); + } + + public ParameterModifierType GetParameterModifier(CType paramType, bool isOut) + { + Name name = m_BSymmgr.GetNameManager().GetPredefName(isOut ? PredefinedName.PN_OUTPARAM : PredefinedName.PN_REFPARAM); + ParameterModifierType pParamModifier = m_typeTable.LookupParameterModifier(name, paramType); + + if (pParamModifier == null) + { + // No existing parammod symbol. Create a new one. + pParamModifier = m_typeFactory.CreateParameterModifier(name, paramType); + pParamModifier.isOut = isOut; + pParamModifier.InitFromParent(); + + m_typeTable.InsertParameterModifier(name, paramType, pParamModifier); + } + else + { + Debug.Assert(pParamModifier.HasErrors() == paramType.HasErrors()); + Debug.Assert(pParamModifier.IsUnresolved() == paramType.IsUnresolved()); + } + + Debug.Assert(pParamModifier.GetParameterType() == paramType); + + return pParamModifier; + } + + public ErrorType GetErrorType( + CType pParentType, + AssemblyQualifiedNamespaceSymbol pParentNS, + Name nameText, + TypeArray typeArgs) + { + Debug.Assert(nameText != null); + Debug.Assert(pParentType == null || pParentNS == null); + if (pParentType == null && pParentNS == null) + { + // Use the root namespace as the parent. + pParentNS = m_BSymmgr.GetRootNsAid(KAID.kaidGlobal); + } + if (typeArgs == null) + { + typeArgs = BSYMMGR.EmptyTypeArray(); + } + + Name name = m_BSymmgr.GetNameFromPtrs(nameText, typeArgs); + Debug.Assert(name != null); + + ErrorType pError = null; + if (pParentType != null) + { + pError = m_typeTable.LookupError(name, pParentType); + } + else + { + Debug.Assert(pParentNS != null); + pError = m_typeTable.LookupError(name, pParentNS); + } + + if (pError == null) + { + // No existing error symbol. Create a new one. + pError = m_typeFactory.CreateError(name, pParentType, pParentNS, nameText, typeArgs); + pError.SetErrors(true); + if (pParentType != null) + { + m_typeTable.InsertError(name, pParentType, pError); + } + else + { + m_typeTable.InsertError(name, pParentNS, pError); + } + } + else + { + Debug.Assert(pError.HasErrors()); + Debug.Assert(pError.nameText == nameText); + Debug.Assert(pError.typeArgs == typeArgs); + } + Debug.Assert(!pError.IsUnresolved()); + + return pError; + } + + public VoidType GetVoid() + { + return this.voidType; + } + + public NullType GetNullType() + { + return this.nullType; + } + + public OpenTypePlaceholderType GetUnitType() + { + return this.typeUnit; + } + + public BoundLambdaType GetAnonMethType() + { + return this.typeAnonMeth; + } + + public MethodGroupType GetMethGrpType() + { + return this.typeMethGrp; + } + + public ArgumentListType GetArgListType() + { + return this.argListType; + } + + public ErrorType GetErrorSym() + { + return this.errorType; + } + + public AggregateSymbol GetNullable() + { + return this.GetOptPredefAgg(PredefinedType.PT_G_OPTIONAL); + } + + public CType SubstType(CType typeSrc, TypeArray typeArgsCls, TypeArray typeArgsMeth, SubstTypeFlags grfst) + { + if (typeSrc == null) + return null; + + var ctx = new SubstContext(typeArgsCls, typeArgsMeth, grfst); + return ctx.FNop() ? typeSrc : SubstTypeCore(typeSrc, ctx); + } + + public CType SubstType(CType typeSrc, TypeArray typeArgsCls) + { + return SubstType(typeSrc, typeArgsCls, null, SubstTypeFlags.NormNone); + } + + public CType SubstType(CType typeSrc, TypeArray typeArgsCls, TypeArray typeArgsMeth) + { + return SubstType(typeSrc, typeArgsCls, typeArgsMeth, SubstTypeFlags.NormNone); + } + + public TypeArray SubstTypeArray(TypeArray taSrc, SubstContext pctx) + { + if (taSrc == null || taSrc.Size == 0 || pctx == null || pctx.FNop()) + return taSrc; + + CType[] prgpts = new CType[taSrc.Size]; + for (int ipts = 0; ipts < taSrc.Size; ipts++) + { + prgpts[ipts] = this.SubstTypeCore(taSrc.Item(ipts), pctx); + } + return m_BSymmgr.AllocParams(taSrc.size, prgpts); + } + + public TypeArray SubstTypeArray(TypeArray taSrc, TypeArray typeArgsCls, TypeArray typeArgsMeth, SubstTypeFlags grfst) + { + if (taSrc == null || taSrc.Size == 0) + return taSrc; + + var ctx = new SubstContext(typeArgsCls, typeArgsMeth, grfst); + + if (ctx.FNop()) + return taSrc; + + CType[] prgpts = new CType[taSrc.Size]; + for (int ipts = 0; ipts < taSrc.Size; ipts++) + { + prgpts[ipts] = SubstTypeCore(taSrc.Item(ipts), ctx); + } + return m_BSymmgr.AllocParams(taSrc.Size, prgpts); + } + + public TypeArray SubstTypeArray(TypeArray taSrc, TypeArray typeArgsCls, TypeArray typeArgsMeth) + { + return this.SubstTypeArray(taSrc, typeArgsCls, typeArgsMeth, SubstTypeFlags.NormNone); + } + + public TypeArray SubstTypeArray(TypeArray taSrc, TypeArray typeArgsCls) + { + return this.SubstTypeArray(taSrc, typeArgsCls, (TypeArray)null, SubstTypeFlags.NormNone); + } + + private CType SubstTypeCore(CType type, SubstContext pctx) + { + CType typeSrc; + CType typeDst; + + switch (type.GetTypeKind()) + { + default: + Debug.Assert(false); + return type; + + case TypeKind.TK_NullType: + case TypeKind.TK_VoidType: + case TypeKind.TK_OpenTypePlaceholderType: + case TypeKind.TK_MethodGroupType: + case TypeKind.TK_BoundLambdaType: + case TypeKind.TK_UnboundLambdaType: + case TypeKind.TK_NaturalIntegerType: + case TypeKind.TK_ArgumentListType: + return type; + + case TypeKind.TK_ParameterModifierType: + typeDst = SubstTypeCore(typeSrc = type.AsParameterModifierType().GetParameterType(), pctx); + return (typeDst == typeSrc) ? type : GetParameterModifier(typeDst, type.AsParameterModifierType().isOut); + + case TypeKind.TK_ArrayType: + typeDst = SubstTypeCore(typeSrc = type.AsArrayType().GetElementType(), pctx); + return (typeDst == typeSrc) ? type : GetArray(typeDst, type.AsArrayType().rank); + + case TypeKind.TK_PointerType: + typeDst = SubstTypeCore(typeSrc = type.AsPointerType().GetReferentType(), pctx); + return (typeDst == typeSrc) ? type : GetPointer(typeDst); + + case TypeKind.TK_NullableType: + typeDst = SubstTypeCore(typeSrc = type.AsNullableType().GetUnderlyingType(), pctx); + return (typeDst == typeSrc) ? type : GetNullable(typeDst); + + case TypeKind.TK_AggregateType: + if (type.AsAggregateType().GetTypeArgsAll().size > 0) + { + AggregateType ats = type.AsAggregateType(); + + TypeArray typeArgs = SubstTypeArray(ats.GetTypeArgsAll(), pctx); + if (ats.GetTypeArgsAll() != typeArgs) + return GetAggregate(ats.getAggregate(), typeArgs); + } + return type; + + case TypeKind.TK_ErrorType: + if (type.AsErrorType().HasParent()) + { + ErrorType err = type.AsErrorType(); + Debug.Assert(err.nameText != null && err.typeArgs != null); + + CType pParentType = null; + if (err.HasTypeParent()) + { + pParentType = SubstTypeCore(err.GetTypeParent(), pctx); + } + + TypeArray typeArgs = SubstTypeArray(err.typeArgs, pctx); + if (typeArgs != err.typeArgs || (err.HasTypeParent() && pParentType != err.GetTypeParent())) + { + return GetErrorType(pParentType, err.GetNSParent(), err.nameText, typeArgs); + } + } + return type; + + case TypeKind.TK_TypeParameterType: + { + TypeParameterSymbol tvs = type.AsTypeParameterType().GetTypeParameterSymbol(); + int index = tvs.GetIndexInTotalParameters(); + if (tvs.IsMethodTypeParameter()) + { + if ((pctx.grfst & SubstTypeFlags.DenormMeth) != 0 && tvs.parent != null) + return type; + Debug.Assert(tvs.GetIndexInOwnParameters() == tvs.GetIndexInTotalParameters()); + if (index < pctx.ctypeMeth) + { + Debug.Assert(pctx.prgtypeMeth != null); + return pctx.prgtypeMeth[index]; + } + else + { + return ((pctx.grfst & SubstTypeFlags.NormMeth) != 0 ? GetStdMethTypeVar(index) : type); + } + } + if ((pctx.grfst & SubstTypeFlags.DenormClass) != 0 && tvs.parent != null) + return type; + return index < pctx.ctypeCls ? pctx.prgtypeCls[index] : + ((pctx.grfst & SubstTypeFlags.NormClass) != 0 ? GetStdClsTypeVar(index) : type); + } + } + } + + public bool SubstEqualTypes(CType typeDst, CType typeSrc, TypeArray typeArgsCls, TypeArray typeArgsMeth, SubstTypeFlags grfst) + { + if (typeDst.Equals(typeSrc)) + { + Debug.Assert(typeDst.Equals(SubstType(typeSrc, typeArgsCls, typeArgsMeth, grfst))); + return true; + } + + var ctx = new SubstContext(typeArgsCls, typeArgsMeth, grfst); + + return !ctx.FNop() && SubstEqualTypesCore(typeDst, typeSrc, ctx); + } + + public bool SubstEqualTypeArrays(TypeArray taDst, TypeArray taSrc, TypeArray typeArgsCls, TypeArray typeArgsMeth, SubstTypeFlags grfst) + { + // Handle the simple common cases first. + if (taDst == taSrc || (taDst != null && taDst.Equals(taSrc))) + { + // The following assertion is not always true and indicates a problem where + // the signature of override method does not match the one inherited from + // the base class. The method match we have found does not take the type + // arguments of the base class into account. So actually we are not overriding + // the method that we "intend" to. This overload resolution problem in nested + // generic types is tracked by DevDiv Bugs 152636. + // Debug.Assert(taDst == SubstTypeArray(taSrc, typeArgsCls, typeArgsMeth, grfst)); + return true; + } + if (taDst.Size != taSrc.Size) + return false; + if (taDst.Size == 0) + return true; + + var ctx = new SubstContext(typeArgsCls, typeArgsMeth, grfst); + + if (ctx.FNop()) + return false; + + for (int i = 0; i < taDst.size; i++) + { + if (!SubstEqualTypesCore(taDst.Item(i), taSrc.Item(i), ctx)) + return false; + } + + return true; + } + + public bool SubstEqualTypesCore(CType typeDst, CType typeSrc, SubstContext pctx) + { + LRecurse: // Label used for "tail" recursion. + + if (typeDst == typeSrc || typeDst.Equals(typeSrc)) + { + return true; + } + + switch (typeSrc.GetTypeKind()) + { + default: + Debug.Assert(false, "Bad Symbol kind in SubstEqualTypesCore"); + return false; + + case TypeKind.TK_NullType: + case TypeKind.TK_VoidType: + case TypeKind.TK_OpenTypePlaceholderType: + // There should only be a single instance of these. + Debug.Assert(typeDst.GetTypeKind() != typeSrc.GetTypeKind()); + return false; + + case TypeKind.TK_ArrayType: + if (typeDst.GetTypeKind() != TypeKind.TK_ArrayType || typeDst.AsArrayType().rank != typeSrc.AsArrayType().rank) + return false; + goto LCheckBases; + + case TypeKind.TK_ParameterModifierType: + if (typeDst.GetTypeKind() != TypeKind.TK_ParameterModifierType || + ((pctx.grfst & SubstTypeFlags.NoRefOutDifference) == 0 && + typeDst.AsParameterModifierType().isOut != typeSrc.AsParameterModifierType().isOut)) + return false; + goto LCheckBases; + + case TypeKind.TK_PointerType: + case TypeKind.TK_NullableType: + if (typeDst.GetTypeKind() != typeSrc.GetTypeKind()) + return false; + LCheckBases: + typeSrc = typeSrc.GetBaseOrParameterOrElementType(); + typeDst = typeDst.GetBaseOrParameterOrElementType(); + goto LRecurse; + + case TypeKind.TK_AggregateType: + if (typeDst.GetTypeKind() != TypeKind.TK_AggregateType) + return false; + { // BLOCK + AggregateType atsSrc = typeSrc.AsAggregateType(); + AggregateType atsDst = typeDst.AsAggregateType(); + + if (atsSrc.getAggregate() != atsDst.getAggregate()) + return false; + + Debug.Assert(atsSrc.GetTypeArgsAll().Size == atsDst.GetTypeArgsAll().Size); + + // All the args must unify. + for (int i = 0; i < atsSrc.GetTypeArgsAll().Size; i++) + { + if (!SubstEqualTypesCore(atsDst.GetTypeArgsAll().Item(i), atsSrc.GetTypeArgsAll().Item(i), pctx)) + return false; + } + } + return true; + + case TypeKind.TK_ErrorType: + if (!typeDst.IsErrorType() || !typeSrc.AsErrorType().HasParent() || !typeDst.AsErrorType().HasParent()) + return false; + { + ErrorType errSrc = typeSrc.AsErrorType(); + ErrorType errDst = typeDst.AsErrorType(); + Debug.Assert(errSrc.nameText != null && errSrc.typeArgs != null); + Debug.Assert(errDst.nameText != null && errDst.typeArgs != null); + + if (errSrc.nameText != errDst.nameText || errSrc.typeArgs.Size != errDst.typeArgs.Size) + return false; + + if (errSrc.HasTypeParent() != errDst.HasTypeParent()) + { + return false; + } + if (errSrc.HasTypeParent()) + { + if (errSrc.GetTypeParent() != errDst.GetTypeParent()) + { + return false; + } + if (!SubstEqualTypesCore(errDst.GetTypeParent(), errSrc.GetTypeParent(), pctx)) + { + return false; + } + } + else + { + if (errSrc.GetNSParent() != errDst.GetNSParent()) + { + return false; + } + } + + // All the args must unify. + for (int i = 0; i < errSrc.typeArgs.Size; i++) + { + if (!SubstEqualTypesCore(errDst.typeArgs.Item(i), errSrc.typeArgs.Item(i), pctx)) + return false; + } + } + return true; + + case TypeKind.TK_TypeParameterType: + { // BLOCK + TypeParameterSymbol tvs = typeSrc.AsTypeParameterType().GetTypeParameterSymbol(); + int index = tvs.GetIndexInTotalParameters(); + + if (tvs.IsMethodTypeParameter()) + { + if ((pctx.grfst & SubstTypeFlags.DenormMeth) != 0 && tvs.parent != null) + { + // typeDst == typeSrc was handled above. + Debug.Assert(typeDst != typeSrc); + return false; + } + Debug.Assert(tvs.GetIndexInOwnParameters() == tvs.GetIndexInTotalParameters()); + Debug.Assert(pctx.prgtypeMeth == null || tvs.GetIndexInTotalParameters() < pctx.ctypeMeth); + if (index < pctx.ctypeMeth && pctx.prgtypeMeth != null) + { + return typeDst == pctx.prgtypeMeth[index]; + } + if ((pctx.grfst & SubstTypeFlags.NormMeth) != 0) + { + return typeDst == GetStdMethTypeVar(index); + } + } + else + { + if ((pctx.grfst & SubstTypeFlags.DenormClass) != 0 && tvs.parent != null) + { + // typeDst == typeSrc was handled above. + Debug.Assert(typeDst != typeSrc); + return false; + } + Debug.Assert(pctx.prgtypeCls == null || tvs.GetIndexInTotalParameters() < pctx.ctypeCls); + if (index < pctx.ctypeCls) + return typeDst == pctx.prgtypeCls[index]; + if ((pctx.grfst & SubstTypeFlags.NormClass) != 0) + return typeDst == GetStdClsTypeVar(index); + } + } + return false; + } + } + + public void ReportMissingPredefTypeError(ErrorHandling errorContext, PredefinedType pt) + { + m_predefTypes.ReportMissingPredefTypeError(errorContext, pt); + } + + public static bool TypeContainsType(CType type, CType typeFind) + { + LRecurse: // Label used for "tail" recursion. + + if (type == typeFind || type.Equals(typeFind)) + return true; + + switch (type.GetTypeKind()) + { + default: + Debug.Assert(false, "Bad Symbol kind in TypeContainsType"); + return false; + + case TypeKind.TK_NullType: + case TypeKind.TK_VoidType: + case TypeKind.TK_OpenTypePlaceholderType: + // There should only be a single instance of these. + Debug.Assert(typeFind.GetTypeKind() != type.GetTypeKind()); + return false; + + case TypeKind.TK_ArrayType: + case TypeKind.TK_NullableType: + case TypeKind.TK_ParameterModifierType: + case TypeKind.TK_PointerType: + type = type.GetBaseOrParameterOrElementType(); + goto LRecurse; + + case TypeKind.TK_AggregateType: + { // BLOCK + AggregateType ats = type.AsAggregateType(); + + for (int i = 0; i < ats.GetTypeArgsAll().Size; i++) + { + if (TypeContainsType(ats.GetTypeArgsAll().Item(i), typeFind)) + return true; + } + } + return false; + + case TypeKind.TK_ErrorType: + if (type.AsErrorType().HasParent()) + { + ErrorType err = type.AsErrorType(); + Debug.Assert(err.nameText != null && err.typeArgs != null); + + for (int i = 0; i < err.typeArgs.Size; i++) + { + if (TypeContainsType(err.typeArgs.Item(i), typeFind)) + return true; + } + if (err.HasTypeParent()) + { + type = err.GetTypeParent(); + goto LRecurse; + } + } + return false; + + case TypeKind.TK_TypeParameterType: + return false; + } + } + + public static bool TypeContainsTyVars(CType type, TypeArray typeVars) + { + LRecurse: // Label used for "tail" recursion. + switch (type.GetTypeKind()) + { + default: + Debug.Assert(false, "Bad Symbol kind in TypeContainsTyVars"); + return false; + + case TypeKind.TK_UnboundLambdaType: + case TypeKind.TK_BoundLambdaType: + case TypeKind.TK_NullType: + case TypeKind.TK_VoidType: + case TypeKind.TK_OpenTypePlaceholderType: + case TypeKind.TK_MethodGroupType: + return false; + + case TypeKind.TK_ArrayType: + case TypeKind.TK_NullableType: + case TypeKind.TK_ParameterModifierType: + case TypeKind.TK_PointerType: + type = type.GetBaseOrParameterOrElementType(); + goto LRecurse; + + case TypeKind.TK_AggregateType: + { // BLOCK + AggregateType ats = type.AsAggregateType(); + + for (int i = 0; i < ats.GetTypeArgsAll().Size; i++) + { + if (TypeContainsTyVars(ats.GetTypeArgsAll().Item(i), typeVars)) + { + return true; + } + } + } + return false; + + case TypeKind.TK_ErrorType: + if (type.AsErrorType().HasParent()) + { + ErrorType err = type.AsErrorType(); + Debug.Assert(err.nameText != null && err.typeArgs != null); + + for (int i = 0; i < err.typeArgs.Size; i++) + { + if (TypeContainsTyVars(err.typeArgs.Item(i), typeVars)) + { + return true; + } + } + if (err.HasTypeParent()) + { + type = err.GetTypeParent(); + goto LRecurse; + } + } + return false; + + case TypeKind.TK_TypeParameterType: + if (typeVars != null && typeVars.Size > 0) + { + int ivar = type.AsTypeParameterType().GetIndexInTotalParameters(); + return ivar < typeVars.Size && type == typeVars.Item(ivar); + } + return true; + } + } + + public static bool ParametersContainTyVar(TypeArray @params, TypeParameterType typeFind) + { + Debug.Assert(@params != null); + Debug.Assert(typeFind != null); + for (int p = 0; p < @params.size; p++) + { + CType sym = @params[p]; + if (TypeContainsType(sym, typeFind)) + { + return true; + } + } + return false; + } + + public AggregateSymbol GetReqPredefAgg(PredefinedType pt) + { + return m_predefTypes.GetReqPredefAgg(pt); + } + + public AggregateSymbol GetOptPredefAgg(PredefinedType pt) + { + return m_predefTypes.GetOptPredefAgg(pt); + } + + public TypeArray CreateArrayOfUnitTypes(int cSize) + { + CType[] ppArray = new CType[cSize]; + for (int i = 0; i < cSize; i++) + { + ppArray[i] = GetUnitType(); + } + return m_BSymmgr.AllocParams(cSize, ppArray); + } + + public TypeArray ConcatenateTypeArrays(TypeArray pTypeArray1, TypeArray pTypeArray2) + { + return m_BSymmgr.ConcatParams(pTypeArray1, pTypeArray2); + } + + public TypeArray GetStdMethTyVarArray(int cTyVars) + { + TypeParameterType[] prgvar = new TypeParameterType[cTyVars]; + + for (int ivar = 0; ivar < cTyVars; ivar++) + { + prgvar[ivar] = GetStdMethTypeVar(ivar); + } + + return m_BSymmgr.AllocParams(cTyVars, (CType[])prgvar); + } + + public CType SubstType(CType typeSrc, SubstContext pctx) + { + return (pctx == null || pctx.FNop()) ? typeSrc : SubstTypeCore(typeSrc, pctx); + } + + public CType SubstType(CType typeSrc, AggregateType atsCls) + { + return SubstType(typeSrc, atsCls, (TypeArray)null); + } + + public CType SubstType(CType typeSrc, AggregateType atsCls, TypeArray typeArgsMeth) + { + return SubstType(typeSrc, atsCls != null ? atsCls.GetTypeArgsAll() : null, typeArgsMeth); + } + + public CType SubstType(CType typeSrc, CType typeCls, TypeArray typeArgsMeth) + { + return SubstType(typeSrc, typeCls.IsAggregateType() ? typeCls.AsAggregateType().GetTypeArgsAll() : null, typeArgsMeth); + } + + public TypeArray SubstTypeArray(TypeArray taSrc, AggregateType atsCls, TypeArray typeArgsMeth) + { + return SubstTypeArray(taSrc, atsCls != null ? atsCls.GetTypeArgsAll() : null, typeArgsMeth); + } + + public TypeArray SubstTypeArray(TypeArray taSrc, AggregateType atsCls) + { + return this.SubstTypeArray(taSrc, atsCls, (TypeArray)null); + } + + public bool SubstEqualTypes(CType typeDst, CType typeSrc, CType typeCls, TypeArray typeArgsMeth) + { + return SubstEqualTypes(typeDst, typeSrc, typeCls.IsAggregateType() ? typeCls.AsAggregateType().GetTypeArgsAll() : null, typeArgsMeth, SubstTypeFlags.NormNone); + } + + public bool SubstEqualTypes(CType typeDst, CType typeSrc, CType typeCls) + { + return SubstEqualTypes(typeDst, typeSrc, typeCls, (TypeArray)null); + } + + //public bool SubstEqualTypeArrays(TypeArray taDst, TypeArray taSrc, AggregateType atsCls, TypeArray typeArgsMeth) + //{ + // return SubstEqualTypeArrays(taDst, taSrc, atsCls != null ? atsCls.GetTypeArgsAll() : (TypeArray)null, typeArgsMeth, SubstTypeFlags.NormNone); + //} + + public TypeParameterType GetStdMethTypeVar(int iv) + { + return stvcMethod.GetTypeVarSym(iv, this, true); + } + + public TypeParameterType GetStdClsTypeVar(int iv) + { + return stvcClass.GetTypeVarSym(iv, this, false); + } + + public TypeParameterType GetTypeParameter(TypeParameterSymbol pSymbol) + { + // These guys should be singletons for each. + + TypeParameterType pTypeParameter = m_typeTable.LookupTypeParameter(pSymbol); + if (pTypeParameter == null) + { + pTypeParameter = m_typeFactory.CreateTypeParameter(pSymbol); + m_typeTable.InsertTypeParameter(pSymbol, pTypeParameter); + } + + return pTypeParameter; + } + + internal void Init(BSYMMGR bsymmgr, PredefinedTypes predefTypes) + { + m_BSymmgr = bsymmgr; + m_predefTypes = predefTypes; + } + + // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + // RUNTIME BINDER ONLY CHANGE + // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + + internal bool GetBestAccessibleType(CSemanticChecker semanticChecker, BindingContext bindingContext, CType typeSrc, out CType typeDst) + { + // This method implements the "best accessible type" algorithm for determining the type + // of untyped arguments in the runtime binder. It is also used in method type inference + // to fix type arguments to types that are accessible. + + // The new type is returned in an out parameter. The result will be true (and the out param + // non-null) only when the algorithm could find a suitable accessible type. + + Debug.Assert(semanticChecker != null); + Debug.Assert(bindingContext != null); + Debug.Assert(typeSrc != null); + + typeDst = null; + + if (semanticChecker.CheckTypeAccess(typeSrc, bindingContext.ContextForMemberLookup())) + { + // If we already have an accessible type, then use it. This is the terminal point of the recursion. + typeDst = typeSrc; + return true; + } + + // These guys have no accessibility concerns. + Debug.Assert(!typeSrc.IsVoidType() && !typeSrc.IsErrorType() && !typeSrc.IsTypeParameterType()); + + if (typeSrc.IsParameterModifierType() || typeSrc.IsPointerType()) + { + // We cannot vary these. + return false; + } + + CType intermediateType; + if ((typeSrc.isInterfaceType() || typeSrc.isDelegateType()) && TryVarianceAdjustmentToGetAccessibleType(semanticChecker, bindingContext, typeSrc.AsAggregateType(), out intermediateType)) + { + // If we have an interface or delegate type, then it can potentially be varied by its type arguments + // to produce an accessible type, and if that's the case, then return that. + // Example: IEnumerable --> IEnumerable + typeDst = intermediateType; + + Debug.Assert(semanticChecker.CheckTypeAccess(typeDst, bindingContext.ContextForMemberLookup())); + return true; + } + + if (typeSrc.IsArrayType() && TryArrayVarianceAdjustmentToGetAccessibleType(semanticChecker, bindingContext, typeSrc.AsArrayType(), out intermediateType)) + { + // Similarly to the interface and delegate case, arrays are covariant in their element type and + // so we can potentially produce an array type that is accessible. + // Example: PrivateConcreteFoo[] --> PublicAbstractFoo[] + typeDst = intermediateType; + + Debug.Assert(semanticChecker.CheckTypeAccess(typeDst, bindingContext.ContextForMemberLookup())); + return true; + } + + if (typeSrc.IsNullableType()) + { + // We have an inaccessible nullable type, which means that the best we can do is System.ValueType. + typeDst = this.GetOptPredefAgg(PredefinedType.PT_VALUE).getThisType(); + + Debug.Assert(semanticChecker.CheckTypeAccess(typeDst, bindingContext.ContextForMemberLookup())); + return true; + } + + if (typeSrc.IsArrayType()) + { + // We have an inaccessible array type for which we could not earlier find a better array type + // with a covariant conversion, so the best we can do is System.Array. + typeDst = this.GetReqPredefAgg(PredefinedType.PT_ARRAY).getThisType(); + + Debug.Assert(semanticChecker.CheckTypeAccess(typeDst, bindingContext.ContextForMemberLookup())); + return true; + } + + Debug.Assert(typeSrc.IsAggregateType()); + + if (typeSrc.IsAggregateType()) + { + // We have an AggregateType, so recurse on its base class. + AggregateType aggType = typeSrc.AsAggregateType(); + AggregateType baseType = aggType.GetBaseClass(); + + if (baseType == null) + { + // This happens with interfaces, for instance. But in that case, the + // conversion to object does exist, is an implicit reference conversion, + // and so we will use it. + baseType = this.GetReqPredefAgg(PredefinedType.PT_OBJECT).getThisType(); + } + + return GetBestAccessibleType(semanticChecker, bindingContext, baseType, out typeDst); + } + + return false; + } + + private bool TryVarianceAdjustmentToGetAccessibleType(CSemanticChecker semanticChecker, BindingContext bindingContext, AggregateType typeSrc, out CType typeDst) + { + Debug.Assert(typeSrc != null); + Debug.Assert(typeSrc.isInterfaceType() || typeSrc.isDelegateType()); + + typeDst = null; + + AggregateSymbol aggSym = typeSrc.GetOwningAggregate(); + AggregateType aggOpenType = aggSym.getThisType(); + + if (!semanticChecker.CheckTypeAccess(aggOpenType, bindingContext.ContextForMemberLookup())) + { + // if the aggregate symbol itself is not accessible, then forget it, there is no + // variance that will help us arrive at an accessible type. + return false; + } + + TypeArray typeArgs = typeSrc.GetTypeArgsThis(); + TypeArray typeParams = aggOpenType.GetTypeArgsThis(); + CType[] newTypeArgsTemp = new CType[typeArgs.size]; + + for (int i = 0; i < typeArgs.size; i++) + { + if (semanticChecker.CheckTypeAccess(typeArgs.Item(i), bindingContext.ContextForMemberLookup())) + { + // we have an accessible argument, this position is not a problem. + newTypeArgsTemp[i] = typeArgs.Item(i); + continue; + } + + if (!typeArgs.Item(i).IsRefType() || !typeParams.Item(i).AsTypeParameterType().Covariant) + { + // This guy is inaccessible, and we are not going to be able to vary him, so we need to fail. + return false; + } + + CType intermediateTypeArg; + if (GetBestAccessibleType(semanticChecker, bindingContext, typeArgs.Item(i), out intermediateTypeArg)) + { + // now we either have a value type (which must be accessible due to the above + // check, OR we have an inaccessible type (which must be a ref type). In either + // case, the recursion worked out and we are OK to vary this argument. + newTypeArgsTemp[i] = intermediateTypeArg; + continue; + } + else + { + Debug.Assert(false, "GetBestAccessibleType unexpectedly failed on a type that was used as a type parameter"); + return false; + } + } + + TypeArray newTypeArgs = semanticChecker.getBSymmgr().AllocParams(typeArgs.size, newTypeArgsTemp); + CType intermediateType = this.GetAggregate(aggSym, typeSrc.outerType, newTypeArgs); + + // All type arguments were varied successfully, which means now we must be accessible. But we could + // have violated constraints. Let's check that out. + + if (!TypeBind.CheckConstraints(semanticChecker, null/*ErrorHandling*/, intermediateType, CheckConstraintsFlags.NoErrors)) + { + // Uh oh, we messed up the type constraints. + return false; + } + + typeDst = intermediateType; + Debug.Assert(semanticChecker.CheckTypeAccess(typeDst, bindingContext.ContextForMemberLookup())); + return true; + } + + private bool TryArrayVarianceAdjustmentToGetAccessibleType(CSemanticChecker semanticChecker, BindingContext bindingContext, ArrayType typeSrc, out CType typeDst) + { + Debug.Assert(typeSrc != null); + + typeDst = null; + + // We are here because we have an array type with an inaccessible element type. If possible, + // we should create a new array type that has an accessible element type for which a + // conversion exists. + + CType elementType = typeSrc.GetElementType(); + if (!elementType.IsRefType()) + { + // Covariant array conversions exist for reference types only. + return false; + } + + CType intermediateType; + if (GetBestAccessibleType(semanticChecker, bindingContext, elementType, out intermediateType)) + { + typeDst = this.GetArray(intermediateType, typeSrc.rank); + + Debug.Assert(semanticChecker.CheckTypeAccess(typeDst, bindingContext.ContextForMemberLookup())); + return true; + } + + return false; + } + + private Dictionary, bool> internalsVisibleToCalculated + = new Dictionary, bool>(); + +#if FEATURE_NETCORE + [SecuritySafeCritical] +#endif + internal bool InternalsVisibleTo(Assembly assemblyThatDefinesAttribute, Assembly assemblyToCheck) + { + bool result; + + var key = Tuple.Create(assemblyThatDefinesAttribute, assemblyToCheck); + if (!internalsVisibleToCalculated.TryGetValue(key, out result)) + { +#if !SILVERLIGHT || FEATURE_NETCORE + AssemblyName assyName = null; + + // Assembly.GetName() requires FileIOPermission to FileIOPermissionAccess.PathDiscovery. + // If we don't have that (we're in low trust), then we are going to effectively turn off + // InternalsVisibleTo. The alternative is to crash when this happens. (TODO: can I ask + // the security system up front without taking an exception?) + + try + { + assyName = assemblyToCheck.GetName(); + } + catch (System.Security.SecurityException) + { + result = false; + goto SetMemo; + } + + result = assemblyThatDefinesAttribute.GetCustomAttributes(true) + .OfType() + .Select(ivta => new AssemblyName(ivta.AssemblyName)) + .Any(an => AssemblyName.ReferenceMatchesDefinition(an, assyName)); + + SetMemo: +#endif + internalsVisibleToCalculated[key] = result; + } + + return result; + } + + // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + // END RUNTIME BINDER ONLY CHANGE + // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + + } +} diff --git a/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/Types/TypeParameterType.cs b/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/Types/TypeParameterType.cs new file mode 100644 index 000000000..21c8a6440 --- /dev/null +++ b/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/Types/TypeParameterType.cs @@ -0,0 +1,66 @@ +// ==++== +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// ==--== + +using System.Diagnostics; + +namespace Microsoft.CSharp.RuntimeBinder.Semantics +{ + ///////////////////////////////////////////////////////////////////////////////// + + class TypeParameterType : CType + { + public TypeParameterSymbol GetTypeParameterSymbol() { return m_pTypeParameterSymbol; } + public void SetTypeParameterSymbol(TypeParameterSymbol pTypePArameterSymbol) { m_pTypeParameterSymbol = pTypePArameterSymbol; } + + public ParentSymbol GetOwningSymbol() { return m_pTypeParameterSymbol.parent; } + + + public bool DependsOn(TypeParameterType pType) + { + Debug.Assert(pType != null); + + // * If a type parameter T is used as a constraint for type parameter S + // then S depends on T. + // * If a type parameter S depends on a type parameter T and T depends on + // U then S depends on U. + + TypeArray pConstraints = GetBounds(); + for (int iConstraint = 0; iConstraint < pConstraints.size; ++iConstraint) + { + CType pConstraint = pConstraints.Item(iConstraint); + if (pConstraint == pType) + { + return true; + } + if (pConstraint.IsTypeParameterType() && + pConstraint.AsTypeParameterType().DependsOn(pType)) + { + return true; + } + } + return false; + } + + // Forward calls into the symbol. + public bool Covariant { get { return m_pTypeParameterSymbol.Covariant; } } + public bool Invariant { get { return m_pTypeParameterSymbol.Invariant; } } + public bool Contravariant { get { return m_pTypeParameterSymbol.Contravariant; } } + public bool IsValueType() { return m_pTypeParameterSymbol.IsValueType(); } + public bool IsReferenceType() { return m_pTypeParameterSymbol.IsReferenceType(); } + public bool IsNonNullableValueType() { return m_pTypeParameterSymbol.IsNonNullableValueType(); } + public bool HasNewConstraint() { return m_pTypeParameterSymbol.HasNewConstraint(); } + public bool HasRefConstraint() { return m_pTypeParameterSymbol.HasRefConstraint(); } + public bool HasValConstraint() { return m_pTypeParameterSymbol.HasValConstraint(); } + public bool IsMethodTypeParameter() { return m_pTypeParameterSymbol.IsMethodTypeParameter(); } + public int GetIndexInOwnParameters() { return m_pTypeParameterSymbol.GetIndexInOwnParameters(); } + public int GetIndexInTotalParameters() { return m_pTypeParameterSymbol.GetIndexInTotalParameters(); } + public TypeArray GetBounds() { return m_pTypeParameterSymbol.GetBounds(); } + public TypeArray GetInterfaceBounds() { return m_pTypeParameterSymbol.GetInterfaceBounds(); } + public AggregateType GetEffectiveBaseClass() { return m_pTypeParameterSymbol.GetEffectiveBaseClass(); } + + private TypeParameterSymbol m_pTypeParameterSymbol; + } +} diff --git a/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/Types/TypeTable.cs b/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/Types/TypeTable.cs new file mode 100644 index 000000000..56b930bcd --- /dev/null +++ b/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/Types/TypeTable.cs @@ -0,0 +1,208 @@ +// ==++== +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// ==--== + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using Microsoft.CSharp.RuntimeBinder.Syntax; + +namespace Microsoft.CSharp.RuntimeBinder.Semantics +{ + struct KeyPair : IEquatable> + { + Key1 m_pKey1; + Key2 m_pKey2; + + public KeyPair(Key1 pKey1, Key2 pKey2) + { + m_pKey1 = pKey1; + m_pKey2 = pKey2; + } + + public bool Equals(KeyPair other) + { + return object.Equals(this.m_pKey1, other.m_pKey1) + && object.Equals(this.m_pKey2, other.m_pKey2); + } + + public override bool Equals(object obj) + { + if (!(obj is KeyPair)) return false; + return this.Equals((KeyPair)obj); + } + + public override int GetHashCode() + { + return (this.m_pKey1 == null ? 0 : this.m_pKey1.GetHashCode()) + + (this.m_pKey2 == null ? 0 : this.m_pKey2.GetHashCode()); + } + } + + class TypeTable + { + // Two way hashes + Dictionary, AggregateType> m_pAggregateTable; + Dictionary, ErrorType> m_pErrorWithTypeParentTable; + Dictionary, ErrorType> m_pErrorWithNamespaceParentTable; + Dictionary, ArrayType> m_pArrayTable; + Dictionary, ParameterModifierType> m_pParameterModifierTable; + + // One way hashes + Dictionary m_pPointerTable; + Dictionary m_pNullableTable; + Dictionary m_pTypeParameterTable; + + public TypeTable() + { + this.m_pAggregateTable = new Dictionary, AggregateType>(); + this.m_pErrorWithNamespaceParentTable = new Dictionary, ErrorType>(); + this.m_pErrorWithTypeParentTable = new Dictionary, ErrorType>(); + this.m_pArrayTable = new Dictionary, ArrayType>(); + this.m_pParameterModifierTable = new Dictionary, ParameterModifierType>(); + this.m_pPointerTable = new Dictionary(); + this.m_pNullableTable = new Dictionary(); + this.m_pTypeParameterTable = new Dictionary(); + } + + public AggregateType LookupAggregate(Name pName, AggregateSymbol pAggregate) + { + var key = new KeyPair(pAggregate, pName); + AggregateType result; + if (m_pAggregateTable.TryGetValue(key, out result)) + { + return result; + } + return null; + } + + public void InsertAggregate( + Name pName, + AggregateSymbol pAggregateSymbol, + AggregateType pAggregate) + { + Debug.Assert(LookupAggregate(pName, pAggregateSymbol) == null); + m_pAggregateTable.Add(new KeyPair(pAggregateSymbol, pName), pAggregate); + } + + public ErrorType LookupError(Name pName, CType pParentType) + { + var key = new KeyPair(pParentType, pName); + ErrorType result; + if (m_pErrorWithTypeParentTable.TryGetValue(key, out result)) + { + return result; + } + return null; + } + + public ErrorType LookupError(Name pName, AssemblyQualifiedNamespaceSymbol pParentNS) + { + var key = new KeyPair(pParentNS, pName); + ErrorType result; + if (m_pErrorWithNamespaceParentTable.TryGetValue(key, out result)) + { + return result; + } + return null; + } + + public void InsertError(Name pName, CType pParentType, ErrorType pError) + { + Debug.Assert(LookupError(pName, pParentType) == null); + m_pErrorWithTypeParentTable.Add(new KeyPair(pParentType, pName), pError); + } + + public void InsertError(Name pName, AssemblyQualifiedNamespaceSymbol pParentNS, ErrorType pError) + { + Debug.Assert(LookupError(pName, pParentNS) == null); + m_pErrorWithNamespaceParentTable.Add(new KeyPair(pParentNS, pName), pError); + } + + public ArrayType LookupArray(Name pName, CType pElementType) + { + var key = new KeyPair(pElementType, pName); + ArrayType result; + if (m_pArrayTable.TryGetValue(key, out result)) + { + return result; + } + return null; + } + + public void InsertArray(Name pName, CType pElementType, ArrayType pArray) + { + Debug.Assert(LookupArray(pName, pElementType) == null); + m_pArrayTable.Add(new KeyPair(pElementType, pName), pArray); + } + + public ParameterModifierType LookupParameterModifier(Name pName, CType pElementType) + { + var key = new KeyPair(pElementType, pName); + ParameterModifierType result; + if (m_pParameterModifierTable.TryGetValue(key, out result)) + { + return result; + } + return null; + } + + public void InsertParameterModifier( + Name pName, + CType pElementType, + ParameterModifierType pParameterModifier) + { + Debug.Assert(LookupParameterModifier(pName, pElementType) == null); + m_pParameterModifierTable.Add(new KeyPair(pElementType, pName), pParameterModifier); + } + + public PointerType LookupPointer(CType pElementType) + { + PointerType result; + if (m_pPointerTable.TryGetValue(pElementType, out result)) + { + return result; + } + return null; + } + + public void InsertPointer(CType pElementType, PointerType pPointer) + { + m_pPointerTable.Add(pElementType, pPointer); + } + + public NullableType LookupNullable(CType pUnderlyingType) + { + NullableType result; + if (m_pNullableTable.TryGetValue(pUnderlyingType, out result)) + { + return result; + } + return null; + } + + public void InsertNullable(CType pUnderlyingType, NullableType pNullable) + { + m_pNullableTable.Add(pUnderlyingType, pNullable); + } + + public TypeParameterType LookupTypeParameter(TypeParameterSymbol pTypeParameterSymbol) + { + TypeParameterType result; + if (m_pTypeParameterTable.TryGetValue(pTypeParameterSymbol, out result)) + { + return result; + } + return null; + } + + public void InsertTypeParameter( + TypeParameterSymbol pTypeParameterSymbol, + TypeParameterType pTypeParameter) + { + m_pTypeParameterTable.Add(pTypeParameterSymbol, pTypeParameter); + } + } +} \ No newline at end of file diff --git a/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/Types/VoidType.cs b/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/Types/VoidType.cs new file mode 100644 index 000000000..b6eb1e3f4 --- /dev/null +++ b/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/Types/VoidType.cs @@ -0,0 +1,16 @@ +// ==++== +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// ==--== + +namespace Microsoft.CSharp.RuntimeBinder.Semantics +{ + // ---------------------------------------------------------------------------- + // VoidType - represents the type "void". + // ---------------------------------------------------------------------------- + + class VoidType : CType + { + } +} diff --git a/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/UnaOpSig.cs b/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/UnaOpSig.cs new file mode 100644 index 000000000..20bcb3231 --- /dev/null +++ b/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/UnaOpSig.cs @@ -0,0 +1,91 @@ +// ==++== +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// ==--== + +using System.Diagnostics; +using Microsoft.CSharp.RuntimeBinder.Syntax; + +namespace Microsoft.CSharp.RuntimeBinder.Semantics +{ + internal partial class ExpressionBinder + { + protected class UnaOpSig + { + public UnaOpSig() + { + } + public UnaOpSig(PredefinedType pt, UnaOpMask grfuom, int cuosSkip, PfnBindUnaOp pfn, UnaOpFuncKind fnkind) + { + this.pt = pt; + this.grfuom = grfuom; + this.cuosSkip = cuosSkip; + this.pfn = pfn; + this.fnkind = fnkind; + } + public PredefinedType pt; + public UnaOpMask grfuom; + public int cuosSkip; + public PfnBindUnaOp pfn; + public UnaOpFuncKind fnkind; + } + + protected class UnaOpFullSig : UnaOpSig + { + private LiftFlags grflt; + private CType type; + + public UnaOpFullSig(CType type, PfnBindUnaOp pfn, LiftFlags grflt, UnaOpFuncKind fnkind) + { + this.pt = PredefinedType.PT_UNDEFINEDINDEX; + this.grfuom = UnaOpMask.None; + this.cuosSkip = 0; + this.pfn = pfn; + this.type = type; + this.grflt = grflt; + this.fnkind = fnkind; + } + /*************************************************************************************************** + Set the values of the UnaOpFullSig from the given UnaOpSig. The ExpressionBinder is needed to get + the predefined type. Returns true iff the predef type is found. + ***************************************************************************************************/ + public UnaOpFullSig(ExpressionBinder fnc, UnaOpSig uos) + { + this.pt = uos.pt; + this.grfuom = uos.grfuom; + this.cuosSkip = uos.cuosSkip; + this.pfn = uos.pfn; + this.fnkind = uos.fnkind; + type = pt != PredefinedType.PT_UNDEFINEDINDEX ? fnc.GetOptPDT(pt) : null; + this.grflt = LiftFlags.None; + } + public bool FPreDef() + { + return pt != PredefinedType.PT_UNDEFINEDINDEX; + } + public bool isLifted() + { + // This is a unary operator, so the second argument should be neither lifted nor converted. + Debug.Assert((grflt & LiftFlags.Lift2) == 0); + Debug.Assert((grflt & LiftFlags.Convert2) == 0); + if (grflt == LiftFlags.None) + { + return false; + } + // We can't both convert and lift. + Debug.Assert(((grflt & LiftFlags.Lift1) == 0) || ((grflt & LiftFlags.Convert1) == 0)); + return true; + } + public bool Convert() + { + return (grflt & LiftFlags.Convert1) != 0; + } + + public new CType GetType() + { + return type; + } + } + } +} diff --git a/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/UtilityTypeExtensions.cs b/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/UtilityTypeExtensions.cs new file mode 100644 index 000000000..4e1edf809 --- /dev/null +++ b/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/UtilityTypeExtensions.cs @@ -0,0 +1,66 @@ +// ==++== +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// ==--== + +using System.Collections.Generic; +using System.Diagnostics; + +namespace Microsoft.CSharp.RuntimeBinder.Semantics +{ + internal static class UtilityTypeExtensions + { + public static IEnumerable InterfaceAndBases(this AggregateType type) + { + Debug.Assert(type != null); + yield return type; + foreach (AggregateType t in type.GetIfacesAll().ToArray()) + yield return t; + } + + public static IEnumerable AllConstraintInterfaces(this TypeArray constraints) + { + Debug.Assert(constraints != null); + foreach (AggregateType c in constraints.ToArray()) + foreach (AggregateType t in c.InterfaceAndBases()) + yield return t; + } + + public static IEnumerable TypeAndBaseClasses(this AggregateType type) + { + Debug.Assert(type != null); + AggregateType t = type; + while (t != null) + { + yield return t; + t = t.GetBaseClass(); + } + } + + public static IEnumerable TypeAndBaseClassInterfaces(this AggregateType type) + { + Debug.Assert(type != null); + foreach (AggregateType b in type.TypeAndBaseClasses()) + foreach (AggregateType t in b.GetIfacesAll().ToArray()) + yield return t; + } + + public static IEnumerable AllPossibleInterfaces(this CType type) + { + Debug.Assert(type != null); + if (type.IsAggregateType()) + { + foreach (CType t in type.AsAggregateType().TypeAndBaseClassInterfaces()) + yield return t; + } + else if (type.IsTypeParameterType()) + { + foreach (CType t in type.AsTypeParameterType().GetEffectiveBaseClass().TypeAndBaseClassInterfaces()) + yield return t; + foreach (CType t in type.AsTypeParameterType().GetInterfaceBounds().AllConstraintInterfaces()) + yield return t; + } + } + } +} \ No newline at end of file diff --git a/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/WithType.cs b/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/WithType.cs new file mode 100644 index 000000000..b496d8339 --- /dev/null +++ b/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Semantics/WithType.cs @@ -0,0 +1,296 @@ +// ==++== +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// ==--== + +using System.Diagnostics; + +namespace Microsoft.CSharp.RuntimeBinder.Semantics +{ + /* + // =========================================================================== + Defines structs that package an aggregate member together with + generic type argument information. + // ===========================================================================*/ + /****************************************************************************** + SymWithType and its cousins. These package an aggregate member (field, + prop, event, or meth) together with the particular instantiation of the + aggregate (the AggregateType). + + The default constructor does nothing so these are not safe to use + uninitialized. Note that when they are used as member of an EXPR they + are automatically zero filled by newExpr. + ******************************************************************************/ + internal class SymWithType + { + AggregateType ats; + Symbol sym; + + public SymWithType() + { + } + + public SymWithType(Symbol sym, AggregateType ats) + { + Set(sym, ats); + } + + public virtual void Clear() + { + this.sym = null; + this.ats = null; + } + + public AggregateType Ats + { + get { return this.ats; } + } + + public Symbol Sym + { + get { return this.sym; } + } + + public new AggregateType GetType() + { + // This conflicts with object.GetType. Turn every usage of this + // into a get on Ats. + return Ats; + } + + public static bool operator ==(SymWithType swt1, SymWithType swt2) + { + if (object.ReferenceEquals(swt1, swt2)) + { + return true; + } + else if (object.ReferenceEquals(swt1, null)) + { + return swt2.sym == null; + } + else if (object.ReferenceEquals(swt2, null)) + { + return swt1.sym == null; + } + return swt1.Sym == swt2.Sym && swt1.Ats == swt2.Ats; + } + + public static bool operator !=(SymWithType swt1, SymWithType swt2) + { + if (object.ReferenceEquals(swt1, swt2)) + { + return false; + } + else if (object.ReferenceEquals(swt1, null)) + { + return swt2.sym != null; + } + else if (object.ReferenceEquals(swt2, null)) + { + return swt1.sym != null; + } + return swt1.Sym != swt2.Sym || swt1.Ats != swt2.Ats; + } + + public override bool Equals(object obj) + { + SymWithType other = obj as SymWithType; + if (other == null) return false; + return this.Sym == other.Sym && this.Ats == other.Ats; + } + + public override int GetHashCode() + { + return (this.Sym != null ? this.Sym.GetHashCode() : 0) + + (this.Ats != null ? this.Ats.GetHashCode() : 0); + } + + // The SymWithType is considered NULL iff the Symbol is NULL. + public static implicit operator bool(SymWithType swt) + { + return swt != null; + } + + // These assert that the Symbol is of the correct type. + public MethodOrPropertySymbol MethProp() + { + return this.Sym as MethodOrPropertySymbol; + } + + public MethodSymbol Meth() + { + return this.Sym as MethodSymbol; + } + + public PropertySymbol Prop() + { + return this.Sym as PropertySymbol; + } + + public FieldSymbol Field() + { + return this.Sym as FieldSymbol; + } + + public EventSymbol Event() + { + return this.Sym as EventSymbol; + } + + public void Set(Symbol sym, AggregateType ats) + { + if (sym == null) + ats = null; + Debug.Assert(ats == null || sym.parent == ats.getAggregate()); + this.sym = sym; + this.ats = ats; + } + } + + internal class MethPropWithType : SymWithType + { + public MethPropWithType() + { + } + + public MethPropWithType(MethodOrPropertySymbol mps, AggregateType ats) + { + Set(mps, ats); + } + } + + internal class MethWithType : MethPropWithType + { + public MethWithType() + { + } + + public MethWithType(MethodSymbol meth, AggregateType ats) + { + Set(meth, ats); + } + } + + internal class PropWithType : MethPropWithType + { + public PropWithType() + { } + + public PropWithType(PropertySymbol prop, AggregateType ats) + { + Set(prop, ats); + } + + public PropWithType(SymWithType swt) + { + Set(swt.Sym as PropertySymbol, swt.Ats); + } + } + + internal class EventWithType : SymWithType + { + public EventWithType() + { + } + + public EventWithType(EventSymbol @event, AggregateType ats) + { + Set(@event, ats); + } + } + + internal class FieldWithType : SymWithType + { + public FieldWithType() + { + } + + public FieldWithType(FieldSymbol field, AggregateType ats) + { + Set(field, ats); + } + } + + /****************************************************************************** + MethPropWithInst and MethWithInst. These extend MethPropWithType with + the method type arguments. Properties will never have type args, but + methods and properties share a lot of code so it's convenient to allow + both here. + + The default constructor does nothing so these are not safe to use + uninitialized. Note that when they are used as member of an EXPR they + are automatically zero filled by newExpr. + ******************************************************************************/ + + internal class MethPropWithInst : MethPropWithType + { + public TypeArray TypeArgs { get; private set; } + + public MethPropWithInst() + { + Set(null, null, null); + } + + public MethPropWithInst(MethodOrPropertySymbol mps, AggregateType ats) + : this(mps, ats, null) + { + } + + public MethPropWithInst(MethodOrPropertySymbol mps, AggregateType ats, TypeArray typeArgs) + { + Set(mps, ats, typeArgs); + } + + public override void Clear() + { + base.Clear(); + TypeArgs = null; + } +#if false + bool operator ==(const MethPropWithInst & mpwi) const + { + return sym == mpwi.sym && ats == mpwi.ats && typeArgs == mpwi.typeArgs; + } + bool operator !=(const MethPropWithInst & mpwi) const + { + return sym != mpwi.sym || ats != mpwi.ats || typeArgs != mpwi.typeArgs; + } +#endif + + public void Set(MethodOrPropertySymbol mps, AggregateType ats, TypeArray typeArgs) + { + if (mps == null) + { + ats = null; + typeArgs = null; + } + Debug.Assert(ats == null || mps != null && mps.getClass() == ats.getAggregate()); +#if false + Debug.Assert(typeArgs == null || typeArgs.Size == 0 || mps != null && mps.IsMethodSymbol()); + Debug.Assert(typeArgs == null|| !mps.IsMethodSymbol() || mps.AsMethodSymbol().typeVars.Size == typeArgs.Size); +#endif + base.Set(mps, ats); + this.TypeArgs = typeArgs; + } + } + + internal class MethWithInst : MethPropWithInst + { + public MethWithInst() + { + } + public MethWithInst(MethodSymbol meth, AggregateType ats) + : this(meth, ats, null) + { + } + public MethWithInst(MethodSymbol meth, AggregateType ats, TypeArray typeArgs) + { + Set(meth, ats, typeArgs); + } + public MethWithInst(MethPropWithInst mpwi) + { + Set(mpwi.Sym.AsMethodSymbol(), mpwi.Ats, mpwi.TypeArgs); + } + } +} diff --git a/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Syntax/KnownName.cs b/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Syntax/KnownName.cs new file mode 100644 index 000000000..5a64abf21 --- /dev/null +++ b/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Syntax/KnownName.cs @@ -0,0 +1,188 @@ +// ==++== +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// ==--== + +using System.Diagnostics; +using System.Threading; + +namespace Microsoft.CSharp.RuntimeBinder.Syntax +{ + internal partial class NameManager + { + class KnownName : Name + { + PredefinedName id = PredefinedName.PN_COUNT; + + public KnownName(string text) + : base(text) + { + } + + public KnownName(string text, PredefinedName id) + : base(text) + { + this.id = id; + } + + public override PredefinedName PredefinedName + { + get { return this.id; } + } + } + + private static NameTable _knownNames; + + private void InitKnownNames() + { + if (_knownNames == null) + { + NameTable tmp = new NameTable(); + + // add all predefined names + Debug.Assert(_predefinedNames.Length == (int)PredefinedName.PN_COUNT); + for (int i = 0, n = _predefinedNames.Length; i < n; i++) + { + Debug.Assert((int)_predefinedNames[i].PredefinedName == i); + Name name = _predefinedNames[i]; + tmp.Add(name); + } + + // add all other names + foreach (KnownName name in _otherNames) + { + tmp.Add(name); + } + + Interlocked.CompareExchange(ref _knownNames, tmp, null); + } + } + + private static KnownName[] _predefinedNames = new KnownName[(int)PredefinedName.PN_COUNT] { + new KnownName(".ctor", PredefinedName.PN_CTOR), + new KnownName("Finalize", PredefinedName.PN_DTOR), + new KnownName(".cctor", PredefinedName.PN_STATCTOR), + new KnownName("*", PredefinedName.PN_PTR), + new KnownName("?*", PredefinedName.PN_NUB), + new KnownName("#", PredefinedName.PN_OUTPARAM), + new KnownName("&", PredefinedName.PN_REFPARAM), + new KnownName("[X\001", PredefinedName.PN_ARRAY0), + new KnownName("[X\002", PredefinedName.PN_ARRAY1), + new KnownName("[X\003", PredefinedName.PN_ARRAY2), + new KnownName("[G\001", PredefinedName.PN_GARRAY0), + new KnownName("[G\002", PredefinedName.PN_GARRAY1), + new KnownName("[G\003", PredefinedName.PN_GARRAY2), + new KnownName("Invoke", PredefinedName.PN_INVOKE), + new KnownName("Length", PredefinedName.PN_LENGTH), + new KnownName("Item", PredefinedName.PN_INDEXER), + new KnownName("$Item$", PredefinedName.PN_INDEXERINTERNAL), + new KnownName("Combine", PredefinedName.PN_COMBINE), + new KnownName("Remove", PredefinedName.PN_REMOVE), + new KnownName("op_Explicit", PredefinedName.PN_OPEXPLICITMN), + new KnownName("op_Implicit", PredefinedName.PN_OPIMPLICITMN), + new KnownName("op_UnaryPlus", PredefinedName.PN_OPUNARYPLUS), + new KnownName("op_UnaryNegation", PredefinedName.PN_OPUNARYMINUS), + new KnownName("op_OnesComplement", PredefinedName.PN_OPCOMPLEMENT), + new KnownName("op_Increment", PredefinedName.PN_OPINCREMENT), + new KnownName("op_Decrement", PredefinedName.PN_OPDECREMENT), + new KnownName("op_Addition", PredefinedName.PN_OPPLUS), + new KnownName("op_Subtraction", PredefinedName.PN_OPMINUS), + new KnownName("op_Multiply", PredefinedName.PN_OPMULTIPLY), + new KnownName("op_Division", PredefinedName.PN_OPDIVISION), + new KnownName("op_Modulus", PredefinedName.PN_OPMODULUS), + new KnownName("op_ExclusiveOr", PredefinedName.PN_OPXOR), + new KnownName("op_BitwiseAnd", PredefinedName.PN_OPBITWISEAND), + new KnownName("op_BitwiseOr", PredefinedName.PN_OPBITWISEOR), + new KnownName("op_LeftShift", PredefinedName.PN_OPLEFTSHIFT), + new KnownName("op_RightShift", PredefinedName.PN_OPRIGHTSHIFT), + new KnownName("op_Equals", PredefinedName.PN_OPEQUALS), + new KnownName("op_Compare", PredefinedName.PN_OPCOMPARE), + new KnownName("op_Equality", PredefinedName.PN_OPEQUALITY), + new KnownName("op_Inequality", PredefinedName.PN_OPINEQUALITY), + new KnownName("op_GreaterThan", PredefinedName.PN_OPGREATERTHAN), + new KnownName("op_LessThan", PredefinedName.PN_OPLESSTHAN), + new KnownName("op_GreaterThanOrEqual", PredefinedName.PN_OPGREATERTHANOREQUAL), + new KnownName("op_LessThanOrEqual", PredefinedName.PN_OPLESSTHANOREQUAL), + new KnownName("op_True", PredefinedName.PN_OPTRUE), + new KnownName("op_False", PredefinedName.PN_OPFALSE), + new KnownName("op_LogicalNot", PredefinedName.PN_OPNEGATION), + new KnownName("Concat", PredefinedName.PN_CONCAT), + new KnownName("Add", PredefinedName.PN_ADD), + new KnownName("get_Length", PredefinedName.PN_GETLENGTH), + new KnownName("get_Chars", PredefinedName.PN_GETCHARS), + new KnownName("CreateDelegate", PredefinedName.PN_CREATEDELEGATE), + new KnownName("FixedElementField", PredefinedName.PN_FIXEDELEMENT), + new KnownName("HasValue", PredefinedName.PN_HASVALUE), + new KnownName("get_HasValue", PredefinedName.PN_GETHASVALUE), + new KnownName("Value", PredefinedName.PN_CAP_VALUE), + new KnownName("get_Value", PredefinedName.PN_GETVALUE), + new KnownName("GetValueOrDefault", PredefinedName.PN_GET_VALUE_OR_DEF), + new KnownName("?", PredefinedName.PN_MISSING), + new KnownName("", PredefinedName.PN_MISSINGSYM), + new KnownName("Lambda", PredefinedName.PN_LAMBDA), + new KnownName("Parameter", PredefinedName.PN_PARAMETER), + new KnownName("Constant", PredefinedName.PN_CONSTANT), + new KnownName("Convert", PredefinedName.PN_CONVERT), + new KnownName("ConvertChecked", PredefinedName.PN_CONVERTCHECKED), + new KnownName("AddChecked", PredefinedName.PN_ADDCHECKED), + new KnownName("Divide", PredefinedName.PN_DIVIDE), + new KnownName("Modulo", PredefinedName.PN_MODULO), + new KnownName("Multiply", PredefinedName.PN_MULTIPLY), + new KnownName("MultiplyChecked", PredefinedName.PN_MULTIPLYCHECKED), + new KnownName("Subtract", PredefinedName.PN_SUBTRACT), + new KnownName("SubtractChecked", PredefinedName.PN_SUBTRACTCHECKED), + new KnownName("And", PredefinedName.PN_AND), + new KnownName("Or", PredefinedName.PN_OR), + new KnownName("ExclusiveOr", PredefinedName.PN_EXCLUSIVEOR), + new KnownName("LeftShift", PredefinedName.PN_LEFTSHIFT), + new KnownName("RightShift", PredefinedName.PN_RIGHTSHIFT), + new KnownName("AndAlso", PredefinedName.PN_ANDALSO), + new KnownName("OrElse", PredefinedName.PN_ORELSE), + new KnownName("Equal", PredefinedName.PN_EQUAL), + new KnownName("NotEqual", PredefinedName.PN_NOTEQUAL), + new KnownName("GreaterThanOrEqual", PredefinedName.PN_GREATERTHANOREQUAL), + new KnownName("GreaterThan", PredefinedName.PN_GREATERTHAN), + new KnownName("LessThan", PredefinedName.PN_LESSTHAN), + new KnownName("LessThanOrEqual", PredefinedName.PN_LESSTHANOREQUAL), + new KnownName("ArrayIndex", PredefinedName.PN_ARRAYINDEX), + new KnownName("Assign", PredefinedName.PN_ASSIGN), + new KnownName("Condition", PredefinedName.PN_CONDITION), + new KnownName("Field", PredefinedName.PN_CAP_FIELD), + new KnownName("Call", PredefinedName.PN_CALL), + new KnownName("New", PredefinedName.PN_NEW), + new KnownName("Quote", PredefinedName.PN_QUOTE), + new KnownName("ArrayLength", PredefinedName.PN_ARRAYLENGTH), + new KnownName("UnaryPlus", PredefinedName.PN_PLUS), + new KnownName("Negate", PredefinedName.PN_NEGATE), + new KnownName("NegateChecked", PredefinedName.PN_NEGATECHECKED), + new KnownName("Not", PredefinedName.PN_NOT), + new KnownName("NewArrayInit", PredefinedName.PN_NEWARRAYINIT), + new KnownName("Property", PredefinedName.PN_EXPRESSION_PROPERTY), + new KnownName("AddEventHandler", PredefinedName.PN_ADDEVENTHANDLER), + new KnownName("RemoveEventHandler", PredefinedName.PN_REMOVEEVENTHANDLER), + new KnownName("InvocationList", PredefinedName.PN_INVOCATIONLIST), + new KnownName("GetOrCreateEventRegistrationTokenTable", PredefinedName.PN_GETORCREATEEVENTREGISTRATIONTOKENTABLE) + }; + + private static KnownName[] _otherNames = new KnownName[] { + new KnownName("true"), + new KnownName("false"), + new KnownName("null"), + new KnownName("base"), + new KnownName("this"), + new KnownName("explicit"), + new KnownName("implicit"), + new KnownName("__arglist"), + new KnownName("__makeref"), + new KnownName("__reftype"), + new KnownName("__refvalue"), + new KnownName("as"), + new KnownName("checked"), + new KnownName("is"), + new KnownName("typeof"), + new KnownName("unchecked"), + new KnownName("void"), + }; + } +} diff --git a/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Syntax/NameManager.cs b/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Syntax/NameManager.cs new file mode 100644 index 000000000..d4c167a1f --- /dev/null +++ b/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Syntax/NameManager.cs @@ -0,0 +1,62 @@ +// ==++== +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// ==--== + +namespace Microsoft.CSharp.RuntimeBinder.Syntax +{ + internal partial class NameManager + { + private NameTable names; + + internal NameManager() + : this(new NameTable()) + { + } + + internal NameManager(NameTable nameTable) + { + names = nameTable; + this.InitKnownNames(); + } + + internal Name Add(string key) + { + if (key == null) + { + throw Error.InternalCompilerError(); + } + Name name = _knownNames.Lookup(key); + if (name == null) + { + name = this.names.Add(key); + } + return name; + } + + internal Name Lookup(string key) + { + if (key == null) + { + throw Error.InternalCompilerError(); + } + Name name = _knownNames.Lookup(key); + if (name == null) + { + name = this.names.Lookup(key); + } + return name; + } + + internal Name GetPredefinedName(PredefinedName id) + { + return _predefinedNames[(int)id]; + } + + internal Name GetPredefName(PredefinedName id) + { + return GetPredefinedName(id); + } + } +} diff --git a/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Syntax/NameTable.cs b/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Syntax/NameTable.cs new file mode 100644 index 000000000..ce0b3e7bf --- /dev/null +++ b/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Syntax/NameTable.cs @@ -0,0 +1,130 @@ +// ==++== +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// ==--== + +namespace Microsoft.CSharp.RuntimeBinder.Syntax +{ + internal class NameTable + { + class Entry + { + internal readonly Name name; + internal readonly int hashCode; + internal Entry next; + + internal Entry(Name name, int hashCode, Entry next) + { + this.name = name; + this.hashCode = hashCode; + this.next = next; + } + } + + Entry[] entries; + int count; + int mask; + int hashCodeRandomizer; + + internal NameTable() + { + mask = 31; + entries = new Entry[mask + 1]; + //hashCodeRandomizer = Environment.TickCount; + hashCodeRandomizer = 0; + } + + public Name Add(string key) + { + int hashCode = ComputeHashCode(key); + for (Entry e = entries[hashCode & mask]; e != null; e = e.next) + { + if (e.hashCode == hashCode && e.name.Text.Equals(key)) + { + return e.name; + } + } + return this.AddEntry(new Name(key), hashCode); + } + + internal void Add(Name name) + { + int hashCode = ComputeHashCode(name.Text); + // make sure it doesn't already exist + for (Entry e = entries[hashCode & mask]; e != null; e = e.next) + { + if (e.hashCode == hashCode && e.name.Text.Equals(name.Text)) + { + throw Error.InternalCompilerError(); + } + } + this.AddEntry(name, hashCode); + } + + public Name Lookup(string key) + { + int hashCode = ComputeHashCode(key); + for (Entry e = entries[hashCode & mask]; e != null; e = e.next) + { + if (e.hashCode == hashCode && e.name.Text.Equals(key)) + { + return e.name; + } + } + return null; + } + + private int ComputeHashCode(string key) + { + int len = key.Length; + int hashCode = len + hashCodeRandomizer; + // use key.Length to eliminate the rangecheck + for (int i = 0; i < key.Length; i++) + { + hashCode += (hashCode << 7) ^ key[i]; + } + // mix it a bit more + hashCode -= hashCode >> 17; + hashCode -= hashCode >> 11; + hashCode -= hashCode >> 5; + return hashCode; + } + + private Name AddEntry(Name name, int hashCode) + { + int index = hashCode & mask; + Entry e = new Entry(name, hashCode, entries[index]); + this.entries[index] = e; + if (count++ == mask) + { + this.Grow(); + } + return e.name; + } + + private void Grow() + { + int newMask = mask * 2 + 1; + Entry[] oldEntries = entries; + Entry[] newEntries = new Entry[newMask + 1]; + + // use oldEntries.Length to eliminate the rangecheck + for (int i = 0; i < oldEntries.Length; i++) + { + Entry e = oldEntries[i]; + while (e != null) + { + int newIndex = e.hashCode & newMask; + Entry tmp = e.next; + e.next = newEntries[newIndex]; + newEntries[newIndex] = e; + e = tmp; + } + } + + entries = newEntries; + mask = newMask; + } + } +} diff --git a/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Syntax/Names.cs b/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Syntax/Names.cs new file mode 100644 index 000000000..95046d1ec --- /dev/null +++ b/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Syntax/Names.cs @@ -0,0 +1,33 @@ +// ==++== +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// ==--== + +namespace Microsoft.CSharp.RuntimeBinder.Syntax +{ + internal class Name + { + private string _text; + + public Name(string text) + { + this._text = text; + } + + public string Text + { + get { return this._text; } + } + + public virtual PredefinedName PredefinedName + { + get { return PredefinedName.PN_COUNT; } + } + + public override string ToString() + { + return this._text; + } + } +} diff --git a/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Syntax/Operators.cs b/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Syntax/Operators.cs new file mode 100644 index 000000000..637c638eb --- /dev/null +++ b/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Syntax/Operators.cs @@ -0,0 +1,92 @@ +// ==++== +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// ==--== + + +namespace Microsoft.CSharp.RuntimeBinder.Syntax +{ + internal enum OperatorKind : uint + { + OP_NONE, + + // Binary + OP_ASSIGN, + OP_ADDEQ, + OP_SUBEQ, + OP_MULEQ, + OP_DIVEQ, + OP_MODEQ, + OP_ANDEQ, + OP_XOREQ, + OP_OREQ, + OP_LSHIFTEQ, + OP_RSHIFTEQ, + OP_QUESTION, + OP_VALORDEF, + OP_LOGOR, + OP_LOGAND, + OP_BITOR, + OP_BITXOR, + OP_BITAND, + OP_EQ, + OP_NEQ, + OP_LT, + OP_LE, + OP_GT, + OP_GE, + OP_IS, + OP_AS, + OP_LSHIFT, + OP_RSHIFT, + OP_ADD, + OP_SUB, + OP_MUL, + OP_DIV, + OP_MOD, + + // Unary + OP_NOP, + OP_UPLUS, + OP_NEG, + OP_BITNOT, + OP_LOGNOT, + OP_PREINC, + OP_PREDEC, + OP_TYPEOF, + OP_CHECKED, + OP_UNCHECKED, + + OP_MAKEREFANY, + OP_REFVALUE, + OP_REFTYPE, + OP_ARGS, + + OP_CAST, + OP_INDIR, + OP_ADDR, + + OP_COLON, + OP_THIS, + OP_BASE, + OP_NULL, + OP_TRUE, + OP_FALSE, + OP_CALL, + OP_DEREF, + OP_PAREN, + OP_POSTINC, + OP_POSTDEC, + OP_DOT, + OP_IMPLICIT, + OP_EXPLICIT, + + OP_EQUALS, + OP_COMPARE, + + OP_DEFAULT, + + OP_LAST + } +} \ No newline at end of file diff --git a/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Syntax/PredefinedName.cs b/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Syntax/PredefinedName.cs new file mode 100644 index 000000000..8689bbfdd --- /dev/null +++ b/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Syntax/PredefinedName.cs @@ -0,0 +1,124 @@ +// ==++== +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// ==--== + +namespace Microsoft.CSharp.RuntimeBinder.Syntax +{ + internal enum PredefinedName + { + PN_CTOR, + PN_DTOR, + PN_STATCTOR, + PN_PTR, + PN_NUB, + PN_OUTPARAM, + PN_REFPARAM, + PN_ARRAY0, + PN_ARRAY1, + PN_ARRAY2, + PN_GARRAY0, + PN_GARRAY1, + PN_GARRAY2, + PN_INVOKE, + PN_LENGTH, + PN_INDEXER, + PN_INDEXERINTERNAL, + PN_COMBINE, + PN_REMOVE, + + // internal method name for conversion operators + // these get mangled when converted to CLS names + PN_OPEXPLICITMN, + PN_OPIMPLICITMN, + + // CLS method names for user defined operators + PN_OPUNARYPLUS, + PN_OPUNARYMINUS, + PN_OPCOMPLEMENT, + PN_OPINCREMENT, + PN_OPDECREMENT, + PN_OPPLUS, + PN_OPMINUS, + PN_OPMULTIPLY, + PN_OPDIVISION, + PN_OPMODULUS, + PN_OPXOR, + PN_OPBITWISEAND, + PN_OPBITWISEOR, + PN_OPLEFTSHIFT, + PN_OPRIGHTSHIFT, + PN_OPEQUALS, + PN_OPCOMPARE, + PN_OPEQUALITY, + PN_OPINEQUALITY, + PN_OPGREATERTHAN, + PN_OPLESSTHAN, + PN_OPGREATERTHANOREQUAL, + PN_OPLESSTHANOREQUAL, + PN_OPTRUE, + PN_OPFALSE, + PN_OPNEGATION, + + PN_CONCAT, + PN_ADD, + PN_GETLENGTH, + PN_GETCHARS, + PN_CREATEDELEGATE, + PN_FIXEDELEMENT, + PN_HASVALUE, + PN_GETHASVALUE, + PN_CAP_VALUE, + PN_GETVALUE, + PN_GET_VALUE_OR_DEF, + PN_MISSING, + PN_MISSINGSYM, + PN_LAMBDA, + PN_PARAMETER, + PN_CONSTANT, + PN_CONVERT, + PN_CONVERTCHECKED, + PN_ADDCHECKED, + PN_DIVIDE, + PN_MODULO, + PN_MULTIPLY, + PN_MULTIPLYCHECKED, + PN_SUBTRACT, + PN_SUBTRACTCHECKED, + PN_AND, + PN_OR, + PN_EXCLUSIVEOR, + PN_LEFTSHIFT, + PN_RIGHTSHIFT, + PN_ANDALSO, + PN_ORELSE, + PN_EQUAL, + PN_NOTEQUAL, + PN_GREATERTHANOREQUAL, + PN_GREATERTHAN, + PN_LESSTHAN, + PN_LESSTHANOREQUAL, + PN_ARRAYINDEX, + PN_ASSIGN, + PN_CONDITION, + PN_CAP_FIELD, + PN_CALL, + PN_NEW, + PN_QUOTE, + PN_ARRAYLENGTH, + PN_PLUS, + PN_NEGATE, + PN_NEGATECHECKED, + PN_NOT, + PN_NEWARRAYINIT, + PN_EXPRESSION_PROPERTY, + + PN_ADDEVENTHANDLER, + PN_REMOVEEVENTHANDLER, + PN_INVOCATIONLIST, + PN_GETORCREATEEVENTREGISTRATIONTOKENTABLE, + + PN_COUNT, // Not a name, this is the total count of predefined names + } +} diff --git a/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Syntax/PredefinedType.cs b/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Syntax/PredefinedType.cs new file mode 100644 index 000000000..a6f37ce56 --- /dev/null +++ b/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Syntax/PredefinedType.cs @@ -0,0 +1,212 @@ +// ==++== +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// ==--== + +namespace Microsoft.CSharp.RuntimeBinder.Syntax +{ + internal enum PredefinedType : uint + { + PT_BYTE, + PT_SHORT, + PT_INT, + PT_LONG, + PT_FLOAT, + PT_DOUBLE, + PT_DECIMAL, + PT_CHAR, + PT_BOOL, + + // "simple" types are certain types that the compiler knows about for conversion and operator purposes.ses. + // Keep these first so that we can build conversion tables on their ordinals... Don't change the orderder + // of the simple types because it will mess up conversion tables. + // The following Quasi-Simple types are considered simple, except they are non-CLS compliant + PT_SBYTE, + PT_USHORT, + PT_UINT, + PT_ULONG, + + // The special "pointer-sized int" types. Note that this are not considered numeric types from the compiler's point of view -- + // they are special only in that they have special signature encodings. + PT_INTPTR, + PT_UINTPTR, + + PT_OBJECT, + + // THE ORDER ABOVE HERE IS IMPORTANT!!! It is used in tables in both fncbind and ilgen + PT_STRING, + PT_DELEGATE, + PT_MULTIDEL, + PT_ARRAY, + PT_EXCEPTION, + PT_TYPE, + PT_MONITOR, + PT_VALUE, + PT_ENUM, + PT_DATETIME, + + // predefined attribute types + PT_SECURITYATTRIBUTE, + PT_SECURITYPERMATTRIBUTE, + PT_UNVERIFCODEATTRIBUTE, + PT_DEBUGGABLEATTRIBUTE, + PT_DEBUGGABLEATTRIBUTE_DEBUGGINGMODES, +#if !SILVERLIGHT + PT_MARSHALBYREF, + PT_CONTEXTBOUND, +#endif + PT_IN, + PT_OUT, + PT_ATTRIBUTE, + PT_ATTRIBUTEUSAGE, + PT_ATTRIBUTETARGETS, + PT_OBSOLETE, + PT_CONDITIONAL, + PT_CLSCOMPLIANT, + PT_GUID, + PT_DEFAULTMEMBER, + PT_PARAMS, + PT_COMIMPORT, + PT_FIELDOFFSET, + PT_STRUCTLAYOUT, + PT_LAYOUTKIND, + PT_MARSHALAS, + PT_DLLIMPORT, + PT_INDEXERNAME, + PT_DECIMALCONSTANT, +#if !SILVERLIGHT + PT_REQUIRED, + PT_DEFAULTVALUE, +#endif + PT_UNMANAGEDFUNCTIONPOINTER, + PT_CALLINGCONVENTION, + PT_CHARSET, + + // predefined types for the BCL + PT_REFANY, +#if !SILVERLIGHT + PT_ARGITERATOR, +#endif + PT_TYPEHANDLE, + PT_FIELDHANDLE, + PT_METHODHANDLE, + PT_ARGUMENTHANDLE, +#if !SILVERLIGHT + PT_HASHTABLE, +#endif + PT_G_DICTIONARY, + PT_IASYNCRESULT, + PT_ASYNCCBDEL, + PT_SECURITYACTION, + PT_IDISPOSABLE, + PT_IENUMERABLE, + PT_IENUMERATOR, + PT_SYSTEMVOID, + PT_RUNTIMEHELPERS, + + // signature MODIFIER for marking volatile fields + PT_VOLATILEMOD, + + // Sets the CoClass for a COM interface wrapper + PT_COCLASS, + + // For instantiating a type variable. + PT_ACTIVATOR, + + // Generic variants of enumerator interfaces + PT_G_IENUMERABLE, + PT_G_IENUMERATOR, + + // Nullable + PT_G_OPTIONAL, + + // Marks a fixed buffer field + PT_FIXEDBUFFER, + + // Sets the module-level default character set marshalling + PT_DEFAULTCHARSET, + + // Used to disable string interning + PT_COMPILATIONRELAXATIONS, + + // Used to enable wrapped exceptions + PT_RUNTIMECOMPATIBILITY, + + // Used for friend assmeblies + PT_FRIENDASSEMBLY, + + // Used to hide compiler-generated code from the debugger + PT_DEBUGGERHIDDEN, + + // Used for type forwarders + PT_TYPEFORWARDER, + + // Used to warn on usage of this instead of command-line options + PT_KEYFILE, + PT_KEYNAME, + PT_DELAYSIGN, + PT_NOTSUPPORTEDEXCEPTION, + PT_THREAD, + PT_COMPILERGENERATED, + + PT_UNSAFEVALUETYPE, + + // special assembly identity attributes + PT_ASSEMBLYFLAGS, + PT_ASSEMBLYVERSION, + PT_ASSEMBLYCULTURE, + + // LINQ + PT_G_IQUERYABLE, + PT_IQUERYABLE, + PT_STRINGBUILDER, + PT_G_ICOLLECTION, + PT_G_ILIST, + PT_EXTENSION, + PT_G_EXPRESSION, + PT_EXPRESSION, + PT_LAMBDAEXPRESSION, + PT_BINARYEXPRESSION, + PT_UNARYEXPRESSION, + PT_CONDITIONALEXPRESSION, + PT_CONSTANTEXPRESSION, + PT_PARAMETEREXPRESSION, + PT_MEMBEREXPRESSION, + PT_METHODCALLEXPRESSION, + PT_NEWEXPRESSION, + PT_BINDING, + PT_MEMBERINITEXPRESSION, + PT_LISTINITEXPRESSION, + PT_TYPEBINARYEXPRESSION, + PT_NEWARRAYEXPRESSION, + PT_MEMBERASSIGNMENT, + PT_MEMBERLISTBINDING, + PT_MEMBERMEMBERBINDING, + PT_INVOCATIONEXPRESSION, + PT_FIELDINFO, + PT_METHODINFO, + PT_CONSTRUCTORINFO, + PT_PROPERTYINFO, + PT_METHODBASE, + PT_MEMBERINFO, + + PT_DEBUGGERDISPLAY, + PT_DEBUGGERBROWSABLE, + PT_DEBUGGERBROWSABLESTATE, + PT_G_EQUALITYCOMPARER, + PT_ELEMENTINITIALIZER, +#if !SILVERLIGHT + PT_UNKNOWNWRAPPER, + PT_DISPATCHWRAPPER, +#endif + PT_MISSING, + + PT_G_IREADONLYLIST, + PT_G_IREADONLYCOLLECTION, + PT_COUNT, + PT_VOID, // (special case) + + PT_UNDEFINEDINDEX = 0xffffffff, + } +} \ No newline at end of file diff --git a/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Tokens/TokenFacts.cs b/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Tokens/TokenFacts.cs new file mode 100644 index 000000000..bd4459d22 --- /dev/null +++ b/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Tokens/TokenFacts.cs @@ -0,0 +1,133 @@ +// ==++== +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// ==--== + +using System; +using System.Text; + +namespace Microsoft.CSharp.RuntimeBinder.Syntax +{ + internal static class TokenFacts + { + internal static string GetText(TokenKind kind) + { + switch (kind) + { + case TokenKind.ArgList: + return "__arglist"; + case TokenKind.MakeRef: + return "__makeref"; + case TokenKind.RefType: + return "__reftype"; + case TokenKind.RefValue: + return "__refvalue"; + case TokenKind.As: + return "as"; + case TokenKind.Base: + return "base"; + case TokenKind.Checked: + return "checked"; + case TokenKind.Explicit: + return "explicit"; + case TokenKind.False: + return "false"; + case TokenKind.Implicit: + return "implicit"; + case TokenKind.Is: + return "is"; + case TokenKind.Null: + return "null"; + case TokenKind.This: + return "this"; + case TokenKind.True: + return "true"; + case TokenKind.TypeOf: + return "typeof"; + case TokenKind.Unchecked: + return "unchecked"; + case TokenKind.Void: + return "void"; + case TokenKind.Equal: + return "="; + case TokenKind.PlusEqual: + return "+="; + case TokenKind.MinusEqual: + return "-="; + case TokenKind.SplatEqual: + return "*="; + case TokenKind.SlashEqual: + return "/="; + case TokenKind.PercentEqual: + return "%="; + case TokenKind.AndEqual: + return "&="; + case TokenKind.HatEqual: + return "^="; + case TokenKind.BarEqual: + return "|="; + case TokenKind.LeftShiftEqual: + return "<<="; + case TokenKind.RightShiftEqual: + return ">>="; + case TokenKind.Question: + return "?"; + case TokenKind.Colon: + return ":"; + case TokenKind.ColonColon: + return "::"; + case TokenKind.LogicalOr: + return "||"; + case TokenKind.LogicalAnd: + return "&&"; + case TokenKind.Bar: + return "|"; + case TokenKind.Hat: + return "^"; + case TokenKind.Ampersand: + return "&"; + case TokenKind.EqualEqual: + return "=="; + case TokenKind.NotEqual: + return "!="; + case TokenKind.LessThan: + return "<"; + case TokenKind.LessThanEqual: + return "<="; + case TokenKind.GreaterThan: + return ">"; + case TokenKind.GreaterThanEqual: + return ">="; + case TokenKind.LeftShift: + return "<<"; + case TokenKind.RightShift: + return ">>"; + case TokenKind.Plus: + return "+"; + case TokenKind.Minus: + return "-"; + case TokenKind.Splat: + return "*"; + case TokenKind.Slash: + return "/"; + case TokenKind.Percent: + return "%"; + case TokenKind.Tilde: + return "~"; + case TokenKind.Bang: + return "!"; + case TokenKind.PlusPlus: + return "++"; + case TokenKind.MinusMinus: + return "--"; + case TokenKind.Dot: + return "."; + case TokenKind.QuestionQuestion: + return "??"; + default: + throw Error.InternalCompilerError(); + } + } + } +} diff --git a/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Tokens/TokenKind.cs b/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Tokens/TokenKind.cs new file mode 100644 index 000000000..639e1710d --- /dev/null +++ b/Microsoft.CSharp/Microsoft/CSharp/ManagedBinder/Tokens/TokenKind.cs @@ -0,0 +1,72 @@ +// ==++== +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// ==--== + +using System; + +namespace Microsoft.CSharp.RuntimeBinder.Syntax +{ + internal enum TokenKind : byte + { + ArgList, + MakeRef, + RefType, + RefValue, + As, + Base, + Checked, + Explicit, + False, + Implicit, + Is, + Null, + This, + True, + TypeOf, + Unchecked, + Void, + + Equal, + PlusEqual, + MinusEqual, + SplatEqual, + SlashEqual, + PercentEqual, + AndEqual, + HatEqual, + BarEqual, + LeftShiftEqual, + RightShiftEqual, + Question, + Colon, + ColonColon, + LogicalOr, + LogicalAnd, + Bar, + Hat, + Ampersand, + EqualEqual, + NotEqual, + LessThan, + LessThanEqual, + GreaterThan, + GreaterThanEqual, + LeftShift, + RightShift, + Plus, + Minus, + Splat, + Slash, + Percent, + Tilde, + Bang, + PlusPlus, + MinusMinus, + Dot, + QuestionQuestion, + + Unknown, + } +} \ No newline at end of file diff --git a/Microsoft.CSharp/Microsoft/CSharp/Microsoft.CSharp.RuntimeBinder.txt b/Microsoft.CSharp/Microsoft/CSharp/Microsoft.CSharp.RuntimeBinder.txt new file mode 100644 index 000000000..e2a13956f --- /dev/null +++ b/Microsoft.CSharp/Microsoft/CSharp/Microsoft.CSharp.RuntimeBinder.txt @@ -0,0 +1,68 @@ +;==++== +; +; Copyright (c) Microsoft Corporation. All rights reserved. +; +;==--== + +; IMPORTANT: This file needs to be kept in sync with ndp\fx\src\csharp.small\Microsoft.CSharp.txt +; +; Silverlight builds currently support only one resource file per project so we need to manually +; keep these files in sync. +; When porting exceptions, they should be added to ndp\fx\src\csharp.small\Error.cs instead +; since SL build puts them in a wrong namespace. + +; NOTE: do not use \", use ' instead +; NOTE: Use # or ; for comments + +; These are the managed resources for System.Core.Dll. See +; ResourceManager documentation and the ResGen tool. + +## ExceptionType=RuntimeBinderInternalCompilerException +InternalCompilerError=An unexpected exception occurred while binding a dynamic operation + +## ExceptionType=ArgumentException +BindRequireArguments=Cannot bind call with no calling object + +## ExceptionType=RuntimeBinderException +BindCallFailedOverloadResolution=Overload resolution failed + +## ExceptionType=ArgumentException +BindBinaryOperatorRequireTwoArguments=Binary operators must be invoked with two arguments + +## ExceptionType=ArgumentException +BindUnaryOperatorRequireOneArgument=Unary operators must be invoked with one argument + +## ExceptionType=RuntimeBinderException +BindPropertyFailedMethodGroup=The name '{0}' is bound to a method and cannot be used like a property + +## ExceptionType=RuntimeBinderException +BindPropertyFailedEvent=The event '{0}' can only appear on the left hand side of += or -= + +## ExceptionType=RuntimeBinderException +BindInvokeFailedNonDelegate=Cannot invoke a non-delegate type + +## ExceptionType=ArgumentException +BindImplicitConversionRequireOneArgument=Implicit conversion takes exactly one argument + +## ExceptionType=ArgumentException +BindExplicitConversionRequireOneArgument=Explicit conversion takes exactly one argument + +## ExceptionType=ArgumentException +BindBinaryAssignmentRequireTwoArguments=Binary operators cannot be invoked with one argument + +## ExceptionType=RuntimeBinderException +BindBinaryAssignmentFailedNullReference=Cannot perform member assignment on a null reference + +## ExceptionType=RuntimeBinderException +NullReferenceOnMemberException=Cannot perform runtime binding on a null reference + +## ExceptionType=RuntimeBinderException +BindCallToConditionalMethod=Cannot dynamically invoke method '{0}' because it has a Conditional attribute + +## ExceptionType=RuntimeBinderException +BindToVoidMethodButExpectResult=Cannot implicitly convert type 'void' to 'object' + +EmptyDynamicView=No further information on this object could be discovered + +##ExceptionType=MissingMemberException +GetValueonWriteOnlyProperty=Write Only properties are not supported \ No newline at end of file diff --git a/Microsoft.CSharp/Microsoft/CSharp/ResetBindException.cs b/Microsoft.CSharp/Microsoft/CSharp/ResetBindException.cs new file mode 100644 index 000000000..859def499 --- /dev/null +++ b/Microsoft.CSharp/Microsoft/CSharp/ResetBindException.cs @@ -0,0 +1,18 @@ +// ==++== +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// ==--== + +using System; + +namespace Microsoft.CSharp.RuntimeBinder +{ + internal sealed class ResetBindException : Exception + { + public ResetBindException() + : base() + { + } + } +} \ No newline at end of file diff --git a/Microsoft.CSharp/Microsoft/CSharp/RuntimeBinder.cs b/Microsoft.CSharp/Microsoft/CSharp/RuntimeBinder.cs new file mode 100644 index 000000000..9a2825a35 --- /dev/null +++ b/Microsoft.CSharp/Microsoft/CSharp/RuntimeBinder.cs @@ -0,0 +1,2033 @@ +// ==++== +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// ==--== + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Dynamic; +using System.Linq; +using System.Linq.Expressions; +using System.Runtime.InteropServices.WindowsRuntime; +using Microsoft.CSharp.RuntimeBinder.Semantics; +using Microsoft.CSharp.RuntimeBinder.Syntax; + +namespace Microsoft.CSharp.RuntimeBinder +{ + internal class RuntimeBinder + { + #region Singleton Implementation + + // The double checking lock, static lock initializer, and volatile instance + // field are all here to make the singleton thread-safe. Please see Richter, + // "CLR via C#" Ch. 24 for more information. This implementation was chosen + // because construction of the RuntimeBinder is expensive. + + private static readonly object s_singletonLock = new object(); + private static volatile RuntimeBinder s_instance; + + public static RuntimeBinder GetInstance() + { + if (s_instance == null) + { + lock (s_singletonLock) + { + if (s_instance == null) + { + s_instance = new RuntimeBinder(); + } + } + } + + return s_instance; + } + + #endregion + + ///////////////////////////////////////////////////////////////////////////////// + // Members + + private SymbolTable m_symbolTable; + private CSemanticChecker m_semanticChecker; + private SymbolLoader SymbolLoader { get { return m_semanticChecker.GetSymbolLoader(); } } + private ExprFactory m_exprFactory; + private OutputContext m_outputContext; + private NameGenerator m_nameGenerator; + private BindingContext m_bindingContext; + private ExpressionBinder m_binder; + private RuntimeBinderController m_controller; + + private readonly object m_bindLock = new object(); + + // This class is used to keep the tuple of runtime object values and + // the type that we want to use for the argument. This is different than the runtime + // value's type because unless the static time type was dynamic, we want to use the + // static time type. Also, we may have null values, in which case we would not be + // able to get the type. + private class ArgumentObject + { + internal Type Type; + internal object Value; + internal CSharpArgumentInfo Info; + } + + ///////////////////////////////////////////////////////////////////////////////// + // Methods + + #region BookKeeping + public RuntimeBinder() + { + Reset(); + } + + private void Reset() + { + m_controller = new RuntimeBinderController(); + m_semanticChecker = new LangCompiler(m_controller, new NameManager()); + + BSYMMGR bsymmgr = m_semanticChecker.getBSymmgr(); + NameManager nameManager = m_semanticChecker.GetNameManager(); + + InputFile infile = bsymmgr.GetMiscSymFactory().CreateMDInfile(nameManager.Lookup(""), (mdToken)0); + infile.SetAssemblyID(bsymmgr.AidAlloc(infile)); + infile.AddToAlias(KAID.kaidThisAssembly); + infile.AddToAlias(KAID.kaidGlobal); + + m_symbolTable = new SymbolTable( + bsymmgr.GetSymbolTable(), + bsymmgr.GetSymFactory(), + nameManager, + m_semanticChecker.GetTypeManager(), + bsymmgr, + m_semanticChecker, + infile); + m_semanticChecker.getPredefTypes().Init(m_semanticChecker.GetErrorContext(), m_symbolTable); + m_semanticChecker.GetTypeManager().InitTypeFactory(m_symbolTable); + SymbolLoader.getPredefinedMembers().RuntimeBinderSymbolTable = m_symbolTable; + SymbolLoader.SetSymbolTable(m_symbolTable); + + m_exprFactory = new ExprFactory(m_semanticChecker.GetSymbolLoader().GetGlobalSymbolContext()); + m_outputContext = new OutputContext(); + m_nameGenerator = new NameGenerator(); + m_bindingContext = BindingContext.CreateInstance( + m_semanticChecker, + m_exprFactory, + m_outputContext, + m_nameGenerator, + false, + true, + false, + false, + false, + false, + 0); + m_binder = new ExpressionBinder(m_bindingContext); + } + + #endregion + + ///////////////////////////////////////////////////////////////////////////////// + + public Expression Bind( + DynamicMetaObjectBinder payload, + IEnumerable parameters, + DynamicMetaObject[] args, + out DynamicMetaObject deferredBinding) + { + // The lock is here to protect this instance of the binder from itself + // when called on multiple threads. The cost in time of a single lock + // on a single thread appears to be negligible and dominated by the cost + // of the bind itself. My timing of 4000 consecutive dynamic calls with + // bind, where the body of the called method is empty, are as follows + // (five samples): + // + // Without lock() With lock() + // = 00:00:10.7597696 = 00:00:10.7222606 + // = 00:00:10.0711116 = 00:00:10.1818496 + // = 00:00:09.9905507 = 00:00:10.1628693 + // = 00:00:09.9892183 = 00:00:10.0750007 + // = 00:00:09.9253234 = 00:00:10.0340266 + // + // ...subsequent calls that were cache hits, i.e., already bound, took less + // than 1/1000 sec for the whole 4000 of them. + + lock (m_bindLock) + { + // this is a strategy for realizing correct binding when the symboltable + // finds a name collision across different types, e.g. one dynamic binding + // uses a type "N.T" and now a second binding uses a different type "N.T". + + // In order to make this work, we have to reset the symbol table and begin + // the second binding over again when we detect the collision. So this is + // something like a longjmp to the beginning of binding. For a single binding, + // if we have to do this more than once, we give an ICE--this would be a + // scenario that needs to know about both N.T's simultaneously to work. + + // See SymbolTable.LoadSymbolsFromType for more information. + + try + { + return BindCore(payload, parameters, args, out deferredBinding); + } + catch (ResetBindException) + { + Reset(); + try + { + return BindCore(payload, parameters, args, out deferredBinding); + } + catch (ResetBindException) + { + Reset(); + Debug.Assert(false, "RuntimeBinderInternalCompilerException", "More than one symbol table name collision in a single binding"); + throw Error.InternalCompilerError(); + } + } + } + } + + private Expression BindCore( + DynamicMetaObjectBinder payload, + IEnumerable parameters, + DynamicMetaObject[] args, + out DynamicMetaObject deferredBinding) + { + if (args.Length < 1) + { + throw Error.BindRequireArguments(); + } + + InitializeCallingContext(payload); + ArgumentObject[] arguments = CreateArgumentArray(payload, parameters, args); + + // On any given bind call, we populate the symbol table with any new + // conversions that we find for any of the types specified. We keep a + // running SymbolTable so that we dont have to reflect over types if + // we've seen them already in the table. + // + // Once we've loaded all the standard conversions into the symbol table, + // we can call into the binder to bind the actual call. + + ICSharpInvokeOrInvokeMemberBinder callOrInvoke = payload as ICSharpInvokeOrInvokeMemberBinder; + PopulateSymbolTableWithPayloadInformation(payload, arguments[0].Type, arguments); + AddConversionsForArguments(arguments); + + // When we do any bind, we perform the following steps: + // + // 1) Create a local variable scope which contains local variable symbols + // for each of the parameters, and the instance argument. + // 2) If we have operators, then we dont need to do lookup. Otherwise, + // look for the name and switch on the result - dispatch according to + // the symbol kind. This results in an EXPR being bound that is the expression. + // 3) Create the EXPRRETURN which returns the call and wrap it in + // an EXPRBOUNDLAMBDA which uses the local variable scope + // created in step (1) as its local scope. + // 4) Call the ExpressionTreeRewriter to generate a set of EXPRCALLs + // that call the static ExpressionTree factory methods. + // 5) Call the EXPRTreeToExpressionTreeVisitor to generate the actual + // Linq expression tree for the whole thing and return it. + + // (1) - Create the locals + Dictionary dictionary = new Dictionary(); + Scope pScope = m_semanticChecker.GetGlobalMiscSymFactory().CreateScope(null); + PopulateLocalScope(payload, pScope, arguments, parameters, dictionary); + + // (1.5) - Check to see if we need to defer. + DynamicMetaObject o = null; + if (DeferBinding(payload, arguments, args, dictionary, out o)) + { + deferredBinding = o; + return null; + } + + // (2) - look the thing up and dispatch. + EXPR pResult = DispatchPayload(payload, arguments, dictionary); + Debug.Assert(pResult != null); + + deferredBinding = null; + Expression e = CreateExpressionTreeFromResult(parameters, arguments, pScope, pResult); + return e; + } + #region Helpers + + private bool DeferBinding( + DynamicMetaObjectBinder payload, + ArgumentObject[] arguments, + DynamicMetaObject[] args, + Dictionary dictionary, + out DynamicMetaObject deferredBinding) + { + // This method deals with any deferrals we need to do. We check deferrals up front + // and bail early if we need to do them. + + // (1) InvokeMember deferral. + // + // This is the deferral for the d.Foo() scenario where Foo actually binds to a + // field or property, and not a method group that is invocable. We defer to + // the standard GetMember/Invoke pattern. + + if (payload is CSharpInvokeMemberBinder) + { + ICSharpInvokeOrInvokeMemberBinder callPayload = payload as ICSharpInvokeOrInvokeMemberBinder; + int arity = callPayload.TypeArguments != null ? callPayload.TypeArguments.Count : 0; + MemberLookup mem = new MemberLookup(); + EXPR callingObject = CreateCallingObjectForCall(callPayload, arguments, dictionary); + + Debug.Assert(m_bindingContext.ContextForMemberLookup() != null); + SymWithType swt = m_symbolTable.LookupMember( + callPayload.Name, + callingObject, + m_bindingContext.ContextForMemberLookup(), + arity, + mem, + (callPayload.Flags & CSharpCallFlags.EventHookup) != 0, + true); + + if (swt != null && swt.Sym.getKind() != SYMKIND.SK_MethodSymbol) + { + // The GetMember only has one argument, and we need to just take the first arg info. + CSharpGetMemberBinder getMember = new CSharpGetMemberBinder(callPayload.Name, false, callPayload.CallingContext, new CSharpArgumentInfo[] { callPayload.ArgumentInfo[0] }); + + // The Invoke has the remainig argument infos. However, we need to redo the first one + // to correspond to the GetMember result. + CSharpArgumentInfo[] argInfos = new CSharpArgumentInfo[callPayload.ArgumentInfo.Count]; + callPayload.ArgumentInfo.CopyTo(argInfos, 0); + + argInfos[0] = CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null); + CSharpInvokeBinder invoke = new CSharpInvokeBinder(callPayload.Flags, callPayload.CallingContext, argInfos); + + DynamicMetaObject[] newArgs = new DynamicMetaObject[args.Length - 1]; + Array.Copy(args, 1, newArgs, 0, args.Length - 1); + deferredBinding = invoke.Defer(getMember.Defer(args[0]), newArgs); + return true; + } + } + + deferredBinding = null; + return false; + } + + private void InitializeCallingContext(DynamicMetaObjectBinder payload) + { + // Set the context if the payload specifies it. Currently we only use this for calls. + Type t = null; + bool bChecked = false; + if (payload is ICSharpInvokeOrInvokeMemberBinder) + { + t = (payload as ICSharpInvokeOrInvokeMemberBinder).CallingContext; + } + else if (payload is CSharpGetMemberBinder) + { + t = (payload as CSharpGetMemberBinder).CallingContext; + } + else if (payload is CSharpSetMemberBinder) + { + CSharpSetMemberBinder b = (CSharpSetMemberBinder)payload; + t = b.CallingContext; + bChecked = b.IsChecked; + } + else if (payload is CSharpGetIndexBinder) + { + t = (payload as CSharpGetIndexBinder).CallingContext; + } + else if (payload is CSharpSetIndexBinder) + { + CSharpSetIndexBinder b = (CSharpSetIndexBinder)payload; + t = b.CallingContext; + bChecked = b.IsChecked; + } + else if (payload is CSharpUnaryOperationBinder) + { + CSharpUnaryOperationBinder b = (CSharpUnaryOperationBinder)payload; + t = b.CallingContext; + bChecked = b.IsChecked; + } + else if (payload is CSharpBinaryOperationBinder) + { + CSharpBinaryOperationBinder b = (CSharpBinaryOperationBinder)payload; + t = b.CallingContext; + bChecked = b.IsChecked; + } + else if (payload is CSharpConvertBinder) + { + CSharpConvertBinder b = (CSharpConvertBinder)payload; + t = b.CallingContext; + bChecked = b.IsChecked; + } + else if (payload is CSharpIsEventBinder) + { + t = (payload as CSharpIsEventBinder).CallingContext; + } + + if (t != null) + { + AggregateSymbol agg = m_symbolTable.GetCTypeFromType(t).AsAggregateType().GetOwningAggregate(); + m_bindingContext.m_pParentDecl = m_semanticChecker.GetGlobalSymbolFactory().CreateAggregateDecl(agg, null); + } + else + { + // The binding context lives across invocations! If we don't reset this, then later calls might + // bind in a previous call's context. + m_bindingContext.m_pParentDecl = null; + } + + m_bindingContext.CheckedConstant = bChecked; + m_bindingContext.CheckedNormal = bChecked; + } + + ///////////////////////////////////////////////////////////////////////////////// + + private Expression CreateExpressionTreeFromResult( + IEnumerable parameters, + ArgumentObject[] arguments, + Scope pScope, + EXPR pResult) + { + // (3) - Place the result in a return statement and create the EXPRBOUNDLAMBDA. + EXPRBOUNDLAMBDA boundLambda = GenerateBoundLambda(arguments, pScope, pResult); + + // (4) - Rewrite the EXPRBOUNDLAMBDA into an expression tree. + EXPR exprTree = ExpressionTreeRewriter.Rewrite(boundLambda, m_exprFactory, SymbolLoader); + + // (5) - Create the actual Expression Tree + Expression e = ExpressionTreeCallRewriter.Rewrite(SymbolLoader.GetTypeManager(), exprTree, parameters); + return e; + } + + ///////////////////////////////////////////////////////////////////////////////// + + private ArgumentObject[] CreateArgumentArray( + DynamicMetaObjectBinder payload, + IEnumerable parameters, + DynamicMetaObject[] args) + { + // Check the payloads to see whether or not we need to get the runtime types for + // these arguments. + + List list = new List(); + Func getArgumentType = null; + Func getArgumentInfo = null; + + // Quick delegate to set the type. + if (payload is ICSharpInvokeOrInvokeMemberBinder) + { + getArgumentInfo = (p, index) => (p as ICSharpInvokeOrInvokeMemberBinder).ArgumentInfo[index]; + } + else if (payload is CSharpBinaryOperationBinder) + { + getArgumentInfo = (p, index) => (p as CSharpBinaryOperationBinder).ArgumentInfo[index]; + } + else if (payload is CSharpUnaryOperationBinder) + { + getArgumentInfo = (p, index) => (p as CSharpUnaryOperationBinder).ArgumentInfo[index]; + } + else if (payload is CSharpGetMemberBinder) + { + getArgumentInfo = (p, index) => (p as CSharpGetMemberBinder).ArgumentInfo[index]; + } + else if (payload is CSharpSetMemberBinder) + { + getArgumentInfo = (p, index) => (p as CSharpSetMemberBinder).ArgumentInfo[index]; + } + else if (payload is CSharpGetIndexBinder) + { + getArgumentInfo = (p, index) => (p as CSharpGetIndexBinder).ArgumentInfo[index]; + } + else if (payload is CSharpSetIndexBinder) + { + getArgumentInfo = (p, index) => (p as CSharpSetIndexBinder).ArgumentInfo[index]; + } + else if (payload is CSharpConvertBinder || payload is CSharpIsEventBinder) + { + getArgumentInfo = (p, index) => CSharpArgumentInfo.None; + } + else + { + Debug.Assert(false, "RuntimeBinderInternalCompilerException", "Unknown payload kind"); + throw Error.InternalCompilerError(); + } + getArgumentType = (p, argInfo, param, arg, index) => + { + Type t = argInfo.UseCompileTimeType ? param.Type : arg.LimitType; + Debug.Assert(t != null); + + if ((argInfo.Flags & (CSharpArgumentInfoFlags.IsRef | CSharpArgumentInfoFlags.IsOut)) != 0) + { + // If we have a ref our an out parameter, make the byref type. + // If we have the receiver of a call or invoke that is ref, it must be because of + // a struct caller. Dont persist the ref for that. + if (!(index == 0 && IsBinderThatCanHaveRefReceiver(p))) + { + t = t.MakeByRefType(); + } + } + else if (!argInfo.UseCompileTimeType) + { + // If we don't have ref or out, then pick the best type to represent this value. + // If the runtime value has a type that is not accessible, then we pick an + // accessible type that is "closest" in some sense, where we recursively widen + // components of type that can validly vary covariantly. + + // This ensures that the type we pick is something that the user could have written. + + CType actualType = m_symbolTable.GetCTypeFromType(t); + CType bestType; + + bool res = m_semanticChecker.GetTypeManager().GetBestAccessibleType(m_semanticChecker, m_bindingContext, actualType, out bestType); + if (!res) + { + // Since the actual type of these arguments are never going to be pointer + // types or ref/out types (they are in fact boxed into an object), we have + // a guarantee that we will always be able to find a best accessible type + // (which, in the worst case, may be object). However, just to be super + // paranoid, let's not let a null type get back into the system. + Debug.Assert(false, "Unexpected failure of GetBestAccessibleType in construction of argument array"); + t = typeof(object); + } + + t = bestType.AssociatedSystemType; + } + + return t; + }; + + int i = 0; + foreach (var curParam in parameters) + { + ArgumentObject a = new ArgumentObject(); + a.Value = args[i].Value; + a.Info = getArgumentInfo(payload, i); + a.Type = getArgumentType(payload, a.Info, curParam, args[i], i); + + Debug.Assert(a.Type != null); + list.Add(a); + + ++i; + } + + return list.ToArray(); + } + + ///////////////////////////////////////////////////////////////////////////////// + + private bool IsBinderThatCanHaveRefReceiver(DynamicMetaObjectBinder binder) + { + // This is true for any binder that is eligible to take value type receiver + // objects as a ref (for mutable operations). Such as calls ("v.M(d)"), + // and indexers ("v[d] = v[d]"). Note that properties are not here because they + // are only dispatched dynamically when the receiver is dynamic, and hence boxed. + return binder is ICSharpInvokeOrInvokeMemberBinder || binder is CSharpSetIndexBinder || binder is CSharpGetIndexBinder; + } + + ///////////////////////////////////////////////////////////////////////////////// + + private void PopulateSymbolTableWithPayloadInformation( + DynamicMetaObjectBinder payload, + Type callingType, + ArgumentObject[] arguments) + { + ICSharpInvokeOrInvokeMemberBinder callOrInvoke; + CSharpGetMemberBinder getmember; + CSharpSetMemberBinder setmember; + + if ((callOrInvoke = payload as ICSharpInvokeOrInvokeMemberBinder) != null) + { + Type type; + + if (callOrInvoke.StaticCall) + { + if (arguments[0].Value == null || !(arguments[0].Value is Type)) + { + Debug.Assert(false, "RuntimeBinderInternalCompilerException", "Cannot make static call without specifying a type"); + throw Error.InternalCompilerError(); + } + type = arguments[0].Value as Type; + } + else + { + type = callingType; + } + m_symbolTable.PopulateSymbolTableWithName( + callOrInvoke.Name, + callOrInvoke.TypeArguments, + type); + + // If it looks like we're invoking a get_ or a set_, load the property as well. + // This is because we need COM indexed properties called via method calls to + // work the same as it used to. + if (callOrInvoke.Name.StartsWith("set_", StringComparison.Ordinal) || + callOrInvoke.Name.StartsWith("get_", StringComparison.Ordinal)) + { + m_symbolTable.PopulateSymbolTableWithName( + callOrInvoke.Name.Substring(4), //remove prefix + callOrInvoke.TypeArguments, + type); + } + } + else if ((getmember = payload as CSharpGetMemberBinder) != null) + { + m_symbolTable.PopulateSymbolTableWithName( + getmember.Name, + null, + arguments[0].Type); + } + else if ((setmember = payload as CSharpSetMemberBinder) != null) + { + m_symbolTable.PopulateSymbolTableWithName( + setmember.Name, + null, + arguments[0].Type); + } + else if (payload is CSharpGetIndexBinder || payload is CSharpSetIndexBinder) + { + m_symbolTable.PopulateSymbolTableWithName( + SpecialNames.Indexer, + null, + arguments[0].Type); + } + else if (payload is CSharpBinaryOperationBinder) + { + CSharpBinaryOperationBinder op = payload as CSharpBinaryOperationBinder; + if (GetCLROperatorName(op.Operation) == null) + { + Debug.Assert(false, "RuntimeBinderInternalCompilerException", "Unknown operator: " + op.Operation); + throw Error.InternalCompilerError(); + } + m_symbolTable.PopulateSymbolTableWithName( + GetCLROperatorName(op.Operation), + null, + arguments[0].Type); + m_symbolTable.PopulateSymbolTableWithName( + GetCLROperatorName(op.Operation), + null, + arguments[1].Type); + } + else if (payload is CSharpUnaryOperationBinder) + { + CSharpUnaryOperationBinder op = payload as CSharpUnaryOperationBinder; + m_symbolTable.PopulateSymbolTableWithName( + GetCLROperatorName(op.Operation), + null, + arguments[0].Type); + } + else if (payload is CSharpIsEventBinder) + { + CSharpIsEventBinder op = payload as CSharpIsEventBinder; + + // Populate the symbol table with the LHS. + m_symbolTable.PopulateSymbolTableWithName( + op.Name, + null, + arguments[0].Info.IsStaticType ? arguments[0].Value as Type : arguments[0].Type); + } + else if (!(payload is CSharpConvertBinder)) + { + // Conversions don't need to do anything, since they're just conversions! + // After we add payload information, we add conversions for all argument + // types anyway, so that will get handled there. + // + // All other unknown payload types will generate an error. + + Debug.Assert(false, "RuntimeBinderInternalCompilerException", "Unknown payload kind"); + throw Error.InternalCompilerError(); + } + + } + + ///////////////////////////////////////////////////////////////////////////////// + + private void AddConversionsForArguments(ArgumentObject[] arguments) + { + foreach (ArgumentObject arg in arguments) + { + m_symbolTable.AddConversionsForType(arg.Type); + } + } + + ///////////////////////////////////////////////////////////////////////////////// + + private EXPR DispatchPayload( + DynamicMetaObjectBinder payload, + ArgumentObject[] arguments, + Dictionary dictionary) + { + EXPR pResult = null; + if (payload is CSharpBinaryOperationBinder) + { + pResult = BindBinaryOperation(payload as CSharpBinaryOperationBinder, arguments, dictionary); + } + else if (payload is CSharpUnaryOperationBinder) + { + pResult = BindUnaryOperation(payload as CSharpUnaryOperationBinder, arguments, dictionary); + } + else if (payload is CSharpSetMemberBinder) + { + pResult = BindAssignment(payload as CSharpSetMemberBinder, arguments, dictionary); + } + else if (payload is CSharpConvertBinder) + { + Debug.Assert(arguments.Length == 1); + { + CSharpConvertBinder conversion = payload as CSharpConvertBinder; + switch (conversion.ConversionKind) + { + case CSharpConversionKind.ImplicitConversion: + pResult = BindImplicitConversion(arguments, conversion.Type, dictionary, false); + break; + case CSharpConversionKind.ExplicitConversion: + pResult = BindExplicitConversion(arguments, conversion.Type, dictionary); + break; + case CSharpConversionKind.ArrayCreationConversion: + pResult = BindImplicitConversion(arguments, conversion.Type, dictionary, true); + break; + default: + Debug.Assert(false, "RuntimeBinderInternalCompilerException", "Unknown conversion kind"); + throw Error.InternalCompilerError(); + } + } + } + else if (payload is ICSharpInvokeOrInvokeMemberBinder) + { + EXPR callingObject = CreateCallingObjectForCall(payload as ICSharpInvokeOrInvokeMemberBinder, arguments, dictionary); + pResult = BindCall(payload as ICSharpInvokeOrInvokeMemberBinder, callingObject, arguments, dictionary); + } + else if (payload is CSharpGetMemberBinder) + { + Debug.Assert(arguments.Length == 1); + pResult = BindProperty(payload, arguments[0], dictionary[0], null, false); + } + else if (payload is CSharpGetIndexBinder) + { + EXPR indexerArguments = CreateArgumentListEXPR(arguments, dictionary, 1, arguments.Length); + pResult = BindProperty(payload, arguments[0], dictionary[0], indexerArguments, false); + } + else if (payload is CSharpSetIndexBinder) + { + pResult = BindAssignment(payload as CSharpSetIndexBinder, arguments, dictionary); + } + else if (payload is CSharpIsEventBinder) + { + pResult = BindIsEvent(payload as CSharpIsEventBinder, arguments, dictionary); + } + else + { + Debug.Assert(false, "RuntimeBinderInternalCompilerException", "Unknown payload kind"); + throw Error.InternalCompilerError(); + } + return pResult; + } + + ///////////////////////////////////////////////////////////////////////////////// + // We take the ArgumentObjects to verify - if the parameter expression tells us + // we have a ref parameter, but the argument object tells us we're not passed by ref, + // then it means it was a ref that the compiler had to insert. This is used when + // we have a call off of a struct for example. If thats the case, dont treat the + // local as a ref type. + + private void PopulateLocalScope( + DynamicMetaObjectBinder payload, + Scope pScope, + ArgumentObject[] arguments, + IEnumerable parameterExpressions, + Dictionary dictionary) + { + // We use the compile time types for the local variables, and then + // cast them to the runtime types for the expression tree. + + int i = 0; + foreach (Expression parameter in parameterExpressions) + { + CType type = m_symbolTable.GetCTypeFromType(parameter.Type); + + // Make sure we're not setting ref for the receiver of a call - the argument + // will be marked as ref if we're calling off a struct, but we dont want + // to persist that in our system. + bool isFirstParamOfCallOrInvoke = false; + if (i == 0 && IsBinderThatCanHaveRefReceiver(payload)) + { + isFirstParamOfCallOrInvoke = true; + } + + // If we have a ref or out, get the parameter modifier type. + if ((parameter is ParameterExpression && (parameter as ParameterExpression).IsByRef) && + (arguments[i].Info.IsByRef || arguments[i].Info.IsOut)) + { + // If we're the first param of a call or invoke, and we're ref, it must be + // because of structs. Dont persist the parameter modifier type. + if (!isFirstParamOfCallOrInvoke) + { + type = m_semanticChecker.GetTypeManager().GetParameterModifier(type, arguments[i].Info.IsOut); + } + } + LocalVariableSymbol local = m_semanticChecker.GetGlobalSymbolFactory().CreateLocalVar(m_semanticChecker.GetNameManager().Add("p" + i), pScope, type); + local.fUsedInAnonMeth = true; + + dictionary.Add(i++, local); + isFirstParamOfCallOrInvoke = false; + } + } + + ///////////////////////////////////////////////////////////////////////////////// + + private EXPRBOUNDLAMBDA GenerateBoundLambda( + ArgumentObject[] arguments, + Scope pScope, + EXPR call) + { + // We dont actually need the real delegate type here - we just need SOME delegate type. + // This is because we never attempt any conversions on the lambda itself. + AggregateType delegateType = m_symbolTable.GetCTypeFromType(typeof(Func)).AsAggregateType(); + LocalVariableSymbol thisLocal = m_semanticChecker.GetGlobalSymbolFactory().CreateLocalVar(m_semanticChecker.GetNameManager().Add("this"), pScope, m_symbolTable.GetCTypeFromType(typeof(object))); + thisLocal.isThis = true; + EXPRBOUNDLAMBDA boundLambda = m_exprFactory.CreateAnonymousMethod(delegateType); + EXPRUNBOUNDLAMBDA unboundLambda = m_exprFactory.CreateLambda(); + + List paramTypes = new List(); + foreach (ArgumentObject o in arguments) + { + paramTypes.Add(o.Type); + } + boundLambda.Initialize(pScope); + + EXPRRETURN returnStatement = m_exprFactory.CreateReturn(0, pScope, call); + EXPRBLOCK block = m_exprFactory.CreateBlock(null, returnStatement, pScope); + boundLambda.OptionalBody = block; + return boundLambda; + } + + #region ExprCreation + + ///////////////////////////////////////////////////////////////////////////////// + + private EXPR CreateLocal(Type type, bool bIsOut, LocalVariableSymbol local) + { + CType ctype = m_symbolTable.GetCTypeFromType(type); + if (bIsOut) + { + Debug.Assert(ctype.IsParameterModifierType()); + ctype = m_semanticChecker.GetTypeManager().GetParameterModifier( + ctype.AsParameterModifierType().GetParameterType(), + true); + } + + // If we can convert, do that. If not, cast it. + EXPRLOCAL exprLocal = m_exprFactory.CreateLocal(EXPRFLAG.EXF_LVALUE, local); + EXPR result = m_binder.tryConvert(exprLocal, ctype); + if (result == null) + { + result = m_binder.mustCast(exprLocal, ctype); + } + result.flags |= EXPRFLAG.EXF_LVALUE; + return result; + } + + ///////////////////////////////////////////////////////////////////////////////// + + private EXPR CreateArgumentListEXPR( + ArgumentObject[] arguments, + Dictionary dictionary, + int startIndex, + int endIndex) + { + EXPR args = null; + EXPR last = null; + + if (arguments != null) + { + for (int i = startIndex; i < endIndex; i++) + { + ArgumentObject argument = arguments[i]; + EXPR arg = CreateArgumentEXPR(argument, dictionary[i]); + + if (args == null) + { + args = arg; + last = args; + } + else + { + // Lists are right-heavy. + m_exprFactory.AppendItemToList(arg, ref args, ref last); + } + } + } + return args; + } + + ///////////////////////////////////////////////////////////////////////////////// + + private EXPR CreateArgumentEXPR(ArgumentObject argument, LocalVariableSymbol local) + { + EXPR arg; + if (argument.Info.LiteralConstant) + { + if (argument.Value == null) + { + if (argument.Info.UseCompileTimeType) + { + arg = m_exprFactory.CreateConstant(m_symbolTable.GetCTypeFromType(argument.Type), new CONSTVAL()); + } + else + { + arg = m_exprFactory.CreateNull(); + } + } + else + { + arg = m_exprFactory.CreateConstant(m_symbolTable.GetCTypeFromType(argument.Type), new CONSTVAL(argument.Value)); + } + } + else + { + // If we have a dynamic argument and it was null, the type is going to be Object. + // But we want it to be typed NullType so we can have null conversions. + + if (!argument.Info.UseCompileTimeType && argument.Value == null) + { + arg = m_exprFactory.CreateNull(); + } + else + { + arg = CreateLocal(argument.Type, argument.Info.IsOut, local); + } + } + + // Now check if we have a named thing. If so, wrap this thing in a named argument. + if (argument.Info.NamedArgument) + { + Debug.Assert(argument.Info.Name != null); + arg = m_exprFactory.CreateNamedArgumentSpecification(SymbolTable.GetName(argument.Info.Name, m_semanticChecker.GetNameManager()), arg); + } + + // If we have an object that was "dynamic" at compile time, we need + // to be able to convert it to every interface that the actual value + // implements. This allows conversion binders and overload resolution + // to behave as though type information is available for these EXPRs, + // even though it may be the case that the actual runtime type is + // inaccessible and therefore unused. + + // This comes in handy for, e.g., iterators (they are nested private + // classes), and COM RCWs without type information (they do not expose + // their interfaces in a usual way). + + // It is CRITICAL that arg.RuntimeObject is non-null ONLY when the + // compile time type of the argument is dynamic, otherwise normal C# + // semantics on typed arguments will be broken. + + if (!argument.Info.UseCompileTimeType && argument.Value != null) + { + arg.RuntimeObject = argument.Value; + arg.RuntimeObjectActualType = m_symbolTable.GetCTypeFromType(argument.Value.GetType()); + } + + return arg; + } + + ///////////////////////////////////////////////////////////////////////////////// + + private EXPRMEMGRP CreateMemberGroupEXPR( + string Name, + IList typeArguments, + EXPR callingObject, + SYMKIND kind) + { + Name name = SymbolTable.GetName(Name, m_semanticChecker.GetNameManager()); + AggregateType callingType; + + if (callingObject.type.IsArrayType()) + { + callingType = m_semanticChecker.GetSymbolLoader().GetReqPredefType(PredefinedType.PT_ARRAY); + } + else if (callingObject.type.IsNullableType()) + { + callingType = callingObject.type.AsNullableType().GetAts(m_semanticChecker.GetSymbolLoader().GetErrorContext()); + } + else if (callingObject.type.IsAggregateType()) + { + callingType = callingObject.type.AsAggregateType(); + } + else + { + callingType = null; + Debug.Assert(false, "MemberGroup on non-array, non-aggregate"); + } + + List callingTypes = new List(); + + // The C# binder expects that only the base virtual method is inserted + // into the list of candidates, and only the type containing the base + // virtual method is inserted into the list of types. However, since we + // dont want to do all the logic, we're just going to insert every type + // that has a member of the given name, and allow the C# binder to filter + // out all overrides. + // + // Find that set of types now. + symbmask_t mask = symbmask_t.MASK_MethodSymbol; + switch (kind) + { + case SYMKIND.SK_PropertySymbol: + case SYMKIND.SK_IndexerSymbol: + mask = symbmask_t.MASK_PropertySymbol; + break; + case SYMKIND.SK_MethodSymbol: + mask = symbmask_t.MASK_MethodSymbol; + break; + default: + Debug.Assert(false, "Unhandled kind"); + break; + } + + // If we have a constructor, only find its type. + bool bIsConstructor = name == SymbolLoader.GetNameManager().GetPredefinedName(PredefinedName.PN_CTOR); + for (AggregateType t = callingType; t != null; t = t.GetBaseClass()) + { + if (m_symbolTable.AggregateContainsMethod(t.GetOwningAggregate(), Name, mask)) + { + callingTypes.Add(t); + } + + // If we have a constructor, run the loop once for the constructor's type, and thats it. + if (bIsConstructor) + { + break; + } + } + + // If this is a WinRT type we have to add all collection interfaces that have this method + // as well so that overload resolution can find them. + if (callingType.IsWindowsRuntimeType()) + { + TypeArray collectioniFaces = callingType.GetWinRTCollectionIfacesAll(SymbolLoader); + + for (int i = 0; i < collectioniFaces.size; i++) + { + CType t = collectioniFaces.Item(i); + // Collection interfaces will be aggregates. + Debug.Assert(t.IsAggregateType()); + + if (m_symbolTable.AggregateContainsMethod(t.AsAggregateType().GetOwningAggregate(), Name, mask)) + { + callingTypes.Add(t); + } + } + } + + EXPRFLAG flags = EXPRFLAG.EXF_USERCALLABLE; + // If its a delegate, mark that on the memgroup. + if (Name == SpecialNames.Invoke && callingObject.type.isDelegateType()) + { + flags |= EXPRFLAG.EXF_DELEGATE; + } + + // For a constructor, we need to seed the memgroup with the constructor flag. + if (Name == SpecialNames.Constructor) + { + flags |= EXPRFLAG.EXF_CTOR; + } + + // If we have an indexer, mark that. + if (Name == SpecialNames.Indexer) + { + flags |= EXPRFLAG.EXF_INDEXER; + } + + TypeArray typeArgumentsAsTypeArray = BSYMMGR.EmptyTypeArray(); + if (typeArguments != null && typeArguments.Count > 0) + { + typeArgumentsAsTypeArray = m_semanticChecker.getBSymmgr().AllocParams( + m_symbolTable.GetCTypeArrayFromTypes(typeArguments)); + } + EXPRMEMGRP memgroup = m_exprFactory.CreateMemGroup(// Tree + flags, name, typeArgumentsAsTypeArray, kind, callingType, null, null, new CMemberLookupResults( + m_semanticChecker.getBSymmgr().AllocParams(callingTypes.Count, callingTypes.ToArray()), + name)); + if (callingObject.isCLASS()) + { + memgroup.SetOptionalLHS(callingObject); + } + else + { + memgroup.SetOptionalObject(callingObject); + } + return memgroup; + } + + ///////////////////////////////////////////////////////////////////////////////// + + private EXPR CreateProperty( + SymWithType swt, + EXPR callingObject, + BindingFlag flags) + { + // For a property, we simply create the EXPRPROP for the thing, call the + // expression tree rewriter, rewrite it, and send it on its way. + + PropertySymbol property = swt.Prop(); + AggregateType propertyType = swt.GetType(); + PropWithType pwt = new PropWithType(property, propertyType); + EXPRMEMGRP pMemGroup = CreateMemberGroupEXPR(property.name.Text, null, callingObject, SYMKIND.SK_PropertySymbol); + + return m_binder.BindToProperty(// For a static property instance, dont set the object. + callingObject.isCLASS() ? null : callingObject, pwt, flags, null, null, pMemGroup); + } + + ///////////////////////////////////////////////////////////////////////////////// + + private EXPR CreateIndexer(SymWithType swt, EXPR callingObject, EXPR arguments, BindingFlag bindFlags) + { + IndexerSymbol index = swt.Sym as IndexerSymbol; + AggregateType ctype = swt.GetType(); + EXPRMEMGRP memgroup = CreateMemberGroupEXPR(index.name.Text, null, callingObject, SYMKIND.SK_PropertySymbol); + + EXPR result = m_binder.BindMethodGroupToArguments(bindFlags, memgroup, arguments); + return ReorderArgumentsForNamedAndOptional(callingObject, result); + } + + ///////////////////////////////////////////////////////////////////////////////// + + private EXPR CreateArray(EXPR callingObject, EXPR optionalIndexerArguments) + { + return m_binder.BindArrayIndexCore(0, callingObject, optionalIndexerArguments); + } + + ///////////////////////////////////////////////////////////////////////////////// + + private EXPR CreateField( + SymWithType swt, + EXPR callingObject) + { + // For a field, simply create the EXPRFIELD and our caller takes care of the rest. + + FieldSymbol fieldSymbol = swt.Field(); + CType returnType = fieldSymbol.GetType(); + AggregateType fieldType = swt.GetType(); + FieldWithType fwt = new FieldWithType(fieldSymbol, fieldType); + + EXPR field = m_binder.BindToField(callingObject.isCLASS() ? null : callingObject, fwt, 0); + return field; + } + + ///////////////////////////////////////////////////////////////////////////////// + + private EXPREVENT CreateEvent( + SymWithType swt, + EXPR callingObject) + { + EventSymbol eventSymbol = swt.Event(); + EXPREVENT e = m_exprFactory.CreateEvent(eventSymbol.type, callingObject, new EventWithType(eventSymbol, swt.GetType())); + return e; + } + #endregion + + #endregion + + #region Calls + ///////////////////////////////////////////////////////////////////////////////// + + private EXPR CreateCallingObjectForCall( + ICSharpInvokeOrInvokeMemberBinder payload, + ArgumentObject[] arguments, + Dictionary dictionary) + { + // Here we have a regular call, so create the calling object off of the first + // parameter and pass it through. + EXPR callingObject; + if (payload.StaticCall) + { + if (arguments[0].Value == null || !(arguments[0].Value is Type)) + { + Debug.Assert(false, "RuntimeBinderInternalCompilerException", "Cannot make static call without specifying a type"); + throw Error.InternalCompilerError(); + } + Type t = arguments[0].Value as Type; + callingObject = m_exprFactory.CreateClass(m_symbolTable.GetCTypeFromType(t), null, t.ContainsGenericParameters ? + m_exprFactory.CreateTypeArguments(SymbolLoader.getBSymmgr().AllocParams(m_symbolTable.GetCTypeArrayFromTypes(t.GetGenericArguments())), null) : null); + } + else + { + // If we have a null argument, just bail and throw. + if (!arguments[0].Info.UseCompileTimeType && arguments[0].Value == null) + { + throw Error.NullReferenceOnMemberException(); + } + + callingObject = m_binder.mustConvert( + CreateArgumentEXPR(arguments[0], dictionary[0]), + m_symbolTable.GetCTypeFromType(arguments[0].Type)); + + if (arguments[0].Type.IsValueType && callingObject.isCAST()) + { + // If we have a struct type, unbox it. + callingObject.flags |= EXPRFLAG.EXF_UNBOXRUNTIME; + } + } + return callingObject; + } + + ///////////////////////////////////////////////////////////////////////////////// + + private EXPR BindCall( + ICSharpInvokeOrInvokeMemberBinder payload, + EXPR callingObject, + ArgumentObject[] arguments, + Dictionary dictionary) + { + if (payload is InvokeBinder && !callingObject.type.isDelegateType()) + { + throw Error.BindInvokeFailedNonDelegate(); + } + + EXPR pResult = null; + int arity = payload.TypeArguments != null ? payload.TypeArguments.Count : 0; + MemberLookup mem = new MemberLookup(); + + Debug.Assert(m_bindingContext.ContextForMemberLookup() != null); + SymWithType swt = m_symbolTable.LookupMember( + payload.Name, + callingObject, + m_bindingContext.ContextForMemberLookup(), + arity, + mem, + (payload.Flags & CSharpCallFlags.EventHookup) != 0, + true); + if (swt == null) + { + mem.ReportErrors(); + Debug.Assert(false, "Why didn't member lookup report an error?"); + } + + if (swt.Sym.getKind() != SYMKIND.SK_MethodSymbol) + { + Debug.Assert(false, "RuntimeBinderInternalCompilerException", "Unexpected type returned from lookup"); + throw Error.InternalCompilerError(); + } + + // At this point, we're set up to do binding. We need to do the following: + // + // 1) Create the EXPRLOCALs for the arguments, linking them to the local + // variable symbols defined above. + // 2) Create the EXPRMEMGRP for the call and the EXPRLOCAL for the object + // of the call, and link the correct local variable symbol as above. + // 3) Do overload resolution to get back an EXPRCALL. + // + // Our caller takes care of the rest. + + // First we need to check the sym that we got back. If we got back a static + // method, then we may be in the situation where the user called the method + // via a simple name call through the phantom overload. If thats the case, + // then we want to sub in a type instead of the object. + EXPRMEMGRP memGroup = CreateMemberGroupEXPR(payload.Name, payload.TypeArguments, callingObject, swt.Sym.getKind()); + if ((payload.Flags & CSharpCallFlags.SimpleNameCall) != 0) + { + callingObject.flags |= EXPRFLAG.EXF_SIMPLENAME; + } + + if ((payload.Flags & CSharpCallFlags.EventHookup) != 0) + { + mem = new MemberLookup(); + SymWithType swtEvent = m_symbolTable.LookupMember( + payload.Name.Split('_')[1], + callingObject, + m_bindingContext.ContextForMemberLookup(), + arity, + mem, + (payload.Flags & CSharpCallFlags.EventHookup) != 0, + true); + if (swtEvent == null) + { + mem.ReportErrors(); + Debug.Assert(false, "Why didn't member lookup report an error?"); + } + + CType eventCType = null; + if (swtEvent.Sym.getKind() == SYMKIND.SK_FieldSymbol) + { + eventCType = swtEvent.Field().GetType(); + } + else if (swtEvent.Sym.getKind() == SYMKIND.SK_EventSymbol) + { + eventCType = swtEvent.Event().type; + } + + Type eventType = SymbolLoader.GetTypeManager().SubstType(eventCType, swtEvent.Ats).AssociatedSystemType; + + if (eventType != null) + { + // If we have an event hookup, first find the event itself. + BindImplicitConversion(new ArgumentObject[] { arguments[1] }, eventType, dictionary, false); + } + memGroup.flags &= ~EXPRFLAG.EXF_USERCALLABLE; + + if (swtEvent.Sym.getKind() == SYMKIND.SK_EventSymbol && swtEvent.Event().IsWindowsRuntimeEvent) + { + return BindWinRTEventAccessor( + new EventWithType(swtEvent.Event(), swtEvent.Ats), + callingObject, + arguments, + dictionary, + payload.Name.StartsWith("add_", StringComparison.Ordinal)); //isAddAccessor? + } + } + + // Check if we have a potential call to an indexed property accessor. + // If so, we'll flag overload resolution to let us call non-callables. + if ((payload.Name.StartsWith("set_", StringComparison.Ordinal) && swt.Sym.AsMethodSymbol().Params.Size > 1) || + (payload.Name.StartsWith("get_", StringComparison.Ordinal) && swt.Sym.AsMethodSymbol().Params.Size > 0)) + { + memGroup.flags &= ~EXPRFLAG.EXF_USERCALLABLE; + } + + pResult = m_binder.BindMethodGroupToArguments(// Tree + BindingFlag.BIND_RVALUEREQUIRED | BindingFlag.BIND_STMTEXPRONLY, memGroup, CreateArgumentListEXPR(arguments, dictionary, 1, arguments.Length)); + + // If overload resolution failed, throw an error. + if (pResult == null || !pResult.isOK()) + { + throw Error.BindCallFailedOverloadResolution(); + } + CheckForConditionalMethodError(pResult); + + return ReorderArgumentsForNamedAndOptional(callingObject, pResult); + } + + private EXPR BindWinRTEventAccessor(EventWithType ewt, EXPR callingObject, ArgumentObject[] arguments, Dictionary dictionary, bool isAddAccessor) + { + // We want to generate either: + // WindowsRuntimeMarshal.AddEventHandler(new Func(x.add_foo), new Action(x.remove_foo), value) + // or + // WindowsRuntimeMarshal.RemoveEventHandler(new Action(x.remove_foo), value) + + Type evtType = ewt.Event().type.AssociatedSystemType; + + // Get new Action(x.remove_foo) + MethPropWithInst removemwi = new MethPropWithInst(ewt.Event().methRemove, ewt.Ats); + EXPRMEMGRP removeMethGrp = m_exprFactory.CreateMemGroup(callingObject, removemwi); + removeMethGrp.flags &= ~EXPRFLAG.EXF_USERCALLABLE; + Type actionType = Expression.GetActionType(typeof(EventRegistrationToken)); + EXPR removeMethArg = m_binder.mustConvert(removeMethGrp, m_symbolTable.GetCTypeFromType(actionType)); + + // The value + EXPR delegateVal = CreateArgumentEXPR(arguments[1], dictionary[1]); + EXPRLIST args; + string methodName; + + if (isAddAccessor) + { + // Get new Func(x.add_foo) + MethPropWithInst addmwi = new MethPropWithInst(ewt.Event().methAdd, ewt.Ats); + EXPRMEMGRP addMethGrp = m_exprFactory.CreateMemGroup(callingObject, addmwi); + addMethGrp.flags &= ~EXPRFLAG.EXF_USERCALLABLE; + Type funcType = Expression.GetFuncType(evtType, typeof(EventRegistrationToken)); + EXPR addMethArg = m_binder.mustConvert(addMethGrp, m_symbolTable.GetCTypeFromType(funcType)); + + args = m_exprFactory.CreateList(addMethArg, removeMethArg, delegateVal); + methodName = SymbolLoader.GetNameManager().GetPredefName(PredefinedName.PN_ADDEVENTHANDLER).Text; + } + else + { + args = m_exprFactory.CreateList(removeMethArg, delegateVal); + methodName = SymbolLoader.GetNameManager().GetPredefName(PredefinedName.PN_REMOVEEVENTHANDLER).Text; + } + + // WindowsRuntimeMarshal.Add\RemoveEventHandler(...) + m_symbolTable.PopulateSymbolTableWithName( methodName, new List { evtType }, typeof(WindowsRuntimeMarshal)); + EXPRCLASS marshalClass = m_exprFactory.CreateClass(m_symbolTable.GetCTypeFromType(typeof(WindowsRuntimeMarshal)), null, null); + EXPRMEMGRP addEventGrp = CreateMemberGroupEXPR(methodName, new List { evtType }, marshalClass, SYMKIND.SK_MethodSymbol); + EXPR expr = m_binder.BindMethodGroupToArguments( + BindingFlag.BIND_RVALUEREQUIRED | BindingFlag.BIND_STMTEXPRONLY, + addEventGrp, + args); + + return expr; + } + + private void CheckForConditionalMethodError(EXPR pExpr) + { + Debug.Assert(pExpr.isCALL()); + if (pExpr.isCALL()) + { + // This mimics the behavior of the native CompilerSymbolLoader in GetConditionalSymbols. Override + // methods cannot have the conditional attribute, but implicitly acquire it from their slot. + + EXPRCALL call = pExpr.asCALL(); + + MethodSymbol method = call.mwi.Meth(); + if (method.isOverride) + { + method = method.swtSlot.Meth(); + } + + object[] conditions = method.AssociatedMemberInfo.GetCustomAttributes(typeof(ConditionalAttribute), false); + if (conditions.Length > 0) + { + throw Error.BindCallToConditionalMethod(method.name); + } + } + } + + private EXPR ReorderArgumentsForNamedAndOptional(EXPR callingObject, EXPR pResult) + { + EXPR arguments; + AggregateType type; + MethodOrPropertySymbol methprop; + EXPRMEMGRP memgroup; + TypeArray typeArgs; + + if (pResult.isCALL()) + { + EXPRCALL call = pResult.asCALL(); + arguments = call.GetOptionalArguments(); + type = call.mwi.Ats; + methprop = call.mwi.Meth(); + memgroup = call.GetMemberGroup(); + typeArgs = call.mwi.TypeArgs; + } + else + { + Debug.Assert(pResult.isPROP()); + EXPRPROP prop = pResult.asPROP(); + arguments = prop.GetOptionalArguments(); + type = prop.pwtSlot.Ats; + methprop = prop.pwtSlot.Prop(); + memgroup = prop.GetMemberGroup(); + typeArgs = null; + } + + ArgInfos argInfo = new ArgInfos(); + bool b; + argInfo.carg = ExpressionBinder.CountArguments(arguments, out b); + m_binder.FillInArgInfoFromArgList(argInfo, arguments); + + // We need to substitute type parameters BEFORE getting the most derived one because + // we're binding against the base method, and the derived method may change the + // generic arguments. + TypeArray parameters = SymbolLoader.GetTypeManager().SubstTypeArray(methprop.Params, type, typeArgs); + methprop = ExpressionBinder.GroupToArgsBinder.FindMostDerivedMethod(SymbolLoader, methprop, callingObject.type); + ExpressionBinder.GroupToArgsBinder.ReOrderArgsForNamedArguments( + methprop, + parameters, + type, + memgroup, + argInfo, + m_semanticChecker.GetTypeManager(), + m_exprFactory, + SymbolLoader); + { + EXPR pList = null; + + // We reordered, so make a new list of them and set them on the constructor. + // Go backwards cause lists are right-flushed. + // Also perform the conversions to the right types. + for (int i = argInfo.carg - 1; i >= 0; i--) + { + EXPR pArg = argInfo.prgexpr[i]; + + // Strip the name-ness away, since we dont need it. + pArg = StripNamedArgument(pArg); + + // Perform the correct conversion. + pArg = m_binder.tryConvert(pArg, parameters[i]); + if (pList == null) + { + pList = pArg; + } + else + { + pList = m_exprFactory.CreateList(pArg, pList); + } + } + if (pResult.isCALL()) + { + pResult.asCALL().SetOptionalArguments(pList); + } + else + { + pResult.asPROP().SetOptionalArguments(pList); + } + } + return pResult; + } + + private EXPR StripNamedArgument(EXPR pArg) + { + if (pArg.isNamedArgumentSpecification()) + { + pArg = pArg.asNamedArgumentSpecification().Value; + } + else if (pArg.isARRINIT()) + { + pArg.asARRINIT().SetOptionalArguments(StripNamedArguments(pArg.asARRINIT().GetOptionalArguments())); + } + + return pArg; + } + + private EXPR StripNamedArguments(EXPR pArg) + { + if (pArg.isLIST()) + { + EXPRLIST list = pArg.asLIST(); + while (list != null) + { + list.SetOptionalElement(StripNamedArgument(list.GetOptionalElement())); + + if (list.GetOptionalNextListNode().isLIST()) + { + list = list.GetOptionalNextListNode().asLIST(); + } + else + { + list.SetOptionalNextListNode(StripNamedArgument(list.GetOptionalNextListNode())); + break; + } + } + } + return StripNamedArgument(pArg); + } + #endregion + + #region Operators + #region UnaryOperators + ///////////////////////////////////////////////////////////////////////////////// + + private EXPR BindUnaryOperation( + CSharpUnaryOperationBinder payload, + ArgumentObject[] arguments, + Dictionary dictionary) + { + if (arguments.Length != 1) + { + throw Error.BindUnaryOperatorRequireOneArgument(); + } + + OperatorKind op = GetOperatorKind(payload.Operation); + EXPR arg1 = CreateArgumentEXPR(arguments[0], dictionary[0]); + arg1.errorString = Operators.GetDisplayName(GetOperatorKind(payload.Operation)); + + if (op == OperatorKind.OP_TRUE || op == OperatorKind.OP_FALSE) + { + // For true and false, we try to convert to bool first. If that + // doesn't work, then we look for user defined operators. + EXPR result = m_binder.tryConvert(arg1, SymbolLoader.GetReqPredefType(PredefinedType.PT_BOOL)); + if (result != null && op == OperatorKind.OP_FALSE) + { + // If we can convert to bool, we need to negate the thing if we're looking for false. + result = m_binder.BindStandardUnaryOperator(OperatorKind.OP_LOGNOT, result); + } + + if (result == null) + { + result = m_binder.bindUDUnop(op == OperatorKind.OP_TRUE ? ExpressionKind.EK_TRUE : ExpressionKind.EK_FALSE, arg1); + } + + // If the result is STILL null, then that means theres no implicit conversion to bool, + // and no user-defined operators for true and false. Just do a must convert to report + // the error. + if (result == null) + { + result = m_binder.mustConvert(arg1, SymbolLoader.GetReqPredefType(PredefinedType.PT_BOOL)); + } + return result; + } + return m_binder.BindStandardUnaryOperator(op, arg1); + } + #endregion + + #region BinaryOperators + + ///////////////////////////////////////////////////////////////////////////////// + + private EXPR BindBinaryOperation( + CSharpBinaryOperationBinder payload, + ArgumentObject[] arguments, + Dictionary dictionary) + { + if (arguments.Length != 2) + { + throw Error.BindBinaryOperatorRequireTwoArguments(); + } + + ExpressionKind ek = Operators.GetExpressionKind(GetOperatorKind(payload.Operation, payload.IsLogicalOperation)); + EXPR arg1 = CreateArgumentEXPR(arguments[0], dictionary[0]); + EXPR arg2 = CreateArgumentEXPR(arguments[1], dictionary[1]); + + arg1.errorString = Operators.GetDisplayName(GetOperatorKind(payload.Operation, payload.IsLogicalOperation)); + arg2.errorString = Operators.GetDisplayName(GetOperatorKind(payload.Operation, payload.IsLogicalOperation)); + + if (ek > ExpressionKind.EK_MULTIOFFSET) + { + ek = (ExpressionKind)(ek - ExpressionKind.EK_MULTIOFFSET); + } + return m_binder.BindStandardBinop(ek, arg1, arg2); + } + #endregion + + ///////////////////////////////////////////////////////////////////////////////// + + private static OperatorKind GetOperatorKind(ExpressionType p) + { + return GetOperatorKind(p, false); + } + + private static OperatorKind GetOperatorKind(ExpressionType p, bool bIsLogical) + { + switch (p) + { + default: + Debug.Assert(false, "RuntimeBinderInternalCompilerException", "Unknown operator: " + p); + throw Error.InternalCompilerError(); + + // Binary Operators + case ExpressionType.Add: + return OperatorKind.OP_ADD; + case ExpressionType.Subtract: + return OperatorKind.OP_SUB; + case ExpressionType.Multiply: + return OperatorKind.OP_MUL; + case ExpressionType.Divide: + return OperatorKind.OP_DIV; + case ExpressionType.Modulo: + return OperatorKind.OP_MOD; + case ExpressionType.LeftShift: + return OperatorKind.OP_LSHIFT; + case ExpressionType.RightShift: + return OperatorKind.OP_RSHIFT; + case ExpressionType.LessThan: + return OperatorKind.OP_LT; + case ExpressionType.GreaterThan: + return OperatorKind.OP_GT; + case ExpressionType.LessThanOrEqual: + return OperatorKind.OP_LE; + case ExpressionType.GreaterThanOrEqual: + return OperatorKind.OP_GE; + case ExpressionType.Equal: + return OperatorKind.OP_EQ; + case ExpressionType.NotEqual: + return OperatorKind.OP_NEQ; + case ExpressionType.And: + return bIsLogical ? OperatorKind.OP_LOGAND : OperatorKind.OP_BITAND; + case ExpressionType.ExclusiveOr: + return OperatorKind.OP_BITXOR; + case ExpressionType.Or: + return bIsLogical ? OperatorKind.OP_LOGOR : OperatorKind.OP_BITOR; + + // Binary in place operators. + case ExpressionType.AddAssign: + return OperatorKind.OP_ADDEQ; + case ExpressionType.SubtractAssign: + return OperatorKind.OP_SUBEQ; + case ExpressionType.MultiplyAssign: + return OperatorKind.OP_MULEQ; + case ExpressionType.DivideAssign: + return OperatorKind.OP_DIVEQ; + case ExpressionType.ModuloAssign: + return OperatorKind.OP_MODEQ; + case ExpressionType.AndAssign: + return OperatorKind.OP_ANDEQ; + case ExpressionType.ExclusiveOrAssign: + return OperatorKind.OP_XOREQ; + case ExpressionType.OrAssign: + return OperatorKind.OP_OREQ; + case ExpressionType.LeftShiftAssign: + return OperatorKind.OP_LSHIFTEQ; + case ExpressionType.RightShiftAssign: + return OperatorKind.OP_RSHIFTEQ; + + // Unary Operators + case ExpressionType.Negate: + return OperatorKind.OP_NEG; + case ExpressionType.UnaryPlus: + return OperatorKind.OP_UPLUS; + case ExpressionType.Not: + return OperatorKind.OP_LOGNOT; + case ExpressionType.OnesComplement: + return OperatorKind.OP_BITNOT; + case ExpressionType.IsTrue: + return OperatorKind.OP_TRUE; + case ExpressionType.IsFalse: + return OperatorKind.OP_FALSE; + + // Increment/Decrement. + case ExpressionType.Increment: + return OperatorKind.OP_PREINC; + case ExpressionType.Decrement: + return OperatorKind.OP_PREDEC; + } + } + + ///////////////////////////////////////////////////////////////////////////////// + + private static string GetCLROperatorName(ExpressionType p) + { + switch (p) + { + default: + return null; + + // Binary Operators + case ExpressionType.Add: + return SpecialNames.CLR_Add; + case ExpressionType.Subtract: + return SpecialNames.CLR_Subtract; + case ExpressionType.Multiply: + return SpecialNames.CLR_Multiply; + case ExpressionType.Divide: + return SpecialNames.CLR_Division; + case ExpressionType.Modulo: + return SpecialNames.CLR_Modulus; + case ExpressionType.LeftShift: + return SpecialNames.CLR_LShift; + case ExpressionType.RightShift: + return SpecialNames.CLR_RShift; + case ExpressionType.LessThan: + return SpecialNames.CLR_LT; + case ExpressionType.GreaterThan: + return SpecialNames.CLR_GT; + case ExpressionType.LessThanOrEqual: + return SpecialNames.CLR_LTE; + case ExpressionType.GreaterThanOrEqual: + return SpecialNames.CLR_GTE; + case ExpressionType.Equal: + return SpecialNames.CLR_Equality; + case ExpressionType.NotEqual: + return SpecialNames.CLR_Inequality; + case ExpressionType.And: + return SpecialNames.CLR_BitwiseAnd; + case ExpressionType.ExclusiveOr: + return SpecialNames.CLR_ExclusiveOr; + case ExpressionType.Or: + return SpecialNames.CLR_BitwiseOr; + + // "op_LogicalNot"; + case ExpressionType.AddAssign: + return SpecialNames.CLR_InPlaceAdd; + case ExpressionType.SubtractAssign: + return SpecialNames.CLR_InPlaceSubtract; + case ExpressionType.MultiplyAssign: + return SpecialNames.CLR_InPlaceMultiply; + case ExpressionType.DivideAssign: + return SpecialNames.CLR_InPlaceDivide; + case ExpressionType.ModuloAssign: + return SpecialNames.CLR_InPlaceModulus; + case ExpressionType.AndAssign: + return SpecialNames.CLR_InPlaceBitwiseAnd; + case ExpressionType.ExclusiveOrAssign: + return SpecialNames.CLR_InPlaceExclusiveOr; + case ExpressionType.OrAssign: + return SpecialNames.CLR_InPlaceBitwiseOr; + case ExpressionType.LeftShiftAssign: + return SpecialNames.CLR_InPlaceLShift; + case ExpressionType.RightShiftAssign: + return SpecialNames.CLR_InPlaceRShift; + + // Unary Operators + case ExpressionType.Negate: + return SpecialNames.CLR_UnaryNegation; + case ExpressionType.UnaryPlus: + return SpecialNames.CLR_UnaryPlus; + case ExpressionType.Not: + return SpecialNames.CLR_LogicalNot; + case ExpressionType.OnesComplement: + return SpecialNames.CLR_OnesComplement; + case ExpressionType.IsTrue: + return SpecialNames.CLR_True; + case ExpressionType.IsFalse: + return SpecialNames.CLR_False; + + case ExpressionType.Increment: + return SpecialNames.CLR_PreIncrement; + case ExpressionType.Decrement: + return SpecialNames.CLR_PreDecrement; + } + } + + #endregion + + #region Properties + ///////////////////////////////////////////////////////////////////////////////// + + private EXPR BindProperty( + DynamicMetaObjectBinder payload, + ArgumentObject argument, + LocalVariableSymbol local, + EXPR optionalIndexerArguments, + bool fEventsPermitted) + { + // If our argument is a static type, then we're calling a static property. + EXPR callingObject = argument.Info.IsStaticType ? + m_exprFactory.CreateClass(m_symbolTable.GetCTypeFromType(argument.Value as Type), null, null) : + CreateLocal(argument.Type, argument.Info.IsOut, local); + + if (!argument.Info.UseCompileTimeType && argument.Value == null) + { + throw Error.NullReferenceOnMemberException(); + } + + // If our argument is a struct type, unbox it. + if (argument.Type.IsValueType && callingObject.isCAST()) + { + // If we have a struct type, unbox it. + callingObject.flags |= EXPRFLAG.EXF_UNBOXRUNTIME; + } + string name = GetName(payload); + BindingFlag bindFlags = GetBindingFlags(payload); + + MemberLookup mem = new MemberLookup(); + SymWithType swt = m_symbolTable.LookupMember(name, callingObject, m_bindingContext.ContextForMemberLookup(), 0, mem, false, false); + if (swt == null) + { + if (optionalIndexerArguments != null) + { + int numIndexArguments = ExpressionIterator.Count(optionalIndexerArguments); + // We could have an array access here. See if its just an array. + if ((argument.Type.IsArray && argument.Type.GetArrayRank() == numIndexArguments) || + argument.Type == typeof(string)) + { + return CreateArray(callingObject, optionalIndexerArguments); + } + } + mem.ReportErrors(); + Debug.Assert(false, "Why didn't member lookup report an error?"); + } + + switch (swt.Sym.getKind()) + { + case SYMKIND.SK_MethodSymbol: + throw Error.BindPropertyFailedMethodGroup(name); + + case SYMKIND.SK_PropertySymbol: + if (swt.Sym is IndexerSymbol) + { + return CreateIndexer(swt, callingObject, optionalIndexerArguments, bindFlags); + } + else + { + BindingFlag flags = 0; + if (payload is CSharpGetMemberBinder || payload is CSharpGetIndexBinder) + { + flags = BindingFlag.BIND_RVALUEREQUIRED; + } + + // Properties can be LValues. + callingObject.flags |= EXPRFLAG.EXF_LVALUE; + return CreateProperty(swt, callingObject, flags); + } + + case SYMKIND.SK_FieldSymbol: + return CreateField(swt, callingObject); + + case SYMKIND.SK_EventSymbol: + if (fEventsPermitted) + { + return CreateEvent(swt, callingObject); + } + else + { + throw Error.BindPropertyFailedEvent(name); + } + + default: + Debug.Assert(false, "RuntimeBinderInternalCompilerException", "Unexpected type returned from lookup"); + throw Error.InternalCompilerError(); + } + } + + #endregion + + #region Casts + ///////////////////////////////////////////////////////////////////////////////// + + private EXPR BindImplicitConversion( + ArgumentObject[] arguments, + Type returnType, + Dictionary dictionary, + bool bIsArrayCreationConversion) + { + if (arguments.Length != 1) + { + throw Error.BindImplicitConversionRequireOneArgument(); + } + + // Load the conversions on the target. + m_symbolTable.AddConversionsForType(returnType); + + EXPR argument = CreateArgumentEXPR(arguments[0], dictionary[0]); + CType destinationType = m_symbolTable.GetCTypeFromType(returnType); + + if (bIsArrayCreationConversion) + { + // If we are converting for an array index, we want to convert to int, uint, + // long, or ulong, depending on what the argument will allow. However, since + // the compiler had to pick a particular type for the return value when it + // made the callsite, we need to make sure that we ultimately return a type + // of that value. So we "mustConvert" to the best type that chooseArrayIndexType + // can find, and then we cast the result of that to the returnType, which is + // incidentally Int32 in the existing compiler. For that cast, we do not consider + // user defined conversions (since the convert is guaranteed to return one of + // the primitive types), and we check for overflow since we don't want truncation. + + CType pDestType = m_binder.chooseArrayIndexType(argument); + if (null == pDestType) + { + pDestType = SymbolLoader.GetReqPredefType(PredefinedType.PT_INT, true); + } + + return m_binder.mustCast( + m_binder.mustConvert(argument, pDestType), + destinationType, + CONVERTTYPE.CHECKOVERFLOW | CONVERTTYPE.NOUDC); + } + + return m_binder.mustConvert(argument, destinationType); + } + + ///////////////////////////////////////////////////////////////////////////////// + + private EXPR BindExplicitConversion(ArgumentObject[] arguments, Type returnType, Dictionary dictionary) + { + if (arguments.Length != 1) + { + throw Error.BindExplicitConversionRequireOneArgument(); + } + + // Load the conversions on the target. + m_symbolTable.AddConversionsForType(returnType); + + EXPR argument = CreateArgumentEXPR(arguments[0], dictionary[0]); + CType destinationType = m_symbolTable.GetCTypeFromType(returnType); + + return m_binder.mustCast(argument, destinationType); + } + + #endregion + + #region Assignments + + ///////////////////////////////////////////////////////////////////////////////// + + private EXPR BindAssignment( + DynamicMetaObjectBinder payload, + ArgumentObject[] arguments, + Dictionary dictionary) + { + if (arguments.Length < 2) + { + throw Error.BindBinaryAssignmentRequireTwoArguments(); + } + string name = GetName(payload); + + // Find the lhs and rhs. + EXPR lhs; + EXPR indexerArguments = null; + bool bIsCompound = false; + + if (payload is CSharpSetIndexBinder) + { + // Get the list of indexer arguments - this is the list of arguments minus the last one. + indexerArguments = CreateArgumentListEXPR(arguments, dictionary, 1, arguments.Length - 1); + bIsCompound = (payload as CSharpSetIndexBinder).IsCompoundAssignment; + } + else + { + bIsCompound = (payload as CSharpSetMemberBinder).IsCompoundAssignment; + } + m_symbolTable.PopulateSymbolTableWithName(name, null, arguments[0].Type); + lhs = BindProperty(payload, arguments[0], dictionary[0], indexerArguments, false); + + int indexOfLast = arguments.Length - 1; + EXPR rhs = CreateArgumentEXPR(arguments[indexOfLast], dictionary[indexOfLast]); + + if (arguments[0] == null) + { + throw Error.BindBinaryAssignmentFailedNullReference(); + } + + return m_binder.bindAssignment(lhs, rhs, bIsCompound); + } + #endregion + + #region Events + ///////////////////////////////////////////////////////////////////////////////// + + private EXPR BindIsEvent( + CSharpIsEventBinder binder, + ArgumentObject[] arguments, + Dictionary dictionary) + { + // The IsEvent binder will never be called without an instance object. This + // is because the compiler only gen's this code for dynamic dots. + + EXPR callingObject = CreateLocal(arguments[0].Type, false, dictionary[0]); + MemberLookup mem = new MemberLookup(); + CType boolType = SymbolLoader.GetReqPredefType(PredefinedType.PT_BOOL); + bool result = false; + + if (arguments[0].Value == null) + { + throw Error.NullReferenceOnMemberException(); + } + + Debug.Assert(m_bindingContext.ContextForMemberLookup() != null); + SymWithType swt = m_symbolTable.LookupMember( + binder.Name, + callingObject, + m_bindingContext.ContextForMemberLookup(), + 0, + mem, + false, + false); + + // If lookup returns an actual event, then this is an event. + if (swt != null && swt.Sym.getKind() == SYMKIND.SK_EventSymbol) + { + result = true; + } + + // If lookup returns the backing field of a field-like event, then + // this is an event. This is due to the Dev10 design change around + // the binding of +=, and the fact that the "IsEvent" binding question + // is only ever asked about the LHS of a += or -=. + if (swt != null && swt.Sym.getKind() == SYMKIND.SK_FieldSymbol && swt.Sym.AsFieldSymbol().isEvent) + { + result = true; + } + + return m_exprFactory.CreateConstant(boolType, ConstValFactory.GetBool(result)); + } + #endregion + + private string GetName(DynamicMetaObjectBinder payload) + { + string result = null; + if (payload is CSharpGetMemberBinder) + { + result = ((CSharpGetMemberBinder)payload).Name; + } + else if (payload is CSharpSetMemberBinder) + { + result = ((CSharpSetMemberBinder)payload).Name; + } + else if (payload is CSharpGetIndexBinder || payload is CSharpSetIndexBinder) + { + result = SpecialNames.Indexer; + } + + Debug.Assert(result != null); + return result; + } + + ///////////////////////////////////////////////////////////////////////////////// + + private BindingFlag GetBindingFlags(DynamicMetaObjectBinder payload) + { + if ((payload is CSharpGetMemberBinder) || + (payload is CSharpGetIndexBinder)) + { + return BindingFlag.BIND_RVALUEREQUIRED; + } + return 0; + } + } +} diff --git a/Microsoft.CSharp/Microsoft/CSharp/RuntimeBinderController.cs b/Microsoft.CSharp/Microsoft/CSharp/RuntimeBinderController.cs new file mode 100644 index 000000000..e74e530cb --- /dev/null +++ b/Microsoft.CSharp/Microsoft/CSharp/RuntimeBinderController.cs @@ -0,0 +1,22 @@ +// ==++== +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// ==--== + +using Microsoft.CSharp.RuntimeBinder.Errors; + +namespace Microsoft.CSharp.RuntimeBinder +{ + ///////////////////////////////////////////////////////////////////////////////// + // This class merely wraps a controller and throws a runtime binder exception + // whenever we get an error during binding. + + internal class RuntimeBinderController : CController + { + public override void SubmitError(CError pError) + { + throw new RuntimeBinderException(pError.Text); + } + } +} diff --git a/Microsoft.CSharp/Microsoft/CSharp/RuntimeBinderException.cs b/Microsoft.CSharp/Microsoft/CSharp/RuntimeBinderException.cs new file mode 100644 index 000000000..fd32365c5 --- /dev/null +++ b/Microsoft.CSharp/Microsoft/CSharp/RuntimeBinderException.cs @@ -0,0 +1,62 @@ +// ==++== +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// ==--== + +using System; +using System.Runtime.Serialization; + +namespace Microsoft.CSharp.RuntimeBinder +{ + /// + /// Represents an error that occurs while processing a dynamic bind in the C# runtime binder. Exceptions of this type differ from in that + /// represents a failure to bind in the sense of a usual compiler error, whereas + /// represents a malfunctioning of the runtime binder itself. + /// +#if !SILVERLIGHT + [Serializable] +#endif + public class RuntimeBinderException : Exception + { + /// + /// Initializes a new instance of the class. + /// + public RuntimeBinderException() + : base() + { + } + + /// + /// Initializes a new instance of the class with a specified error message. + /// + /// The error message that explains the reason for the exception. + public RuntimeBinderException(string message) + : base(message) + { + } + + /// + /// Initializes a new instance of the class with a specified error message + /// and a reference to the inner exception that is the cause of this exception. + /// + /// The error message that explains the reason for the exception. + /// The exception that is the cause of the current exception, or a null reference if no inner exception is specified. + public RuntimeBinderException(string message, Exception innerException) + : base(message, innerException) + { + } + +#if !SILVERLIGHT + /// + /// Initializes a new instance of the class with serialized data. + /// + /// The that holds the serialized object data about the exception being thrown. + /// The that contains contextual information about the source or destination. + protected RuntimeBinderException(SerializationInfo info, StreamingContext context) + : base(info, context) + { + } +#endif + } +} \ No newline at end of file diff --git a/Microsoft.CSharp/Microsoft/CSharp/RuntimeBinderInternalCompilerException.cs b/Microsoft.CSharp/Microsoft/CSharp/RuntimeBinderInternalCompilerException.cs new file mode 100644 index 000000000..3b61ac5ec --- /dev/null +++ b/Microsoft.CSharp/Microsoft/CSharp/RuntimeBinderInternalCompilerException.cs @@ -0,0 +1,62 @@ +// ==++== +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// ==--== + +using System; +using System.Runtime.Serialization; + +namespace Microsoft.CSharp.RuntimeBinder +{ + /// + /// Represents an error that occurs while processing a dynamic bind in the C# runtime binder. Exceptions of this type differ from in that + /// represents a failure to bind in the sense of a usual compiler error, whereas + /// represents a malfunctioning of the runtime binder itself. + /// +#if !SILVERLIGHT + [Serializable] +#endif + public class RuntimeBinderInternalCompilerException : Exception + { + /// + /// Initializes a new instance of the class. + /// + public RuntimeBinderInternalCompilerException() + : base() + { + } + + /// + /// Initializes a new instance of the class with a specified error message. + /// + /// The error message that explains the reason for the exception. + public RuntimeBinderInternalCompilerException(string message) + : base(message) + { + } + + /// + /// Initializes a new instance of the class with a specified error message + /// and a reference to the inner exception that is the cause of this exception. + /// + /// The error message that explains the reason for the exception. + /// The exception that is the cause of the current exception, or a null reference if no inner exception is specified. + public RuntimeBinderInternalCompilerException(string message, Exception innerException) + : base(message, innerException) + { + } + +#if !SILVERLIGHT + /// + /// Initializes a new instance of the class with serialized data. + /// + /// The that holds the serialized object data about the exception being thrown. + /// The that contains contextual information about the source or destination. + protected RuntimeBinderInternalCompilerException(SerializationInfo info, StreamingContext context) + : base(info, context) + { + } +#endif + } +} \ No newline at end of file diff --git a/Microsoft.CSharp/Microsoft/CSharp/SpecialNames.cs b/Microsoft.CSharp/Microsoft/CSharp/SpecialNames.cs new file mode 100644 index 000000000..ef951f877 --- /dev/null +++ b/Microsoft.CSharp/Microsoft/CSharp/SpecialNames.cs @@ -0,0 +1,60 @@ +// ==++== +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// ==--== + +namespace Microsoft.CSharp.RuntimeBinder +{ + internal static class SpecialNames + { + public const string ImplicitConversion = "op_Implicit"; + public const string ExplicitConversion = "op_Explicit"; + public const string Invoke = "Invoke"; + public const string Constructor = ".ctor"; + public const string Indexer = "$Item$"; + + // Binary Operators + public const string CLR_Add = "op_Addition"; + public const string CLR_Subtract = "op_Subtraction"; + public const string CLR_Multiply = "op_Multiply"; + public const string CLR_Division = "op_Division"; + public const string CLR_Modulus = "op_Modulus"; + public const string CLR_LShift = "op_LeftShift"; + public const string CLR_RShift = "op_RightShift"; + public const string CLR_LT = "op_LessThan"; + public const string CLR_GT = "op_GreaterThan"; + public const string CLR_LTE = "op_LessThanOrEqual"; + public const string CLR_GTE = "op_GreaterThanOrEqual"; + public const string CLR_Equality = "op_Equality"; + public const string CLR_Inequality = "op_Inequality"; + public const string CLR_BitwiseAnd = "op_BitwiseAnd"; + public const string CLR_ExclusiveOr = "op_ExclusiveOr"; + public const string CLR_BitwiseOr = "op_BitwiseOr"; + public const string CLR_LogicalNot = "op_LogicalNot"; + + // In place binary operators. + public const string CLR_InPlaceAdd = "op_Addition"; + public const string CLR_InPlaceSubtract = "op_Subtraction"; + public const string CLR_InPlaceMultiply = "op_Multiply"; + public const string CLR_InPlaceDivide = "op_Division"; + public const string CLR_InPlaceModulus = "op_Modulus"; + public const string CLR_InPlaceBitwiseAnd = "op_BitwiseAnd"; + public const string CLR_InPlaceExclusiveOr = "op_ExclusiveOr"; + public const string CLR_InPlaceBitwiseOr = "op_BitwiseOr"; + public const string CLR_InPlaceLShift = "op_LeftShift"; + public const string CLR_InPlaceRShift = "op_RightShift"; + + // Unary Operators + public const string CLR_UnaryNegation = "op_UnaryNegation"; + public const string CLR_UnaryPlus = "op_UnaryPlus"; + public const string CLR_OnesComplement = "op_OnesComplement"; + public const string CLR_True = "op_True"; + public const string CLR_False = "op_False"; + + public const string CLR_PreIncrement = "op_Increment"; + public const string CLR_PostIncrement = "op_Increment"; + public const string CLR_PreDecrement = "op_Decrement"; + public const string CLR_PostDecrement = "op_Decrement"; + } +} diff --git a/Microsoft.CSharp/Microsoft/CSharp/SymbolTable.cs b/Microsoft.CSharp/Microsoft/CSharp/SymbolTable.cs new file mode 100644 index 000000000..7d85efafd --- /dev/null +++ b/Microsoft.CSharp/Microsoft/CSharp/SymbolTable.cs @@ -0,0 +1,2318 @@ +// ==++== +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// ==--== + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Diagnostics; +using System.Globalization; +using System.Linq; +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using System.Runtime.InteropServices.WindowsRuntime; +using System.Security.Permissions; +using Microsoft.CSharp.RuntimeBinder.Semantics; +using Microsoft.CSharp.RuntimeBinder.Syntax; + +namespace Microsoft.CSharp.RuntimeBinder +{ + internal class SymbolTable + { + ///////////////////////////////////////////////////////////////////////////////// + // Members + private HashSet m_typesWithConversionsLoaded; + private HashSet m_namesLoadedForEachType; + + // Members from the managed binder. + private SYMTBL m_symbolTable; + private SymFactory m_symFactory; + private NameManager m_nameManager; + private TypeManager m_typeManager; + private BSYMMGR m_bsymmgr; + private CSemanticChecker m_semanticChecker; + + private NamespaceSymbol m_rootNamespace; + private InputFile m_infile; + +#if !SILVERLIGHT + private static Func s_IsInvokableDelegate = GetIsInvokableDelegate(); + + private static Func GetIsInvokableDelegate() + { + Func isInvokableDelegate = null; + + MethodInfo isInvokable = typeof(MethodBase).GetMethod( + "get_IsDynamicallyInvokable", + BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, + null, + Type.EmptyTypes, + null); + + if (isInvokable != null) + { + AppDomain currentDomain = AppDomain.CurrentDomain; + + // AppDomains in a Windows Store app process are always Homogenous and fully trusted + if (currentDomain.IsHomogenous && currentDomain.IsFullyTrusted) + { + try + { + isInvokableDelegate = (Func)isInvokable.CreateDelegate(typeof(Func)); + } + catch (System.Security.SecurityException) + { + // Microsoft.CSharp.dll is fully transparent so we cannot do a security assert here. + // CreateDelegate will do a MemberAccess demand here since MethodBase.IsDynamicallyInvokable is internal. + // That will fail with a SecurityException in partial trust in which case we will know that we are not in + // a Windows Store app process. We can ignore that exception because the Reflection Safe feature + // is only enabled in a Windows Store app process. In classic scenarios IsDynamicallyInvokable will alwasy be true anyway. + // This is really a hack as we don't have time to make IsDynamicallyInvokable public in Dev11 and we don't + // want to make Microsoft.CSharp a friend of mscorlib. This code should be replaced with a static call + // to IsDynamicallyInvokable when we make it public in Dev12. + } + } + } + + return isInvokableDelegate; + } +#endif // SILVERLIGHT + + ///////////////////////////////////////////////////////////////////////////////// + + private sealed class NameHashKey + { + internal readonly Type type; + internal readonly string name; + + public NameHashKey(Type type, string name) + { + this.type = type; + this.name = name; + } + + public override bool Equals(object obj) + { + NameHashKey h = obj as NameHashKey; + return h != null && type.Equals(h.type) && name.Equals(h.name); + } + + public override int GetHashCode() + { + return type.GetHashCode() ^ name.GetHashCode(); + } + } + + ///////////////////////////////////////////////////////////////////////////////// + + internal SymbolTable( + SYMTBL symTable, + SymFactory symFactory, + NameManager nameManager, + TypeManager typeManager, + BSYMMGR bsymmgr, + CSemanticChecker semanticChecker, + + InputFile infile) + { + m_symbolTable = symTable; + m_symFactory = symFactory; + m_nameManager = nameManager; + m_typeManager = typeManager; + m_bsymmgr = bsymmgr; + m_semanticChecker = semanticChecker; + + m_infile = infile; + + ClearCache(); + } + + ///////////////////////////////////////////////////////////////////////////////// + + internal void ClearCache() + { + m_typesWithConversionsLoaded = new HashSet(); + m_namesLoadedForEachType = new HashSet(); + m_rootNamespace = m_bsymmgr.GetRootNS(); + + // Now populate object. + LoadSymbolsFromType(typeof(object)); + } + + ///////////////////////////////////////////////////////////////////////////////// + + internal void PopulateSymbolTableWithName( + string name, + IEnumerable typeArguments, + Type callingType) + { + // The first argument is the object that we're calling off of. + if (callingType.IsGenericType) + { + callingType = callingType.GetGenericTypeDefinition(); + } + if (name == SpecialNames.Indexer) + { + // TODO: What about named indexers? + if (callingType == typeof(string)) + { + name = "Chars"; + } + else + { + name = "Item"; + } + } + NameHashKey key = new NameHashKey(callingType, name); + + // If we've already populated this name/type pair, then just leave. + if (m_namesLoadedForEachType.Contains(key)) + { + return; + } + + // Add the names. + IEnumerable members = AddNamesOnType(key, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static); + + // Take each member and load each type's conversions into the symbol table. + if (members != null) + { + foreach (MemberInfo member in members) + { + if (member is MethodInfo) + { + foreach (ParameterInfo param in (member as MethodInfo).GetParameters()) + { + AddConversionsForType(param.ParameterType); + } + } + else if (member is ConstructorInfo) + { + foreach (ParameterInfo param in (member as ConstructorInfo).GetParameters()) + { + AddConversionsForType(param.ParameterType); + } + } + } + } + + // Take each type argument and load its conversions into the symbol table. + if (typeArguments != null) + { + foreach (Type o in typeArguments) + { + AddConversionsForType(o); + } + } + } + + ///////////////////////////////////////////////////////////////////////////////// + + internal SymWithType LookupMember( + string name, + EXPR callingObject, + ParentSymbol context, + int arity, + MemberLookup mem, + bool allowSpecialNames, + bool requireInvocable) + { + CType type = callingObject.type; + + if (type.IsArrayType()) + { + type = m_semanticChecker.GetSymbolLoader().GetReqPredefType(PredefinedType.PT_ARRAY); + } + if (type.IsNullableType()) + { + type = type.AsNullableType().GetAts(m_semanticChecker.GetSymbolLoader().GetErrorContext()); + } + + if (!mem.Lookup( + m_semanticChecker, + type, + callingObject, + context, + GetName(name), + arity, + MemLookFlags.TypeVarsAllowed | + (allowSpecialNames ? 0 : MemLookFlags.UserCallable) | + (name == SpecialNames.Indexer ? MemLookFlags.Indexer : 0) | + (name == SpecialNames.Constructor ? MemLookFlags.Ctor : 0) | + (requireInvocable ? MemLookFlags.MustBeInvocable : 0))) + { + return null; + } + return mem.SwtFirst(); + } + + ///////////////////////////////////////////////////////////////////////////////// + + #region InheritanceHierarchy + private IEnumerable AddNamesOnType(NameHashKey key, BindingFlags flags) + { + Debug.Assert(!m_namesLoadedForEachType.Contains(key)); + + // We need to declare all of its inheritance hierarchy. + List inheritance = CreateInheritanceHierarchyList(key.type); + + // Now add every method as it appears in the inheritance hierarchy. + return AddNamesInInheritanceHierarchy(key.name, flags, inheritance); + } + + ///////////////////////////////////////////////////////////////////////////////// + + private IEnumerable AddNamesInInheritanceHierarchy(string name, BindingFlags flags, List inheritance) + { + IEnumerable result = new MemberInfo[0]; + + foreach (Type t in inheritance) + { + Type type = t; + if (type.IsGenericType) + { + type = type.GetGenericTypeDefinition(); + } + NameHashKey key = new NameHashKey(type, name); + + // Now loop over all methods and add them. + IEnumerable members = from member in type.GetMembers(flags) + where member.Name == name && member.DeclaringType == type + select member; + IEnumerable events = from member in type.GetMembers(flags) + where member.Name == name && member.DeclaringType == type && member is EventInfo + select member; + if (members.Any()) + { + CType cType = GetCTypeFromType(type); + if (!(cType is AggregateType)) + continue; + AggregateSymbol aggregate = (cType as AggregateType).getAggregate(); + FieldSymbol addedField = null; + + // We need to add fields before the actual events, so do the first iteration + // excludint events. + foreach (MemberInfo member in members) + { + if (member is MethodInfo) + { + MethodKindEnum kind = MethodKindEnum.Actual; + if (member.Name == SpecialNames.Invoke) + { + kind = MethodKindEnum.Invoke; + } + else if (member.Name == SpecialNames.ImplicitConversion) + { + kind = MethodKindEnum.ImplicitConv; + } + else if (member.Name == SpecialNames.ExplicitConversion) + { + kind = MethodKindEnum.ExplicitConv; + } + AddMethodToSymbolTable( + member, + aggregate, + kind); + } + else if (member is ConstructorInfo) + { + AddMethodToSymbolTable( + member, + aggregate, + MethodKindEnum.Constructor); + } + else if (member is PropertyInfo) + { + AddPropertyToSymbolTable(member as PropertyInfo, aggregate); + } + else if (member is FieldInfo) + { + // Store this field so that if we also find an event, we can + // mark it as the backing field of the event. + Debug.Assert(addedField == null); + addedField = AddFieldToSymbolTable(member as FieldInfo, aggregate); + } + } + foreach (EventInfo e in events) + { + AddEventToSymbolTable(e, aggregate, addedField); + } + + result = result.Concat(members); + } + + m_namesLoadedForEachType.Add(key); + } + + return result; + } + + ///////////////////////////////////////////////////////////////////////////////// + + private List CreateInheritanceHierarchyList(Type type) + { + List list = new List(); + list.Insert(0, type); + for (Type parent = type.BaseType; parent != null; parent = parent.BaseType) + { + // Load it in the symbol table. + LoadSymbolsFromType(parent); + + // Insert into our list of Types. + list.Insert(0, parent); + } + + // If we have a WinRT type then we should load the members of it's collection interfaces + // as well as those members are on this type as far as the user is concerned. + CType ctype = GetCTypeFromType(type); + if (ctype.IsWindowsRuntimeType()) + { + TypeArray collectioniFaces = ctype.AsAggregateType().GetWinRTCollectionIfacesAll(m_semanticChecker.GetSymbolLoader()); + + for (int i = 0; i < collectioniFaces.size; i++) + { + CType collectionType = collectioniFaces.Item(i); + Debug.Assert(collectionType.isInterfaceType()); + + // Insert into our list of Types. + list.Insert(0, collectionType.AssociatedSystemType); + } + } + return list; + } + #endregion + + #region GetName + ///////////////////////////////////////////////////////////////////////////////// + + private Name GetName(string p) + { + if (p == null) + { + p = string.Empty; + } + return SymbolTable.GetName(p, m_nameManager); + } + + ///////////////////////////////////////////////////////////////////////////////// + + private Name GetName(Type type) + { + string name = type.Name; + if (type.IsGenericType) + { + // Trim the name to remove the ` at the end. + name = name.Split('`')[0]; + } + return SymbolTable.GetName(name, m_nameManager); + } + + ///////////////////////////////////////////////////////////////////////////////// + + internal static Name GetName(string p, NameManager nameManager) + { + Name name = nameManager.Lookup(p); + if (name == null) + { + return nameManager.Add(p); + } + return name; + } + #endregion + + #region TypeParameters + ///////////////////////////////////////////////////////////////////////////////// + + private TypeArray GetMethodTypeParameters(MethodInfo method, MethodSymbol parent) + { + if (method.IsGenericMethod) + { + Type[] genericArguments = method.GetGenericArguments(); + CType[] ctypes = new CType[genericArguments.Length]; + for (int i = 0; i < genericArguments.Length; i++) + { + Type t = genericArguments[i]; + ctypes[i] = LoadMethodTypeParameter(parent, t); + } + + // After we load the type parameters, we need to resolve their bounds. + for (int i = 0; i < genericArguments.Length; i++) + { + Type t = genericArguments[i]; + ctypes[i].AsTypeParameterType().GetTypeParameterSymbol().SetBounds( + m_bsymmgr.AllocParams( + GetCTypeArrayFromTypes(t.GetGenericParameterConstraints()))); + } + return m_bsymmgr.AllocParams(ctypes.Length, ctypes); + } + return BSYMMGR.EmptyTypeArray(); + } + + ///////////////////////////////////////////////////////////////////////////////// + + private TypeArray GetAggregateTypeParameters(Type type, AggregateSymbol agg) + { + if (type.IsGenericType) + { + Type genericDefinition = type.GetGenericTypeDefinition(); + Type[] genericArguments = genericDefinition.GetGenericArguments(); + List ctypes = new List(); + int outerParameters = agg.isNested() ? agg.GetOuterAgg().GetTypeVarsAll().size : 0; + + for (int i = 0; i < genericArguments.Length; i++) + { + // Suppose we have the following: + // + // class A + // { + // class B + // { + // } + // } + // + // B will have m+n generic arguments - { A1, A2, ..., An, B1, B2, ..., Bn }. + // As we enumerate these, we need to skip type parameters whose GenericParameterPosition + // is less than n, since the first n type parameters are { A1, A2, ..., An }. + + Type t = genericArguments[i]; + + if (t.GenericParameterPosition < outerParameters) + { + continue; + } + + CType ctype = null; + if (t.IsGenericParameter && t.DeclaringType == genericDefinition) + { + ctype = LoadClassTypeParameter(agg, t); + } + else + { + ctype = GetCTypeFromType(t); + } + + // We check to make sure we own the type parameter - this is because we're + // currently calculating TypeArgsThis, NOT TypeArgsAll. + if (ctype.AsTypeParameterType().GetOwningSymbol() == agg) + { + ctypes.Add(ctype); + } + } + return m_bsymmgr.AllocParams(ctypes.Count, ctypes.ToArray()); + } + return BSYMMGR.EmptyTypeArray(); + } + + ///////////////////////////////////////////////////////////////////////////////// + + private TypeParameterType LoadClassTypeParameter(AggregateSymbol parent, Type t) + { + for (AggregateSymbol p = parent; p != null; p = p.parent.IsAggregateSymbol() ? p.parent.AsAggregateSymbol() : null) + { + for (TypeParameterSymbol typeParam = m_bsymmgr.LookupAggMember( + GetName(t), p, symbmask_t.MASK_TypeParameterSymbol) as TypeParameterSymbol; + typeParam != null; + typeParam = BSYMMGR.LookupNextSym(typeParam, p, symbmask_t.MASK_TypeParameterSymbol) as TypeParameterSymbol) + { + if (AreTypeParametersEquivalent(typeParam.GetTypeParameterType().AssociatedSystemType, t)) + { + return typeParam.GetTypeParameterType(); + } + } + } + return AddTypeParameterToSymbolTable(parent, null, t, true); + } + + ///////////////////////////////////////////////////////////////////////////////// + + private bool AreTypeParametersEquivalent(Type t1, Type t2) + { + Debug.Assert(t1.IsGenericParameter && t2.IsGenericParameter); + + if (t1 == t2) + { + return true; + } + + Type t1Original = GetOriginalTypeParameterType(t1); + Type t2Original = GetOriginalTypeParameterType(t2); + + return t1Original == t2Original; + } + + ///////////////////////////////////////////////////////////////////////////////// + + // GetOriginalTypeParameterType + // This was added so that LoadClassTypeParameter would not fail to find outer + // type parameters when given a System.Type from an outer class and a matching + // type parameter from in an inner class. In Reflection type parameters are + // always declared by their inner most declaring class. For example, given: + // + // class A { + // class B { } + // } + // + // in Reflection there are two Ts, A's T, and B's T. In our world there is + // only A's T. + // + // So this method here drills down and finds the type parameter type corresponding + // to the position of the given type parameter, from the outer most containing + // type. So in the above example, given B's T from reflection, this will return + // A's T, so that you can make a reference comparison of type parameters coming + // from different nesting levels. + // + // There is an exception, we don't handle the case where you have type parameters + // coming from different partially constructed methods. E.g. + // + // class A { + // public void M { } + // } + // + // A.M + // A.M + // + // In the above two methods, the two U's are different in Reflection. Here we just + // return the type parameter type given if it is in a method, we do not try to + // generalize these occurrences for reference equality. We don't need to because + // the bug this solves, 846409, only suffers problems on class type parameters. + // + private Type GetOriginalTypeParameterType(Type t) + { + Debug.Assert(t.IsGenericParameter); + + int pos = t.GenericParameterPosition; + + Type parentType = t.DeclaringType; + if (parentType != null && parentType.IsGenericType) + { + parentType = parentType.GetGenericTypeDefinition(); + } + + if (t.DeclaringMethod != null) + { + MethodBase parentMethod = t.DeclaringMethod; + + if (parentType.GetGenericArguments() == null || pos >= parentType.GetGenericArguments().Length) + { + return t; + } + } + + while (parentType.GetGenericArguments().Length > pos) + { + Type nextParent = parentType.DeclaringType; + if (nextParent != null && nextParent.IsGenericType) + { + nextParent = nextParent.GetGenericTypeDefinition(); + } + + if (nextParent != null && nextParent.GetGenericArguments() != null && nextParent.GetGenericArguments().Length > pos) + { + parentType = nextParent; + } + else + { + break; + } + } + + return parentType.GetGenericArguments()[pos]; + } + + ///////////////////////////////////////////////////////////////////////////////// + + private TypeParameterType LoadMethodTypeParameter(MethodSymbol parent, Type t) + { + for (Symbol sym = parent.firstChild; sym != null; sym = sym.nextChild) + { + if (!sym.IsTypeParameterSymbol()) + { + continue; + } + + if (AreTypeParametersEquivalent(sym.AsTypeParameterSymbol().GetTypeParameterType().AssociatedSystemType, t)) + { + return sym.AsTypeParameterSymbol().GetTypeParameterType(); + } + } + return AddTypeParameterToSymbolTable(null, parent, t, false); + } + + ///////////////////////////////////////////////////////////////////////////////// + + private TypeParameterType AddTypeParameterToSymbolTable( + AggregateSymbol agg, + MethodSymbol meth, + Type t, + bool bIsAggregate) + { + Debug.Assert((agg != null && bIsAggregate) || (meth != null && !bIsAggregate)); + + TypeParameterSymbol typeParam; + if (bIsAggregate) + { + typeParam = m_symFactory.CreateClassTypeParameter( + GetName(t), + agg, + t.GenericParameterPosition, + t.GenericParameterPosition); + } + else + { + typeParam = m_symFactory.CreateMethodTypeParameter( + GetName(t), + meth, + t.GenericParameterPosition, + t.GenericParameterPosition); + } + + if ((t.GenericParameterAttributes & GenericParameterAttributes.Covariant) != 0) + { + typeParam.Covariant = true; + } + if ((t.GenericParameterAttributes & GenericParameterAttributes.Contravariant) != 0) + { + typeParam.Contravariant = true; + } + + SpecCons cons = SpecCons.None; + + if ((t.GenericParameterAttributes & GenericParameterAttributes.DefaultConstructorConstraint) != 0) + { + cons |= SpecCons.New; + } + if ((t.GenericParameterAttributes & GenericParameterAttributes.ReferenceTypeConstraint) != 0) + { + cons |= SpecCons.Ref; + } + if ((t.GenericParameterAttributes & GenericParameterAttributes.NotNullableValueTypeConstraint) != 0) + { + cons |= SpecCons.Val; + } + + typeParam.SetConstraints(cons); + typeParam.SetAccess(ACCESS.ACC_PUBLIC); + TypeParameterType typeParamType = m_typeManager.GetTypeParameter(typeParam); + + return typeParamType; + } + + #endregion + + #region LoadTypeChain + ///////////////////////////////////////////////////////////////////////////////// + + private CType LoadSymbolsFromType(Type originalType) + { + List declarationChain = BuildDeclarationChain(originalType); + + Type type = originalType; + CType ret = null; + bool bIsByRef = type.IsByRef; + if (bIsByRef) + { + type = type.GetElementType(); + } + + NamespaceOrAggregateSymbol current = m_rootNamespace; + NamespaceOrAggregateSymbol next = null; + + // Go through the declaration chain and add namespaces and types for + // each element in the chain. + for (int i = 0; i < declarationChain.Count; i++) + { + object o = declarationChain[i]; + if (o is Type) + { + Type t = o as Type; + Name name = null; + name = GetName(t); + next = m_symbolTable.LookupSym(name, current, symbmask_t.MASK_AggregateSymbol).AsAggregateSymbol(); + + // Make sure we match arity as well when we find an aggregate. + if (next != null) + { + next = FindSymWithMatchingArity(next as AggregateSymbol, t); + } + + // In the event that two different types exist that have the same name, they + // cannot both have entries in the symbol table with our current architecture. + // This can happen in dynamic, since the runtime binder lives across all + // call sites in an appdomain, and assemblies can have been loaded at runtime + // that have different types with the same name. + + // In the real compiler, this would have been an error and name lookup would + // be ambiguous, but here we never have to lookup names of types for real (only + // names of members). + + // The tactical fix is this: if we encounter this situation, where we have + // identically named types that are not the same, then we are going to clear + // the entire symbol table and restart this binding. This solution is not + // without its own problems, since it is possible to conceive of a single + // dynamic binding that needs to simultaneously know about both of the + // similarly named types, but we are not going to try to solve that + // scenario here. + + if (next != null && next is AggregateSymbol) + { + Type existingType = (next as AggregateSymbol).AssociatedSystemType; + Type newType = t.IsGenericType ? t.GetGenericTypeDefinition() : t; + + // We use "IsEquivalentTo" so that unified local types for NoPIA do + // not trigger a reset. There are other mechanisms to make those sorts + // of types work in some scenarios. + if (!existingType.IsEquivalentTo(newType)) + { + throw new ResetBindException(); + } + } + + // If we haven't found this type yet, then add it to our symbol table. + if (next == null || t.IsNullableType()) + { + // Note that if we have anything other than an AggregateSymbol, + // we must be at the end of the line - that is, nothing else can + // have children. + + CType ctype = ProcessSpecialTypeInChain(current, t); + if (ctype != null) + { + // If we had an aggregate type, its possible we're not at the end. + // This will happen for nullable for instance. + if (ctype.IsAggregateType()) + { + next = ctype.AsAggregateType().GetOwningAggregate(); + } + else + { + ret = ctype; + break; + } + } + else + { + // This is a regular class. + next = AddAggregateToSymbolTable(current, t); + } + } + + if (t == type) + { + ret = GetConstructedType(type, next.AsAggregateSymbol()); + break; + } + } + else if (o is MethodInfo) + { + // We cant be at the end. + Debug.Assert(i + 1 < declarationChain.Count); + ret = ProcessMethodTypeParameter(o as MethodInfo, declarationChain[++i] as Type, current as AggregateSymbol); + break; + } + else + { + Debug.Assert(o is string); + next = AddNamespaceToSymbolTable(current, o as string); + } + current = next; + } + + Debug.Assert(ret != null); + if (bIsByRef) + { + ret = m_typeManager.GetParameterModifier(ret, false); + } + return ret; + } + + ///////////////////////////////////////////////////////////////////////////////// + + private TypeParameterType ProcessMethodTypeParameter(MethodInfo methinfo, Type t, AggregateSymbol parent) + { + MethodSymbol meth = FindMatchingMethod(methinfo, parent); + if (meth == null) + { + meth = AddMethodToSymbolTable(methinfo, parent, MethodKindEnum.Actual); + + // Because we return null from AddMethodToSymbolTable when we have a MethodKindEnum.Actual + // and the method that we're trying to add is a special name, we need to assert that + // we indeed have added a method. This is because no special name should have a method + // type parameter on it. + Debug.Assert(meth != null); + } + return LoadMethodTypeParameter(meth, t); + } + + ///////////////////////////////////////////////////////////////////////////////// + + private CType GetConstructedType(Type type, AggregateSymbol agg) + { + // We've found the one we want, so return it. + if (type.IsGenericType) + { + // If we're a generic type, then we need to add the type arguments. + List types = new List(); + + foreach (Type argument in type.GetGenericArguments()) + { + types.Add(GetCTypeFromType(argument)); + } + + TypeArray typeArray = m_bsymmgr.AllocParams(types.ToArray()); + AggregateType aggType = m_typeManager.GetAggregate(agg, typeArray); + return aggType; + } + CType ctype = agg.getThisType(); + return ctype; + } + + ///////////////////////////////////////////////////////////////////////////////// + + private CType ProcessSpecialTypeInChain(NamespaceOrAggregateSymbol parent, Type t) + { + CType ctype; + if (t.IsGenericParameter) + { + AggregateSymbol agg = parent as AggregateSymbol; + Debug.Assert(agg != null); + ctype = LoadClassTypeParameter(agg, t); + return ctype; + } + else if (t.IsArray) + { + // Now we return an array of nesting level corresponding to the rank. + ctype = m_typeManager.GetArray(GetCTypeFromType(t.GetElementType()), t.GetArrayRank()); + return ctype; + } + else if (t.IsPointer) + { + // Now we return the pointer type that we want. + ctype = m_typeManager.GetPointer(GetCTypeFromType(t.GetElementType())); + return ctype; + } + else if (t.IsNullableType()) + { + // Get a nullable type of the underlying type. + if (t.GetGenericArguments()[0].DeclaringType == t) + { + // If the generic argument for nullable is our child, then we're + // declaring the initial Nullable. + AggregateSymbol agg = m_symbolTable.LookupSym( + GetName(t), parent, symbmask_t.MASK_AggregateSymbol).AsAggregateSymbol(); + if (agg != null) + { + agg = FindSymWithMatchingArity(agg, t); + if (agg != null) + { + Debug.Assert(agg.getThisType().AssociatedSystemType == t); + return agg.getThisType(); + } + } + return AddAggregateToSymbolTable(parent, t).getThisType(); + } + ctype = m_typeManager.GetNullable(GetCTypeFromType(t.GetGenericArguments()[0])); + return ctype; + } + return null; + } + + ///////////////////////////////////////////////////////////////////////////////// + + private static List BuildDeclarationChain(Type callingType) + { + // We need to build the parent chain of the calling type. Since we only + // have the type itself, first we need to build the chain up from the + // type down to the root namespace, then we need to ensure that + // the chain exists in our symbol table by searching from the root namespace + // back down to the calling type. Also note that if we have a method type + // parameter, then we'll also add the MethodBase to the chain. + // + // Note that we'll populate this list in a hybrid way - we'll add the + // types for the type part of the chain, and we'll just add the string names + // of the namespaces. + + // Strip off the ref-ness. + if (callingType.IsByRef) + { + callingType = callingType.GetElementType(); + } + + List callChain = new List(); + for (Type t = callingType; t != null; t = t.DeclaringType) + { + callChain.Add(t); + + if (t.IsGenericParameter && t.DeclaringMethod != null) + { + MethodBase methodBase = t.DeclaringMethod; + ParameterInfo[] parameters = methodBase.GetParameters(); + + bool bAdded = false; + foreach (MethodInfo methinfo in from m in t.DeclaringType.GetMethods(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static) + where m.MetadataToken == methodBase.MetadataToken + select m) + { + if (!methinfo.IsGenericMethod) + { + continue; + } + + Debug.Assert(!bAdded); + callChain.Add(methinfo); + bAdded = true; + } + Debug.Assert(bAdded); + } + } + callChain.Reverse(); + + // Now take out the namespaces and add them to the end of the chain. + + if (callingType.Namespace != null) + { + string[] namespaces = callingType.Namespace.Split('.'); + int index = 0; + foreach (string s in namespaces) + { + callChain.Insert(index++, s); + } + } + return callChain; + } + + ///////////////////////////////////////////////////////////////////////////////// + + private AggregateSymbol FindSymWithMatchingArity(AggregateSymbol aggregateSymbol, Type type) + { + for (AggregateSymbol agg = aggregateSymbol; + agg != null; + agg = BSYMMGR.LookupNextSym(agg, agg.Parent, symbmask_t.MASK_AggregateSymbol) as AggregateSymbol) + { + if (agg.GetTypeVarsAll().size == type.GetGenericArguments().Length) + { + return agg; + } + } + return null; + } + + ///////////////////////////////////////////////////////////////////////////////// + + private NamespaceSymbol AddNamespaceToSymbolTable(NamespaceOrAggregateSymbol parent, string sz) + { + Name name = GetName(sz); + NamespaceSymbol ns = m_symbolTable.LookupSym(name, parent, symbmask_t.MASK_NamespaceSymbol).AsNamespaceSymbol(); + if (ns == null) + { + ns = m_symFactory.CreateNamespace(name, parent as NamespaceSymbol); + } + ns.AddAid(KAID.kaidGlobal); + ns.AddAid(KAID.kaidThisAssembly); + ns.AddAid(m_infile.GetAssemblyID()); + + return ns; + } + #endregion + + #region CTypeFromType + ///////////////////////////////////////////////////////////////////////////////// + + internal CType[] GetCTypeArrayFromTypes(IList types) + { + if (types == null) + { + return null; + } + + CType[] ctypes = new CType[types.Count]; + + int i = 0; + foreach (Type t in types) + { + Debug.Assert(t != null); + ctypes[i++] = GetCTypeFromType(t); + } + + return ctypes; + } + + ///////////////////////////////////////////////////////////////////////////////// + + internal CType GetCTypeFromType(Type t) + { + return LoadSymbolsFromType(t); + } + #endregion + + #region Aggregates + ///////////////////////////////////////////////////////////////////////////////// + + private AggregateSymbol AddAggregateToSymbolTable( + NamespaceOrAggregateSymbol parent, + Type type) + { + AggregateSymbol agg = m_symFactory.CreateAggregate(GetName(type), parent, m_infile, m_typeManager); + agg.AssociatedSystemType = type.IsGenericType ? type.GetGenericTypeDefinition() : type; + agg.AssociatedAssembly = type.Assembly; + + // We have to set the TypeVars, access, and the AggKind before we can set the aggState + // because of the assertion checking the compiler does. + AggKindEnum kind; + if (type.IsInterface) + { + kind = AggKindEnum.Interface; + } + else if (type.IsEnum) + { + kind = AggKindEnum.Enum; + agg.SetUnderlyingType(GetCTypeFromType(Enum.GetUnderlyingType(type)).AsAggregateType()); + } + else if (type.IsValueType) + { + kind = AggKindEnum.Struct; + } + else + { + // If it derives from Delegate or MulticastDelegate, then its + // a delegate type. However, MuticastDelegate itself is not a + // delegate type. + if (type.BaseType != null && + (type.BaseType.FullName == "System.MulticastDelegate" || + type.BaseType.FullName == "System.Delegate") && + type.FullName != "System.MulticastDelegate") + { + kind = AggKindEnum.Delegate; + } + else + { + kind = AggKindEnum.Class; + } + } + agg.SetAggKind(kind); + agg.SetTypeVars(BSYMMGR.EmptyTypeArray()); + + ACCESS access; + if (type.IsPublic) + { + access = ACCESS.ACC_PUBLIC; + } +#if SILVERLIGHT + else if (type.DeclaringType != null) +#else + else if (type.IsNested) +#endif + { + // If its nested, we may have other accessibility options. + if (type.IsNestedAssembly || type.IsNestedFamANDAssem) + { + // Note that we dont directly support NestedFamANDAssem, but we're just + // going to default to internal. + access = ACCESS.ACC_INTERNAL; + } + else if (type.IsNestedFamORAssem) + { + access = ACCESS.ACC_INTERNALPROTECTED; + } + else if (type.IsNestedPrivate) + { + access = ACCESS.ACC_PRIVATE; + } + else if (type.IsNestedFamily) + { + access = ACCESS.ACC_PROTECTED; + } + else + { + Debug.Assert(type.IsPublic || type.IsNestedPublic); + access = ACCESS.ACC_PUBLIC; + } + } + else + { + // We're not public and we're not nested - we must be internal. + access = ACCESS.ACC_INTERNAL; + } + agg.SetAccess(access); + + if (!type.IsGenericParameter) + { + agg.SetTypeVars(GetAggregateTypeParameters(type, agg)); + } + + if (type.IsGenericType) + { + Type genericDefinition = type.GetGenericTypeDefinition(); + Type[] genericArguments = genericDefinition.GetGenericArguments(); + + // After we load the type parameters, we need to resolve their bounds. + for (int i = 0; i < agg.GetTypeVars().size; i++) + { + Type t = genericArguments[i]; + if (agg.GetTypeVars().Item(i).IsTypeParameterType()) + { + agg.GetTypeVars().Item(i).AsTypeParameterType().GetTypeParameterSymbol().SetBounds( + m_bsymmgr.AllocParams( + GetCTypeArrayFromTypes(t.GetGenericParameterConstraints()))); + } + } + } + + agg.SetAnonymousType(false); + agg.SetAbstract(type.IsAbstract); + + { + string typeName = type.FullName; + if (type.IsGenericType) + { + typeName = type.GetGenericTypeDefinition().FullName; + } + if (typeName != null && PredefinedTypeFacts.IsPredefinedType(typeName)) + { + PredefinedTypes.InitializePredefinedType(agg, PredefinedTypeFacts.GetPredefTypeIndex(typeName)); + } + } + agg.SetLayoutError(false); + agg.SetSealed(type.IsSealed); + agg.SetUnmanagedStruct(false); + agg.SetManagedStruct(false); + agg.SetHasExternReference(false); + + agg.SetComImport(type.IsImport); + + AggregateType baseAggType = agg.getThisType(); + if (type.BaseType != null) + { + // type.BaseType can be null for Object or for interface types. + Type t = type.BaseType; + if (t.IsGenericType) + { + t = t.GetGenericTypeDefinition(); + } + agg.SetBaseClass(GetCTypeFromType(t).AsAggregateType()); + } + agg.SetTypeManager(m_typeManager); + agg.SetFirstUDConversion(null); + SetInterfacesOnAggregate(agg, type); + agg.SetHasPubNoArgCtor(type.GetConstructors().Any(c => c.GetParameters().Length == 0)); + + // If we have a delegate, get its invoke and constructor methods as well. + if (agg.IsDelegate()) + { + PopulateSymbolTableWithName(SpecialNames.Constructor, null, type); + PopulateSymbolTableWithName(SpecialNames.Invoke, null, type); + } + + return agg; + } + + ///////////////////////////////////////////////////////////////////////////////// + + private void SetInterfacesOnAggregate(AggregateSymbol aggregate, Type type) + { + Type[] interfaces; + + if (type.IsGenericType) + { + type = type.GetGenericTypeDefinition(); + } + interfaces = type.GetInterfaces(); + + // We wont be able to find the difference between Ifaces and + // IfacesAll anymore - at runtime, the class implements all of its + // Ifaces and IfacesAll, so theres no way to differentiate. + // + // This actually doesn't matter though - for conversions and methodcalls, + // we dont really care where they've come from as long as we know the overall + // set of IfacesAll. + + aggregate.SetIfaces(m_bsymmgr.AllocParams(interfaces.Length, GetCTypeArrayFromTypes(interfaces))); + aggregate.SetIfacesAll(aggregate.GetIfaces()); + } + #endregion + + #region Field + ///////////////////////////////////////////////////////////////////////////////// + + private FieldSymbol AddFieldToSymbolTable(FieldInfo fieldInfo, AggregateSymbol aggregate) + { + FieldSymbol field = m_symbolTable.LookupSym( + GetName(fieldInfo.Name), + aggregate, + symbmask_t.MASK_FieldSymbol) as FieldSymbol; + if (field != null) + { + return field; + } + + field = m_symFactory.CreateMemberVar(GetName(fieldInfo.Name), aggregate, null, 0); + field.AssociatedFieldInfo = fieldInfo; + + field.isStatic = fieldInfo.IsStatic; + ACCESS access; + if (fieldInfo.IsPublic) + { + access = ACCESS.ACC_PUBLIC; + } + else if (fieldInfo.IsPrivate) + { + access = ACCESS.ACC_PRIVATE; + } + else if (fieldInfo.IsFamily) + { + access = ACCESS.ACC_PROTECTED; + } + else if (fieldInfo.IsAssembly || fieldInfo.IsFamilyAndAssembly) + { + access = ACCESS.ACC_INTERNAL; + } + else + { + Debug.Assert(fieldInfo.IsFamilyOrAssembly); + access = ACCESS.ACC_INTERNALPROTECTED; + } + field.SetAccess(access); + field.isReadOnly = fieldInfo.IsInitOnly; + field.isEvent = false; + field.isAssigned = true; + field.SetType(GetCTypeFromType(fieldInfo.FieldType)); + + return field; + } + #endregion + + #region Events + + ///////////////////////////////////////////////////////////////////////////////// + + private EventSymbol AddEventToSymbolTable(EventInfo eventInfo, AggregateSymbol aggregate, FieldSymbol addedField) + { + EventSymbol ev = m_symbolTable.LookupSym( + GetName(eventInfo.Name), + aggregate, + symbmask_t.MASK_EventSymbol) as EventSymbol; + if (ev != null) + { + Debug.Assert(ev.AssociatedEventInfo == eventInfo); + return ev; + } + + ev = m_symFactory.CreateEvent(GetName(eventInfo.Name), aggregate, null); + ev.AssociatedEventInfo = eventInfo; + + // EventSymbol + ACCESS access = ACCESS.ACC_PRIVATE; + if (eventInfo.GetAddMethod(true) != null) + { + ev.methAdd = AddMethodToSymbolTable(eventInfo.GetAddMethod(true), aggregate, MethodKindEnum.EventAccessor); + ev.methAdd.SetEvent(ev); + ev.isOverride = ev.methAdd.IsOverride(); + + access = ev.methAdd.GetAccess(); + } + if (eventInfo.GetRemoveMethod(true) != null) + { + ev.methRemove = AddMethodToSymbolTable(eventInfo.GetRemoveMethod(true), aggregate, MethodKindEnum.EventAccessor); + ev.methRemove.SetEvent(ev); + ev.isOverride = ev.methRemove.IsOverride(); + + access = ev.methRemove.GetAccess(); + } + Debug.Assert(ev.methAdd != null || ev.methRemove != null); + ev.isStatic = false; + ev.type = GetCTypeFromType(eventInfo.EventHandlerType); + + // Symbol + ev.SetAccess(access); + + if (ev.methAdd.RetType.AssociatedSystemType == typeof(EventRegistrationToken) && + ev.methRemove.Params.Item(0).AssociatedSystemType == typeof(EventRegistrationToken)) + { + ev.IsWindowsRuntimeEvent = true; + } + + // If we imported a field on the same aggregate, with the same name, and it also + // has the same type, then that field is the backing field for this event, and + // we mark it as such. This is used for the CSharpIsEventBinder. + // In the case of a WindowsRuntime event, the field will be of type + // EventRegistrationTokenTable. + Type evtRegTokenTable = typeof(EventRegistrationTokenTable<>).MakeGenericType(ev.type.AssociatedSystemType); + if (addedField != null && addedField.GetType() != null && + (addedField.GetType() == ev.type || + addedField.GetType().AssociatedSystemType == evtRegTokenTable)) + { + addedField.isEvent = true; + } + + return ev; + } + #endregion + + #region Properties + ///////////////////////////////////////////////////////////////////////////////// + + internal void AddPredefinedPropertyToSymbolTable(AggregateSymbol type, Name property) + { + AggregateType aggtype = type.getThisType(); + Type t = aggtype.AssociatedSystemType; + + var props = from x in t.GetProperties() + where x.Name == property.Text + select x; + + foreach (PropertyInfo pi in props) + { + AddPropertyToSymbolTable(pi, type); + } + } + + ///////////////////////////////////////////////////////////////////////////////// + + private PropertySymbol AddPropertyToSymbolTable(PropertyInfo property, AggregateSymbol aggregate) + { + Name name; + bool isIndexer = property.GetIndexParameters() != null && property.GetIndexParameters().Length != 0; + + if (isIndexer) + { + name = GetName(SpecialNames.Indexer); + } + else + { + name = GetName(property.Name); + } + PropertySymbol prop = m_symbolTable.LookupSym( + name, + aggregate, + symbmask_t.MASK_PropertySymbol) as PropertySymbol; + + // If we already had one, see if it matches. + if (prop != null) + { + PropertySymbol prevProp = null; + + // We'll have multiple properties with the same name if we have indexers. + // In that case, we need to look at every indexer to see if we find one with + // the matching associated sym that we want. + while (prop != null) + { + if (prop.AssociatedPropertyInfo.IsEquivalentTo(property)) + { + return prop; + } + + prevProp = prop; + prop = m_semanticChecker.SymbolLoader.LookupNextSym(prop, prop.parent, symbmask_t.MASK_PropertySymbol).AsPropertySymbol(); + } + + prop = prevProp; + if (isIndexer) + { + // We have an indexer for a different property info, so + // create a new symbol for it. + prop = null; + } + } + + // If we already had a property but its associated info doesnt match, + // then we repurpose the property that we've found. This can happen + // in the case of generic instantiations. + // + // Note that this is a bit of a hack - the best way to fix this is + // by not depending on the instantiated properties at all, but rather depending + // on their non-instantiated generic form, which can be gotten from the + // parent's generic type definition's member. From there, we'll also need to + // keep track of the instantiation as we move along, so that when we need the + // associated property, we can instantiate it correctly. + // + // This seems far too heavyweight - since we know we will never bind to more + // than one property per payload, lets just blast it each time. + if (prop == null) + { + if (isIndexer) + { + prop = m_semanticChecker.GetSymbolLoader().GetGlobalMiscSymFactory().CreateIndexer(name, aggregate, GetName(property.Name), null); + prop.Params = CreateParameterArray(null, property.GetIndexParameters()); + } + else + { + prop = m_symFactory.CreateProperty(GetName(property.Name), aggregate, null); + prop.Params = BSYMMGR.EmptyTypeArray(); + } + } + prop.AssociatedPropertyInfo = property; + + prop.isStatic = property.GetGetMethod(true) != null ? property.GetGetMethod(true).IsStatic : property.GetSetMethod(true).IsStatic; + prop.isParamArray = DoesMethodHaveParameterArray(property.GetIndexParameters()); + prop.swtSlot = null; + prop.RetType = GetCTypeFromType(property.PropertyType); + prop.isOperator = isIndexer; + + // Determine if its an override. We should always have an accessor, unless + // the metadata was bogus. + if (property.GetAccessors(true) != null) + { + MethodInfo accessor = property.GetAccessors(true)[0]; // Must have at least one. + prop.isOverride = accessor.IsVirtual && accessor.IsHideBySig && accessor.GetBaseDefinition() != accessor; + prop.isHideByName = !accessor.IsHideBySig; + } + + SetParameterDataForMethProp(prop, property.GetIndexParameters()); + + // Get and set. + MethodInfo methGet = property.GetGetMethod(true); + MethodInfo methSet = property.GetSetMethod(true); + ACCESS access = ACCESS.ACC_PRIVATE; + if (methGet != null) + { + prop.methGet = AddMethodToSymbolTable(methGet, aggregate, MethodKindEnum.PropAccessor); + + // If we have an indexed property, leave the method as a method we can call, + // and mark the property as bogus. + if (isIndexer || prop.methGet.Params.size == 0) + { + prop.methGet.SetProperty(prop); + } + else + { + prop.setBogus(true); + prop.methGet.SetMethKind(MethodKindEnum.Actual); + } + + if (prop.methGet.GetAccess() > access) + { + access = prop.methGet.GetAccess(); + } + } + if (methSet != null) + { + prop.methSet = AddMethodToSymbolTable(methSet, aggregate, MethodKindEnum.PropAccessor); + + // If we have an indexed property, leave the method as a method we can call, + // and mark the property as bogus. + if (isIndexer || prop.methSet.Params.size == 1) + { + prop.methSet.SetProperty(prop); + } + else + { + prop.setBogus(true); + prop.methSet.SetMethKind(MethodKindEnum.Actual); + } + + if (prop.methSet.GetAccess() > access) + { + access = prop.methSet.GetAccess(); + } + } + + // The access of the property is the least restrictive access of its getter/setter. + prop.SetAccess(access); + + return prop; + } + + #endregion + + #region Methods + ///////////////////////////////////////////////////////////////////////////////// + + internal void AddPredefinedMethodToSymbolTable(AggregateSymbol type, Name methodName) + { + Type t = type.getThisType().AssociatedSystemType; + + // If we got here, it means we couldn't find it in our initial lookup. Means we haven't loaded it from reflection yet. + // Lets go and do that now. + // Check if we have constructors or not. + if (methodName == m_nameManager.GetPredefinedName(PredefinedName.PN_CTOR)) + { + var ctors = from m in t.GetConstructors() + where m.Name == methodName.Text + select m; + + foreach (ConstructorInfo c in ctors) + { + AddMethodToSymbolTable( + c, + type, + MethodKindEnum.Constructor); + } + } + else + { + var methods = from m in t.GetMethods() + where m.Name == methodName.Text && m.DeclaringType == t + select m; + + foreach (MethodInfo m in methods) + { + AddMethodToSymbolTable( + m, + type, + m.Name == SpecialNames.Invoke ? MethodKindEnum.Invoke : MethodKindEnum.Actual); + } + } + } + + ///////////////////////////////////////////////////////////////////////////////// + + private static bool IsMethodDynamicallyInvokable(MethodBase method) + { +#if SILVERLIGHT + return true; +#else + // If MethodBase.IsDynamicallyInvokable doesn't exist we should fall back to the default behavior: everything is invokable. + return s_IsInvokableDelegate == null || + s_IsInvokableDelegate(method); +#endif + } + + private MethodSymbol AddMethodToSymbolTable(MemberInfo member, AggregateSymbol callingAggregate, MethodKindEnum kind) + { + MethodInfo method = member as MethodInfo; + ConstructorInfo ctor = member as ConstructorInfo; + + Debug.Assert(method != null || ctor != null); + Debug.Assert(member.DeclaringType == member.ReflectedType); + + // If we are trying to add an actual method via MethodKindEnum.Actual, and + // the memberinfo is a special name, and its not static, then return null. + // We'll re-add the thing later with some other method kind. + // + // This will happen for things like indexers and properties. The ones that have + // special names that we DO want to allow adding are things like operators, which + // are static and will not be added again later. + + if (kind == MethodKindEnum.Actual && // MethKindEnum.Actual + (method == null || // Not a ConstructorInfo + (!method.IsStatic && method.IsSpecialName))) // Not static and is a special name + { + return null; + } + + MethodSymbol methodSymbol = FindMatchingMethod(member, callingAggregate); + if (methodSymbol != null) + { + return methodSymbol; + } + + ParameterInfo[] parameters = method != null ? method.GetParameters() : ctor.GetParameters(); + // First create the method. + methodSymbol = m_symFactory.CreateMethod(GetName(member.Name), callingAggregate, null); + methodSymbol.AssociatedMemberInfo = member; + methodSymbol.SetMethKind(kind); + if (kind == MethodKindEnum.ExplicitConv || kind == MethodKindEnum.ImplicitConv) + { + callingAggregate.SetHasConversion(); + methodSymbol.SetConvNext(callingAggregate.GetFirstUDConversion()); + callingAggregate.SetFirstUDConversion(methodSymbol); + } + ACCESS access; + if (method != null) + { + if (method.IsPublic && IsMethodDynamicallyInvokable(method)) + { + access = ACCESS.ACC_PUBLIC; + } + else if (method.IsPrivate || (method.IsPublic && !IsMethodDynamicallyInvokable(method))) + { + access = ACCESS.ACC_PRIVATE; + } + else if (method.IsFamily) + { + access = ACCESS.ACC_PROTECTED; + } + else if (method.IsAssembly || method.IsFamilyAndAssembly) + { + access = ACCESS.ACC_INTERNAL; + } + else + { + Debug.Assert(method.IsFamilyOrAssembly); + access = ACCESS.ACC_INTERNALPROTECTED; + } + } + else + { + Debug.Assert(ctor != null); + if (ctor.IsPublic && IsMethodDynamicallyInvokable(ctor)) + { + access = ACCESS.ACC_PUBLIC; + } + else if (ctor.IsPrivate || (ctor.IsPublic && !IsMethodDynamicallyInvokable(ctor))) + { + access = ACCESS.ACC_PRIVATE; + } + else if (ctor.IsFamily) + { + access = ACCESS.ACC_PROTECTED; + } + else if (ctor.IsAssembly || ctor.IsFamilyAndAssembly) + { + access = ACCESS.ACC_INTERNAL; + } + else + { + Debug.Assert(ctor.IsFamilyOrAssembly); + access = ACCESS.ACC_INTERNALPROTECTED; + } + } + methodSymbol.SetAccess(access); + + methodSymbol.isExtension = false; // We dont support extension methods. + methodSymbol.isExternal = false; + methodSymbol.MetadataToken = member.MetadataToken; + + if (method != null) + { + methodSymbol.typeVars = GetMethodTypeParameters(method, methodSymbol); + methodSymbol.isVirtual = method.IsVirtual; + methodSymbol.isAbstract = method.IsAbstract; + methodSymbol.isStatic = method.IsStatic; + methodSymbol.isOverride = method.IsVirtual && method.IsHideBySig && method.GetBaseDefinition() != method; + methodSymbol.isOperator = IsOperator(method); + methodSymbol.swtSlot = GetSlotForOverride(method); + methodSymbol.isVarargs = (method.CallingConvention & CallingConventions.VarArgs) == CallingConventions.VarArgs; + methodSymbol.RetType = GetCTypeFromType(method.ReturnType); + } + else + { + methodSymbol.typeVars = BSYMMGR.EmptyTypeArray(); + methodSymbol.isVirtual = ctor.IsVirtual; + methodSymbol.isAbstract = ctor.IsAbstract; + methodSymbol.isStatic = ctor.IsStatic; + methodSymbol.isOverride = false; + methodSymbol.isOperator = false; + methodSymbol.swtSlot = null; + methodSymbol.isVarargs = false; + methodSymbol.RetType = m_typeManager.GetVoid(); + } + methodSymbol.modOptCount = GetCountOfModOpts(parameters); + + methodSymbol.useMethInstead = false; + methodSymbol.isParamArray = DoesMethodHaveParameterArray(parameters); + methodSymbol.isHideByName = false; + + methodSymbol.errExpImpl = null; + methodSymbol.Params = CreateParameterArray(methodSymbol.AssociatedMemberInfo, parameters); + methodSymbol.declaration = null; + + SetParameterDataForMethProp(methodSymbol, parameters); + + return methodSymbol; + } + + ///////////////////////////////////////////////////////////////////////////////// + + private void SetParameterDataForMethProp(MethodOrPropertySymbol methProp, ParameterInfo[] parameters) + { + if (parameters.Length > 0) + { + // See if we have a param array. + object[] attributes = parameters[parameters.Length - 1].GetCustomAttributes(false); + if (attributes != null) + { + foreach (object o in attributes) + { + if (o is System.ParamArrayAttribute) + { + methProp.isParamArray = true; + } + } + } + + // Mark the names of the parameters, and their default values. + for (int i = 0; i < parameters.Length; i++) + { + SetParameterAttributes(methProp, parameters, i); + + // Insert the name. + methProp.ParameterNames.Add(GetName(parameters[i].Name)); + } + } + } + + ///////////////////////////////////////////////////////////////////////////////// + + private void SetParameterAttributes(MethodOrPropertySymbol methProp, ParameterInfo[] parameters, int i) + { + if (((parameters[i].Attributes & ParameterAttributes.Optional) != 0) && + !parameters[i].ParameterType.IsByRef) + { + methProp.SetOptionalParameter(i); + PopulateSymbolTableWithName("Value", new Type[] { typeof(Missing) }, typeof(Missing)); // We might need this later + } + + object[] attrs; + + // Get MarshalAsAttribute + if ((parameters[i].Attributes & ParameterAttributes.HasFieldMarshal) != 0) + { + if ((attrs = parameters[i].GetCustomAttributes(typeof(MarshalAsAttribute), false)) != null + && attrs.Length > 0) + { + MarshalAsAttribute attr = (MarshalAsAttribute)attrs[0]; + methProp.SetMarshalAsParameter(i, attr.Value); + } + } + +#if !SILVERLIGHT + // Get IUnknownConstantAttribute + if ((attrs = parameters[i].GetCustomAttributes(typeof(IUnknownConstantAttribute), false)) != null + && attrs.Length > 0) + { + methProp.SetUnknownConstantParameter(i); + } +#endif + +#if !SILVERLIGHT + // GetIDispatchConstantAttribute + if ((attrs = parameters[i].GetCustomAttributes(typeof(IDispatchConstantAttribute), false)) != null + && attrs.Length > 0) + { + methProp.SetDispatchConstantParameter(i); + } +#endif + + // Get the various kinds of default values + if ((attrs = parameters[i].GetCustomAttributes(typeof(DateTimeConstantAttribute), false)) != null + && attrs.Length > 0) + { + // Get DateTimeConstant + + DateTimeConstantAttribute attr = (DateTimeConstantAttribute)attrs[0]; + + ConstValFactory factory = new ConstValFactory(); + CONSTVAL cv = factory.Create(((System.DateTime)attr.Value).Ticks); + CType cvType = m_semanticChecker.GetSymbolLoader().GetReqPredefType(PredefinedType.PT_DATETIME); + methProp.SetDefaultParameterValue(i, cvType, cv); + } + else if ((attrs = parameters[i].GetCustomAttributes(typeof(DecimalConstantAttribute), false)) != null + && attrs.Length > 0) + { + // Get DecimalConstant + + DecimalConstantAttribute attr = (DecimalConstantAttribute)attrs[0]; + + ConstValFactory factory = new ConstValFactory(); + CONSTVAL cv = factory.Create(attr.Value); + CType cvType = m_semanticChecker.GetSymbolLoader().GetOptPredefType(PredefinedType.PT_DECIMAL); + methProp.SetDefaultParameterValue(i, cvType, cv); + } + else if (((parameters[i].Attributes & ParameterAttributes.HasDefault) != 0) && + !parameters[i].ParameterType.IsByRef) + { + // Only set a default value if we have one, and the type that we're + // looking at isn't a by ref type or a type parameter. + + ConstValFactory factory = new ConstValFactory(); + CONSTVAL cv = cv = ConstValFactory.GetNullRef(); + CType cvType = m_semanticChecker.GetSymbolLoader().GetReqPredefType(PredefinedType.PT_OBJECT); + + // We need to use RawDefaultValue, because DefaultValue is too clever. + + if (parameters[i].RawDefaultValue != null) + { + object defValue = parameters[i].RawDefaultValue; + Type defType = defValue.GetType(); + + if (defType == typeof(System.Byte)) + { + cv = factory.Create((System.Byte)defValue); + cvType = m_semanticChecker.GetSymbolLoader().GetReqPredefType(PredefinedType.PT_BYTE); + } + else if (defType == typeof(System.Int16)) + { + cv = factory.Create((System.Int16)defValue); + cvType = m_semanticChecker.GetSymbolLoader().GetReqPredefType(PredefinedType.PT_SHORT); + } + else if (defType == typeof(System.Int32)) + { + cv = factory.Create((System.Int32)defValue); + cvType = m_semanticChecker.GetSymbolLoader().GetReqPredefType(PredefinedType.PT_INT); + } + else if (defType == typeof(System.Int64)) + { + cv = factory.Create((System.Int64)defValue); + cvType = m_semanticChecker.GetSymbolLoader().GetReqPredefType(PredefinedType.PT_LONG); + } + else if (defType == typeof(System.Single)) + { + cv = factory.Create((System.Single)defValue); + cvType = m_semanticChecker.GetSymbolLoader().GetReqPredefType(PredefinedType.PT_FLOAT); + } + else if (defType == typeof(System.Double)) + { + cv = factory.Create((System.Double)defValue); + cvType = m_semanticChecker.GetSymbolLoader().GetReqPredefType(PredefinedType.PT_DOUBLE); + } + else if (defType == typeof(System.Decimal)) + { + cv = factory.Create((System.Decimal)defValue); + cvType = m_semanticChecker.GetSymbolLoader().GetReqPredefType(PredefinedType.PT_DECIMAL); + } + else if (defType == typeof(System.Char)) + { + cv = factory.Create((System.Char)defValue); + cvType = m_semanticChecker.GetSymbolLoader().GetReqPredefType(PredefinedType.PT_CHAR); + } + else if (defType == typeof(System.Boolean)) + { + cv = factory.Create((System.Boolean)defValue); + cvType = m_semanticChecker.GetSymbolLoader().GetReqPredefType(PredefinedType.PT_BOOL); + } + else if (defType == typeof(System.SByte)) + { + cv = factory.Create((System.SByte)defValue); + cvType = m_semanticChecker.GetSymbolLoader().GetReqPredefType(PredefinedType.PT_SBYTE); + } + else if (defType == typeof(System.UInt16)) + { + cv = factory.Create((System.UInt16)defValue); + cvType = m_semanticChecker.GetSymbolLoader().GetReqPredefType(PredefinedType.PT_USHORT); + } + else if (defType == typeof(System.UInt32)) + { + cv = factory.Create((System.UInt32)defValue); + cvType = m_semanticChecker.GetSymbolLoader().GetReqPredefType(PredefinedType.PT_UINT); + } + else if (defType == typeof(System.UInt64)) + { + cv = factory.Create((System.UInt64)defValue); + cvType = m_semanticChecker.GetSymbolLoader().GetReqPredefType(PredefinedType.PT_ULONG); + } + else if (defType == typeof(System.String)) + { + cv = factory.Create((System.String)defValue); + cvType = m_semanticChecker.GetSymbolLoader().GetReqPredefType(PredefinedType.PT_STRING); + } + // if we fall off the end of this cascading if, we get Object/null + // because that's how we initialized the constval. + } + methProp.SetDefaultParameterValue(i, cvType, cv); + } + } + + ///////////////////////////////////////////////////////////////////////////////// + + private MethodSymbol FindMatchingMethod(MemberInfo method, AggregateSymbol callingAggregate) + { + MethodSymbol meth = m_bsymmgr.LookupAggMember(GetName(method.Name), callingAggregate, symbmask_t.MASK_MethodSymbol).AsMethodSymbol(); + while (meth != null) + { + if (meth.AssociatedMemberInfo.IsEquivalentTo(method)) + { + return meth; + } + meth = BSYMMGR.LookupNextSym(meth, callingAggregate, symbmask_t.MASK_MethodSymbol).AsMethodSymbol(); + } + return null; + } + + ///////////////////////////////////////////////////////////////////////////////// + + private uint GetCountOfModOpts(ParameterInfo[] parameters) + { + uint count = 0; +#if !SILVERLIGHT + foreach (ParameterInfo p in parameters) + { + if (p.GetOptionalCustomModifiers() != null) + { + count += (uint)p.GetOptionalCustomModifiers().Length; + } + } +#endif + return count; + } + + ///////////////////////////////////////////////////////////////////////////////// + + private TypeArray CreateParameterArray(MemberInfo associatedInfo, ParameterInfo[] parameters) + { + List types = new List(); + + foreach (ParameterInfo p in parameters) + { + types.Add(GetTypeOfParameter(p, associatedInfo)); + } + + MethodInfo mi = associatedInfo as MethodInfo; + + if (mi != null && (mi.CallingConvention & CallingConventions.VarArgs) == CallingConventions.VarArgs) + { + types.Add(m_typeManager.GetArgListType()); + } + + return m_bsymmgr.AllocParams(types.Count, types.ToArray()); + } + + ///////////////////////////////////////////////////////////////////////////////// + + private CType GetTypeOfParameter(ParameterInfo p, MemberInfo m) + { + Type t = p.ParameterType; + CType ctype; + if (t.IsGenericParameter && t.DeclaringMethod != null && t.DeclaringMethod == m) + { + // If its a method type parameter from ourselves, just find it. + ctype = LoadMethodTypeParameter(FindMethodFromMemberInfo(m), t); + } + else + { + ctype = GetCTypeFromType(t); + } + + // Check if we have an out parameter. +#if SILVERLIGHT && !FEATURE_NETCORE + if (ctype.IsParameterModifierType() && p.IsOut && ((p.Attributes & ParameterAttributes.In) == 0)) +#else + if (ctype.IsParameterModifierType() && p.IsOut && !p.IsIn) +#endif + { + CType parameterType = ctype.AsParameterModifierType().GetParameterType(); + ctype = m_typeManager.GetParameterModifier(parameterType, true); + } + + return ctype; + } + + ///////////////////////////////////////////////////////////////////////////////// + + private bool DoesMethodHaveParameterArray(ParameterInfo[] parameters) + { + if (parameters.Length == 0) + { + return false; + } + + ParameterInfo p = parameters[parameters.Length - 1]; + object[] attributes = p.GetCustomAttributes(false); + + foreach (object o in attributes) + { + if (o is ParamArrayAttribute) + { + return true; + } + } + return false; + } + + ///////////////////////////////////////////////////////////////////////////////// + + private SymWithType GetSlotForOverride(MethodInfo method) + { + if (method.IsVirtual && method.IsHideBySig) + { + MethodInfo baseMethodInfo = method.GetBaseDefinition(); + if (baseMethodInfo == method) + { + // We just found ourselves, so we dont care here. + return null; + } + + // We have the base class method that we're overriding. We can assume + // that all the parent aggregate symbols were added, and that we added + // the methods in order. As such, our parent methods should be in the + // symbol table at this point. + + AggregateSymbol aggregate = GetCTypeFromType(baseMethodInfo.DeclaringType).getAggregate(); + MethodSymbol baseMethod = FindMethodFromMemberInfo(baseMethodInfo); + Debug.Assert(baseMethod != null); + + return new SymWithType(baseMethod, aggregate.getThisType()); + } + return null; + } + + ///////////////////////////////////////////////////////////////////////////////// + + private MethodSymbol FindMethodFromMemberInfo(MemberInfo baseMemberInfo) + { + CType t = GetCTypeFromType(baseMemberInfo.DeclaringType); + Debug.Assert(t.IsAggregateType()); + AggregateSymbol aggregate = t.getAggregate(); + Debug.Assert(aggregate != null); + + MethodSymbol meth = m_semanticChecker.SymbolLoader.LookupAggMember( + GetName(baseMemberInfo.Name), + aggregate, + symbmask_t.MASK_MethodSymbol).AsMethodSymbol(); + for (; + meth != null && !meth.AssociatedMemberInfo.IsEquivalentTo(baseMemberInfo); + meth = m_semanticChecker.SymbolLoader.LookupNextSym(meth, aggregate, symbmask_t.MASK_MethodSymbol).AsMethodSymbol()) ; + + return meth; + } + + ///////////////////////////////////////////////////////////////////////////////// + + internal bool AggregateContainsMethod(AggregateSymbol agg, string szName, symbmask_t mask) + { + return m_semanticChecker.SymbolLoader.LookupAggMember(GetName(szName), agg, mask) != null; + } + #endregion + + #region Conversions + ///////////////////////////////////////////////////////////////////////////////// + + internal void AddConversionsForType(Type type) + { + for (Type t = type; t.BaseType != null; t = t.BaseType) + { + AddConversionsForOneType(t); + } + } + + ///////////////////////////////////////////////////////////////////////////////// + + private void AddConversionsForOneType(Type type) + { + if (type.IsGenericType) + { + type = type.GetGenericTypeDefinition(); + } + + if (m_typesWithConversionsLoaded.Contains(type)) + { + return; + } + m_typesWithConversionsLoaded.Add(type); + + // Always make the aggregate for the type, regardless of whether or not + // there are any conversions. + CType t = GetCTypeFromType(type); + + if (!t.IsAggregateType()) + { + CType endT; + while ((endT = t.GetBaseOrParameterOrElementType()) != null) + { + t = endT; + } + } + + if (t.IsTypeParameterType()) + { + // Add conversions for the bounds. + foreach (CType bound in t.AsTypeParameterType().GetBounds().ToArray()) + { + AddConversionsForType(bound.AssociatedSystemType); + } + return; + } + + Debug.Assert(t is AggregateType); + AggregateSymbol aggregate = t.AsAggregateType().getAggregate(); + + // Now find all the conversions and make them. + IEnumerable conversions = from conversion in type.GetMethods( + BindingFlags.Public | BindingFlags.Static) + where (conversion.Name == SpecialNames.ImplicitConversion || conversion.Name == SpecialNames.ExplicitConversion) + && conversion.DeclaringType == type + && conversion.IsSpecialName + && !conversion.IsGenericMethod + select conversion; + + foreach (MethodInfo conversion in conversions) + { + MethodSymbol method = AddMethodToSymbolTable( + conversion, + aggregate, + conversion.Name == SpecialNames.ImplicitConversion ? + MethodKindEnum.ImplicitConv : + MethodKindEnum.ExplicitConv); + } + + + } + #endregion + + #region Operators + ///////////////////////////////////////////////////////////////////////////////// + + private bool IsOperator(MethodInfo method) + { + return method.IsSpecialName && + method.IsStatic && + (method.Name == SpecialNames.ImplicitConversion || + method.Name == SpecialNames.ExplicitConversion || + + // Binary Operators + method.Name == SpecialNames.CLR_Add || + method.Name == SpecialNames.CLR_Subtract || + method.Name == SpecialNames.CLR_Multiply || + method.Name == SpecialNames.CLR_Division || + method.Name == SpecialNames.CLR_Modulus || + method.Name == SpecialNames.CLR_LShift || + method.Name == SpecialNames.CLR_RShift || + method.Name == SpecialNames.CLR_LT || + method.Name == SpecialNames.CLR_GT || + method.Name == SpecialNames.CLR_LTE || + method.Name == SpecialNames.CLR_GTE || + method.Name == SpecialNames.CLR_Equality || + method.Name == SpecialNames.CLR_Inequality || + method.Name == SpecialNames.CLR_BitwiseAnd || + method.Name == SpecialNames.CLR_ExclusiveOr || + method.Name == SpecialNames.CLR_BitwiseOr || + method.Name == SpecialNames.CLR_LogicalNot || + + // Binary inplace operators. + method.Name == SpecialNames.CLR_InPlaceAdd || + method.Name == SpecialNames.CLR_InPlaceSubtract || + method.Name == SpecialNames.CLR_InPlaceMultiply || + method.Name == SpecialNames.CLR_InPlaceDivide || + method.Name == SpecialNames.CLR_InPlaceModulus || + method.Name == SpecialNames.CLR_InPlaceBitwiseAnd || + method.Name == SpecialNames.CLR_InPlaceExclusiveOr || + method.Name == SpecialNames.CLR_InPlaceBitwiseOr || + method.Name == SpecialNames.CLR_InPlaceLShift || + method.Name == SpecialNames.CLR_InPlaceRShift || + + // Unary Operators + method.Name == SpecialNames.CLR_UnaryNegation || + method.Name == SpecialNames.CLR_UnaryPlus || + method.Name == SpecialNames.CLR_OnesComplement || + method.Name == SpecialNames.CLR_True || + method.Name == SpecialNames.CLR_False || + + method.Name == SpecialNames.CLR_PreIncrement || + method.Name == SpecialNames.CLR_PostIncrement || + method.Name == SpecialNames.CLR_PreDecrement || + method.Name == SpecialNames.CLR_PostDecrement); + } + + #endregion + } + + #region Extensions + ///////////////////////////////////////////////////////////////////////////////// + + internal static class RuntimeBinderExtensions + { + internal static bool IsNullableType(this Type t) + { + return t.IsGenericType && t.GetGenericTypeDefinition() == typeof(Nullable<>); + } + +#if SILVERLIGHT + // In Silverlight, Type.IsEquivalentTo is not public. And worse, Type is a MemberInfo, + // so we need to establish the correct behavior for Type.IsEquivalentTo in contrast + // to the MemberInfo.IsEquivalentTo extension method. + + internal static bool IsEquivalentTo(this Type t1, Type t2) + { + return t1 == t2; + } +#endif + + // This method is intended as a means to detect when MemberInfos are the same, + // modulo the fact that they can appear to have different but equivalent local + // No-PIA types. It is used above, by the symbol table, to determine whether + // or not members have been added to an AggSym or not. + + internal static bool IsEquivalentTo(this MemberInfo mi1, MemberInfo mi2) + { + if (mi1 == null || mi2 == null) + { + return mi1 == null && mi2 == null; + } + + if (mi1 == mi2 || (mi1.DeclaringType.IsGenericallyEqual(mi2.DeclaringType) && mi1.MetadataToken == mi2.MetadataToken)) + { + return true; + } + + if (mi1 is MethodInfo && mi2 is MethodInfo) + { + MethodInfo method1 = mi1 as MethodInfo; + MethodInfo method2 = mi2 as MethodInfo; + ParameterInfo[] pis1; + ParameterInfo[] pis2; + + // Note: we don't allow generic methods here because No-PIA methods + // are never generic, and allowing for them would unnecc. complicate + // the tests. + + return method1 != method2 + && !method1.IsGenericMethod + && !method2.IsGenericMethod + && method1.Name == method2.Name + && method1.DeclaringType.IsEquivalentTo(method2.DeclaringType) + && method1.ReturnType.IsEquivalentTo(method2.ReturnType) + && (pis1 = method1.GetParameters()).Length == (pis2 = method2.GetParameters()).Length + && Enumerable.Zip(pis1, pis2, (pi1, pi2) => pi1.IsEquivalentTo(pi2)).All(x => x); + } + + if (mi1 is ConstructorInfo && mi2 is ConstructorInfo) + { + ConstructorInfo ctor1 = mi1 as ConstructorInfo; + ConstructorInfo ctor2 = mi2 as ConstructorInfo; + ParameterInfo[] pis1; + ParameterInfo[] pis2; + + return ctor1 != ctor2 + && ctor1.DeclaringType.IsEquivalentTo(ctor2.DeclaringType) + && (pis1 = ctor1.GetParameters()).Length == (pis2 = ctor2.GetParameters()).Length + && Enumerable.Zip(pis1, pis2, (pi1, pi2) => pi1.IsEquivalentTo(pi2)).All(x => x); + } + + if (mi1 is PropertyInfo && mi2 is PropertyInfo) + { + PropertyInfo prop1 = mi1 as PropertyInfo; + PropertyInfo prop2 = mi2 as PropertyInfo; + + return prop1 != prop2 + && prop1.Name == prop2.Name + && prop1.DeclaringType.IsEquivalentTo(prop2.DeclaringType) + && prop1.PropertyType.IsEquivalentTo(prop2.PropertyType) + && prop1.GetGetMethod(true).IsEquivalentTo(prop2.GetGetMethod(true)) + && prop1.GetSetMethod(true).IsEquivalentTo(prop2.GetSetMethod(true)); + } + + return false; + } + + internal static bool IsEquivalentTo(this ParameterInfo pi1, ParameterInfo pi2) + { + if (pi1 == null || pi2 == null) + { + return pi1 == null && pi2 == null; + } + + if (pi1 == pi2) + { + return true; + } + + return pi1.ParameterType.IsEquivalentTo(pi2.ParameterType); + } + + internal static bool IsGenericallyEqual(this Type t1, Type t2) + { + if (t1 == null || t2 == null) + { + return t1 == null && t2 == null; + } + + if (t1 == t2) + { + return true; + } + + if (t1.IsGenericType && t2.IsGenericType) + { + Type t1def = t1.GetGenericTypeDefinition(); + Type t2def = t2.GetGenericTypeDefinition(); + + return t1def == t2def; + } + + return false; + } + } + #endregion +} diff --git a/Microsoft.VisualBasic/Microsoft.VisualBasic/VBCore.vb b/Microsoft.VisualBasic/Microsoft.VisualBasic/VBCore.vb new file mode 100644 index 000000000..f270b8425 --- /dev/null +++ b/Microsoft.VisualBasic/Microsoft.VisualBasic/VBCore.vb @@ -0,0 +1,115 @@ +' Copyright (c) Microsoft Corporation. All rights reserved. +Option Strict On +Option Infer On +Option Explicit On +Option Compare Binary + + + +Namespace Global.Microsoft.VisualBasic + Namespace CompilerServices + + + Public Class ProjectData + Private Sub New() + End Sub + Public Overloads Shared Sub SetProjectError(ex As Global.System.Exception) + End Sub + Public Overloads Shared Sub SetProjectError(ex As Global.System.Exception, lErl As Integer) + End Sub + Public Shared Sub ClearProjectError() + End Sub + End Class + + + + Partial Public Class Utils + Public Shared Function CopyArray(arySrc As Global.System.Array, aryDest As Global.System.Array) As Global.System.Array + If arySrc Is Nothing Then + Return aryDest + End If + Dim lLength As Integer + lLength = arySrc.Length + If lLength = 0 Then + Return aryDest + End If + If aryDest.Rank() <> arySrc.Rank() Then + Throw New Global.System.InvalidCastException() + End If + Dim iDim As Integer + For iDim = 0 To aryDest.Rank() - 2 + If aryDest.GetUpperBound(iDim) <> arySrc.GetUpperBound(iDim) Then + Throw New Global.System.ArrayTypeMismatchException() + End If + Next iDim + If lLength > aryDest.Length Then + lLength = aryDest.Length + End If + If arySrc.Rank > 1 Then + Dim LastRank As Integer = arySrc.Rank + Dim lenSrcLastRank As Integer = arySrc.GetLength(LastRank - 1) + Dim lenDestLastRank As Integer = aryDest.GetLength(LastRank - 1) + If lenDestLastRank = 0 Then + Return aryDest + End If + Dim lenCopy As Integer = If(lenSrcLastRank > lenDestLastRank, lenDestLastRank, lenSrcLastRank) + Dim i As Integer + For i = 0 To (arySrc.Length \ lenSrcLastRank) - 1 + Global.System.Array.Copy(arySrc, i * lenSrcLastRank, aryDest, i * lenDestLastRank, lenCopy) + Next i + Else + Global.System.Array.Copy(arySrc, aryDest, lLength) + End If + Return aryDest + End Function + End Class + + + + Public Class StaticLocalInitFlag + Public State As Short + End Class + + + + Public Class IncompleteInitialization + Inherits Global.System.Exception + Public Sub New() + MyBase.New() + End Sub + End Class + + + + Public Class DesignerGeneratedAttribute + Inherits Global.System.Attribute + End Class + + + + Public Class OptionCompareAttribute + Inherits Global.System.Attribute + End Class + + End Namespace + + + + Public Class HideModuleNameAttribute + Inherits Global.System.Attribute + End Class + + + Public Module Constants + Public Const vbCrLf As String = ChrW(13) & ChrW(10) + Public Const vbNewLine As String = ChrW(13) & ChrW(10) + Public Const vbCr As String = ChrW(13) + Public Const vbLf As String = ChrW(10) + Public Const vbBack As String = ChrW(8) + Public Const vbFormFeed As String = ChrW(12) + Public Const vbTab As String = ChrW(9) + Public Const vbVerticalTab As String = ChrW(11) + Public Const vbNullChar As String = ChrW(0) + Public Const vbNullString As String = Nothing + End Module +End Namespace diff --git a/Microsoft.VisualBasic/runtime.latebinder/helpers/Utils.vb b/Microsoft.VisualBasic/runtime.latebinder/helpers/Utils.vb new file mode 100644 index 000000000..af9c09bbc --- /dev/null +++ b/Microsoft.VisualBasic/runtime.latebinder/helpers/Utils.vb @@ -0,0 +1,1489 @@ +' Copyright (c) Microsoft Corporation. All rights reserved. + + + + +Imports System +Imports System.Security +Imports System.Security.Permissions +Imports System.Text +Imports System.Globalization +Imports System.Runtime.InteropServices +Imports System.Reflection +Imports System.Diagnostics +Imports Microsoft.VisualBasic.CompilerServices.ExceptionUtils +Imports Microsoft.VisualBasic.CompilerServices.Symbols +'Imports System.Runtime.ConstrainedExecution + +Namespace Microsoft.VisualBasic.CompilerServices + +#If TELESTO Then + 'FIXME: + Public NotInheritable Class Utils +#Else + _ + Partial Public NotInheritable Class Utils +#End If + + ' Prevent creation. + Private Sub New() + End Sub + + Friend Const SEVERITY_ERROR As Integer = &H80000000I + Friend Const FACILITY_CONTROL As Integer = &HA0000I + Friend Const FACILITY_RPC As Integer = &H10000I + Friend Const FACILITY_ITF As Integer = &H40000I + Friend Const SCODE_FACILITY As Integer = &H1FFF0000I + Private Const ERROR_INVALID_PARAMETER As Integer = 87 + + Friend Const chPeriod As Char = "."c + Friend Const chSpace As Char = ChrW(32) + Friend Const chIntlSpace As Char = ChrW(&H3000) + Friend Const chZero As Char = "0"c + Friend Const chHyphen As Char = "-"c + Friend Const chPlus As Char = "+"c + Friend Const chLetterA As Char = "A"c + Friend Const chLetterZ As Char = "Z"c + Friend Const chColon As Char = ":"c + Friend Const chSlash As Char = "/"c + Friend Const chBackslash As Char = "\"c + Friend Const chTab As Char = ControlChars.Tab + Friend Const chCharH0A As Char = ChrW(&HA) + Friend Const chCharH0B As Char = ChrW(&HB) + Friend Const chCharH0C As Char = ChrW(&HC) + Friend Const chCharH0D As Char = ChrW(&HD) + Friend Const chLineFeed As Char = ChrW(10) + Friend Const chDblQuote As Char = ChrW(34) + + Friend Const chGenericManglingChar As Char = "`"c + + Friend Const OptionCompareTextFlags As CompareOptions = (CompareOptions.IgnoreCase Or CompareOptions.IgnoreWidth Or CompareOptions.IgnoreKanaType) + + ' DON'T ACCESS DIRECTLY! Go through the property below + Private Shared m_VBAResourceManager As System.Resources.ResourceManager + Private Shared m_TriedLoadingResourceManager As Boolean + Private Shared ReadOnly ResourceManagerSyncObj As Object = New Object + + Private Shared m_DebugResourceManager As System.Resources.ResourceManager + Private Shared m_TriedLoadingDebugResourceManager As Boolean + Private Shared ReadOnly DebugResourceManagerSyncObj As Object = New Object + + Private Shared m_FallbackResourceManager As System.Resources.ResourceManager + Private Shared m_TriedLoadingFallbackResourceManager As Boolean + Private Shared ReadOnly FallbackResourceManagerSyncObj As Object = New Object + + Private Const ResourceMsgDefault As String = "Message text unavailable. Resource file 'Microsoft.VisualBasic resources' not found." + Private Const VBDefaultErrorID As String = "ID95" + Friend Shared m_achIntlSpace() As Char = {chSpace, chIntlSpace} + Private Shared ReadOnly VoidType As Type = System.Type.GetType("System.Void") + Private Shared m_VBRuntimeAssembly As System.Reflection.Assembly + + '============================================================================ + ' Shared Error functions + '============================================================================ + + Friend Shared ReadOnly Property VBAResourceManager() As System.Resources.ResourceManager + Get + + If Not m_VBAResourceManager Is Nothing Then + Return m_VBAResourceManager + End If + + SyncLock ResourceManagerSyncObj + If Not m_TriedLoadingResourceManager Then + Try + m_VBAResourceManager = New System.Resources.ResourceManager("Microsoft.VisualBasic.LateBinder", VBRuntimeAssembly) + Catch ex As StackOverflowException + Throw ex + Catch ex As OutOfMemoryException + Throw ex + Catch ex As System.Threading.ThreadAbortException + Throw ex + Catch + End Try + m_TriedLoadingResourceManager = True + End If + End SyncLock + + Return m_VBAResourceManager + End Get + End Property + + Friend Shared ReadOnly Property DebugResourceManager() As System.Resources.ResourceManager + Get + + If Not m_DebugResourceManager Is Nothing Then + Return m_DebugResourceManager + End If + + SyncLock DebugResourceManagerSyncObj + If Not m_TriedLoadingDebugResourceManager Then + Try + Dim assemblyString As String = ("Microsoft.VisualBasic.debug.resources, Version=2.0.5.0, Culture=en-US, PublicKeyToken=31bf3856ad364e35") + Dim a As Reflection.Assembly = Reflection.Assembly.Load(assemblyString) + + Dim baseName As String = "Microsoft.VisualBasic.debug" + m_DebugResourceManager = New System.Resources.ResourceManager(baseName, a) + Catch ex As StackOverflowException + Throw ex + Catch ex As OutOfMemoryException + Throw ex + Catch ex As System.Threading.ThreadAbortException + Throw ex + Catch + End Try + m_TriedLoadingDebugResourceManager = True + End If + End SyncLock + + Return m_DebugResourceManager + End Get + End Property + + + Friend Shared ReadOnly Property FallbackResourceManager() As System.Resources.ResourceManager + Get + + If Not m_FallbackResourceManager Is Nothing Then + Return m_FallbackResourceManager + End If + + SyncLock FallbackResourceManagerSyncObj + If Not m_TriedLoadingFallbackResourceManager Then + Try + m_FallbackResourceManager = New System.Resources.ResourceManager("mscorlib", GetType(Object).Assembly) + Catch ex As StackOverflowException + Throw ex + Catch ex As OutOfMemoryException + Throw ex + Catch ex As System.Threading.ThreadAbortException + Throw ex + Catch + End Try + m_TriedLoadingFallbackResourceManager = True + End If + End SyncLock + + Return m_FallbackResourceManager + End Get + End Property + + 'max allowed length of arguments when preparing Uri + 'Same value as in System and System.Core + Private Const trimsize As Integer = 1024 + Private Shared Function GetFallbackMessage(ByVal name As String, ByVal ParamArray args() As Object) As String + Dim result As String = Nothing + Dim curCultureInfo As Globalization.CultureInfo = GetCultureInfo() + + If FallbackResourceManager IsNot Nothing Then + Dim fallbackStr As String + fallbackStr = FallbackResourceManager.GetString("NoDebugResources", Nothing) + + If fallbackStr IsNot Nothing Then + ' build up arg string + Dim sb As New Text.StringBuilder() + If args IsNot Nothing Then + For i As Integer = 0 To args.Length - 1 + Dim value As String = TryCast(args(i), String) + If value IsNot Nothing Then + If value.Length <= trimsize Then + sb.Append(value) + Else + sb.Append(value.Substring(0, trimsize - 3) + "...") + End If + If i < args.Length - 1 Then + sb.Append(curCultureInfo.TextInfo.ListSeparator) + End If + End If + Next + End If + Dim argStr As String = sb.ToString() + If argStr Is Nothing Then + argStr = "" + End If + result = String.Format(curCultureInfo, fallbackStr, name, argStr, GetAssemblyFileVersion(), "Microsoft.VisualBasic.dll", UriEncode(name)) + End If + End If + + 'last-ditch effort; just give back name + If result Is Nothing Then + result = name + End If + Return result + End Function + + Private Shared Function GetAssemblyFileVersion() As String + Dim attributes As Object() = VBRuntimeAssembly.GetCustomAttributes(GetType(Reflection.AssemblyFileVersionAttribute), False) + If attributes.Length <> 1 Then + Return "" + End If + Dim fileVersionAttribute As Reflection.AssemblyFileVersionAttribute = TryCast(attributes(0), Reflection.AssemblyFileVersionAttribute) + If fileVersionAttribute Is Nothing Then + Return "" + End If + Return fileVersionAttribute.Version + End Function + + Private Shared Function UriEncode(ByVal url As String) As String + If url Is Nothing Then + Return Nothing + End If + + Dim bytes As Byte() = System.Text.Encoding.UTF8.GetBytes(url) + Dim cSpaces As Integer = 0 + Dim cUnsafe As Integer = 0 + Dim count As Integer = bytes.Length + + + ' count them first + For i As Integer = 0 To count - 1 + Dim ch As Char = ChrW(bytes(i)) + + If ch = " "c Then + cSpaces += 1 + ElseIf Not IsSafe(ch) Then + cUnsafe += 1 + End If + Next + ' nothing to expand? + Dim skipExpand As Boolean = (cSpaces = 0 AndAlso cUnsafe = 0) + + If Not skipExpand Then + ' expand not 'safe' characters into %XX, spaces to +s + Dim expandedBytes(count + cUnsafe * 2) As Byte + Dim pos As Integer = 0 + + For i As Integer = 0 To count - 1 + Dim b As Byte = bytes(i) + Dim ch As Char = ChrW(b) + + If IsSafe(ch) Then + expandedBytes(pos) = b + pos += 1 + ElseIf ch = " "c Then + expandedBytes(pos) = AscW("+"c) + pos += 1 + Else + expandedBytes(pos) = AscW("%"c) + pos += 1 + expandedBytes(pos) = CByte(AscW(IntToHex((b >> 4) And &HF))) + pos += 1 + expandedBytes(pos) = CByte(AscW(IntToHex(b And &HF))) + pos += 1 + End If + Next + bytes = expandedBytes + End If + + Return Text.Encoding.UTF8.GetString(bytes, 0, bytes.Length) + End Function + + Private Shared Function IntToHex(ByVal n As Integer) As Char + System.Diagnostics.Debug.Assert(n < &H10) + + If n <= 9 Then + Return ChrW(n + AscW("0"c)) + Else + Return ChrW(n - 10 + AscW("a"c)) + End If + End Function + + + ' Set of safe chars, from RFC 1738.4 minus '+' + Private Shared Function IsSafe(ByVal ch As Char) As Boolean + If ch >= "a"c AndAlso ch <= "z"c OrElse ch >= "A"c AndAlso ch <= "Z"c OrElse ch >= "0"c AndAlso ch <= "9"c Then + Return True + End If + + Select Case ch + Case "-"c, "_"c, "."c, "!"c, "*"c, "\"c, "("c, ")"c + Return True + End Select + + Return False + End Function + + Friend Shared Function GetResourceString(ByVal ResourceId As vbErrors) As String + Return GetResourceString("ID" & CStr(ResourceId)) + End Function + + +#If TELESTO Then + 'FIXME: + Friend Shared Function GetResourceString(ByVal ResourceKey As String) As String +#Else + _ + Friend Shared Function GetResourceString(ByVal ResourceKey As String) As String +#End If + + Dim s As String = Nothing + + Try + If VBAResourceManager IsNot Nothing Then + s = VBAResourceManager.GetString(ResourceKey, Nothing) + End If + If s Is Nothing And DebugResourceManager IsNot Nothing Then + s = DebugResourceManager.GetString(ResourceKey, Nothing) + End If + ' this may be unknown error, so try getting default message + If s Is Nothing And DebugResourceManager IsNot Nothing Then + s = DebugResourceManager.GetString(VBDefaultErrorID) + End If + + 'if we have found nothing, most likely the debug resources are missing. + 'get a fallback message. + If s Is Nothing Then + s = GetFallbackMessage(ResourceKey) + End If + Catch ex As StackOverflowException + Throw ex + Catch ex As OutOfMemoryException + Throw ex + Catch ex As System.Threading.ThreadAbortException + Throw ex + Catch + s = ResourceMsgDefault + End Try + + Return s + End Function + + Friend Shared Function GetResourceString(ByVal ResourceKey As String, ByVal NotUsed As Boolean) As String + 'This version does NOT return a default message if not found. + Dim s As String = Nothing + + Try + If VBAResourceManager IsNot Nothing Then + s = VBAResourceManager.GetString(ResourceKey, Nothing) + End If + If s Is Nothing And DebugResourceManager IsNot Nothing Then + s = DebugResourceManager.GetString(ResourceKey, Nothing) + End If + Catch ex As StackOverflowException + Throw ex + Catch ex As OutOfMemoryException + Throw ex + Catch ex As System.Threading.ThreadAbortException + Throw ex + Catch + s = Nothing + End Try + Return s + End Function + + '***************************************************************************** + ';GetResourceString + ' + 'Summary: Retrieves a resource string and formats it by replacing placeholders + ' with params. For example if the unformatted string is + ' "Hello, {0}" then GetString("StringID", "World") will return "Hello, World" + ' This one is exposed because I have to be able to get at localized error + ' strings from the MY template + ' Param: ID - Identifier for the string to be retrieved + ' Param: Args - An array of params used to replace placeholders. + 'Returns: The resource string if found or an error message string + '***************************************************************************** + Public Shared Function GetResourceString(ByVal ResourceKey As String, ByVal ParamArray Args() As String) As String + + Debug.Assert(Not ResourceKey = "", "ResourceKey is missing") + Debug.Assert(Not Args Is Nothing, "No Args") + + Dim UnformattedString As String = Nothing + Dim FormattedString As String = Nothing + Try + If VBAResourceManager IsNot Nothing Then + UnformattedString = VBAResourceManager.GetString(ResourceKey, Nothing) + End If + If UnformattedString Is Nothing And DebugResourceManager IsNot Nothing Then + UnformattedString = DebugResourceManager.GetString(ResourceKey, Nothing) + End If + + 'if we have found nothing, most likely the debug resources are missing. + 'get a fallback message. + If UnformattedString Is Nothing Then + UnformattedString = GetFallbackMessage(ResourceKey, Args) + Else + 'Replace plceholders with items from the passed in array + FormattedString = String.Format(GetCultureInfo(), UnformattedString, Args) + End If + + 'Rethrow hosting exceptions + Catch ex As StackOverflowException + Throw ex + Catch ex As OutOfMemoryException + Throw ex + Catch ex As System.Threading.ThreadAbortException + Throw ex + Catch ex As Exception + End Try + + 'Return the string if we have one otherwise return a default error message + If FormattedString IsNot Nothing Then + Return FormattedString + ElseIf UnformattedString IsNot Nothing Then + Return UnformattedString + Else + Return ResourceKey + End If + End Function +#If Not LATEBINDING Then + ' *** VB6 COMMENTS FOR STDFORMAT FUNCTION *** + ' writing "standard format". We must use '.' for decimal and we must not + ' have a leading zero. First, replace the system decimal with a period. + ' second. Strip the leading zero if one exists. This is post-processing + ' work to deal with standard OLE functionality where all variant conversions + ' are based on the system LCID but where Str$()/Write# is supposed to always + ' use a fixed format. + + Friend Shared Function StdFormat(ByVal s As String) As String + Dim nfi As NumberFormatInfo + Dim iIndex As Integer + Dim c0, c1, c2 As Char + Dim sb As StringBuilder + + nfi = Threading.Thread.CurrentThread.CurrentCulture.NumberFormat + iIndex = s.IndexOf(nfi.NumberDecimalSeparator) + + If iIndex = -1 Then + Return s + End If + + Try + c0 = s.Chars(0) + c1 = s.Chars(1) + c2 = s.Chars(2) + Catch ex As StackOverflowException + Throw ex + Catch ex As OutOfMemoryException + Throw ex + Catch ex As System.Threading.ThreadAbortException + Throw ex + Catch + 'Ignore, should default to 0 values + End Try + + If s.Chars(iIndex) = chPeriod Then + 'Optimization: no period replacement needed + 'avoids creating stringbuilder and copying string + + 'If format is "0.xxxx" then replace 0 with space + If c0 = chZero AndAlso c1 = chPeriod Then + Return s.Substring(1) + + 'If format is "-0.xxxx", "+0.xxxx", " 0.xxxx" then shift everything down over the zero + ElseIf (c0 = chHyphen OrElse c0 = chPlus OrElse c0 = chSpace) AndAlso c1 = chZero AndAlso c2 = chPeriod Then + 'Fall down below and use a stringbuilder + Else + 'No change + Return s + End If + End If + + sb = New StringBuilder(s) + sb.Chars(iIndex) = chPeriod ' change decimal separator to "." + + 'If format is "0.xxxx" then replace 0 with space + If (c0 = chZero AndAlso c1 = chPeriod) Then + StdFormat = sb.ToString(1, sb.Length - 1) + 'If format is "-0.xxxx", "+0.xxxx", " 0.xxxx" then shift everything down over the zero + ElseIf (c0 = chHyphen OrElse c0 = chPlus OrElse c0 = chSpace) AndAlso c1 = chZero AndAlso c2 = chPeriod Then + sb.Remove(1, 1) + StdFormat = sb.ToString() + Else + StdFormat = sb.ToString() + End If + End Function +#If Not TELESTO Then + Friend Shared Function OctFromLong(ByVal Val As Long) As String + 'System.Radix is being removed from the .NET platform, so compute this locally. + Dim Buffer As String = "" + Dim ModVal As Integer + Dim CharZero As Integer = Convert.ToInt32(chZero) + Dim Negative As Boolean + + If Val < 0 Then + Val = Int64.MaxValue + Val + 1 + Negative = True + End If + + 'Pull apart the number and put the digits (in reverse order) into the buffer. + Do + ModVal = CInt(Val Mod 8) + Val = Val >> 3 + Buffer = Buffer & ChrW(ModVal + CharZero) + Loop While Val > 0 + + Buffer = StrReverse(Buffer) + + If Negative Then + Buffer = "1" & Buffer + End If + + Return Buffer + End Function + + Friend Shared Function OctFromULong(ByVal Val As ULong) As String + 'System.Radix is being removed from the .NET platform, so compute this locally. + Dim Buffer As String = "" + Dim ModVal As Integer + Dim CharZero As Integer = Convert.ToInt32(chZero) + + 'Pull apart the number and put the digits (in reverse order) into the buffer. + Do + ModVal = CInt(Val Mod 8UL) + Val = Val >> 3 + Buffer = Buffer & ChrW(ModVal + CharZero) + Loop While Val <> 0UL + + Buffer = StrReverse(Buffer) + + Return Buffer + End Function +#End If 'not TELESTO + +#If Not TELESTO Then 'In TELESTO we don't allow them to set the time or date + '*** SECURITY CHECK - SECURITY CHECK - SECURITY CHECK - SECURITY CHECK - SECURITY CHECK - SECURITY CHECK *** + _ + _ + _ + _ + Friend Shared Sub SetTime(ByVal dtTime As DateTime) + Dim systime As New NativeTypes.SystemTime + + SafeNativeMethods.GetLocalTime(systime) + + systime.wHour = CShort(dtTime.Hour) + systime.wMinute = CShort(dtTime.Minute) + systime.wSecond = CShort(dtTime.Second) + systime.wMilliseconds = CShort(dtTime.Millisecond) + + If UnsafeNativeMethods.SetLocalTime(systime) = 0 Then + If Marshal.GetLastWin32Error() = ERROR_INVALID_PARAMETER Then + Throw New ArgumentException(GetResourceString(ResID.Argument_InvalidValue)) + Else + Throw New SecurityException(GetResourceString(ResID.SetLocalTimeFailure)) + End If + End If + + End Sub + + '*** SECURITY CHECK - SECURITY CHECK - SECURITY CHECK - SECURITY CHECK - SECURITY CHECK - SECURITY CHECK *** + _ + _ + _ + _ + Friend Shared Sub SetDate(ByVal vDate As DateTime) + Dim systime As New NativeTypes.SystemTime + + SafeNativeMethods.GetLocalTime(systime) + + systime.wYear = CShort(vDate.Year) + systime.wMonth = CShort(vDate.Month) + systime.wDay = CShort(vDate.Day) + + If UnsafeNativeMethods.SetLocalTime(systime) = 0 Then + If Marshal.GetLastWin32Error() = ERROR_INVALID_PARAMETER Then + Throw New ArgumentException(GetResourceString(ResID.Argument_InvalidValue)) + Else + Throw New SecurityException(GetResourceString(ResID.SetLocalDateFailure)) + End If + End If + + End Sub +#End If + Friend Shared Function GetDateTimeFormatInfo() As DateTimeFormatInfo + Return System.Threading.Thread.CurrentThread.CurrentCulture.DateTimeFormat + End Function + + Public Shared Sub ThrowException(ByVal hr As Integer) + Throw VbMakeException(hr) + End Sub + +#If Not TELESTO Then + Friend Shared Function MapHRESULT(ByVal lNumber As Integer) As Integer + If lNumber > 0 Then + Return lNumber + End If + + If (lNumber And SCODE_FACILITY) = FACILITY_CONTROL Then + Return (lNumber And &HFFFFI) + End If + + Select Case lNumber + + ' FACILITY_NULL errors + Case E_NOTIMPL + MapHRESULT = vbErrors.NotYetImplemented + + Case E_NOINTERFACE + MapHRESULT = vbErrors.OLENotSupported + + Case E_ABORT + MapHRESULT = vbErrors.Abort + + ' FACILITY_DISPATCH - IDispatch errors. + Case DISP_E_UNKNOWNINTERFACE + MapHRESULT = vbErrors.OLENoPropOrMethod + Case DISP_E_MEMBERNOTFOUND + MapHRESULT = vbErrors.OLENoPropOrMethod + Case DISP_E_PARAMNOTFOUND + MapHRESULT = vbErrors.NamedParamNotFound + Case DISP_E_TYPEMISMATCH + MapHRESULT = vbErrors.TypeMismatch + Case DISP_E_UNKNOWNNAME + MapHRESULT = vbErrors.OLENoPropOrMethod + Case DISP_E_NONAMEDARGS + MapHRESULT = vbErrors.NamedArgsNotSupported + Case DISP_E_BADVARTYPE + MapHRESULT = vbErrors.InvalidTypeLibVariable + Case DISP_E_OVERFLOW + MapHRESULT = vbErrors.Overflow + Case DISP_E_BADINDEX + MapHRESULT = vbErrors.OutOfBounds + Case DISP_E_UNKNOWNLCID + MapHRESULT = vbErrors.LocaleSettingNotSupported + Case DISP_E_ARRAYISLOCKED + MapHRESULT = vbErrors.ArrayLocked + Case DISP_E_BADPARAMCOUNT + MapHRESULT = vbErrors.FuncArityMismatch + Case DISP_E_PARAMNOTOPTIONAL + MapHRESULT = vbErrors.ParameterNotOptional + Case DISP_E_NOTACOLLECTION + MapHRESULT = vbErrors.NotEnum + Case DISP_E_DIVBYZERO + MapHRESULT = vbErrors.DivByZero + ' FACILITY_DISPATCH - Typelib errors. + Case TYPE_E_BUFFERTOOSMALL + MapHRESULT = vbErrors.BufferTooSmall + Case &H80028017I + MapHRESULT = vbErrors.IdentNotMember + Case TYPE_E_INVDATAREAD + MapHRESULT = vbErrors.InvDataRead + Case TYPE_E_UNSUPFORMAT + MapHRESULT = vbErrors.UnsupFormat + Case TYPE_E_REGISTRYACCESS + MapHRESULT = vbErrors.RegistryAccess + Case TYPE_E_LIBNOTREGISTERED + MapHRESULT = vbErrors.LibNotRegistered + Case TYPE_E_UNDEFINEDTYPE + MapHRESULT = vbErrors.UndefinedType + Case TYPE_E_QUALIFIEDNAMEDISALLOWED + MapHRESULT = vbErrors.QualifiedNameDisallowed + Case TYPE_E_INVALIDSTATE + MapHRESULT = vbErrors.InvalidState + Case TYPE_E_WRONGTYPEKIND + MapHRESULT = vbErrors.WrongTypeKind + Case TYPE_E_ELEMENTNOTFOUND + MapHRESULT = vbErrors.ElementNotFound + Case TYPE_E_AMBIGUOUSNAME + MapHRESULT = vbErrors.AmbiguousName + Case TYPE_E_NAMECONFLICT + MapHRESULT = vbErrors.ModNameConflict + Case TYPE_E_UNKNOWNLCID + MapHRESULT = vbErrors.UnknownLcid + Case TYPE_E_DLLFUNCTIONNOTFOUND + MapHRESULT = vbErrors.InvalidDllFunctionName + Case TYPE_E_BADMODULEKIND + MapHRESULT = vbErrors.BadModuleKind + Case TYPE_E_SIZETOOBIG + MapHRESULT = vbErrors.SizeTooBig + Case TYPE_E_TYPEMISMATCH + MapHRESULT = vbErrors.TypeMismatch + Case TYPE_E_OUTOFBOUNDS + MapHRESULT = vbErrors.OutOfBounds + Case TYPE_E_IOERROR + MapHRESULT = vbErrors.IOError + Case TYPE_E_CANTCREATETMPFILE + MapHRESULT = vbErrors.CantCreateTmpFile + Case TYPE_E_CANTLOADLIBRARY + MapHRESULT = vbErrors.DLLLoadErr + Case TYPE_E_INCONSISTENTPROPFUNCS + MapHRESULT = vbErrors.InconsistentPropFuncs + Case TYPE_E_CIRCULARTYPE + MapHRESULT = vbErrors.CircularType + + ' FACILITY_STORAGE errors + Case STG_E_INVALIDFUNCTION + MapHRESULT = vbErrors.BadFunctionId + Case STG_E_FILENOTFOUND + MapHRESULT = vbErrors.FileNotFound + Case STG_E_PATHNOTFOUND + MapHRESULT = vbErrors.PathNotFound + Case STG_E_TOOMANYOPENFILES + MapHRESULT = vbErrors.TooManyFiles + Case STG_E_ACCESSDENIED + MapHRESULT = vbErrors.PermissionDenied + Case STG_E_INVALIDHANDLE + MapHRESULT = vbErrors.ReadFault + Case STG_E_INSUFFICIENTMEMORY + MapHRESULT = vbErrors.OutOfMemory + Case STG_E_NOMOREFILES + MapHRESULT = vbErrors.TooManyFiles + Case STG_E_DISKISWRITEPROTECTED + MapHRESULT = vbErrors.PermissionDenied + Case STG_E_SEEKERROR + MapHRESULT = vbErrors.SeekErr + Case STG_E_WRITEFAULT + MapHRESULT = vbErrors.WriteFault + Case STG_E_READFAULT + MapHRESULT = vbErrors.ReadFault + Case STG_E_SHAREVIOLATION + MapHRESULT = vbErrors.PathFileAccess + Case STG_E_LOCKVIOLATION + MapHRESULT = vbErrors.PermissionDenied + Case STG_E_FILEALREADYEXISTS + MapHRESULT = vbErrors.FileAlreadyExists + Case STG_E_MEDIUMFULL + MapHRESULT = vbErrors.DiskFull + Case STG_E_INVALIDHEADER + MapHRESULT = vbErrors.InvDataRead + Case STG_E_INVALIDNAME + MapHRESULT = vbErrors.FileNotFound + Case STG_E_UNKNOWN + MapHRESULT = vbErrors.InvDataRead + Case STG_E_UNIMPLEMENTEDFUNCTION + MapHRESULT = vbErrors.NotYetImplemented + Case STG_E_INUSE + MapHRESULT = vbErrors.PermissionDenied + Case STG_E_NOTCURRENT + MapHRESULT = vbErrors.PermissionDenied + Case STG_E_REVERTED + MapHRESULT = vbErrors.WriteFault + Case STG_E_CANTSAVE + MapHRESULT = vbErrors.IOError + Case STG_E_OLDFORMAT + MapHRESULT = vbErrors.UnsupFormat + Case STG_E_OLDDLL + MapHRESULT = vbErrors.UnsupFormat + Case STG_E_SHAREREQUIRED + MapHRESULT = vbErrors.ShareRequired + Case STG_E_NOTFILEBASEDSTORAGE + MapHRESULT = vbErrors.UnsupFormat + Case STG_E_EXTANTMARSHALLINGS + MapHRESULT = vbErrors.UnsupFormat + + ' FACILITY_ITF errors. + Case CLASS_E_NOTLICENSED + MapHRESULT = vbErrors.CantCreateObject + Case REGDB_E_CLASSNOTREG + MapHRESULT = vbErrors.CantCreateObject + Case MK_E_UNAVAILABLE + MapHRESULT = vbErrors.CantCreateObject + Case MK_E_INVALIDEXTENSION + MapHRESULT = vbErrors.OLEFileNotFound + Case MK_E_CANTOPENFILE + MapHRESULT = vbErrors.OLEFileNotFound + Case CO_E_CLASSSTRING + MapHRESULT = vbErrors.CantCreateObject + Case CO_E_APPNOTFOUND + MapHRESULT = vbErrors.CantCreateObject + Case CO_E_APPDIDNTREG + MapHRESULT = vbErrors.CantCreateObject + + ' FACILITY_WIN32 errors + Case E_ACCESSDENIED + MapHRESULT = vbErrors.PermissionDenied + Case E_OUTOFMEMORY + MapHRESULT = vbErrors.OutOfMemory + Case E_INVALIDARG + MapHRESULT = vbErrors.IllegalFuncCall + Case &H800706BAI + MapHRESULT = vbErrors.ServerNotFound + + ' FACILITY_WINDOWS - I don't know why this differs from FACILITY_WIN32 + Case CO_E_SERVER_EXEC_FAILURE + MapHRESULT = vbErrors.CantCreateObject + + Case Else + + MapHRESULT = lNumber + + End Select + + End Function +#End If 'not TELESTO +#End If + Friend Shared Function GetCultureInfo() As CultureInfo + Return System.Threading.Thread.CurrentThread.CurrentCulture + End Function + +#If Not TELESTO Then + _ + Public Shared Function SetCultureInfo(ByVal Culture As CultureInfo) As System.Object + Dim PreviousCulture As CultureInfo = System.Threading.Thread.CurrentThread.CurrentCulture + System.Threading.Thread.CurrentThread.CurrentCulture = Culture + Return PreviousCulture + End Function +#End If + Friend Shared Function GetInvariantCultureInfo() As CultureInfo + Return CultureInfo.InvariantCulture + End Function + + Friend Shared ReadOnly Property VBRuntimeAssembly() As System.Reflection.Assembly + Get + If Not m_VBRuntimeAssembly Is Nothing Then + Return m_VBRuntimeAssembly + End If + + ' if the cached assembly ref has not been set, then set it here + m_VBRuntimeAssembly = System.Reflection.Assembly.GetExecutingAssembly() + Return m_VBRuntimeAssembly + End Get + End Property +#If Not LATEBINDING Then + 'Helper that gets called for Redim + Public Shared Function CopyArray(ByVal arySrc As System.Array, ByVal aryDest As System.Array) As System.Array + + If arySrc Is Nothing Then + Return aryDest + End If + + Dim lLength As Integer + + lLength = arySrc.Length + If lLength = 0 Then + Return aryDest + End If + + If aryDest.Rank() <> arySrc.Rank() Then + Throw VbMakeException(New InvalidCastException(GetResourceString(ResID.Array_RankMismatch)), vbErrors.OutOfBounds) + End If + + 'Validate the upper has not changed + Dim iDim As Integer + For iDim = 0 To aryDest.Rank() - 2 'Do not check last dimension + If aryDest.GetUpperBound(iDim) <> arySrc.GetUpperBound(iDim) Then + Throw VbMakeException(New ArrayTypeMismatchException(GetResourceString(ResID.Array_TypeMismatch)), vbErrors.OutOfBounds) + End If + Next iDim + + If lLength > aryDest.Length Then + lLength = aryDest.Length + End If + + 'if this is multi-dimensional, we have to do our own copy + 'REVIEW VSW#395788: the BCL should have a member that does this for us + If arySrc.Rank > 1 Then + + Dim LastRank As Integer = arySrc.Rank + Dim lenSrcLastRank As Integer = arySrc.GetLength(LastRank - 1) + Dim lenDestLastRank As Integer = aryDest.GetLength(LastRank - 1) + + 'if the last rank has 0 size, then this array has no elements, so just return + If lenDestLastRank = 0 Then + Return aryDest + End If + + 'get the correct copy length, regardless if the user increased or decreased the last rank's size + Dim lenCopy As Integer = System.Math.Min(lenSrcLastRank, lenDestLastRank) + + Dim i As Integer + 'split the source array into chunks the size of the last rank and copy each chunk one-by-one + For i = 0 To (arySrc.Length \ lenSrcLastRank) - 1 + System.Array.Copy(arySrc, i * lenSrcLastRank, aryDest, i * lenDestLastRank, lenCopy) + Next i + + Else + System.Array.Copy(arySrc, aryDest, lLength) + End If + + Return aryDest + + End Function +#End If + Friend Shared Function ToHalfwidthNumbers(ByVal s As String, ByVal culture As CultureInfo) As String + +#If TELESTO Then 'Telesto doesn't have OS support for doing this mapping + Return s +#Else + Const LANG_CHINESE As Integer = &H4I + Const LANG_JAPANESE As Integer = &H11I + Const LANG_KOREAN As Integer = &H12I + + Dim lcid As Integer = culture.LCID + Dim langid As Integer = (lcid And &H3FF) + + If langid <> LANG_CHINESE AndAlso langid <> LANG_JAPANESE AndAlso langid <> LANG_KOREAN Then + Return s + End If + + Return vbLCMapString( _ + culture, _ + NativeTypes.LCMAP_HALFWIDTH, _ + s) +#End If + +#If 0 Then + 'Keep this around for a while + 'The above code is compatible with VB6, but to be more + 'unicode aware, all languages should support fullwidth numbers &HFF10 - &HFF19 + 'The problem arises when fullwidth decimal and other symbols are used + 'we need to understand what rules should apply when converting these to + 'halfwidth values + For i = 0 To s.Length - 1 + ch = s.Chars(i) + If Convert.ToInt32(ch) > 255 Then + If Char.IsDigit(ch) Then + If sb Is Nothing Then + sb = New Text.StringBuilder(s) + End If + sb.Chars(i) = Convert.ToChar(CShort(Char.GetNumericValue(ch) + &h30)) + ElseIf ch = ChrW(&HFF0E) Then + If sb Is Nothing Then + sb = New Text.StringBuilder(s) + End If + sb.Chars(i) = "."c + End If + End If + + Next i + If sb Is Nothing Then + Return s + End If + Return sb.ToString() +#End If + End Function + + 'CONSIDER: Seems odd that this function would throw exceptions when the name suggests that + 'no exceptions will be thrown in failure cases. + Friend Shared Function IsHexOrOctValue(ByVal Value As String, ByRef i64Value As Int64) As Boolean + + Dim ch As Char + Dim Length As Integer + Dim FirstNonspace As Integer + Dim TmpValue As String + + Length = Value.Length + + Do While (FirstNonspace < Length) + ch = Value.Chars(FirstNonspace) + 'We check that the length is at least FirstNonspace + 2 because otherwise the function + 'will throw undesired exceptions. + If ch = "&"c AndAlso FirstNonspace + 2 < Length Then + GoTo GetSpecialValue + End If + If ch <> chSpace AndAlso ch <> chIntlSpace Then + Return False + End If + FirstNonspace += 1 + Loop + + Return False + +GetSpecialValue: + ch = System.Char.ToLower(Value.Chars(FirstNonspace + 1), CultureInfo.InvariantCulture) + + TmpValue = ToHalfwidthNumbers(Value.Substring(FirstNonspace + 2), GetCultureInfo()) + If ch = "h"c Then + i64Value = System.Convert.ToInt64(TmpValue, 16) + ElseIf ch = "o"c Then + i64Value = System.Convert.ToInt64(TmpValue, 8) + Else + Throw New FormatException + End If + Return True + End Function + + 'CONSIDER: Seems odd that this function would throw exceptions when the name suggests that + 'no exceptions will be thrown in failure cases. + Friend Shared Function IsHexOrOctValue(ByVal Value As String, ByRef ui64Value As UInt64) As Boolean + + Dim ch As Char + Dim Length As Integer + Dim FirstNonspace As Integer + Dim TmpValue As String + + Length = Value.Length + + Do While (FirstNonspace < Length) + ch = Value.Chars(FirstNonspace) + 'We check that the length is at least FirstNonspace + 2 because otherwise the function + 'will throw undesired exceptions. + If ch = "&"c AndAlso FirstNonspace + 2 < Length Then + GoTo GetSpecialValue + End If + If ch <> chSpace AndAlso ch <> chIntlSpace Then + Return False + End If + FirstNonspace += 1 + Loop + + Return False + +GetSpecialValue: + ch = System.Char.ToLower(Value.Chars(FirstNonspace + 1), CultureInfo.InvariantCulture) + + TmpValue = ToHalfwidthNumbers(Value.Substring(FirstNonspace + 2), GetCultureInfo()) + If ch = "h"c Then + ui64Value = System.Convert.ToUInt64(TmpValue, 16) + ElseIf ch = "o"c Then + ui64Value = System.Convert.ToUInt64(TmpValue, 8) + Else + Throw New FormatException + End If + Return True + End Function + + + Friend Shared Function VBFriendlyName(ByVal Obj As Object) As String + If Obj Is Nothing Then + Return "Nothing" + End If + + Return VBFriendlyName(Obj.GetType, Obj) + End Function + + Friend Shared Function VBFriendlyName(ByVal typ As System.Type) As String + Return VBFriendlyNameOfType(typ) + End Function + + Friend Shared Function VBFriendlyName(ByVal typ As System.Type, ByVal o As Object) As String +#If Not TELESTO Then 'No COM in Telesto + If typ.IsCOMObject AndAlso (typ.FullName = "System.__ComObject") Then + Return TypeNameOfCOMObject(o, False) + End If +#End If + Return VBFriendlyNameOfType(typ) + End Function + + Friend Shared Function VBFriendlyNameOfType(ByVal typ As System.Type, Optional ByVal FullName As Boolean = False) As String + + Dim Result As String + Dim ArraySuffix As String + + ArraySuffix = GetArraySuffixAndElementType(typ) + + Debug.Assert(typ IsNot Nothing AndAlso Not typ.IsArray, "Error in array type processing!!!") + + + Dim tc As TypeCode + If typ.IsEnum Then + tc = TypeCode.Object + Else + tc = Type.GetTypeCode(typ) + End If + + Select Case tc + + Case TypeCode.Boolean : Result = "Boolean" + Case TypeCode.SByte : Result = "SByte" + Case TypeCode.Byte : Result = "Byte" + Case TypeCode.Int16 : Result = "Short" + Case TypeCode.UInt16 : Result = "UShort" + Case TypeCode.Int32 : Result = "Integer" + Case TypeCode.UInt32 : Result = "UInteger" + Case TypeCode.Int64 : Result = "Long" + Case TypeCode.UInt64 : Result = "ULong" + Case TypeCode.Decimal : Result = "Decimal" + Case TypeCode.Single : Result = "Single" + Case TypeCode.Double : Result = "Double" + Case TypeCode.DateTime : Result = "Date" + Case TypeCode.Char : Result = "Char" + Case TypeCode.String : Result = "String" + Case TypeCode.DBNull : Result = "DBNull" + + Case Else + + If IsGenericParameter(typ) Then + Result = typ.Name + Exit Select + End If + + Dim Qualifier As String = Nothing 'yes, defaults to nothing but makes a warning go away about use before assignment + Dim Name As String + + Dim GenericArgsSuffix As String = GetGenericArgsSuffix(typ) + + If FullName Then + ' WORK AROUND: System.Type.IsNested is not available in Silverlight CLR. + If typ.DeclaringType IsNot Nothing Then + Qualifier = VBFriendlyNameOfType(typ.DeclaringType, FullName:=True) + Name = typ.Name + Else + Name = typ.FullName + ' Some types do not have FullName + If Name Is Nothing Then + Name = typ.Name + End If + End If + Else + Name = typ.Name + End If + + If GenericArgsSuffix IsNot Nothing Then + Dim ManglingCharIndex As Integer = Name.LastIndexOf(chGenericManglingChar) + + If ManglingCharIndex <> -1 Then + Name = Name.Substring(0, ManglingCharIndex) + End If + + Result = Name & GenericArgsSuffix + Else + Result = Name + End If + + If Qualifier IsNot Nothing Then + Result = Qualifier & chPeriod & Result + End If + + End Select + + + If ArraySuffix IsNot Nothing Then + Result = Result & ArraySuffix + End If + + Return Result + End Function + + Private Shared Function GetArraySuffixAndElementType(ByRef typ As Type) As String + + If Not typ.IsArray Then + Return Nothing + End If + + Dim ArraySuffix As New Text.StringBuilder + + 'Notice the reversing - VB array notation is reverse of clr array notation + 'i.e. (,)() in VB is [][,] in clr + ' + Do + + ArraySuffix.Append("(") + ArraySuffix.Append(","c, typ.GetArrayRank() - 1) + ArraySuffix.Append(")") + + typ = typ.GetElementType + + Loop While typ.IsArray + + Return ArraySuffix.ToString() + End Function + + Private Shared Function GetGenericArgsSuffix(ByVal typ As Type) As String + + If Not typ.IsGenericType Then + Return Nothing + End If + + Dim TypeArgs As Type() = typ.GetGenericArguments + Dim TotalTypeArgsCount As Integer = TypeArgs.Length + Dim TypeArgsCount As Integer = TotalTypeArgsCount + + ' WORK AROUND: System.Type.IsNested is not available in Silverlight CLR. + If typ.DeclaringType IsNot Nothing AndAlso typ.DeclaringType.IsGenericType Then + TypeArgsCount = TypeArgsCount - typ.DeclaringType.GetGenericArguments().Length + End If + + If TypeArgsCount = 0 Then + Return Nothing + End If + + Dim GenericArgsSuffix As New Text.StringBuilder + GenericArgsSuffix.Append("(Of ") + + For i As Integer = TotalTypeArgsCount - TypeArgsCount To TotalTypeArgsCount - 1 + + GenericArgsSuffix.Append(VBFriendlyNameOfType(TypeArgs(i))) + + If i <> TotalTypeArgsCount - 1 Then + GenericArgsSuffix.Append(","c) + End If + Next + + GenericArgsSuffix.Append(")") + + Return GenericArgsSuffix.ToString + End Function + + Friend Shared Function ParameterToString(ByVal Parameter As ParameterInfo) As String + + Dim ResultString As String = "" + Dim ParameterType As Type = Parameter.ParameterType + + If Parameter.IsOptional Then + ResultString &= "[" + End If + + If ParameterType.IsByRef Then + ResultString &= "ByRef " + ParameterType = ParameterType.GetElementType + ElseIf IsParamArray(Parameter) Then + ResultString &= "ParamArray " + End If + + ResultString &= Parameter.Name & " As " & VBFriendlyNameOfType(ParameterType, FullName:=True) + + If Parameter.IsOptional Then + + Dim DefaultValue As Object = Parameter.DefaultValue + + If DefaultValue Is Nothing Then + ResultString &= " = Nothing" + Else + Dim DefaultValueType As System.Type = DefaultValue.GetType + If DefaultValueType IsNot VoidType Then + If IsEnum(DefaultValueType) Then +#If TELESTO Then + Throw new InvalidOperationException() 'FIXME: System.Enum.GetName() is not supported on TELESTO +#Else + ResultString &= " = " & System.Enum.GetName(DefaultValueType, DefaultValue) +#End If + Else + ResultString &= " = " & CStr(DefaultValue) + End If + End If + End If + + ResultString &= "]" + End If + + Return ResultString + End Function + + Public Shared Function MethodToString(ByVal Method As Reflection.MethodBase) As String + + Dim ReturnType As System.Type = Nothing + Dim First As Boolean + MethodToString = "" + + If Method.MemberType = MemberTypes.Method Then ReturnType = DirectCast(Method, MethodInfo).ReturnType + + If Method.IsPublic Then + MethodToString &= "Public " + ElseIf Method.IsPrivate Then + MethodToString &= "Private " + ElseIf Method.IsAssembly Then + MethodToString &= "Friend " + End If + + If (Method.Attributes And System.Reflection.MethodAttributes.Virtual) <> 0 Then + If Not Method.DeclaringType.IsInterface Then + MethodToString &= "Overrides " + End If + ElseIf IsShared(Method) Then + MethodToString &= "Shared " + End If + + Dim Op As UserDefinedOperator = UserDefinedOperator.UNDEF + If IsUserDefinedOperator(Method) Then + Op = MapToUserDefinedOperator(Method) + End If + + If Op <> UserDefinedOperator.UNDEF Then + If Op = UserDefinedOperator.Narrow Then + MethodToString &= "Narrowing " + ElseIf Op = UserDefinedOperator.Widen Then + MethodToString &= "Widening " + End If + MethodToString &= "Operator " + ElseIf ReturnType Is Nothing OrElse ReturnType Is VoidType Then + MethodToString &= "Sub " + Else + MethodToString &= "Function " + End If + + If Op <> UserDefinedOperator.UNDEF Then + MethodToString &= OperatorNames(Op) + ElseIf Method.MemberType = MemberTypes.Constructor Then + MethodToString &= "New" + Else + MethodToString &= Method.Name + End If + + If IsGeneric(Method) Then + MethodToString &= "(Of " + First = True + For Each t As Type In GetTypeParameters(Method) + If Not First Then MethodToString &= ", " Else First = False + MethodToString &= VBFriendlyNameOfType(t) + Next + MethodToString &= ")" + End If + + MethodToString &= "(" + First = True + + For Each Parameter As ParameterInfo In Method.GetParameters() + + If Not First Then + MethodToString &= ", " + Else + First = False + End If + + MethodToString &= ParameterToString(Parameter) + Next + + MethodToString &= ")" + + If ReturnType Is Nothing OrElse ReturnType Is VoidType Then + 'Sub has no return type + Else + MethodToString &= " As " & VBFriendlyNameOfType(ReturnType, FullName:=True) + End If + + End Function + + Private Enum PropertyKind + ReadWrite + [ReadOnly] + [WriteOnly] + End Enum + + Friend Shared Function PropertyToString(ByVal Prop As Reflection.PropertyInfo) As String + + Dim ResultString As String = "" + + Dim Kind As PropertyKind = PropertyKind.ReadWrite + Dim Parameters As ParameterInfo() + Dim PropertyType As Type + + 'Most of the work will be done using the Getter or Setter. + Dim Accessor As MethodInfo = Prop.GetGetMethod + + If Accessor IsNot Nothing Then + If Prop.GetSetMethod IsNot Nothing Then + Kind = PropertyKind.ReadWrite + Else + Kind = PropertyKind.ReadOnly + End If + + Parameters = Accessor.GetParameters + PropertyType = Accessor.ReturnType + Else + Kind = PropertyKind.WriteOnly + + Accessor = Prop.GetSetMethod + Dim SetParameters As ParameterInfo() = Accessor.GetParameters + Parameters = New ParameterInfo(SetParameters.Length - 2) {} + System.Array.Copy(SetParameters, Parameters, Parameters.Length) + PropertyType = SetParameters(SetParameters.Length - 1).ParameterType + End If + + ResultString &= "Public " + + If (Accessor.Attributes And MethodAttributes.Virtual) <> 0 Then + If Not Prop.DeclaringType.IsInterface Then + ResultString &= "Overrides " + End If + ElseIf IsShared(Accessor) Then + ResultString &= "Shared " + End If + + If Kind = PropertyKind.ReadOnly Then ResultString &= "ReadOnly " + If Kind = PropertyKind.WriteOnly Then ResultString &= "WriteOnly " + + ResultString &= "Property " & Prop.Name & "(" + + Dim First As Boolean = True + + For Each Parameter As ParameterInfo In Parameters + If Not First Then ResultString &= ", " Else First = False + + ResultString &= ParameterToString(Parameter) + Next + + ResultString &= ") As " & VBFriendlyNameOfType(PropertyType, FullName:=True) + + Return ResultString + End Function + +#If Not TELESTO Then 'Used by old Everett helper function. + + Friend Shared Function AdjustArraySuffix(ByVal sRank As String) As String + Dim OneChar As Char + Dim RevResult As String = Nothing + Dim length As Integer = sRank.Length + While length > 0 + OneChar = sRank.Chars(length - 1) + Select Case OneChar + Case ")"c + RevResult = RevResult + "("c + Case "("c + RevResult = RevResult + ")"c + Case ","c + RevResult = RevResult + OneChar + Case Else + RevResult = OneChar + RevResult + End Select + length = length - 1 + End While + Return RevResult + End Function + +#End If 'NOT TELESTO + + Friend Shared Function MemberToString(ByVal Member As MemberInfo) As String + Select Case Member.MemberType + Case MemberTypes.Method, MemberTypes.Constructor + Return MethodToString(DirectCast(Member, MethodBase)) + + Case MemberTypes.Field + Return FieldToString(DirectCast(Member, FieldInfo)) + + Case MemberTypes.Property + Return PropertyToString(DirectCast(Member, PropertyInfo)) + + Case Else + Return Member.Name + End Select + End Function + + Friend Shared Function FieldToString(ByVal Field As FieldInfo) As String + Dim rtype As System.Type + FieldToString = "" + + rtype = Field.FieldType + + If Field.IsPublic Then + FieldToString &= "Public " + ElseIf Field.IsPrivate Then + FieldToString &= "Private " + ElseIf Field.IsAssembly Then + FieldToString &= "Friend " + ElseIf Field.IsFamily Then + FieldToString &= "Protected " + ElseIf Field.IsFamilyOrAssembly Then + FieldToString &= "Protected Friend " + End If + + FieldToString &= Field.Name + FieldToString &= " As " + FieldToString &= VBFriendlyNameOfType(rtype, FullName:=True) + End Function + End Class + +#If Not TELESTO Then 'Used by Single Instance code in My.Application + _ + Friend NotInheritable Class SafeMemoryMappedViewOfFileHandle : Inherits Microsoft.Win32.SafeHandles.SafeHandleZeroOrMinusOneIsInvalid + + Friend Sub New() + MyBase.New(True) + End Sub + + Friend Sub New(ByVal handle As System.IntPtr, ByVal ownsHandle As Boolean) + MyBase.New(ownsHandle) + SetHandle(handle) + End Sub + + _ + _ + _ + _ + Protected Overrides Function ReleaseHandle() As Boolean + Try + If UnsafeNativeMethods.UnmapViewOfFile(handle) Then + Return True + End If + Return False + Finally + handle = IntPtr.Zero 'either way mark this as invalid now + End Try + End Function + End Class +#End If '#if Not TELESTO +End Namespace + diff --git a/Microsoft.VisualBasic/runtime/msvbalib/ApplicationServices/ApplicationBase.vb b/Microsoft.VisualBasic/runtime/msvbalib/ApplicationServices/ApplicationBase.vb new file mode 100644 index 000000000..7cfb80c43 --- /dev/null +++ b/Microsoft.VisualBasic/runtime/msvbalib/ApplicationServices/ApplicationBase.vb @@ -0,0 +1,169 @@ +' Copyright (c) Microsoft Corporation. All rights reserved. + +Option Strict On +Option Explicit On + +Imports System.Reflection +Imports System.ComponentModel +Imports System.Security.Permissions +Imports Microsoft.VisualBasic +Imports Microsoft.VisualBasic.MyServices +Imports Microsoft.VisualBasic.CompilerServices +Imports ExUtils = Microsoft.VisualBasic.CompilerServices.ExceptionUtils + + +Namespace Microsoft.VisualBasic.ApplicationServices + + '''************************************************************************** + ''' ;ApplicationBase + ''' + ''' Abstract class that defines the application Startup/Shutdown model for VB + ''' Windows Applications such as console, winforms, dll, service. + ''' + ''' + _ + Public Class ApplicationBase + + + '= PUBLIC ============================================================= + + Public Sub New() + End Sub + + '''************************************************************************** + ''' ;GetEnvironmentVariable + ''' + ''' Return the value of the specified environment variable. + ''' + ''' A String containing the name of the environment variable. + ''' A string containing the value of the environment variable. + ''' if Name is Nothing. (Framework) + ''' if caller does not have EnvironmentPermission.Read. (Framework) + ''' if the specified environment variable does not exist. (Ours) + Public Function GetEnvironmentVariable(ByVal name As String) As String + + ' Framework returns Null if not found. + Dim VariableValue As String = System.Environment.GetEnvironmentVariable(name) + + ' Since the explicity requested a specific environment variable and we couldn't find it, throw + If VariableValue Is Nothing Then + Throw ExUtils.GetArgumentExceptionWithArgName("name", ResID.MyID.EnvVarNotFound_Name, name) + End If + + Return VariableValue + End Function + + '''************************************************************************** + ''' ;Log + ''' + ''' Provides access to logging capability. + ''' + ''' Returns a Microsoft.VisualBasic.Windows.Log object used for logging to OS log, debug window + ''' and a delimited text file or xml log. + ''' + Public ReadOnly Property Log() As Logging.Log + Get + If m_Log Is Nothing Then + m_Log = New Logging.Log + End If + Return m_Log + End Get + End Property + + '''************************************************************************** + ''' ;Info + ''' + ''' Returns the info about the application. If we are executing in a DLL, we still return the info + ''' about the application, not the DLL. + ''' + ''' + ''' + Public ReadOnly Property Info() As AssemblyInfo + _ + Get + If m_Info Is Nothing Then + Dim Assembly As System.Reflection.Assembly = System.Reflection.Assembly.GetEntryAssembly() + If Assembly Is Nothing Then 'It can be nothing if we are an add-in or a dll on the web + Assembly = System.Reflection.Assembly.GetCallingAssembly() + End If + m_Info = New AssemblyInfo(Assembly) + End If + Return m_Info + End Get + End Property + + '********************************************************************** + ';Culture + ' + 'Summary: + ' Get the information about the current culture used by the current thread. + 'Returns: + ' The CultureInfo object that represents the culture used by the current thread. + '********************************************************************** + Public ReadOnly Property Culture() As System.Globalization.CultureInfo + Get + Return System.Threading.Thread.CurrentThread.CurrentCulture + End Get + End Property + + '********************************************************************** + ';UICulture + ' + 'Summary: + ' Get the information about the current culture used by the Resource + ' Manager to look up culture-specific resource at run time. + 'Returns: + ' The CultureInfo object that represents the culture used by the + ' Resource Manager to look up culture-specific resources at run time. + '********************************************************************** + Public ReadOnly Property UICulture() As System.Globalization.CultureInfo + Get + Return System.Threading.Thread.CurrentThread.CurrentUICulture + End Get + End Property + + '********************************************************************** + ';ChangeCulture + ' + 'Summary: + ' Change the culture currently in used by the current thread. + 'Params: + ' CultureName: name of the culture as a String. For a list of possible + ' names, see http://msdn.microsoft.com/library/en-us/cpref/html/frlrfSystemGlobalizationCultureInfoClassTopic.asp + 'Remarks: + ' CultureInfo constructor will throw exceptions if CultureName is Nothing + ' or an invalid CultureInfo ID. We are not catching those exceptions. + ' Because SQL uses fibers, you can't change the culture of a thread. + '********************************************************************** + _ + Public Sub ChangeCulture(ByVal cultureName As String) + System.Threading.Thread.CurrentThread.CurrentCulture = New System.Globalization.CultureInfo(cultureName) + End Sub + + '********************************************************************** + ';ChangeUICulture + ' + 'Summary: + ' Change the culture currently used by the Resource Manager to look + ' up culture-specific resource at runtime. + 'Params: + ' CultureName: name of the culture as a String. For a list of possible + ' names, see http://msdn.microsoft.com/library/en-us/cpref/html/frlrfSystemGlobalizationCultureInfoClassTopic.asp + 'Remarks: + ' CultureInfo constructor will throw exceptions if CultureName is Nothing + ' or an invalid CultureInfo ID. We are not catching those exceptions. + '********************************************************************** + Public Sub ChangeUICulture(ByVal cultureName As String) + System.Threading.Thread.CurrentThread.CurrentUICulture = New System.Globalization.CultureInfo(cultureName) + End Sub + + '= FRIEND ============================================================= + + '= PROTECTED ========================================================== + + '= PRIVATE ========================================================== + + Private m_Log As Logging.Log 'Lazy-initialized and cached log object. + Private m_Info As AssemblyInfo ' The executing application (the EntryAssembly) + End Class 'ApplicationBase +End Namespace diff --git a/Microsoft.VisualBasic/runtime/msvbalib/ApplicationServices/AssemblyInfo.vb b/Microsoft.VisualBasic/runtime/msvbalib/ApplicationServices/AssemblyInfo.vb new file mode 100644 index 000000000..5c1506a06 --- /dev/null +++ b/Microsoft.VisualBasic/runtime/msvbalib/ApplicationServices/AssemblyInfo.vb @@ -0,0 +1,297 @@ +' Copyright (c) Microsoft Corporation. All rights reserved. +Option Strict On +Option Explicit On + +Imports System +Imports System.Reflection +Imports System.Diagnostics +Imports System.Collections +Imports System.Collections.ObjectModel +Imports System.Security.Permissions +Imports Microsoft.VisualBasic.CompilerServices +Imports Microsoft.VisualBasic.CompilerServices.ExceptionUtils + +Namespace Microsoft.VisualBasic.ApplicationServices + + '''************************************************************************** + ''' ;AssemblyInfo + ''' + ''' A class that contains the information about an Application. This information can be + ''' specified using the assembly attributes (contained in AssemblyInfo.vb file in case of + ''' a VB project in Visual Studio .NET). + ''' + ''' This class is based on the FileVersionInfo class of the framework, but + ''' reduced to a number of relevant properties. + _ + Public Class AssemblyInfo + + '= PUBLIC ============================================================= + + '''************************************************************************** + ''' ;New + ''' + ''' Create an AssemblyInfo from an assembly + ''' + ''' The assembly for which we want to obtain the information. + Public Sub New(ByVal currentAssembly As System.Reflection.Assembly) + If CurrentAssembly Is Nothing Then + Throw GetArgumentNullException("CurrentAssembly") + End If + m_Assembly = CurrentAssembly + End Sub + + ' NOTE: All properties below work in Low Trust Zone. + + '''************************************************************************** + ''' ;Description + ''' + ''' Get the description associated with the assembly. + ''' + ''' A String containing the AssemblyDescriptionAttribute associated with the assembly. + ''' if the AssemblyDescriptionAttribute is not defined. + Public ReadOnly Property Description() As String + Get + If m_Description Is Nothing Then + Dim Attribute As AssemblyDescriptionAttribute = _ + CType(GetAttribute(GetType(AssemblyDescriptionAttribute)), AssemblyDescriptionAttribute) + If Attribute Is Nothing Then + m_Description = "" + Else + m_Description = Attribute.Description + End If + End If + Return m_Description + End Get + End Property + + '''************************************************************************** + ''' ;CompanyName + ''' + ''' Get the company name associated with the assembly. + ''' + ''' A String containing the AssemblyCompanyAttribute associated with the assembly. + ''' if the AssemblyCompanyAttribute is not defined. + Public ReadOnly Property CompanyName() As String + Get + If m_CompanyName Is Nothing Then + Dim Attribute As AssemblyCompanyAttribute = _ + CType(GetAttribute(GetType(AssemblyCompanyAttribute)), AssemblyCompanyAttribute) + If Attribute Is Nothing Then + m_CompanyName = "" + Else + m_CompanyName = Attribute.Company + End If + End If + Return m_CompanyName + End Get + End Property + + '''************************************************************************** + ''' ;Title + ''' + ''' Get the title associated with the assembly. + ''' + ''' A String containing the AssemblyTitleAttribute associated with the assembly. + ''' if the AssemblyTitleAttribute is not defined. + Public ReadOnly Property Title() As String + Get + If m_Title Is Nothing Then + Dim Attribute As AssemblyTitleAttribute = _ + CType(GetAttribute(GetType(AssemblyTitleAttribute)), AssemblyTitleAttribute) + If Attribute Is Nothing Then + m_Title = "" + Else + m_Title = Attribute.Title + End If + End If + Return m_Title + End Get + End Property + + '''************************************************************************** + ''' ;Copyright + ''' + ''' Get the copyright notices associated with the assembly. + ''' + ''' A String containing the AssemblyCopyrightAttribute associated with the assembly. + ''' if the AssemblyCopyrightAttribute is not defined. + Public ReadOnly Property Copyright() As String + Get + If m_Copyright Is Nothing Then + Dim Attribute As AssemblyCopyrightAttribute = CType(GetAttribute(GetType(AssemblyCopyrightAttribute)), AssemblyCopyrightAttribute) + If Attribute Is Nothing Then + m_Copyright = "" + Else + m_Copyright = Attribute.Copyright + End If + End If + Return m_Copyright + End Get + End Property + + '''************************************************************************** + ''' ;Trademark + ''' + ''' Get the trademark notices associated with the assembly. + ''' + ''' A String containing the AssemblyTrademarkAttribute associated with the assembly. + ''' if the AssemblyTrademarkAttribute is not defined. + Public ReadOnly Property Trademark() As String + Get + If m_Trademark Is Nothing Then + Dim Attribute As AssemblyTrademarkAttribute = CType(GetAttribute(GetType(AssemblyTrademarkAttribute)), AssemblyTrademarkAttribute) + If Attribute Is Nothing Then + m_Trademark = "" + Else + m_Trademark = Attribute.Trademark + End If + End If + Return m_Trademark + End Get + End Property + + '''************************************************************************** + ''' ;ProductName + ''' + ''' Get the product name associated with the assembly. + ''' + ''' A String containing the AssemblyProductAttribute associated with the assembly. + ''' if the AssemblyProductAttribute is not defined. + Public ReadOnly Property ProductName() As String + Get + If m_ProductName Is Nothing Then + Dim Attribute As AssemblyProductAttribute = CType(GetAttribute(GetType(AssemblyProductAttribute)), AssemblyProductAttribute) + If Attribute Is Nothing Then + m_ProductName = "" + Else + m_ProductName = Attribute.Product + End If + End If + Return m_ProductName + End Get + End Property + + '''************************************************************************** + ''' ;Version + ''' + ''' Get the version number of the assembly. + ''' + ''' A System.Version class containing the version number of the assembly + ''' Cannot use AssemblyVersionAttribute since it always return Nothing. + Public ReadOnly Property Version() As System.Version + Get + ' CONSIDER: Version will not work in low-trust zone. VSWhidbey 192212. + Return m_Assembly.GetName().Version + End Get + End Property + + ' NOTE: The properties below will not work in low-trust zone. + ' .NET Framework will demand FileIOPermission.PathDiscovery. + + '''************************************************************************** + ''' ;AssemblyName + ''' + ''' Get the name of the file containing the manifest (usually the .exe file). + ''' + ''' A String containing the file name. + Public ReadOnly Property AssemblyName() As String + Get + Return m_Assembly.GetName.Name + End Get + End Property + + '''************************************************************************** + ''' ;DirectoryPath + ''' + ''' Gets the directory where the assembly lives. + ''' + ''' + ''' If you are calling this from an EXE, gives you the directory path of the exe assembly. If you + ''' call this from a DLL, it gives you the directory path of the DLL assembly + Public ReadOnly Property DirectoryPath() As String + Get + Return IO.Path.GetDirectoryName(m_Assembly.Location) + End Get + End Property + + '''****************************************************************************** + ''' ;LoadedAssemblies + ''' + ''' Returns the names of all assemblies loaded by the current application. + ''' + ''' A ReadOnlyCollection(Of Assembly) containing all the loaded assemblies. + ''' attempt on an unloaded application domain. + Public ReadOnly Property LoadedAssemblies() As ReadOnlyCollection(Of Reflection.Assembly) + Get + Dim Result As New Collection(Of Reflection.Assembly) + For Each Assembly As Reflection.Assembly In AppDomain.CurrentDomain.GetAssemblies() + Result.Add(Assembly) + Next + Return New ReadOnlyCollection(Of Reflection.Assembly)(Result) + End Get + End Property + + '''****************************************************************************** + ''' ;StackTrace + ''' + ''' Returns the current stack trace information. + ''' + ''' A string containing stack trace information. Value can be String.Empty. + ''' The requested stack trace information is out of range. + Public ReadOnly Property StackTrace() As String + Get + Return Environment.StackTrace + End Get + End Property + + '''****************************************************************************** + ''' ;WorkingSet + ''' + ''' Gets the amount of physical memory mapped to the process context. + ''' + ''' + ''' A 64-bit signed integer containing the size of physical memory mapped to the process context, in bytes. + ''' + Public ReadOnly Property WorkingSet() As Long + Get + Return Environment.WorkingSet + End Get + End Property + + + '= PRIVATE ============================================================ + + '''************************************************************************** + ''' ;GetAttribute + ''' + ''' Get an attribute from the assembly and throw exception if the attribute does not exist. + ''' + ''' The type of the required attribute. + ''' The attribute with the given type gotten from the assembly, or Nothing. + Private Function GetAttribute(ByVal AttributeType As Type) As Object + + Debug.Assert(m_Assembly IsNot Nothing, "Null m_Assembly!!!") + + ' NOTE (MSDN): inherit: This argument is ignored for objects of type Assembly + Dim Attributes() As Object = m_Assembly.GetCustomAttributes(AttributeType, inherit:=True) + + If Attributes.Length = 0 Then + Return Nothing + Else + Return Attributes(0) + End If + End Function + + ' Private fields. + Private m_Assembly As Assembly ' The assembly with the information. + + ' Since these properties will not change during run time, they're cached. + ' "" is not Nothing so use Nothing to mark an un-accessed property. + Private m_Description As String = Nothing ' Cache the assembly's description. + Private m_Title As String = Nothing ' Cache the assembly's title. + Private m_ProductName As String = Nothing ' Cache the assembly's product name. + Private m_CompanyName As String = Nothing ' Cache the assembly's company name. + Private m_Trademark As String = Nothing ' Cache the assembly's trademark. + Private m_Copyright As String = Nothing ' Cache the assembly's copyright. + End Class +End Namespace diff --git a/Microsoft.VisualBasic/runtime/msvbalib/ApplicationServices/ConsoleApplicationBase.vb b/Microsoft.VisualBasic/runtime/msvbalib/ApplicationServices/ConsoleApplicationBase.vb new file mode 100644 index 000000000..71b380c58 --- /dev/null +++ b/Microsoft.VisualBasic/runtime/msvbalib/ApplicationServices/ConsoleApplicationBase.vb @@ -0,0 +1,113 @@ +' Copyright (c) Microsoft Corporation. All rights reserved. + +Option Strict On +Option Explicit On + +Imports System.Reflection +Imports System.ComponentModel +Imports System.Security.Permissions +Imports Microsoft.VisualBasic +Imports Microsoft.VisualBasic.MyServices +Imports Microsoft.VisualBasic.CompilerServices +Imports ExUtils = Microsoft.VisualBasic.CompilerServices.ExceptionUtils + + +Namespace Microsoft.VisualBasic.ApplicationServices + + '''************************************************************************** + ''' ;ConsoleApplicationBase + ''' + ''' Abstract class that defines the application Startup/Shutdown model for VB + ''' Windows Applications such as console, winforms, dll, service. + ''' + ''' + _ + Public Class ConsoleApplicationBase : Inherits ApplicationBase + + '= PUBLIC ============================================================= + + '''************************************************************************** + ''' ;New + ''' + ''' Constructs the application Shutdown/Startup model object + ''' + ''' We have to have a parameterless ctor because the platform specific Application + ''' object derives from this one and it doesn't define a ctor. The partial class generated by the + ''' designer defines the ctor in order to configure the application. + Public Sub New() + MyBase.New() + End Sub + + '''************************************************************************** + ''' ;CommandLineArgs + ''' + ''' Returns the command line arguments for the current application. + ''' + ''' + ''' This function differs from System.Environment.GetCommandLineArgs in that the + ''' path of the executing file (the 0th entry) is omitted from the returned collection + Public ReadOnly Property CommandLineArgs() As System.Collections.ObjectModel.ReadOnlyCollection(Of String) + Get + If m_CommandLineArgs Is Nothing Then + 'Get rid of Arg(0) which is the path of the executing program. Main(args() as string) doesn't report the name of the app and neither will we + Dim EnvArgs As String() = System.Environment.GetCommandLineArgs + If EnvArgs.GetLength(0) >= 2 Then '1 element means no args, just the executing program. >= 2 means executing program + one or more command line arguments + Dim NewArgs(EnvArgs.GetLength(0) - 2) As String 'dimming z(0) gives a z() of 1 element. + System.Array.Copy(EnvArgs, 1, NewArgs, 0, EnvArgs.GetLength(0) - 1) 'copy everything but the 0th element (the path of the executing program) + m_CommandLineArgs = New System.Collections.ObjectModel.ReadOnlyCollection(Of String)(NewArgs) + Else + m_CommandLineArgs = New System.Collections.ObjectModel.ReadOnlyCollection(Of String)(New String() {}) 'provide the empty set + End If + End If + Return m_CommandLineArgs + End Get + End Property + + '''************************************************************************* + ''';Deployment + ''' + ''' Gives access to the current ApplicationDeployment via My + ''' + ''' The current ApplicationDeployment + ''' + Public ReadOnly Property Deployment() As System.Deployment.Application.ApplicationDeployment + Get + Return System.Deployment.Application.ApplicationDeployment.CurrentDeployment + End Get + End Property + + '''************************************************************************* + ''';IsNetworkDeployed + ''' + ''' Indicates whether or not the current application was deployed + ''' + ''' True if the current application was deployed, otherwise False + ''' + Public ReadOnly Property IsNetworkDeployed() As Boolean + Get + Return System.Deployment.Application.ApplicationDeployment.IsNetworkDeployed + End Get + End Property + + '= PROTECTED ============================================================= + + '''************************************************************************* + ''';InternalCommandLine + ''' + ''' Allows derived classes to set what the command line should look like. WindowsFormsApplicationBase calls this + ''' for instance because we snag the command line from Main(). + ''' + ''' + Protected WriteOnly Property InternalCommandLine() As System.Collections.ObjectModel.ReadOnlyCollection(Of String) + Set(ByVal value As System.Collections.ObjectModel.ReadOnlyCollection(Of String)) + m_CommandLineArgs = value + End Set + End Property + + '= FRIEND ============================================================= + + '= PRIVATE ========================================================== + + Private m_CommandLineArgs As System.Collections.ObjectModel.ReadOnlyCollection(Of String) ' Lazy-initialized and cached collection of command line arguments. + End Class 'ApplicationBase +End Namespace diff --git a/Microsoft.VisualBasic/runtime/msvbalib/ApplicationServices/User.vb b/Microsoft.VisualBasic/runtime/msvbalib/ApplicationServices/User.vb new file mode 100644 index 000000000..55297447f --- /dev/null +++ b/Microsoft.VisualBasic/runtime/msvbalib/ApplicationServices/User.vb @@ -0,0 +1,277 @@ +' Copyright (c) Microsoft Corporation. All rights reserved. +Option Explicit On +Option Strict On + +Imports Microsoft.VisualBasic.CompilerServices +Imports System +Imports System.Collections +Imports System.ComponentModel +Imports System.Diagnostics +Imports System.Security.Permissions +Imports System.Security.Principal + +Namespace Microsoft.VisualBasic.ApplicationServices + + '''************************************************************************ + ''';User + ''' + ''' Class abstracting the computer user + ''' + ''' + _ + Public Class User + + '==PUBLIC************************************************************** + + '''******************************************************************** + ''';New + ''' + ''' Creates an instance of User + ''' + ''' + Public Sub New() + End Sub + + '''******************************************************************** + ''';Name + ''' + ''' The name of the current user + ''' + ''' + Public ReadOnly Property Name() As String + Get + Return InternalPrincipal.Identity.Name + End Get + End Property + + '''******************************************************************** + ''';CurrentPrincipal + ''' + ''' The current IPrincipal which represents the current user + ''' + ''' An IPrincipal representing the current user + ''' + Public Property CurrentPrincipal() As IPrincipal + Get + Return InternalPrincipal + End Get + Set(ByVal value As IPrincipal) + InternalPrincipal = value + End Set + End Property + + '''******************************************************************** + ''';InitializeWithWindowsUser + ''' + ''' Sets My.User to point at the logged on windows user. + ''' + ''' + Public Sub InitializeWithWindowsUser() + System.Threading.Thread.CurrentPrincipal = New System.Security.Principal.WindowsPrincipal(System.Security.Principal.WindowsIdentity.GetCurrent) + End Sub + + '''******************************************************************** + ''';IsAuthenticated + ''' + ''' Indicates whether or not the current user has been authenticated. + ''' + ''' + Public ReadOnly Property IsAuthenticated() As Boolean + Get + Return InternalPrincipal.Identity.IsAuthenticated + End Get + End Property + + '''******************************************************************** + ''';IsInRole + ''' + ''' Indicates whether or not the current user is a member of the passed in role + ''' + ''' The name of the role + ''' True if the user is a member of the role otherwise False + ''' + Public Function IsInRole(ByVal role As String) As Boolean + Return InternalPrincipal.IsInRole(role) + End Function + + '''******************************************************************** + ''';IsInRole + ''' + ''' Indicates whether or not the current user is a member of the passed in built in role + ''' + ''' An enum representing a built in role + ''' True if the user is a member of the role otherwise False + ''' + ''' For windows users, the built in roles map to WindowsBuiltInRoles. For non windows, the + ''' built in roles map to the name of the role (ie BuiltInRole.Administrator maps to "Administrator") + ''' + Public Function IsInRole(ByVal role As BuiltInRole) As Boolean + ValidateBuiltInRoleEnumValue(role, "role") + + Dim converter As TypeConverter = TypeDescriptor.GetConverter(GetType(BuiltInRole)) + If IsWindowsPrincipal() Then + Dim windowsRole As WindowsBuiltInRole = DirectCast(converter.ConvertTo(role, GetType(WindowsBuiltInRole)), WindowsBuiltInRole) + Return DirectCast(InternalPrincipal, WindowsPrincipal).IsInRole(windowsRole) + Else + Return InternalPrincipal.IsInRole(converter.ConvertToString(role)) + End If + End Function + + '==PROTECTED*********************************************************** + + ''';InternalPrincipal + ''' + ''' The principal representing the current user. + ''' + ''' An IPrincipal representing the current user + ''' + ''' This should be overriden by derived classes that don't get the current + ''' user from the current thread + ''' + Protected Overridable Property InternalPrincipal() As IPrincipal + Get + Return System.Threading.Thread.CurrentPrincipal + End Get + Set(ByVal value As IPrincipal) + System.Threading.Thread.CurrentPrincipal = value + End Set + End Property + + '==PRIVATE************************************************************ + + '''******************************************************************* + ''';IsWindowsPrincipal + ''' + ''' Indicates whether or not the current principal is a WindowsPrincipal + ''' + ''' True if the current principal is a WindowsPrincipal, otherwise False + ''' + Private Function IsWindowsPrincipal() As Boolean + Return TypeOf InternalPrincipal Is WindowsPrincipal + End Function + + '''**************************************************************** + ''';ValidateBuiltInRoleEnumValue + ''' + ''' Determine if a value passed as a BuiltInRole enum is a legal BuiltInRole + ''' enum value + ''' + ''' + ''' + Friend Shared Sub ValidateBuiltInRoleEnumValue(ByVal testMe As BuiltInRole, ByVal parameterName As String) + 'Can't do a range check because the enum represents non-sequential values from all over + If testMe = BuiltInRole.AccountOperator OrElse _ + testMe = BuiltInRole.Administrator OrElse _ + testMe = BuiltInRole.BackupOperator OrElse _ + testMe = BuiltInRole.Guest OrElse _ + testMe = BuiltInRole.PowerUser OrElse _ + testMe = BuiltInRole.PrintOperator OrElse _ + testMe = BuiltInRole.Replicator OrElse _ + testMe = BuiltInRole.SystemOperator OrElse _ + testMe = BuiltInRole.User Then + Return 'it's good + End If + Throw New System.ComponentModel.InvalidEnumArgumentException(parameterName, CType(testMe, Integer), GetType(BuiltInRole)) + End Sub + + End Class 'User + + '''********************************************************************** + ''';BuiltInRole + ''' + ''' An enum of built in roles + ''' + ''' These map to the WindowsBuiltInRoles + _ + Public Enum BuiltInRole As Integer + '!!!!!!!!! Any changes to this enum must have an accompanying change made to User::ValidateBuiltInRoleEnumValue() + AccountOperator = WindowsBuiltInRole.AccountOperator + Administrator = WindowsBuiltInRole.Administrator + BackupOperator = WindowsBuiltInRole.BackupOperator + Guest = WindowsBuiltInRole.Guest + PowerUser = WindowsBuiltInRole.PowerUser + PrintOperator = WindowsBuiltInRole.PrintOperator + Replicator = WindowsBuiltInRole.Replicator + SystemOperator = WindowsBuiltInRole.SystemOperator + User = WindowsBuiltInRole.User + End Enum + + '''********************************************************************** + ''';BuiltInRoleConverter + ''' + ''' Enables converting BuiltInRoles to WindowsBuiltInRoles + ''' + ''' + _ + Public Class BuiltInRoleConverter + Inherits TypeConverter + + '==PUBLIC************************************************************ + + '''****************************************************************** + ''';New + ''' + ''' Creates converter + ''' + ''' + Public Sub New() + MyBase.New() + End Sub + + '''****************************************************************** + ''';CanConvertTo + ''' + ''' Extends the default TypeConverter to indicate we can convert to WindowsBuiltInRoles + ''' + ''' + ''' + ''' + ''' + Public Overrides Function CanConvertTo(ByVal context As System.ComponentModel.ITypeDescriptorContext, ByVal destinationType As System.Type) As Boolean + If destinationType IsNot Nothing AndAlso destinationType.Equals(GetType(WindowsBuiltInRole)) Then + Return True + End If + + Return MyBase.CanConvertTo(context, destinationType) + End Function + + '''****************************************************************** + ''';ConvertTo + ''' + ''' Extends the default TypeConvert to enable converting to WindowsBuiltInRoles + ''' + ''' + ''' + ''' + ''' + ''' + ''' + Public Overrides Function ConvertTo(ByVal context As System.ComponentModel.ITypeDescriptorContext, ByVal culture As System.Globalization.CultureInfo, ByVal value As Object, ByVal destinationType As System.Type) As Object + If destinationType IsNot Nothing AndAlso destinationType.Equals(GetType(WindowsBuiltInRole)) Then + User.ValidateBuiltInRoleEnumValue(DirectCast(value, BuiltInRole), "value") + Return GetWindowsBuiltInRole(value) + End If + Return MyBase.ConvertTo(context, culture, value, destinationType) + End Function + + '==PRIVATE********************************************************* + + '''**************************************************************** + ''';GetWindowsBuiltInRole + ''' + ''' Returns the WindowsBuiltInRole that corresponds to the passed in BuiltInRole + ''' + ''' + ''' The WindowsBuiltInrole + ''' + Private Function GetWindowsBuiltInRole(ByVal role As Object) As WindowsBuiltInRole + Dim roleName As String = [Enum].GetName(GetType(BuiltInRole), role) + Dim windowsRole As Object = [Enum].Parse(GetType(WindowsBuiltInRole), roleName) + If windowsRole IsNot Nothing Then + Return DirectCast(windowsRole, WindowsBuiltInRole) + End If + End Function + End Class 'BuiltInRoleConverter + +End Namespace + diff --git a/Microsoft.VisualBasic/runtime/msvbalib/ApplicationServices/WebUser.vb b/Microsoft.VisualBasic/runtime/msvbalib/ApplicationServices/WebUser.vb new file mode 100644 index 000000000..d7f61b281 --- /dev/null +++ b/Microsoft.VisualBasic/runtime/msvbalib/ApplicationServices/WebUser.vb @@ -0,0 +1,62 @@ +' Copyright (c) Microsoft Corporation. All rights reserved. +Option Explicit On +Option Strict Off + +Imports Microsoft.VisualBasic.CompilerServices +Imports System +Imports System.Security.Permissions +Imports System.Security.Principal + +Namespace Microsoft.VisualBasic.ApplicationServices + + '''************************************************************************ + ''';WebUser + ''' + ''' Class abstracting a web application user + ''' + ''' + _ + Public Class WebUser + Inherits User + + '==PUBLIC************************************************************** + + '''******************************************************************** + ''';New + ''' + ''' Creates an instance of a WebUser + ''' + ''' + Public Sub New() + End Sub + + '==PROTECTED************************************************************ + + '''********************************************************************* + ''';InternalPrincipal + ''' + ''' Gets the current user from the HTTPContext + ''' + ''' An IPrincipal representing the current user + ''' + Protected Overrides Property InternalPrincipal() As IPrincipal + Get + Dim httpContext As Object = Microsoft.VisualBasic.MyServices.Internal.SkuSafeHttpContext.Current() + If httpContext Is Nothing Then + Throw ExceptionUtils.GetInvalidOperationException(CompilerServices.ResID.WebNotSupportedOnThisSKU) + Else + Return httpContext.User + End If + End Get + Set(ByVal value As IPrincipal) + Dim httpContext As Object = Microsoft.VisualBasic.MyServices.Internal.SkuSafeHttpContext.Current() + If httpContext Is Nothing Then + Throw ExceptionUtils.GetInvalidOperationException(CompilerServices.ResID.WebNotSupportedOnThisSKU) + Else + httpContext.User = value + End If + End Set + End Property + + End Class +End Namespace diff --git a/Microsoft.VisualBasic/runtime/msvbalib/ApplicationServices/WindowsFormsApplicationBase.vb b/Microsoft.VisualBasic/runtime/msvbalib/ApplicationServices/WindowsFormsApplicationBase.vb new file mode 100644 index 000000000..a783841d7 --- /dev/null +++ b/Microsoft.VisualBasic/runtime/msvbalib/ApplicationServices/WindowsFormsApplicationBase.vb @@ -0,0 +1,1515 @@ +' Copyright (c) Microsoft Corporation. All rights reserved. + '****************************************************************************** + +Option Strict On +Option Explicit On + +Imports System +Imports System.Reflection +Imports System.Threading +Imports System.Diagnostics +Imports System.ComponentModel +Imports System.Globalization +Imports System.Security +Imports System.Security.AccessControl +Imports System.Security.Permissions +Imports System.Runtime.Remoting +Imports System.Runtime.Remoting.Lifetime +Imports System.Runtime.Remoting.Channels +Imports System.Runtime.Remoting.Channels.Tcp +Imports System.Runtime.Versioning +Imports Microsoft.Win32.SafeHandles +Imports Microsoft.VisualBasic.CompilerServices +Imports Microsoft.VisualBasic.CompilerServices.Utils + +Namespace Microsoft.VisualBasic.ApplicationServices + + '!!!!!!!!!! Any changes to this enum must be reflected in ValidateAuthenticationModeEnumValue() + Public Enum AuthenticationMode + Windows + ApplicationDefined + End Enum + + '!!!!!!!!!! Any changes to this enum must be reflected in ValidateShutdownModeEnumValue() + Public Enum ShutdownMode + AfterMainFormCloses + AfterAllFormsClose + End Enum + +#Region "Application model Event Delegates and Event Argument definitions" + + '--- Event Argument definitions + + '''************************************************************************** + ''' ;UnhandledExceptionEventArgs + ''' + ''' Provides the exception encountered along with a flag on whether to abort the program + ''' + ''' + _ + Public Class UnhandledExceptionEventArgs : Inherits System.Threading.ThreadExceptionEventArgs + + Sub New(ByVal exitApplication As Boolean, ByVal exception As System.Exception) + MyBase.new(exception) + m_ExitApplication = exitApplication + End Sub + + '''************************************************************************** + ''' ;ExitApplication + ''' + ''' Indicates whether the application should exit upon exiting the exception handler + ''' + ''' + ''' + Public Property ExitApplication() As Boolean + Get + Return m_ExitApplication + End Get + Set(ByVal value As Boolean) + m_ExitApplication = value + End Set + End Property + + Private m_ExitApplication As Boolean + + End Class + + '''************************************************************************** + ''' ;StartupEventArgs + ''' + ''' Provides context for the Startup event. + ''' + ''' + _ + Public Class StartupEventArgs : Inherits System.ComponentModel.CancelEventArgs + + '''************************************************************************** + ''' ;New + ''' + ''' Create a new instance of the StartupEventArgs. + ''' + ''' + ''' + Public Sub New(ByVal args As System.Collections.ObjectModel.ReadOnlyCollection(Of String)) + If args Is Nothing Then + args = New System.Collections.ObjectModel.ReadOnlyCollection(Of String)(Nothing) + End If + m_CommandLine = args + End Sub + + '''************************************************************************** + ''' ;CommandLine + ''' + ''' Returns the command line sent to this application + ''' + ''' + Public ReadOnly Property CommandLine() As System.Collections.ObjectModel.ReadOnlyCollection(Of String) + Get + Return m_CommandLine + End Get + End Property + + Private m_CommandLine As System.Collections.ObjectModel.ReadOnlyCollection(Of String) + End Class + + '''************************************************************************** + ''' ;StartupNextInstanceEventArgs + ''' + ''' Provides context for the StartupNextInstance event. + ''' + ''' + Public Class StartupNextInstanceEventArgs : Inherits EventArgs + + '''************************************************************************** + ''' ;New + ''' + ''' Create a new instance of the StartupNextInstanceEventArgs. + ''' + ''' + ''' + Public Sub New(ByVal args As System.Collections.ObjectModel.ReadOnlyCollection(Of String), ByVal bringToForegroundFlag As Boolean) + If args Is Nothing Then + args = New System.Collections.ObjectModel.ReadOnlyCollection(Of String)(Nothing) + End If + m_CommandLine = args + m_BringToForeground = bringToForegroundFlag + End Sub + + '''************************************************************************** + ''' ;BringToForeground + ''' + ''' Indicates whether we will bring the application to the foreground when processing the + ''' StartupNextInstance event. + ''' + ''' + ''' + Public Property BringToForeground() As Boolean + Get + Return m_BringToForeground + End Get + Set(ByVal value As Boolean) + m_BringToForeground = value + End Set + End Property + + '''************************************************************************** + ''' ;CommandLine + ''' + ''' Returns the command line sent to this application + ''' + ''' I'm using Me.CommandLine so that it is consistent with my.net and to assure they + ''' always return the same values + Public ReadOnly Property CommandLine() As System.Collections.ObjectModel.ReadOnlyCollection(Of String) + Get + Return m_CommandLine + End Get + End Property + + Private m_BringToForeground As Boolean + Private m_CommandLine As System.Collections.ObjectModel.ReadOnlyCollection(Of String) + End Class + + '--- Event Delegate definitions + + '''************************************************************************** + ''' ;StartupEventHandler + ''' + ''' Signature for the Startup event handler + ''' + ''' + ''' + ''' + Public Delegate Sub StartupEventHandler(ByVal sender As Object, ByVal e As StartupEventArgs) + + '''************************************************************************** + ''' ;StartupNextInstanceEventHandler + ''' + ''' Signature for the StartupNextInstance event handler + ''' + ''' + ''' + ''' + Public Delegate Sub StartupNextInstanceEventHandler(ByVal sender As Object, ByVal e As StartupNextInstanceEventArgs) + + '''************************************************************************** + ''' ;ShutdownEventHandler + ''' + ''' Signature for the Shutdown event handler + ''' + ''' + ''' + ''' + Public Delegate Sub ShutdownEventHandler(ByVal sender As Object, ByVal e As EventArgs) + + '''************************************************************************** + ''' ;UnhandledExceptionEventHandler + ''' + ''' Signature for the UnhandledException event handler + ''' ''' + ''' + ''' + Public Delegate Sub UnhandledExceptionEventHandler(ByVal sender As Object, ByVal e As UnhandledExceptionEventArgs) +#End Region + + '''************************************************************************** + ''' ;NoStartupFormException + ''' + ''' Exception for when the WinForms VB application model isn't supplied with a startup form + ''' + ''' + _ + _ + Public Class NoStartupFormException : Inherits System.Exception + + '''******************************************************************** + ''';New + ''' + ''' Creates a new exception + ''' + ''' + Public Sub New() + MyBase.New(GetResourceString(ResID.MyID.AppModel_NoStartupForm)) + End Sub + + Public Sub New(ByVal message As String) + MyBase.New(message) + End Sub + + Public Sub New(ByVal message As String, ByVal inner As System.Exception) + MyBase.New(message, inner) + End Sub + + ' Deserialization constructor must be defined since we are serializable + _ + Protected Sub New(ByVal info As System.Runtime.Serialization.SerializationInfo, ByVal context As System.Runtime.Serialization.StreamingContext) + MyBase.New(info, context) + End Sub + End Class + + '''************************************************************************** + ''' ;CantStartSingleInstanceException + ''' + ''' Exception for when we launch a single-instance application and it can't connect with the + ''' original instance. + ''' + ''' + _ + _ + Public Class CantStartSingleInstanceException : Inherits System.Exception + + '''******************************************************************** + ''';New + ''' + ''' Creates a new exception + ''' + ''' + Public Sub New() + MyBase.New(GetResourceString(ResID.MyID.AppModel_SingleInstanceCantConnect)) + End Sub + + Public Sub New(ByVal message As String) + MyBase.New(message) + End Sub + + Public Sub New(ByVal message As String, ByVal inner As System.Exception) + MyBase.New(message, inner) + End Sub + + ' Deserialization constructor must be defined since we are serializable + _ + Protected Sub New(ByVal info As System.Runtime.Serialization.SerializationInfo, ByVal context As System.Runtime.Serialization.StreamingContext) + MyBase.New(info, context) + End Sub + End Class + + + '''************************************************************************** + ''' ;WindowsFormsApplicationBase + ''' + ''' Provides the infrastructure for the VB Windows Forms application model + ''' + ''' Don't put access on this definition. The expanding class will define it + ''' Note that this class is not safe for YUKON. That's ok because this app model is only + ''' used in Windows FORMS projects and YUKON doesn't allow WinForms projects. So we won't be + ''' using the application model in YUKON. If we ever reconsider, we'll have to rework how we handle + ''' exceptions in DoApplicationModel() + _ + Public Class WindowsFormsApplicationBase : Inherits ConsoleApplicationBase + + Public Event Startup As StartupEventHandler + Public Event StartupNextInstance As StartupNextInstanceEventHandler + Public Event Shutdown As ShutdownEventHandler + + ' ;Event NetworkAvailabilityChanged + Public Custom Event NetworkAvailabilityChanged As Global.Microsoft.VisualBasic.Devices.NetworkAvailableEventHandler + 'This is a custom event because we want to hook up the NetworkAvailabilityChanged event only if the user writes a handler for it. + 'The reason being that it is very expensive to handle and kills our application startup perf. + AddHandler(ByVal value As Global.Microsoft.VisualBasic.Devices.NetworkAvailableEventHandler) + SyncLock m_NetworkAvailChangeLock + If m_NetworkAvailabilityEventHandlers Is Nothing Then m_NetworkAvailabilityEventHandlers = New System.Collections.ArrayList + m_NetworkAvailabilityEventHandlers.Add(value) + m_TurnOnNetworkListener = True 'We don't want to create the network object now - it takes a snapshot of the executionContext and our IPrincipal isn't on the thread yet. We know we need to create it and we will at the appropriate time + If m_NetworkObject Is Nothing And m_FinishedOnInitilaize = True Then 'But the user may be doing an Addhandler of their own in which case we need to make sure to honor the request. If we aren't past OnInitialize() yet we shouldn't do it but the flag above catches that case + m_NetworkObject = New Microsoft.VisualBasic.Devices.Network + AddHandler m_NetworkObject.NetworkAvailabilityChanged, AddressOf Me.NetworkAvailableEventAdaptor + End If + End SyncLock + End AddHandler + + RemoveHandler(ByVal value As Global.Microsoft.VisualBasic.Devices.NetworkAvailableEventHandler) + If m_NetworkAvailabilityEventHandlers IsNot Nothing AndAlso m_NetworkAvailabilityEventHandlers.Count > 0 Then + m_NetworkAvailabilityEventHandlers.Remove(value) + 'Last one to leave, turn out the lights... + If m_NetworkAvailabilityEventHandlers.Count = 0 Then + RemoveHandler m_NetworkObject.NetworkAvailabilityChanged, AddressOf Me.NetworkAvailableEventAdaptor + If m_NetworkObject IsNot Nothing Then + m_NetworkObject.DisconnectListener() 'Stop listening to network change events because we are going to go away + m_NetworkObject = Nothing 'no sense hanging on to this if nobody is listening. + End If + End If + End If + End RemoveHandler + + RaiseEvent(ByVal sender As Object, ByVal e As Global.Microsoft.VisualBasic.Devices.NetworkAvailableEventArgs) + If m_NetworkAvailabilityEventHandlers IsNot Nothing Then + For Each handler As Global.Microsoft.VisualBasic.Devices.NetworkAvailableEventHandler In m_NetworkAvailabilityEventHandlers + Try + If handler IsNot Nothing Then handler.Invoke(sender, e) + Catch ex As Exception + If Not OnUnhandledException(New UnhandledExceptionEventArgs(True, ex)) Then + Throw 'the user didn't write a handler so throw the error up the chain + End If + End Try + Next + End If + End RaiseEvent + End Event + + ';Event UnhandledException + Public Custom Event UnhandledException As UnhandledExceptionEventHandler + 'This is a custom event because we want to hook up System.Windows.Forms.Application.ThreadException only if the user writes a + 'handler for this event. We only want to hook the ThreadException event if the user is handling this event because the act of listening to + 'Application.ThreadException causes WinForms to snuff exceptions and we only want WinForms to do that if we are assured that the user wrote their own handler + 'to deal with the error instead + AddHandler(ByVal value As UnhandledExceptionEventHandler) + If m_UnhandledExceptionHandlers Is Nothing Then m_UnhandledExceptionHandlers = New System.Collections.ArrayList + m_UnhandledExceptionHandlers.Add(value) + 'Only add the listener once so we don't fire the UnHandledException event over and over for the same exception + If m_UnhandledExceptionHandlers.Count = 1 Then AddHandler System.Windows.Forms.Application.ThreadException, AddressOf Me.OnUnhandledExceptionEventAdaptor + End AddHandler + + RemoveHandler(ByVal value As UnhandledExceptionEventHandler) + If m_UnhandledExceptionHandlers IsNot Nothing AndAlso m_UnhandledExceptionHandlers.Count > 0 Then + m_UnhandledExceptionHandlers.Remove(value) + 'Last one to leave, turn out the lights... + If m_UnhandledExceptionHandlers.Count = 0 Then RemoveHandler System.Windows.Forms.Application.ThreadException, AddressOf Me.OnUnhandledExceptionEventAdaptor + End If + End RemoveHandler + + RaiseEvent(ByVal sender As Object, ByVal e As UnhandledExceptionEventArgs) + If m_UnhandledExceptionHandlers IsNot Nothing Then + m_ProcessingUnhandledExceptionEvent = True 'In the case that we throw from the unhandled exception handler, we don't want to run the unhandled exception handler again + For Each handler As UnhandledExceptionEventHandler In m_UnhandledExceptionHandlers + If handler IsNot Nothing Then handler.Invoke(sender, e) + Next + m_ProcessingUnhandledExceptionEvent = False 'Now that we are out of the unhandled exception handler, treat exceptions normally again. + End If + End RaiseEvent + End Event + + '= PUBLIC ============================================================= + + '''************************************************************************** + ''' ;New + ''' + ''' Constructs the application Shutdown/Startup model object + ''' + ''' + ''' We have to have a parameterless ctor because the platform specific Application object + ''' derives from this one and it doesn't define a ctor because the partial class generated by the + ''' designer does that to configure the application. + Public Sub New() + Me.New(AuthenticationMode.Windows) + End Sub + + '''************************************************************************** + ''' ;New + ''' + ''' Constructs the application Shutdown/Startup model object + ''' + ''' + _ + Public Sub New(ByVal authenticationMode As AuthenticationMode) + MyBase.New() + m_Ok2CloseSplashScreen = True 'Default to true in case there is no splash screen so we won't block forever waiting for it to appear. + ValidateAuthenticationModeEnumValue(authenticationMode, "authenticationMode") + + 'Setup Windows Authentication if that's what the user wanted. Note, we want to do this now, before the Network object gets created because + 'the network object will be doing a AsyncOperationsManager.CreateOperation() which captures the execution context. So we have to have our + 'principal on the thread before that happens. + If authenticationMode = authenticationMode.Windows Then + Try + 'Consider: - sadly, a call to: System.Security.SecurityManager.IsGranted(New SecurityPermission(SecurityPermissionFlag.ControlPrincipal)) + 'will only check THIS caller so you'll always get TRUE. What is needed is a way to get to the value of this on a demand basis. So I try/catch instead for now + 'but would rather be able to IF my way around this block. + System.Threading.Thread.CurrentPrincipal = New System.Security.Principal.WindowsPrincipal(System.Security.Principal.WindowsIdentity.GetCurrent) + Catch ex As System.Security.SecurityException + End Try + End If + + m_AppContext = New WinFormsAppContext(Me) + + 'We need to set the WindowsFormsSynchronizationContext because the network object is going to get created after this ctor runs + '(network gets created during event hookup) and we need the context in place for it to latch on to. The WindowsFormsSynchronizationContext + 'won't otherwise get created until OnCreateMainForm() when the startup form is created and by then it is too late. + 'When the startup form gets created, WinForms is going to push our context into the previous context and then restore it when Application.Run() exits. + Call New System.Security.Permissions.UIPermission(UIPermissionWindow.AllWindows).Assert() + m_AppSyncronizationContext = AsyncOperationManager.SynchronizationContext + AsyncOperationManager.SynchronizationContext = New System.Windows.Forms.WindowsFormsSynchronizationContext() + System.Security.PermissionSet.RevertAssert() 'CLR also reverts if we throw or when we return from this function + End Sub + + '''************************************************************************** + ''' ;Run + ''' + ''' Entry point to kick off the VB Startup/Shutdown Application model + ''' + ''' The command line from Main() + ''' + _ + Public Sub Run(ByVal commandLine As String()) + 'Microsoft.VisualBasic.MsgBox("Attach Debugger") 'jtw - uncomment to facilitate debugging single-instance applications + 'Prime the command line args with what we recieve from Main() so that Click-Once windows apps don't have to do a System.Environment call which would require permissions. + MyBase.InternalCommandLine = New System.Collections.ObjectModel.ReadOnlyCollection(Of String)(commandLine) + + 'Is this a single-instance application? + If Not Me.IsSingleInstance Then + DoApplicationModel() 'This isn't a Single-Instance application + Else 'This is a Single-Instance application + Dim ApplicationInstanceID As String = GetApplicationInstanceID(Assembly.GetCallingAssembly) 'Note: Must pass the calling assembly from here so we can get the running app. Otherwise, can break single instance - see hosting problem in Whidbey 415231 + m_MemoryMappedID = ApplicationInstanceID & "Map" + Dim SemaphoreID As String = ApplicationInstanceID & "Event" + Dim MessageRecievedSemaphoreID As String = ApplicationInstanceID & "Event2" + m_StartNextInstanceCallback = New System.Threading.SendOrPostCallback(AddressOf Me.OnStartupNextInstanceMarshallingAdaptor) + + 'Create our event and ACL it down so only the current user has rights to use it + Call New SecurityPermission(System.Security.Permissions.SecurityPermissionFlag.ControlPrincipal).Assert() 'I need to get the current identity so I can do my ACLS + Dim CurrentUser As String = System.Security.Principal.WindowsIdentity.GetCurrent().Name() + Dim OperatingSystemSupportsAuthentication As Boolean = CurrentUser <> "" 'The OS must support the concept of a NTUser to support authentication on the server and in this case the app is a server + System.Security.CodeAccessPermission.RevertAssert() + + Dim WeAreTheFirstInstance As Boolean + If OperatingSystemSupportsAuthentication = True Then 'We can only use an ACL if we have a user (EventWaitHandleAccessRule creates a NTAccount object) + 'Build an ACL so we can lock down our semaphore. Don't want external users to be able to signal/unsignal our event which leads to crashes or denial of service, respectively. + Dim WaitHandleRules As EventWaitHandleAccessRule = New EventWaitHandleAccessRule(CurrentUser, EventWaitHandleRights.FullControl, AccessControlType.Allow) + Dim EventWaitHandleSecurity As New EventWaitHandleSecurity() + Call New SecurityPermission(System.Security.Permissions.SecurityPermissionFlag.ControlPrincipal).Assert() 'I need to get the current identity so I can do my ACLS + EventWaitHandleSecurity.AddAccessRule(WaitHandleRules) + System.Security.CodeAccessPermission.RevertAssert() + + 'Event names are scoped such that two different users logged onto Windows at the same time will get unique events even though the event names are the same. + 'EventWaitHandle uses the SafeWaitHandle internally and correct creation pattern so this is the safe way to prevent a handle leak. + m_FirstInstanceSemaphore = New System.Threading.EventWaitHandle(False, Threading.EventResetMode.ManualReset, SemaphoreID, WeAreTheFirstInstance, EventWaitHandleSecurity) + m_MessageRecievedSemaphore = New System.Threading.EventWaitHandle(False, Threading.EventResetMode.AutoReset, MessageRecievedSemaphoreID, False, EventWaitHandleSecurity) + Else + 'win 95/98/me don't support ACLS + m_FirstInstanceSemaphore = New System.Threading.EventWaitHandle(False, Threading.EventResetMode.ManualReset, SemaphoreID, WeAreTheFirstInstance) + m_MessageRecievedSemaphore = New System.Threading.EventWaitHandle(False, Threading.EventResetMode.AutoReset, MessageRecievedSemaphoreID) + End If + + If WeAreTheFirstInstance Then + '--- This is the first instance of a single-instance application to run. This is the instance that subsequent instances will attach to. + Try + Dim ServerChannel As TcpServerChannel = DirectCast(RegisterChannel(ChannelType.Server, OperatingSystemSupportsAuthentication), TcpServerChannel) 'Register the server channel that will listen for incoming connection messages from clients + Dim Communicator As New RemoteCommunicator(Me, m_MessageRecievedSemaphore) + Dim NameOfRemoteCommunicator As String = ApplicationInstanceID & ".rem" + + Call New System.Security.Permissions.SecurityPermission(System.Security.Permissions.SecurityPermissionFlag.RemotingConfiguration).Assert() + RemotingServices.Marshal(Communicator, NameOfRemoteCommunicator) 'Convert this marshal by ref object into an instance of an ObjRef class which can be serialized for transmission between application domains (publishes this with the remoting layer so that it knows about this object - this essentially maps the object to the URL. At creation the object was ready to go - this just tells the remoting layer that the class is ready to be connected to + System.Security.CodeAccessPermission.RevertAssert() + + 'Stashes the URL for our remote object where we can get at it on subsequent launches of this app + Dim URLofRemoteCommunicatorObject As String = ServerChannel.GetUrlsForUri(NameOfRemoteCommunicator)(0) + WriteUrlToMemoryMappedFile(URLofRemoteCommunicatorObject) 'subsequent instances will get the memory mapped file to find the URL for our remote object + m_FirstInstanceSemaphore.Set() 'We are now far enough along to allow subsequent instances to attach to this one. + DoApplicationModel() + Finally 'Application has exited + If m_MessageRecievedSemaphore IsNot Nothing Then + m_MessageRecievedSemaphore.Close() + End If + If m_FirstInstanceSemaphore IsNot Nothing Then 'we let this go more aggressively earlier during OnRun() but only if we shutdown cleanly so check again. + m_FirstInstanceSemaphore.Close() 'Let go so that subsequent instances don't try to connect to this process which is on its way out. + End If + If m_FirstInstanceMemoryMappedFileHandle IsNot Nothing AndAlso Not m_FirstInstanceMemoryMappedFileHandle.IsInvalid Then + m_FirstInstanceMemoryMappedFileHandle.Close() + End If + End Try + Else '--- We are launching a subsequent instance. + 'Wait until the original app is on its feet so we can attach to it. We need to prevent a race condition where a subsequent instance starts up before this original + ' instance is ready to be attached to. Surprisingly, this race can easily occur. When opening several files with a single-instance app from file explorer, for instance. + ' See VS Whidbey #346690 + ' The timeout is only there so we don't wait forever on a horked semaphore in case something bad happened and couldn't get cleaned up for whatever reason in the first instance. + Dim ObtainedSignal As Boolean = m_FirstInstanceSemaphore.WaitOne(SECOND_INSTANCE_TIMEOUT, False) + If Not ObtainedSignal Then Throw New CantStartSingleInstanceException + + 'We are good to attach to the original instance + RegisterChannel(ChannelType.Client, OperatingSystemSupportsAuthentication) 'register a client channel to communicate with the remote object. We can't use a default channel because we need authentication turned on to match the server channel we established during the 1st instance + Dim URLofRemoteCommunicatorObject As String = ReadUrlFromMemoryMappedFile() + If URLofRemoteCommunicatorObject Is Nothing Then + Throw New CantStartSingleInstanceException + End If + Dim Communicator As RemoteCommunicator = DirectCast(RemotingServices.Connect(GetType(RemoteCommunicator), URLofRemoteCommunicatorObject), RemoteCommunicator) + + 'To run single instance in low-trust (e.g. InternetZone), we need the following permissions: + Dim PermissionsToDoRemoting As New System.Security.PermissionSet(PermissionState.None) + PermissionsToDoRemoting.AddPermission(New System.Security.Permissions.SecurityPermission(SecurityPermissionFlag.SerializationFormatter Or SecurityPermissionFlag.ControlPrincipal Or SecurityPermissionFlag.UnmanagedCode)) + PermissionsToDoRemoting.AddPermission(New System.Net.DnsPermission(PermissionState.Unrestricted)) 'Unrestricted is required + PermissionsToDoRemoting.AddPermission(New System.Net.SocketPermission(System.Net.NetworkAccess.Connect, Net.TransportType.Tcp, HOST_NAME, System.Net.SocketPermission.AllPorts)) 'I use AllPorts because we don't know which port we will have hooked up on. We get whatever port was available at runtime + PermissionsToDoRemoting.AddPermission(New System.Security.Permissions.EnvironmentPermission(EnvironmentPermissionAccess.Read, "USERNAME")) 'Environment permissions + PermissionsToDoRemoting.Assert() + + Communicator.RunNextInstance(MyBase.CommandLineArgs) 'Fires the StartupNextInstance event in the original instance of the app, then returns. + System.Security.PermissionSet.RevertAssert() 'revert all previous asserts for the current frame. No need to finally block this - CLR removes asserts if an exception is thrown. + + 'Because we posted through a remoting call, we need to provide time for the message to be sent before we exit this process. To guarantee that, we'll wait until the message gets through + ObtainedSignal = m_MessageRecievedSemaphore.WaitOne(ATTACH_TIMEOUT, False) + If Not ObtainedSignal Then Throw New CantStartSingleInstanceException + End If + End If 'Single-Instance application + End Sub + + '''************************************************************************** + ''' ;OpenForms + ''' + ''' Returns the collection of forms that are open. We no longer have thread + ''' affinity meaning that this is the WinForms collection that contains Forms that may + ''' have been opened on another thread then the one we are calling in on right now. + ''' + ''' + ''' + Public ReadOnly Property OpenForms() As System.Windows.Forms.FormCollection + Get + Return System.Windows.Forms.Application.OpenForms + End Get + End Property + + '''************************************************************************** + ''' ;MainForm + ''' + ''' Provides access to the main form for this application + ''' + ''' + ''' + Protected Property MainForm() As System.Windows.Forms.Form + Get + Return IIf(m_AppContext IsNot Nothing, m_AppContext.MainForm, Nothing) + End Get + Set(ByVal value As System.Windows.Forms.Form) + If value Is Nothing Then + Throw ExceptionUtils.GetArgumentNullException("MainForm", ResID.MyID.General_PropertyNothing, "MainForm") + End If + If value Is m_SplashScreen Then + Throw New ArgumentException(GetResourceString(ResID.MyID.AppModel_SplashAndMainFormTheSame)) + End If + m_AppContext.MainForm = value + End Set + End Property + + '''************************************************************************** + ''' ;SplashScreen + ''' + ''' Provides access to the splash screen for this application + ''' + ''' + ''' + Public Property SplashScreen() As System.Windows.Forms.Form + Get + Return m_SplashScreen + End Get + Set(ByVal value As System.Windows.Forms.Form) + If value IsNot Nothing AndAlso value Is m_AppContext.MainForm Then 'allow for the case where they set splash screen = nothing and mainForm is currently nothing + Throw New ArgumentException(GetResourceString(ResID.MyID.AppModel_SplashAndMainFormTheSame)) + End If + m_SplashScreen = value + End Set + End Property + + '''************************************************************************** + ''' ;MinimumSplashScreenDisplayTime + ''' + ''' The splash screen timeout specifies whether there is a minimum time that the splash + ''' screen should be displayed for. When not set then the splash screen is hidden + ''' as soon as the main form becomes active. + ''' + ''' The minimum amount of time, in milliseconds, to display the splash screen. + ''' + Public Property MinimumSplashScreenDisplayTime() As Integer + Get + Return m_MinimumSplashExposure + End Get + Set(ByVal value As Integer) + m_MinimumSplashExposure = value + End Set + End Property + + '''************************************************************************** + ''' ;UseCompatibleTextRendering + ''' + ''' Whidbey tried to change the text rendering engine from GDI+ (used in Everett) to GDI. + ''' This turned out to be a breaking change that didn't work out. + ''' The idea is that new Whidbey apps will use the new GDI renderer by default, + ''' but there must be a way to lock back to use the Everett GDI+ renderer. + ''' The user can shadow this function to return True if they want their app + ''' to use the Everett GDI+ render. We read this function in Main() (My template) to + ''' determine how to set the text rendering flag on the WinForms application object. + ''' + ''' + ''' True - Use Everett GDI+ renderer. False - use the Whidbey GDI renderer + ''' + Protected Shared ReadOnly Property UseCompatibleTextRendering() As Boolean + Get + Return False + End Get + End Property + + '''************************************************************************** + ''' ;ApplicationContext + ''' + ''' Provides the WinForms application context that we are running on + ''' + ''' + ''' + Public ReadOnly Property ApplicationContext() As System.Windows.Forms.ApplicationContext + Get + Return m_AppContext + End Get + End Property + + '''************************************************************************** + ''' ;SaveMySettingsOnExit + ''' + ''' Informs My.Settings whether to save the settings on exit or not + ''' + ''' + ''' + Public Property SaveMySettingsOnExit() As Boolean + Get + Return m_SaveMySettingsOnExit + End Get + Set(ByVal value As Boolean) + m_SaveMySettingsOnExit = value + End Set + End Property + + '''************************************************************************** + ''' ;DoEvents + ''' + ''' Processes all windows messages currently in the message queue + ''' + ''' + Public Sub DoEvents() + System.Windows.Forms.Application.DoEvents() + End Sub + + '= PROTECTED ========================================================== + + '''************************************************************************** + ''' ;OnInitialize + ''' + ''' This exposes the first in a series of extensibility points for the Startup process. By default, it shows + ''' the splash screen and does rudimentary processing of the command line to see if /nosplash or its + ''' variants was passed in. + ''' + ''' + ''' Returning True indicates that we should continue on with the application Startup sequence + ''' This extensibility point is exposed for people who want to override the Startup sequence at the earliest possible point + ''' to + _ + Protected Overridable Function OnInitialize(ByVal commandLineArgs As System.Collections.ObjectModel.ReadOnlyCollection(Of String)) As Boolean + ' EnableVisualStyles + If m_EnableVisualStyles Then + System.Windows.Forms.Application.EnableVisualStyles() + End If + + 'We'll handle /nosplash for you + If Not (commandLineArgs.Contains("/nosplash") OrElse Me.CommandLineArgs.Contains("-nosplash")) Then + ShowSplashScreen() + End If + + m_FinishedOnInitilaize = True 'we are now at a point where we can allow the network object to be created since the iprincipal is on the thread by now. + Return True 'true means to not bail out but keep on running after OnIntiailize() finishes + End Function + + '''************************************************************************** + ''' ;OnStartup + ''' + ''' Extensibility point which raises the Startup event + ''' + ''' + ''' + ''' + Protected Overridable Function OnStartup(ByVal eventArgs As StartupEventArgs) As Boolean + eventArgs.Cancel = False + 'It is important not to create the network object until the ExecutionContext has everything on it. By now the principal will be on the thread so + 'we can create the network object. The timing is important because the network object has an AsyncOperationsManager in it that marshals + 'the network changed event to the main thread. The asycnOperationsManager does a CreateOperation() which makes a copy of the executionContext + 'That execution context shows up on your thread during the callback so I delay creating the network object (and consequently the capturing of the + 'execution context) until the principal has been set on the thread. + 'this avoid the problem in VS whidbey 458908 where My.User isn't set during the NetworkAvailabilityChanged event. This problem would just extend + 'itself to any future callback that involved the asyncOperationsManager so this is where we need to create objects that have a asyncOperationsContext + 'in them. + If m_TurnOnNetworkListener = True And m_NetworkObject Is Nothing Then 'the is nothing check is to avoid hooking the object more than once + m_NetworkObject = New Microsoft.VisualBasic.Devices.Network + AddHandler m_NetworkObject.NetworkAvailabilityChanged, AddressOf Me.NetworkAvailableEventAdaptor + End If + RaiseEvent Startup(Me, eventArgs) + Return Not eventArgs.Cancel + End Function + + '''************************************************************************** + ''' ;OnStartupNextInstance + ''' + ''' Extensibility point which raises the StartupNextInstance + ''' + ''' + _ + _ + Protected Overridable Sub OnStartupNextInstance(ByVal eventArgs As StartupNextInstanceEventArgs) + RaiseEvent StartupNextInstance(Me, eventArgs) + 'Activate the original instance + Call New System.Security.Permissions.UIPermission(UIPermissionWindow.SafeSubWindows Or UIPermissionWindow.SafeTopLevelWindows).Assert() + If eventArgs.BringToForeground = True AndAlso Me.MainForm IsNot Nothing Then + If MainForm.WindowState = System.Windows.Forms.FormWindowState.Minimized Then + MainForm.WindowState = System.Windows.Forms.FormWindowState.Normal + End If + MainForm.Activate() + End If + End Sub + + '''************************************************************************** + ''' ;OnRun + ''' + ''' At this point, the command line args should have been processed and the application will create the + ''' main form and enter the message loop. + ''' + ''' + _ + _ + Protected Overridable Sub OnRun() + If Me.MainForm Is Nothing Then + OnCreateMainForm() 'A designer overrides OnCreateMainForm() to set the main form we are supposed to use + If Me.MainForm Is Nothing Then Throw New NoStartupFormException + + 'When we have a splash screen that hasn't timed out before the main form is ready to paint, we want to + 'block the main form from painting. To do that I let the form get past the Load() event and hold it until + 'the splash screen goes down. Then I let the main form continue it's startup sequence. The ordering of + 'Form startup events for reference is: Ctor(), Load Event, Layout event, Shown event, Activated event, Paint event + AddHandler Me.MainForm.Load, AddressOf MainFormLoadingDone + End If + + 'Run() eats all exceptions (unless running under the debugger) If the user wrote an UnhandledException handler we will hook + 'the System.Windows.Forms.Application.ThreadException event (see Public Custom Event UnhandledException) which will raise our + 'UnhandledException Event. If our user didn't write an UnhandledException event, then we land in the try/catch handler for Forms.Application.Run() + Try + System.Windows.Forms.Application.Run(m_AppContext) + Finally + 'When Run() returns, the context we pushed in our ctor (which was a WindowsFormsSynchronizationContext) is restored. But we are going to dispose it + 'so we need to disconnect the network listener so that it can't fire any events in response to changing network availability conditions through a dead context. VSWHIDBEY #343374 + If m_NetworkObject IsNot Nothing Then m_NetworkObject.DisconnectListener() + + 'The app is exiting - if another instance has launched, don't let it attach to this one. + If m_FirstInstanceSemaphore IsNot Nothing Then + m_FirstInstanceSemaphore.Close() + m_FirstInstanceSemaphore = Nothing + End If + + AsyncOperationManager.SynchronizationContext = m_AppSyncronizationContext 'Restore the prior sync context + m_AppSyncronizationContext = Nothing + End Try + End Sub + + '''************************************************************************** + ''' ;OnCreateSplashScreen + ''' + ''' A designer will override this method and provide a splash screen if this application has one. + ''' + ''' For instance, a designer would override this method and emit: Me.Splash = new Splash + ''' where Splash was designated in the application designer as being the splash screen for this app + _ + Protected Overridable Sub OnCreateSplashScreen() + End Sub + + '''************************************************************************** + ''' ;OnCreateMainForm + ''' + ''' Provides a hook that designers will override to set the main form. + ''' + ''' + _ + Protected Overridable Sub OnCreateMainForm() + End Sub + + '''************************************************************************** + ''' ;OnShutdown + ''' + ''' The last in a series of extensibility points for the Shutdown process + ''' + ''' + _ + Protected Overridable Sub OnShutdown() + RaiseEvent Shutdown(Me, System.EventArgs.Empty) + End Sub + + '''************************************************************************** + ''' ;OnUnhandledException + ''' + ''' Raises the UnHandled exception event and exits the application if the event handler indicated + ''' that execution shouldn't continue + ''' + ''' + ''' True indicates the exception event was raised / False it was not + ''' + _ + Protected Overridable Function OnUnhandledException(ByVal e As UnhandledExceptionEventArgs) As Boolean + If m_UnhandledExceptionHandlers IsNot Nothing AndAlso m_UnhandledExceptionHandlers.Count > 0 Then 'Does the user have a handler for this event? + 'We don't put a try/catch around the handler event so that exceptions in there will bubble out - else we will have a recursive exception handler + RaiseEvent UnhandledException(Me, e) + If e.ExitApplication = True Then System.Windows.Forms.Application.Exit() + Return True 'User handled the event + End If + Return False 'Nobody was listening to the UnhandledException event + End Function + + '''************************************************************************** + ''' ;ShowSplashScreen + ''' + ''' Uses the extensibility model to see if there is a splash screen provided for this app and if there is, + ''' displays it. + ''' + ''' + _ + Protected Sub ShowSplashScreen() + If Not m_DidSplashScreen Then + m_DidSplashScreen = True + If m_SplashScreen Is Nothing Then + OnCreateSplashScreen() 'If the user specified a splash screen, the designer will have overriden this method to set it + End If + If m_SplashScreen IsNot Nothing Then + 'Some splash screens have minimum face time they are supposed to get. We'll set up a time to let us know when we can take it down. + If m_MinimumSplashExposure > 0 Then + m_Ok2CloseSplashScreen = False 'Don't close until the timer expires. + m_SplashTimer = New System.Timers.Timer(m_MinimumSplashExposure) + AddHandler m_SplashTimer.Elapsed, AddressOf MinimumSplashExposureTimeIsUp + m_SplashTimer.AutoReset = False + 'We'll enable it in DisplaySplash() once the splash screen thread gets running + Else + m_Ok2CloseSplashScreen = True 'No timeout so just close it when then main form comes up + End If + 'Run the splash screen on another thread so we don't starve it for events and painting while the main form gets its act together + Dim SplashThread As New System.Threading.Thread(AddressOf DisplaySplash) + SplashThread.Start() + End If + End If + End Sub + + '''************************************************************************** + ''' ;HideSplashScreen + ''' + ''' Hide the splash screen. The splash screen was created on another thread + ''' thread (main thread) than the one it was run on (secondary thread for the + ''' splash screen so it doesn't block app startup. We need to invoke the close. + ''' This function gets called from the main thread by the app fx. + ''' + ''' + _ + _ + Protected Sub HideSplashScreen() + SyncLock m_SplashLock 'This ultimately wasn't necessary. I suppose we better keep it for backwards compat + 'Dev10 590587 - we now activate the main form before calling Dispose on the Splash screen. (we're just + ' swapping the order of the two If blocks). This is to fix the issue where the main form + ' doesn't come to the front after the Splash screen disappears + If Me.MainForm IsNot Nothing Then + Call New System.Security.Permissions.UIPermission(UIPermissionWindow.AllWindows).Assert() + Me.MainForm.Activate() + System.Security.PermissionSet.RevertAssert() 'CLR also reverts if we throw or when we return from this function + End If + If m_SplashScreen IsNot Nothing AndAlso Not m_SplashScreen.IsDisposed Then + Dim TheBigGoodbye As New DisposeDelegate(AddressOf m_SplashScreen.Dispose) + m_SplashScreen.Invoke(TheBigGoodbye) + m_SplashScreen = Nothing + End If + End SyncLock + End Sub + + '''************************************************************************** + ''' ;ShutdownStyle + ''' + ''' Determines when this application will terminate (when the main form goes down, all forms) + ''' + ''' + ''' + Protected Friend Property ShutdownStyle() As ShutdownMode + Get + Return m_ShutdownStyle + End Get + Set(ByVal value As ShutdownMode) + ValidateShutdownModeEnumValue(value, "value") + m_ShutdownStyle = value + End Set + End Property + + '''************************************************************************** + ''' ;EnableVisualStyles + ''' + ''' Determines whether this application will use the XP Windows styles for windows, controls, etc. + ''' + ''' + ''' + Protected Property EnableVisualStyles() As Boolean + Get + Return m_EnableVisualStyles + End Get + Set(ByVal value As Boolean) + m_EnableVisualStyles = value + End Set + End Property + + '''************************************************************************** + ''' ;IsSingleInstance + ''' + ''' + ''' + ''' + ''' + Protected Property IsSingleInstance() As Boolean + Get + Return m_IsSingleInstance + End Get + Set(ByVal value As Boolean) + m_IsSingleInstance = value + End Set + End Property + + '= Private ========================================================== + + '''************************************************************************** + ''' ;ValidateAuthenticationModeEnumValue + ''' + ''' Validates that the value being passed as an AuthenticationMode enum is a legal value + ''' + ''' + ''' + Private Sub ValidateAuthenticationModeEnumValue(ByVal value As AuthenticationMode, ByVal paramName As String) + If value < ApplicationServices.AuthenticationMode.Windows OrElse value > ApplicationServices.AuthenticationMode.ApplicationDefined Then + Throw New System.ComponentModel.InvalidEnumArgumentException(paramName, value, GetType(AuthenticationMode)) + End If + End Sub + + '''************************************************************************** + ''' ;ValidateShutdownModeEnumValue + ''' + ''' Validates that the value being passed as an ShutdownMode enum is a legal value + ''' + ''' + ''' + Private Sub ValidateShutdownModeEnumValue(ByVal value As ShutdownMode, ByVal paramName As String) + If value < ShutdownMode.AfterMainFormCloses OrElse value > ShutdownMode.AfterAllFormsClose Then + Throw New System.ComponentModel.InvalidEnumArgumentException(paramName, value, GetType(ShutdownMode)) + End If + End Sub + + '''************************************************************************** + ''' ;DisplaySplash + ''' + ''' Displays the splash screen. We get called here from a different thread than what the + ''' main form is starting up on. This allows us to process events for the Splash screen so + ''' it doesn't freeze up while the main form is getting it together. + ''' + ''' + Private Sub DisplaySplash() + Debug.Assert(m_SplashScreen IsNot Nothing, "We should have never get here if there is no splash screen") + If m_SplashTimer IsNot Nothing Then 'We only have a timer if there is a minimum time that the splash screen is supposed to be displayed. + m_SplashTimer.Enabled = True 'enable the timer now that we are about to show the splash screen + End If + System.Windows.Forms.Application.Run(m_SplashScreen) + End Sub + + '''************************************************************************** + ''' ;MinimumSplashExposureTimeIsUp + ''' + ''' If a splash screen has a minimum time out, then once that is up we check to see whether + ''' we should close the splash screen. If the main form has activated then we close it. + ''' Note that we are getting called on a secondary thread here which isn't necessairly + ''' associated with any form. Don't touch forms from this function. + ''' + ''' + Private Sub MinimumSplashExposureTimeIsUp(ByVal sender As Object, ByVal e As System.Timers.ElapsedEventArgs) + If m_SplashTimer IsNot Nothing Then 'We only have a timer if there was a minimum timeout on the splash screen + m_SplashTimer.Dispose() + m_SplashTimer = Nothing + End If + m_Ok2CloseSplashScreen = True + End Sub + + '''************************************************************************** + ''' ;MainFormLoadingDone + ''' + ''' The Load() event happens before the Shown and Paint events. When we get called here + ''' we know that the form load event is done and that the form is about to paint + ''' itself for the first time. + ''' We can now hide the splash screen. + ''' Note that this function gets called from the main thread - the same thread + ''' that creates the startup form. + ''' + ''' + ''' + ''' + Private Sub MainFormLoadingDone(ByVal sender As Object, ByVal e As System.EventArgs) + RemoveHandler Me.MainForm.Load, AddressOf MainFormLoadingDone 'We don't want this event to call us again. + + 'block until the splash screen time is up. See MinimumSplashExposureTimeIsUp() which releases us + While Not m_Ok2CloseSplashScreen + DoEvents() 'In case Load() event, which we are waiting for, is doing stuff that requires windows messages. our timer message doesn't count because it is on another thread. + End While + + HideSplashScreen() + End Sub + + '''************************************************************************** + ''' ;WinFormsAppContext + ''' + ''' Encapsulates an ApplicationContext. We have our own to get the shutdown behaviors we + ''' offer in the application model. This derivation of the ApplicationContext listens for when + ''' the main form closes and provides for shutting down when the main form closes or the + ''' last form closes, depending on the mode this application is running in. + ''' + ''' + Private Class WinFormsAppContext : Inherits System.Windows.Forms.ApplicationContext + Sub New(ByVal App As WindowsFormsApplicationBase) + m_App = App + End Sub + + '''************************************************************************** + ''' ;OnMainFormClosed + ''' + ''' Handles the two types of application shutdown: + ''' 1 - shutdown when the main form closes + ''' 2 - shutdown only after the last form closes + ''' + ''' + ''' + ''' + _ + Protected Overrides Sub OnMainFormClosed(ByVal sender As Object, ByVal e As System.EventArgs) + If m_App.ShutdownStyle = ShutdownMode.AfterMainFormCloses Then + MyBase.OnMainFormClosed(sender, e) + Else 'identify a new main form so we can keep running + Call New System.Security.Permissions.UIPermission(UIPermissionWindow.AllWindows).Assert() + Dim forms As System.Windows.Forms.FormCollection = System.Windows.Forms.Application.OpenForms + System.Security.PermissionSet.RevertAssert() 'CLR also reverts if we throw or when we return from this function. + If forms.Count > 0 Then + 'Note: Initially I used Process::MainWindowHandle to obtain an open form. But that is bad for two reasons: + '1 - It appears to be broken and returns NULL sometimes even when there is still a window around. WinForms people are looking at that issue. + '2 - It returns the first window it hits from enum thread windows, which is not necessarily a windows forms form, so that doesn't help us even if it did work + 'all the time. So I'll use one of our open forms. We may not necessairily get a visible form here but that's ok. Some apps may run on an invisible window + 'and we need to keep them going until all windows close. + Me.MainForm = forms(0) + Else + MyBase.OnMainFormClosed(sender, e) + End If + End If + End Sub + + Private m_App As WindowsFormsApplicationBase + End Class 'WinFormsAppContext + + '''************************************************************************** + ''' ;OnUnhandledExceptionAdaptor + ''' + ''' Handles the Windows.Forms.Application.ThreadException event and raises our Unhandled + ''' exception event + ''' + ''' + ''' Our UnHandledException event has a different signature then the Windows.Forms.Application + ''' unhandled exception event so we do the translation here before raising our event. + ''' + Private Sub OnUnhandledExceptionEventAdaptor(ByVal sender As Object, ByVal e As Threading.ThreadExceptionEventArgs) + OnUnhandledException(New Microsoft.VisualBasic.ApplicationServices.UnhandledExceptionEventArgs(True, e.Exception)) + End Sub + + '''************************************************************************** + ''' ;OnStartupNextInstanceMarshallingAdaptor + ''' + ''' The call we get from the Async Operations manager has a different signature then what + ''' we'd like to pass to our OnStartupNextInstanceMarshallingAdaptor. So we do the translation + ''' here and then call OnStartupNextInstance + ''' + ''' + ''' + Private Sub OnStartupNextInstanceMarshallingAdaptor(ByVal args As Object) + OnStartupNextInstance(New StartupNextInstanceEventArgs(CType(args, System.Collections.ObjectModel.ReadOnlyCollection(Of String)), True)) 'by default, we set BringToFront as True since that's the behavior most people will want + End Sub + + '''************************************************************************** + ''' ;NetworkAvailableAdaptor + ''' + ''' Handles the Network.NetworkAvailability event (on the correct thread) and raises the + ''' NetworkAvailabilityChanged event + ''' + ''' Contains the Network instance that raised the event + ''' Contains whether the network is available or not + ''' + Private Sub NetworkAvailableEventAdaptor(ByVal sender As Object, ByVal e As Microsoft.VisualBasic.Devices.NetworkAvailableEventArgs) + RaiseEvent NetworkAvailabilityChanged(sender, e) + End Sub + + Private Const HOST_NAME As String = "127.0.0.1" '127.0.0.1 is a loopback network connection to your own machine. If you telnet, ftp, etc. to 127.0.0.1 you are connected to your own machine. There's no place like 127.0.0.1 ;-) Xp SP2 prevents connections to all IP addresses that are in the loopback address range except for 127.0.0.1. + Private Const SECOND_INSTANCE_TIMEOUT As Integer = 2500 'milliseconds. How long a subsequent instance will wait for the original instance to get on its feet. This is only useful if the 1st instance went belly up and stranded the semaphore for some reason - we just wouldn't want to hang forever is all. + Private Const ATTACH_TIMEOUT As Integer = 2500 'millisconds. How long we wait for the remoting infrastructure to acknowledge that it sent the remote call to the 1st instance. A 1/10 of a second should be sufficient so 2.5 seconds is really allowing for a margin of error + Private m_UnhandledExceptionHandlers As System.Collections.ArrayList + Private m_ProcessingUnhandledExceptionEvent As Boolean + Private m_TurnOnNetworkListener As Boolean 'Tracks whether we need to create the network object so we can listen to the NetworkAvailabilityChanged event + Private m_FinishedOnInitilaize As Boolean 'Whether we have made it through the processing of OnInitialize + Private m_NetworkAvailabilityEventHandlers As System.Collections.ArrayList + Private m_FirstInstanceSemaphore As System.Threading.EventWaitHandle 'Used to determine if we are the first instance or not and to prevent race conditions if we are launching subsequent instances. Owned by the first instance to run + Private m_MessageRecievedSemaphore As System.Threading.EventWaitHandle 'Used to let us know when the first instance has been contacted + Private m_NetworkObject As Microsoft.VisualBasic.Devices.Network + Private m_MemoryMappedID As String 'global OS handles must have a unique ID + _ + Private m_FirstInstanceMemoryMappedFileHandle As SafeFileHandle 'Used to communicate the URL of the single instance between processes. We hang on to it for the life of the process so that it is available to subsequent instances. It's a SafeHandle so it'll be sure to release the handle when the app exits. + Private m_IsSingleInstance As Boolean 'whether this app runs using Word like instancing behavior + Private m_ShutdownStyle As ShutdownMode 'defines when the application decides to close + Private m_EnableVisualStyles As Boolean 'whether to use Windows XP styles + Private m_DidSplashScreen As Boolean 'we only need to show the splash screen once. Protect the user from himself if they are overriding our app model. + Private Delegate Sub DisposeDelegate() 'used to marshal a call to Dispose on the Splash Screen + Private m_Ok2CloseSplashScreen As Boolean 'For splash screens with a minimum display time, this let's us know when that time has expired and it is ok to close the splash screen. + Private m_SplashScreen As System.Windows.Forms.Form + Private m_MinimumSplashExposure As Integer = 2000 'Minimum amount of time to show the splash screen. 0 means hide as soon as the app comes up. + Private m_SplashTimer As Timers.Timer + Private m_SplashLock As New Object + Private m_AppContext As WinFormsAppContext + Private m_AppSyncronizationContext As SynchronizationContext + Private m_NetworkAvailChangeLock As New Object 'sync object + Private m_SaveMySettingsOnExit As Boolean 'Informs My.Settings whether to save the settings on exit or not + Private m_StartNextInstanceCallback As Threading.SendOrPostCallback 'Used for marshalling the start next instance event to the foreground thread + + 'REMOTING SUPPORT --------- + + '''************************************************************************** + ''' ;RunNextInstanceDelegate + ''' + ''' Provides the delegate to the entry point for subseqent instances of a single instance + ''' + ''' + ''' + Private ReadOnly Property RunNextInstanceDelegate() As System.Threading.SendOrPostCallback + Get + Return m_StartNextInstanceCallback + End Get + End Property + + '''************************************************************************** + ''' ;ReadUrlFromMemoryMappedFile + ''' + ''' For single instance applications, when subsequent instances start up they + ''' need to get to the URL of the remoting object that will connect them to the + ''' original instance. I store the URL of the remote object in a memory-mapped + ''' file which this function reads the URL from. + ''' + ''' + ''' + _ + _ + _ + Private Function ReadUrlFromMemoryMappedFile() As String + + Debug.Assert(m_MemoryMappedID IsNot Nothing, "You can't call this function unless you've first written to the memory mapped file") + + Const FILE_MAP_READ As Integer = &H4 + Dim URL As String + + 'OPEN THE EXISTING MEMORY MAPPED FILE THAT HAS THE URL STOWED IN IT. WE NEED IT TO CONTACT THE ORIGINAL INSTANCE + 'This gets tricky as far as managing the OS handles goes. We'll have another handle out on our memory mapped file and we are about to + 'get a handle for the mapped view of it. We need both closed by the time we leave this method so we don't leak handles and don't leak + 'the memory that we map into this process from the memory mapped file. I'm using SafeHandle classes under the covers to assure that we'll + 'release everything even in the face of abject calamity. + Using MemoryMappedFileHandle As SafeFileHandle = UnsafeNativeMethods.OpenFileMapping(FILE_MAP_READ, False, m_MemoryMappedID) + If MemoryMappedFileHandle.IsInvalid Then + Return Nothing + End If + + 'READ THE URL OUT OF THE MEMORY MAPPED FILE + 'Note, the fact that StartAddressOfMappedView is in a using block gaurantees that it will be kept alive past the + 'time of the call to MapViewOfFile - so we don't need to worry about a race condition with the finalizer in + 'StartAddressOfMappedView releasing the handle during the time we need access to it. + Using StartAddressOfMappedView As SafeMemoryMappedViewOfFileHandle = _ + UnsafeNativeMethods.MapViewOfFile(MemoryMappedFileHandle.DangerousGetHandle, FILE_MAP_READ, 0, 0, UIntPtr.Zero) + + If StartAddressOfMappedView.IsInvalid Then + 'This exception does a get last error and generates the appropriate message for it + Throw ExceptionUtils.GetWin32Exception(ResID.MyID.AppModel_CantGetMemoryMappedFile) + End If + + URL = System.Runtime.InteropServices.Marshal.PtrToStringUni(StartAddressOfMappedView.DangerousGetHandle) + End Using 'Releases StartAddressOfMappedView which Unmaps the memory mapped file view + End Using 'Releases MemoryMappedFileHandle + + Return URL + End Function + + '''************************************************************************** + ''' ;WriteUrlToMemoryMappedFile + ''' + ''' For single instance applications, when subsequent instances start up they + ''' need to get to the URL of the remoting object that will connect them to the + ''' original instance. I store the URL of the remote object in a memory-mapped + ''' file which subsequent instances can read from. + ''' + ''' + ''' + _ + _ + _ + Private Sub WriteUrlToMemoryMappedFile(ByVal URL As String) + + Debug.Assert(m_FirstInstanceMemoryMappedFileHandle Is Nothing, "m_MemoryMappedFileHandle shouldn't be set already") + + Const SDDL_REVISION_1 As Integer = 1 'see sddl.h for info on SDDL_REVISION_1 value These days it is 1. + Const PAGE_READWRITE As Integer = &H4 + Const FILE_MAP_WRITE As Integer = &H2 + Const SYSTEM_PAGING_FILE_ID As Integer = &HFFFFFFFF + + 'This doesn't need to be a SafeHandle--it's just a constant that represents the Paging file. It's not a handle to an actual OS resource + 'But it does need to be a HandleRef so it doesn't get collected during the PINVOKE calls + Dim PAGE_FILE_HANDLE As New System.Runtime.InteropServices.HandleRef(Nothing, New IntPtr(SYSTEM_PAGING_FILE_ID)) + + Using SecurityAttributes As New NativeTypes.SECURITY_ATTRIBUTES + SecurityAttributes.bInheritHandle = False 'no inheritance of permissions desired for the memory mapped file handle + 'see p. 186-187 in writing secure code vol 2 on the SDDL string + 'see winerror.h for system.runtime.interop.marshal.getlastwin32error() meanings if !ok + 'see http://msdn.microsoft.com/library/default.asp?url=/library/en-us/secauthz/security/ace_strings.asp for how to build the SDL string. + Dim SecurityDescriptorIsOK As Boolean + Try + Const RIGHTS As String = "D:(A;;GA;;;CO)(A;;GR;;;AU)" + 'D: Means we are defining a DACL (discretionary access control list) + '(A;;GA;;;CO) means A=Allow GA=general access to CO=creater owner (that's us) + '(A;;GR;;;AU) means A=Allow GR=read rights to AU=authenticated users + 'The main point here is to keep people from scribbing on the contents of the memory mapped file. We have other mitigations + 'if they read the memory mapped file because they have to have the same identity as the creator of the m.m. file in order to + 'make use of it in our remoting mechanism. + 'SECURITY REVIEW: - I'm new to the security description string. Make sure this is ok. + 'NOTE: This method allocates memory that SecurityAttributes.lpSecurityDescriptor points to. Fortunately, SecurityAttributes frees the memory for us in its Dispose() + + Call New SecurityPermission(SecurityPermissionFlag.UnmanagedCode).Assert() + SecurityDescriptorIsOK = NativeMethods.ConvertStringSecurityDescriptorToSecurityDescriptor(RIGHTS, SDDL_REVISION_1, SecurityAttributes.lpSecurityDescriptor, IntPtr.Zero) + System.Security.CodeAccessPermission.RevertAssert() + + 'The SDDL language isn't supported on platforms < win2000. ACLS aren't supported on win 95/98/me/CE so this really only affects NT4 + Catch ex As System.EntryPointNotFoundException + SecurityAttributes.lpSecurityDescriptor = IntPtr.Zero 'use the default ACLS the user + Catch ex As System.DllNotFoundException + SecurityAttributes.lpSecurityDescriptor = IntPtr.Zero 'use the default ACLS the user + End Try + + If Not SecurityDescriptorIsOK Then + SecurityAttributes.lpSecurityDescriptor = IntPtr.Zero 'use the default ACLS of the user + End If + + 'This is a SafeFileHandle so we won't leak the handle in the event of an async exception. It lives for the duration of this process & + 'is released during the finally block for the app. In the event of an async exception that prevents the app's finally from running, it + 'gets released during critical finalization as it is a SafeHandle. + m_FirstInstanceMemoryMappedFileHandle = UnsafeNativeMethods.CreateFileMapping( _ + PAGE_FILE_HANDLE, SecurityAttributes, PAGE_READWRITE, 0, (URL.Length + 1) * 2, m_MemoryMappedID) + + If m_FirstInstanceMemoryMappedFileHandle.IsInvalid Then + Throw ExceptionUtils.GetWin32Exception(ResID.MyID.AppModel_CantGetMemoryMappedFile) + End If + End Using 'SecurityAttributes - we are agressive about disposing this as it hangs on to unmanaged memory + + 'I'm using a SafeHandle below so that we are guaranteed that the view will get unmapped even in the event of a calamity. + 'In the single-process case this isn't that interesting since the OS will reclaim everything anyway when the process dies. + 'But if we were running in an AppDomain that is getting created/destroyed repeatedly then we'd have + 'an ongoing memory leak since app domain unloads don't reclaim lost OS handles and unmanaged memory since the process + 'is still hanging around. + Using StartAddressOfMappedFile As SafeMemoryMappedViewOfFileHandle = UnsafeNativeMethods.MapViewOfFile( _ + m_FirstInstanceMemoryMappedFileHandle.DangerousGetHandle, FILE_MAP_WRITE, 0, 0, UIntPtr.Zero) + + If StartAddressOfMappedFile.IsInvalid Then + Throw ExceptionUtils.GetWin32Exception(ResID.MyID.AppModel_CantGetMemoryMappedFile) + End If + + Dim UrlChars() As Char = URL.ToCharArray() + System.Runtime.InteropServices.Marshal.Copy(UrlChars, 0, StartAddressOfMappedFile.DangerousGetHandle, UrlChars.Length) + End Using 'Releases StartAddressOfMappedFile which unmaps the memory for the view. + 'Note that though we've unmapped the view, because we are hanging on to the file handle in m_FirstInstanceMemoryMappedFileHandle + 'we won't lose our memory mapped file until this instance goes down. Which is what we want because subsequent instances that launch will need + 'the memory mapped file so they can read the URL we just wrote. When the original instance (this one) goes down then we release the memory mapped file. + End Sub + + '''************************************************************************** + ''' ;RegisterChannel + ''' + ''' Provides an authenticated (via windows impersonation) channel for remoting purposes + ''' This channel is tied to LOCALHOST + ''' + ''' A channel that can be used as either a server or client channel + ''' First instance will create a server channel, subsequent instances will create a client channel. VSWhidbey 563668. + ''' Whether we can use authentication on the channel or not + ''' Attempts to connect to a port opened on localhost from another machine + ''' will fail - which helps us secure this port. But a normal user in a terminal-server session + ''' can see the port and talk to it. Therefore we made an authenticated channel so that we + ''' can know who is making the request over the channel + _ + Private Function RegisterChannel(ByVal ChannelType As ChannelType, ByVal ChannelIsSecure As Boolean) As IChannel + 'To run single instance in low-trust (e.g. InternetZone), we need the following permissions (differs from connecting - don't need DNS permission. See where we do this in Run()) + Dim PermissionsToDoRemoting As New System.Security.PermissionSet(PermissionState.None) + PermissionsToDoRemoting.AddPermission(New System.Security.Permissions.SecurityPermission(SecurityPermissionFlag.SerializationFormatter Or SecurityPermissionFlag.ControlPrincipal Or SecurityPermissionFlag.UnmanagedCode)) + PermissionsToDoRemoting.AddPermission(New System.Net.SocketPermission(Net.NetworkAccess.Accept, Net.TransportType.Tcp, HOST_NAME, 0)) + PermissionsToDoRemoting.AddPermission(New System.Security.Permissions.EnvironmentPermission(EnvironmentPermissionAccess.Read, "USERNAME")) + PermissionsToDoRemoting.AddPermission(New System.Security.Permissions.SecurityPermission(System.Security.Permissions.SecurityPermissionFlag.RemotingConfiguration)) + PermissionsToDoRemoting.Assert() + + ' See http://msdn2.microsoft.com/en-us/library/kw7c6kwc.aspx for channel properties. + Dim ChannelProperties As System.Collections.IDictionary = New System.Collections.Hashtable(3) + ChannelProperties.Add("bindTo", HOST_NAME) + ChannelProperties.Add("port", 0) 'Listen on any open port. + ChannelProperties.Add("name", String.Empty) ' ignore names but avoid naming collisions, this will avoid an exception when the application registers a default channel by itself VSWhidbey 563668. + If ChannelIsSecure = True Then 'Only >= NT support authentication on the server + ChannelProperties.Add("secure", True) 'we use authentication to keep people from talking to our port from other terminal server instances + ChannelProperties.Add("tokenimpersonationlevel", System.Security.Principal.TokenImpersonationLevel.Impersonation) + ChannelProperties.Add("impersonate", True) + End If + Dim AuthenticatingChannel As IChannel = Nothing + ' Create the specific TCP channel. This helps when the application register a generic channel / client channel to do remoting itself. + ' The remoting framework will not pick our server channel to use to talk to the customer's server. VSWhidbey 563668. + If ChannelType = ChannelType.Server Then + AuthenticatingChannel = New TcpServerChannel(ChannelProperties, Nothing) + Else + AuthenticatingChannel = New TcpClientChannel(ChannelProperties, Nothing) + End If + ChannelServices.RegisterChannel(AuthenticatingChannel, ChannelIsSecure) + + System.Security.PermissionSet.RevertAssert() + Return AuthenticatingChannel + End Function + + '''************************************************************************** + ''' ;RemoteCommunicator + ''' + ''' This class is used in the Single-Instance application scenario. It marshals a cross process + ''' (using remoting) from a subsequent instance of an application to the original instance and + ''' instigates the StartupNextInstance() event being fired on the original instance. + ''' + ''' + Private Class RemoteCommunicator : Inherits System.MarshalByRefObject + + '''************************************************************************** + ''' ;New + ''' + ''' Constructs a new RemoteCommunicator + ''' + ''' Internal class used to encapsulate marshaling the single instance app event + ''' from a subsequent process to the original process + ''' + _ + Friend Sub New(ByVal appObject As WindowsFormsApplicationBase, ByVal ConnectionMadeSemaphore As System.Threading.EventWaitHandle) + Call New SecurityPermission(System.Security.Permissions.SecurityPermissionFlag.ControlPrincipal).Assert() 'I need to get the current identity so I can do my ACLS + m_OriginalUser = System.Security.Principal.WindowsIdentity.GetCurrent + System.Security.CodeAccessPermission.RevertAssert() + + m_AsyncOp = AsyncOperationManager.CreateOperation(Nothing) 'We need to hang on to the syncronization context associated with the thread the network object is created on + m_StartNextInstanceDelegate = appObject.RunNextInstanceDelegate + m_ConnectionMadeSemaphore = ConnectionMadeSemaphore + End Sub + + '''************************************************************************** + ''' ;RunNextInstance + ''' + ''' Makes sure that the right person is trying to start up a subsequent instance and marshal + ''' the call to the original instance. + ''' + ''' + ''' + ''' In systems before NT, there is no security. Attempts to get the current user + ''' will return "" so verification 'succeeds' on those earlier systems. + ''' + _ + _ + Public Sub RunNextInstance(ByVal Args As System.Collections.ObjectModel.ReadOnlyCollection(Of String)) + 'Prevent the vulnerability in which a normal user could terminal server into a server box and invoke (if they figured out the port and our objects) + 'our 2nd instance mechanism, passing whatever command line args they want to a running instance. We mitigate that threat by using a channel + 'that(uses) impersonation for authentication purposes. To prevent somebody else from talking to our remote object, make sure that our caller + 'is the same user that the 1st instance launched under + Call New SecurityPermission(System.Security.Permissions.SecurityPermissionFlag.ControlPrincipal).Assert() 'I need to get the current identity so I can do my ACLS + If m_OriginalUser.User <> System.Security.Principal.WindowsIdentity.GetCurrent.User Then + Return 'don't tell them what happened - just don't connect. Somebody is trying to spoof us. CLR clears security assert as we return so no revert needed. + End If + m_ConnectionMadeSemaphore.Set() + System.Security.CodeAccessPermission.RevertAssert() + m_AsyncOp.Post(m_StartNextInstanceDelegate, Args) 'Marshal the call over to the original instance + End Sub + + '''************************************************************************** + ''' ;InitializeLifetimeService + ''' + ''' Set our lease so that it doesn't expire. The remote object will be destroyed when the application goes down + ''' + ''' + ''' + _ + Public Overrides Function InitializeLifetimeService() As Object + Return Nothing + End Function + + Private m_StartNextInstanceDelegate As Threading.SendOrPostCallback + Private m_AsyncOp As System.ComponentModel.AsyncOperation 'Used for marshalling the start next instance call to the foreground thread + Private m_OriginalUser As System.Security.Principal.WindowsIdentity 'Track who started the first instance so we can identify spoofers trying to call in on our remote object + Private m_ConnectionMadeSemaphore As System.Threading.EventWaitHandle 'Note that we don't manage the lifetime of the ConnectionMadeSemaphore handle so don't try to close it in this class. + End Class 'RemoteCommunicator + + '''************************************************************************** + ''' ;DoApplicationModel + ''' + ''' Runs the user's program through the VB Startup/Shutdown application model + ''' + ''' + Private Sub DoApplicationModel() + + Dim EventArgs As New StartupEventArgs(MyBase.CommandLineArgs) + + 'Only do the try/catch if we aren't running under the debugger. If we do try/catch under the debugger the debugger never gets a crack at exceptions which breaks the exception helper + If Not System.Diagnostics.Debugger.IsAttached Then + 'NO DEBUGGER ATTACHED - we use a catch so that we can run our UnhandledException code + 'Note - Sadly, code changes within this IF (that don't pertain to exception handling) need to be mirrored in the ELSE debugger attached clause below + Try + If OnInitialize(MyBase.CommandLineArgs) Then + If OnStartup(EventArgs) = True Then + OnRun() + OnShutdown() + End If + End If + Catch ex As System.Exception + 'This catch is for exceptions that happen during the On* methods above, but have occurred outside of the message pump (which exceptions we would + 'have already seen via our hook of System.Windows.Forms.Application.ThreadException) + If m_ProcessingUnhandledExceptionEvent Then + Throw 'If the UnhandledException handler threw for some reason, throw that error out to the system. + Else 'We had an exception, but not during the OnUnhandledException handler so give the user a chance to look at what happened in the UnhandledException event handler + If Not OnUnhandledException(New UnhandledExceptionEventArgs(True, ex)) = True Then + Throw 'the user didn't write a handler so throw the error out to the system + End If + End If + End Try + Else 'DEBUGGER ATTACHED - we don't have an uber catch when debugging so the exception will bubble out to the exception helper + 'We also don't hook up the Application.ThreadException event because WinForms ignores it when we are running under the debugger + If OnInitialize(MyBase.CommandLineArgs) Then + If OnStartup(EventArgs) = True Then + OnRun() + OnShutdown() + End If + End If + End If + End Sub + + '''************************************************************************** + ''' ;GetApplicationInstanceID + ''' + ''' Generates the name for the remote singleton that we use to channel multiple instances + ''' to the same application model thread. + ''' + ''' + ''' + _ + Private Function GetApplicationInstanceID(ByVal Entry As Assembly) As String + 'CONSIDER: We may want to make this public so users can set up what single instance means to them, e.g. for us, seperate paths mean different instances, etc. + + Dim Permissions As New System.Security.PermissionSet(PermissionState.None) + Permissions.AddPermission(New FileIOPermission(PermissionState.Unrestricted)) 'Chicken and egg problem. All I need is PathDiscovery for the location of this assembly but to get the location of the assembly (see Getname below) I need to know the path which I can't get without asserting... + Permissions.AddPermission(New SecurityPermission(System.Security.Permissions.SecurityPermissionFlag.UnmanagedCode)) + Permissions.Assert() + + Dim Guid As System.Guid = System.Runtime.InteropServices.Marshal.GetTypeLibGuidForAssembly(Entry) + Dim Version As String = Entry.GetName.Version.ToString + Dim VersionParts As String() = Version.Split(CType(".", Char())) + System.Security.PermissionSet.RevertAssert() + + 'Note: We used to make the terminal server session ID part of the key. It turns out to be unnecessary and the call to + 'NativeMethods.ProcessIdToSessionId(System.Diagnostics.Process.GetCurrentProcess.Id, TerminalSessionID) was not supported on Win98, anyway. + 'It turns out that terminal server sessions, even when you are logged in as the same user to multiple terminal server sessions on the same + 'machine, are separate. So you can have session 1 running as and have a global system object named "FOO" that won't conflict with + 'any other global system object named "FOO" whether it be in session 2 running as or session n running as whoever. + 'So it isn't necessary to make the session id part of the unique name that identifies a + + Return Guid.ToString + VersionParts(0) + "." + VersionParts(1) 'Re: version parts, we have the major, minor, build, revision. We key off major+minor. + End Function + + ''' ;ChannelType + ''' + ''' Used in RegisterChannel method. Generate either TcpServerChannel or TcpClientChannel + ''' + Private Enum ChannelType As Byte + Client = 1 + Server = 0 + End Enum + End Class 'WindowsFormsApplicationBase +End Namespace + diff --git a/Microsoft.VisualBasic/runtime/msvbalib/Collection.vb b/Microsoft.VisualBasic/runtime/msvbalib/Collection.vb new file mode 100644 index 000000000..e8040b7a7 --- /dev/null +++ b/Microsoft.VisualBasic/runtime/msvbalib/Collection.vb @@ -0,0 +1,935 @@ +' Copyright (c) Microsoft Corporation. All rights reserved. + +Imports System +Imports System.Collections +#If TELESTO Then +Imports System.Collections.Generic +#End If +Imports System.Globalization +Imports System.Diagnostics +#If Not TELESTO Then +Imports System.Runtime.Serialization +#End If +Imports System.Security +Imports System.Security.Permissions + +Imports Microsoft.VisualBasic.CompilerServices +Imports Microsoft.VisualBasic.CompilerServices.ExceptionUtils +Imports Microsoft.VisualBasic.CompilerServices.Utils + +Namespace Microsoft.VisualBasic + +#If TELESTO Then + ' Types in Telesto aren't serializable _ + ' Debugger display stuff not supported. _ + _ + Public NotInheritable Class Collection : Implements IEnumerable, ICollection, IList 'Telesto doesn't support ISerializable, IDeserializationCallback +#Else + _ + _ + _ + Public NotInheritable Class Collection : Implements ICollection, IList, ISerializable, IDeserializationCallback +#End If + + '= PUBLIC ============================================================= + + '''************************************************************************** + ''' ;New + ''' + ''' Ctor. + ''' + ''' + Public Sub New() + MyBase.New() + Initialize(GetCultureInfo) + End Sub + + '***************************************************************************** + 'These methods are 1 based + '***************************************************************************** + + '''************************************************************************** + ''' ;Add + ''' + ''' Add an item to the collection + ''' + ''' + ''' + ''' + ''' + ''' + Public Sub Add(ByVal Item As Object, Optional ByVal Key As String = Nothing, Optional ByVal Before As Object = Nothing, Optional ByVal After As Object = Nothing) + + 'Before and After are mutually exclusive + If (Not Before Is Nothing) AndAlso (Not After Is Nothing) Then + Throw New ArgumentException(GetResourceString(ResID.Collection_BeforeAfterExclusive)) + End If + + 'Create a new node + Dim NewNode As Node = New Node(Key, Item) + + 'If a key was specified, add the new node to the hashtable of keys. If this is + ' a duplicate key, we'll get an argument exception. This prevents us from + ' having to do a hashtable look-up to verify the key is unique. + 'However, we then have to be careful to remove the node from the hashtable if we fail to add the item to the + ' list because the Before or After keys were bad. + If Key IsNot Nothing Then + Try + m_KeyedNodesHash.Add(Key, NewNode) + Catch ex As ArgumentException + Debug.Assert(m_KeyedNodesHash.ContainsKey(Key), "We got an argumentexception from a hashtable add, but it wasn't a duplicate key. Please file a bug. What other kind of argument exception could we get here?") + '... Duplicate key. Throw our own exception and our own message. + Throw VbMakeException(New ArgumentException(GetResourceString(ResID.Collection_DuplicateKey)), vbErrors.DuplicateKey) + End Try + End If + + Try + 'Add the item to list and also (if a key was specified) to the hashtable + If (Before Is Nothing) AndAlso (After Is Nothing) Then 'Neither Before nor After have been specified + 'Add the value to the linked list + m_ItemsList.Add(NewNode) + ElseIf Before IsNot Nothing Then + 'Before has been specified + Debug.Assert(Before IsNot Nothing, "Huh?") + Dim BeforeString As String = TryCast(Before, String) + + If BeforeString IsNot Nothing Then + Dim BeforeNode As Node = Nothing + If Not m_KeyedNodesHash.TryGetValue(BeforeString, BeforeNode) Then + Throw New ArgumentException(GetResourceString(ResID.Argument_InvalidValue1, "Before")) + End If + Debug.Assert(BeforeNode IsNot Nothing) + + m_ItemsList.InsertBefore(NewNode, BeforeNode) + Else + m_ItemsList.Insert(CInt(Before) - 1, NewNode) 'Convert from 1 based to 0 based. + End If + Else + 'After has been specified + Debug.Assert(After IsNot Nothing, "Huh?") + Dim AfterString As String = TryCast(After, String) + + If AfterString IsNot Nothing Then + Dim AfterNode As Node = Nothing + If Not m_KeyedNodesHash.TryGetValue(AfterString, AfterNode) Then + Throw New ArgumentException(GetResourceString(ResID.Argument_InvalidValue1, "After")) + End If + Debug.Assert(AfterNode IsNot Nothing) + m_ItemsList.InsertAfter(NewNode, AfterNode) + Else + m_ItemsList.Insert(CInt(After), NewNode) 'Conversion from 1 based to 0 based offsets need to add 1. + End If + End If + Catch ex As OutOfMemoryException + Throw + Catch ex As Threading.ThreadAbortException + Throw + Catch ex As StackOverflowException + Throw + Catch ex As Exception + 'We couldn't add the item to the list because the Before or After key was not found. We need to back out the + ' insert that we did into the hash table. + If Key IsNot Nothing Then + m_KeyedNodesHash.Remove(Key) + End If + + Throw + End Try + + 'Adjust the ForEach iterators + AdjustEnumeratorsOnNodeInserted(NewNode) + End Sub + + '''************************************************************************** + ''' ;Clear + ''' + ''' Clears all items in the collection + ''' + ''' + Public Sub Clear() + m_KeyedNodesHash.Clear() + m_ItemsList.Clear() + + 'Notify the enumerators + Dim i As Integer = m_Iterators.Count - 1 + While i >= 0 + Dim Ref As WeakReference = DirectCast(m_Iterators(i), WeakReference) + If Ref.IsAlive Then + Dim Enumerator As ForEachEnum = CType(Ref.Target, ForEachEnum) + If Not Enumerator Is Nothing Then + Enumerator.AdjustOnListCleared() + End If + Else + m_Iterators.RemoveAt(i) + End If + i -= 1 + End While + End Sub + + '''************************************************************************** + ''' ;Contains + ''' + ''' Returns true iff the given key is in the collection + ''' + ''' + ''' True - the given key is in the collection. False otherwise. + ''' + + Public Function Contains(ByVal Key As String) As Boolean + If Key Is Nothing Then + Throw New ArgumentException(GetResourceString(ResID.Argument_InvalidValue1, "Key")) + End If + Return m_KeyedNodesHash.ContainsKey(Key) + End Function + + Public Overloads Sub Remove(ByVal Key As String) + Dim Node As Node = Nothing + If m_KeyedNodesHash.TryGetValue(Key, Node) Then + Debug.Assert(Node IsNot Nothing) + + 'Adjust the ForEach iterators + AdjustEnumeratorsOnNodeRemoved(Node) 'Must be done before the prev/next pointers are removed + + 'Remove the item from the list and hash table (we know it has a key) + Debug.Assert(Node.m_Key IsNot Nothing, "How can that be? We just found it by its key.") + m_KeyedNodesHash.Remove(Key) + m_ItemsList.RemoveNode(Node) + + 'Remove prev/next pointers because the iterators will be thrown off if this isn't done. Also it's easier for debugging. + Node.m_Prev = Nothing + Node.m_Next = Nothing + Else + Debug.Assert(Node Is Nothing) + Throw New ArgumentException(GetResourceString(ResID.Argument_InvalidValue1, "Key")) + End If + End Sub + + Public Overloads Sub Remove(ByVal Index As Integer) + IndexCheck(Index) + Dim Node As Node = m_ItemsList.RemoveAt(Index - 1) '0 based + Debug.Assert(Node IsNot Nothing, "Should have thrown exception rather than return Nothing") + + AdjustEnumeratorsOnNodeRemoved(Node) 'Must be done before the prev/next pointers are removed + + 'Remove from the hash table if it has a key + If Node.m_Key IsNot Nothing Then + m_KeyedNodesHash.Remove(Node.m_Key) + End If + + 'Remove prev/next pointers because the iterators will be thrown off if this isn't done. Also it's easier for debugging. + Node.m_Prev = Nothing + Node.m_Next = Nothing + End Sub + + Default Public Overloads ReadOnly Property Item(ByVal Index As Integer) As Object + 'This method uses 1 based arrays. + Get + IndexCheck(Index) + Dim Node As Node = m_ItemsList.Item(Index - 1) + Debug.Assert(Node IsNot Nothing, "Should have thrown rather than returning Nothing") + Return Node.m_Value + End Get + End Property + + Default Public Overloads ReadOnly Property Item(ByVal Key As String) As Object + Get + If Key Is Nothing Then + 'Backwards compat with Everett - throw IndexOutOfRange if Key = Nothing + Throw New IndexOutOfRangeException(GetResourceString(ResID.Argument_CollectionIndex)) + End If + + Dim Node As Node = Nothing + If Not m_KeyedNodesHash.TryGetValue(Key, Node) Then + Throw New ArgumentException(GetResourceString(ResID.Argument_InvalidValue1, "Index")) + End If + Debug.Assert(Node IsNot Nothing) + + Return Node.m_Value + End Get + End Property + +#If TELESTO Then + 'FIXME: + Default Public Overloads ReadOnly Property Item(ByVal Index As Object) As Object +#Else + _ + Default Public Overloads ReadOnly Property Item(ByVal Index As Object) As Object +#End If + Get + If (TypeOf Index Is String) OrElse (TypeOf Index Is Char) OrElse (TypeOf Index Is Char()) Then + ' Index is string and is being treated as key + Dim Key As String = CStr(Index) + Return Me.Item(Key) + Else + ' Index is being treated as numeric expression + Dim IndexValue As Integer + Try + IndexValue = CInt(Index) + Catch ex As StackOverflowException + Throw ex + Catch ex As OutOfMemoryException + Throw ex + Catch ex As System.Threading.ThreadAbortException + Throw ex + Catch + Throw New ArgumentException(GetResourceString(ResID.Argument_InvalidValue1, "Index")) + End Try + + Return Me.Item(IndexValue) + End If + End Get + End Property + + Public ReadOnly Property Count() As Integer + Get + Return m_ItemsList.Count + End Get + End Property + + Public Function GetEnumerator() As IEnumerator + + ' Remove Dead Iterators if any from Iterator list m_Iterators + Dim oldWeakref As WeakReference + Dim i As Integer = m_Iterators.Count - 1 + + While (i >= 0) + oldWeakref = CType(m_Iterators(i), WeakReference) + If Not oldWeakref.IsAlive Then + m_Iterators.RemoveAt(i) + End If + i -= 1 + End While + + ' Create A New Iterator, add to Iterator List and return + Dim enumerator As ForEachEnum = New ForEachEnum(Me) + Dim weakref As WeakReference = New WeakReference(enumerator) + enumerator.WeakRef = weakref + m_Iterators.Add(weakref) + Return enumerator + End Function + + '= FRIEND ============================================================= + + Friend Sub RemoveIterator(ByVal weakref As WeakReference) + m_Iterators.Remove(weakref) + End Sub + + Friend Sub AddIterator(ByVal weakref As WeakReference) + m_Iterators.Add(weakref) + End Sub + + 'Returns the first node in the linked list + Friend Function GetFirstListNode() As Node + Return m_ItemsList.GetFirstListNode() + End Function + + '*************************************************************** + ' + ' Nested class: Node + ' + '*************************************************************** + Friend NotInheritable Class Node + 'Constructor. Key or Value may be Nothing + Friend Sub New(ByVal Key As String, ByVal Value As Object) + m_Value = Value + m_Key = Key + End Sub + + Friend m_Value As Object 'The value. + Friend m_Key As String 'The key. If Nothing, the collection item has no user-specified key. + Friend m_Next As Node 'Doubly-linked list pointer to the next node + Friend m_Prev As Node 'Doubly-linked list pointer to the previous node + +#If 0 Then 'For debugging purposes + Public Overrides Function ToString() As String + Dim s As New System.Text.StringBuilder + + s.Append("Node (") + If m_Key Is Nothing Then + s.Append("no key") + Else + s.Append("key=""" & m_Key & """") + End If + s.Append("): ") + Try + s.Append(m_Value.ToString()) + Catch ex As OutOfMemoryException + Throw + Catch ex As Threading.ThreadAbortException + Throw + Catch ex As StackOverflowException + Throw + Catch ex As Exception + s.Append(ex.Message) + End Try + + Return s.ToString() + End Function +#End If + End Class 'Node + + '''****************************************************************************** + ''' ;CollectionDebugView + ''' + ''' Debugger proxy for the Collection class. Provides a view of the collection + ''' that just displays the collection contents. + ''' + ''' + Friend NotInheritable Class CollectionDebugView + Public Sub New(ByVal RealClass As Collection) + m_InstanceBeingWatched = RealClass + End Sub + + 'Returns an array with all items in the collection. + _ + Public ReadOnly Property Items() As Object() + Get + Dim Count As Integer = m_InstanceBeingWatched.Count + If Count = 0 Then + Return Nothing + End If + + Dim Results(Count) As Object + Results(0) = GetResourceString(ResID.EmptyPlaceHolderMessage) + + For Index As Integer = 1 To Count + Dim NewNode As Node = m_InstanceBeingWatched.InternalItemsList.Item(Index - 1) + Results(Index) = New KeyValuePair(NewNode.m_Key, NewNode.m_Value) + Next Index + Return Results + End Get + End Property + + Private m_InstanceBeingWatched As Collection + End Class + + '= PRIVATE ============================================================= + + '''****************************************************************************** + ''' ;Initialize + ''' + ''' Initialize all internal structures to set up an empty collection. + ''' + ''' the culture info to use for key comparisons for the lifetime of this collection. + ''' + ''' + Private Sub Initialize(ByVal CultureInfo As CultureInfo, Optional ByVal StartingHashCapacity As Integer = 0) + Debug.Assert(CultureInfo IsNot Nothing) + + If StartingHashCapacity > 0 Then + m_KeyedNodesHash = New Generic.Dictionary(Of String, Node)(StartingHashCapacity, StringComparer.Create(CultureInfo, ignoreCase:=True)) + Else + m_KeyedNodesHash = New Generic.Dictionary(Of String, Node)(StringComparer.Create(CultureInfo, ignoreCase:=True)) + End If + m_ItemsList = New FastList() +#If TELESTO Then + m_Iterators = New List(Of Object) +#Else + m_Iterators = New ArrayList +#End IF + + +#If Not TELESTO Then + m_CultureInfo = CultureInfo +#End If + End Sub + + '*************************************************************** + ' + ' Nested class: FastList + ' + '*************************************************************** + Private NotInheritable Class FastList + + '= FRIEND ============================================================= + + Friend Sub New() + MyBase.New() + End Sub + + Friend Sub Add(ByVal Node As Node) + If m_StartOfList Is Nothing Then + m_StartOfList = Node + Else + m_EndOfList.m_Next = Node + Node.m_Prev = m_EndOfList + End If + m_EndOfList = Node + m_Count += 1 + End Sub + + 'Searches for a given value in the list. Returns its node's index or -1 if not found. + Friend Function IndexOfValue(ByVal Value As Object) As Integer + Dim CurrentNode As Node = m_StartOfList + Dim Index As Integer = 0 + + While Not CurrentNode Is Nothing + If DataIsEqual(CurrentNode.m_Value, Value) Then + Return Index + End If + CurrentNode = CurrentNode.m_Next + Index += 1 + End While + Return -1 + End Function + + Friend Sub RemoveNode(ByVal NodeToBeDeleted As Node) + Debug.Assert(Not (NodeToBeDeleted Is Nothing), "How can we remove a non-existent node ?") + DeleteNode(NodeToBeDeleted, NodeToBeDeleted.m_Prev) + End Sub + + 'Removes the node at the given index. + ' Returns the node that was removed + Friend Function RemoveAt(ByVal Index As Integer) As Node + Dim CurrentNode As Node = m_StartOfList + Dim CurrentIndex As Integer = 0 + Dim PrevNode As Node = Nothing + + While CurrentIndex < Index AndAlso (Not (CurrentNode Is Nothing)) + PrevNode = CurrentNode + CurrentNode = CurrentNode.m_Next + CurrentIndex += 1 + End While + + If (CurrentNode Is Nothing) Then + Throw New ArgumentOutOfRangeException("Index") + End If + + DeleteNode(CurrentNode, PrevNode) + Return CurrentNode + End Function + + Friend Function Count() As Integer + Return m_Count + End Function + + Friend Sub Clear() + m_StartOfList = Nothing + m_EndOfList = Nothing + m_Count = 0 + End Sub + + 'Retrieves the node at the given index (0-based) + Friend ReadOnly Property Item(ByVal Index As Integer) As Node + Get + Dim N As Node = GetNodeAtIndex(Index) + If (N Is Nothing) Then + Throw New ArgumentOutOfRangeException("Index") + End If + Return N + End Get + End Property + + Friend Sub Insert(ByVal Index As Integer, ByVal Node As Node) + Dim PrevNode As Node = Nothing + + ' (Index > m_Count) is here for a reason. We allow insertion immediately beyond the end of the + ' list i.e. if there are 0 to m_Count -1 elements, we allow insertion into the + ' m_Count index + If (Index < 0) OrElse (Index > m_Count) Then + Throw New ArgumentOutOfRangeException("Index") + End If + + Dim NodeAtIndex As Node = GetNodeAtIndex(Index, PrevNode) 'Note: PrevNode passed ByRef + Insert(Node, PrevNode, NodeAtIndex) + End Sub + + 'Inserts a node into the list. + ' The item is inserted before NodeToInsertBefore (may not be Nothing). + Friend Sub InsertBefore(ByVal Node As Node, ByVal NodeToInsertBefore As Node) + Debug.Assert(NodeToInsertBefore IsNot Nothing, "FastList.InsertBefore: NodeToInsertBefore may not be nothing") + Insert(Node, NodeToInsertBefore.m_Prev, NodeToInsertBefore) + End Sub + + + 'Inserts a node into the list. + ' The item is inserted after NodeToInsertAfter (may not be Nothing). + Friend Sub InsertAfter(ByVal Node As Node, ByVal NodeToInsertAfter As Node) + Debug.Assert(NodeToInsertAfter IsNot Nothing, "FastList.InsertAfter: NodeToInsertAfter may not be nothing") + Insert(Node, NodeToInsertAfter, NodeToInsertAfter.m_Next) + End Sub + + 'Returns the first node in the list + Friend Function GetFirstListNode() As Node + Return m_StartOfList + End Function + + '= PRIVATE ============================================================= + + Private Function DataIsEqual(ByVal obj1 As Object, ByVal obj2 As Object) As Boolean + If obj1 Is obj2 Then + Return True + End If + + If obj1.GetType() Is obj2.GetType() Then + Return Object.Equals(obj1, obj2) + Else + Return False + End If + End Function + + Private Function GetNodeAtIndex(ByVal Index As Integer, Optional ByRef PrevNode As Node = Nothing) As Node + Dim CurrentNode As Node = m_StartOfList + Dim CurrentIndex As Integer = 0 + PrevNode = Nothing + + While CurrentIndex < Index AndAlso (Not (CurrentNode Is Nothing)) + PrevNode = CurrentNode + CurrentNode = CurrentNode.m_Next + CurrentIndex += 1 + End While + + Return CurrentNode + End Function + + 'Inserts the given node into the list between the two given nodes (may be Nothing at the beginning/end of the list) + Private Sub Insert(ByVal Node As Node, ByVal PrevNode As Node, ByVal CurrentNode As Node) + Node.m_Next = CurrentNode + + If Not CurrentNode Is Nothing Then + CurrentNode.m_Prev = Node + End If + + If PrevNode Is Nothing Then + m_StartOfList = Node + Else + PrevNode.m_Next = Node + Node.m_Prev = PrevNode + End If + + If Node.m_Next Is Nothing Then + m_EndOfList = Node + End If + + m_Count += 1 + End Sub + + Private Sub DeleteNode(ByVal NodeToBeDeleted As Node, ByVal PrevNode As Node) + Debug.Assert(Not (NodeToBeDeleted Is Nothing), "How can we delete a non-existent node ?") + + If PrevNode Is Nothing Then ' are we are deleting the first node ? + + Debug.Assert(NodeToBeDeleted Is m_StartOfList, "How can any node besides the first node not have a previous node ?") + + m_StartOfList = m_StartOfList.m_Next + + If m_StartOfList Is Nothing Then + m_EndOfList = Nothing + Else + m_StartOfList.m_Prev = Nothing + End If + Else + PrevNode.m_Next = NodeToBeDeleted.m_Next + If PrevNode.m_Next Is Nothing Then + m_EndOfList = PrevNode + Else + PrevNode.m_Next.m_Prev = PrevNode + End If + End If + m_Count -= 1 + End Sub + + Private m_StartOfList As Node + Private m_EndOfList As Node + Private m_Count As Integer = 0 + End Class 'FastList + + 'NOTE: This structure exists so that the debugger windows can show the items in a collection. See the Items property. + Private Structure KeyValuePair + Friend Sub New(ByVal NewKey As Object, ByVal NewValue As Object) + m_Key = NewKey + m_Value = NewValue + End Sub + Private m_Key As Object + Private m_Value As Object + Public ReadOnly Property Key() As Object + Get + Return m_Key + End Get + End Property + Public ReadOnly Property Value() As Object + Get + Return m_Value + End Get + End Property + End Structure + + Private Sub AdjustEnumeratorsOnNodeInserted(ByVal NewNode As Node) + AdjustEnumeratorsHelper(NewNode, ForEachEnum.AdjustIndexType.Insert) + End Sub + + Private Sub AdjustEnumeratorsOnNodeRemoved(ByVal RemovedNode As Node) + AdjustEnumeratorsHelper(RemovedNode, ForEachEnum.AdjustIndexType.Remove) + End Sub + + Private Sub AdjustEnumeratorsHelper(ByVal NewOrRemovedNode As Node, ByVal Type As ForEachEnum.AdjustIndexType) + Debug.Assert(NewOrRemovedNode IsNot Nothing, "AdjustIndexes: Node shouldn't be Nothing") + Dim weakref As WeakReference + Dim i As Integer = m_Iterators.Count - 1 + + While (i >= 0) + weakref = CType(m_Iterators(i), WeakReference) + If weakref.IsAlive Then + Dim enumerator As ForEachEnum = CType(weakref.Target, ForEachEnum) + If Not enumerator Is Nothing Then + enumerator.Adjust(NewOrRemovedNode, Type) '1 based + End If + Else + m_Iterators.RemoveAt(i) + End If + + i -= 1 + End While + End Sub + + Private Sub IndexCheck(ByVal Index As Integer) + If (Index < 1 OrElse Index > m_ItemsList.Count) Then + Throw New IndexOutOfRangeException(GetResourceString(ResID.Argument_CollectionIndex)) + End If + End Sub + + Private Function InternalItemsList() As FastList + Return m_ItemsList + End Function + + +#Region "Serialization implementation" +#If Not TELESTO Then + Private Const SERIALIZATIONKEY_KEYS As String = "Keys" + Private Const SERIALIZATIONKEY_KEYSCOUNT As String = "KeysCount" 'Number of items with a user-defined key (not same as keys array length) + Private Const SERIALIZATIONKEY_VALUES As String = "Values" + Private Const SERIALIZATIONKEY_CULTUREINFO As String = "CultureInfo" + + 'DeSerialization constructor + Private Sub New(ByVal info As SerializationInfo, ByVal context As StreamingContext) + m_DeserializationInfo = info + End Sub + + _ + Private Sub GetObjectData(ByVal info As SerializationInfo, ByVal context As StreamingContext) Implements ISerializable.GetObjectData + Dim Keys(Me.Count - 1) As String + Dim Values(Me.Count - 1) As Object + + Dim Node As Node = GetFirstListNode() + Dim Index As Integer + Dim ElementsWithKey As Integer = 0 + While Node IsNot Nothing + If Node.m_Key IsNot Nothing Then + ElementsWithKey += 1 + End If + + Keys(Index) = Node.m_Key + Values(Index) = Node.m_Value + + Index += 1 + Node = Node.m_Next + End While + Debug.Assert(Index = Me.Count) + Debug.Assert(ElementsWithKey <= Me.Count) + + info.AddValue(SERIALIZATIONKEY_KEYS, Keys, GetType(String())) + info.AddValue(SERIALIZATIONKEY_KEYSCOUNT, ElementsWithKey, GetType(Int32)) + info.AddValue(SERIALIZATIONKEY_VALUES, Values, GetType(Object())) + info.AddValue(SERIALIZATIONKEY_CULTUREINFO, m_CultureInfo) + End Sub + + Private Sub OnDeserialization(ByVal sender As Object) Implements IDeserializationCallback.OnDeserialization + Try + 'Initialization using the saved culture info + Dim CultureInfo As CultureInfo = DirectCast(m_DeserializationInfo.GetValue(SERIALIZATIONKEY_CULTUREINFO, GetType(CultureInfo)), CultureInfo) + If CultureInfo Is Nothing Then + Throw New SerializationException(GetResourceString(ResID.Serialization_MissingCultureInfo)) + End If + + 'Validate the keys and values arrays + Dim Keys() As String = DirectCast(m_DeserializationInfo.GetValue(SERIALIZATIONKEY_KEYS, GetType(String())), String()) + Dim Values() As Object = DirectCast(m_DeserializationInfo.GetValue(SERIALIZATIONKEY_VALUES, GetType(Object())), Object()) + + If Keys Is Nothing Then + Throw New SerializationException(GetResourceString(ResID.Serialization_MissingKeys)) + End If + + If Values Is Nothing Then + Throw New SerializationException(GetResourceString(ResID.Serialization_MissingValues)) + End If + + If Keys.Length <> Values.Length Then + Throw New SerializationException(GetResourceString(ResID.Serialization_KeyValueDifferentSizes)) + End If + + 'Get the number of elements that have an associated key (the Keys array contains an item for all elements, and if that + ' item doesn't have a key, that element in the array is Nothing, so Keys.Length isn't the same) + Dim ElementsWithKey As Integer = m_DeserializationInfo.GetInt32(SERIALIZATIONKEY_KEYSCOUNT) + + 'It's okay if ElementsWithKey isn't in the deserialization info - we'll just assume a value of zero, which causes + ' us to not give a starting hash table capacity + If ElementsWithKey < 0 OrElse ElementsWithKey > Keys.Length Then + Debug.Assert(ElementsWithKey >= 0, "Bad deserialization data? ElementsWithKey is bad. Ignoring and recovering because it's only an optimization anyway.") + ElementsWithKey = 0 + End If + + Initialize(CultureInfo, ElementsWithKey) 'Giving the hash table the appropriate starting capacity should speed up hash insertions since it doesn't have to resize + + 'Add all elements to the collection. We always insert to the end, which is fast and preserves our original order. + For Index As Integer = 0 To Keys.Length - 1 + Me.Add(Values(Index), Keys(Index)) 'If Keys(Index) is Nothing, the value will be added without a key + Next + Debug.Assert(Me.Count = Keys.Length) + m_DeserializationInfo = Nothing + Finally + If m_DeserializationInfo IsNot Nothing Then 'something bad happened - reset the collection to a pristine state so they don't access who knows what is left over in the collection + m_DeserializationInfo = Nothing + Me.Initialize(GetCultureInfo) + End If + End Try + End Sub +#End If 'not TELESTO +#End Region + +#Region "Interface Implementation" + + '***************************************************************************** + 'These methods are 0 based. + '***************************************************************************** + Private Function ICollectionGetEnumerator() As IEnumerator Implements ICollection.GetEnumerator + Return GetEnumerator() + End Function + + Private ReadOnly Property ICollectionCount() As Integer Implements ICollection.Count + Get + Return m_ItemsList.Count + End Get + End Property + + Private ReadOnly Property ICollectionIsSynchronized() As Boolean Implements ICollection.IsSynchronized + Get + Return False + End Get + End Property + + Private ReadOnly Property ICollectionSyncRoot() As Object Implements ICollection.SyncRoot + Get + Return Me + End Get + End Property + + Private ReadOnly Property IListIsFixedSize() As Boolean Implements IList.IsFixedSize + Get + Return False + End Get + End Property + + Private ReadOnly Property IListIsReadOnly() As Boolean Implements IList.IsReadOnly + Get + Return False + End Get + End Property + + Private Sub ICollectionCopyTo(ByVal [array] As System.Array, ByVal index As Integer) Implements ICollection.CopyTo + '*** BEGIN ARG CHECKS + If [array] Is Nothing Then + Throw New ArgumentNullException(GetResourceString(ResID.Argument_InvalidNullValue1, "array")) + End If + + If [array].Rank <> 1 Then + Throw New ArgumentException(GetResourceString(ResID.Argument_RankEQOne1, "array")) + End If + + If (index < 0) OrElse ([array].Length - index < Count) Then + Throw New ArgumentException(GetResourceString(ResID.Argument_InvalidValue1, "index")) + End If + '*** END ARG CHECKS + + Dim i As Integer + Dim objArray As Object() = TryCast(array, Object()) + + If objArray IsNot Nothing Then + For i = 1 To Count + objArray(index + i - 1) = Me.Item(i) + Next i + Else + For i = 1 To Count + [array].SetValue(Me.Item(i), index + i - 1) + Next i + End If + End Sub + + Private Function IListAdd(ByVal value As Object) As Integer Implements IList.Add + Add(value, Nothing) + Return m_ItemsList.Count - 1 'IList is 0 based. Return a 0-based index. If m_ItemsList.Count is zero, we threw during Add() so no need to gate the Count = 0 case. + End Function + + + 'CONSIDER: why not delegate to Collection.Add? Same for the others... + Private Sub IListInsert(ByVal index As Integer, ByVal value As Object) Implements IList.Insert + Dim NewNode As New Node(Nothing, value) + m_ItemsList.Insert(index, NewNode) 'FastList is 0-indexed just like IList, so no transformation of "index" needed + 'No key, so no need to add to the hash table. + 'Adjust the ForEach iterators + AdjustEnumeratorsOnNodeInserted(NewNode) + End Sub + + Private Sub IListRemoveAt(ByVal index As Integer) Implements IList.RemoveAt + Dim Node As Node = m_ItemsList.RemoveAt(index) '0 based + Debug.Assert(Node IsNot Nothing, "Should have thrown exception rather than return Nothing") + + AdjustEnumeratorsOnNodeRemoved(Node) 'Adjust the ForEach iterators + If Node.m_Key IsNot Nothing Then + m_KeyedNodesHash.Remove(Node.m_Key) + End If + + Node.m_Prev = Nothing + Node.m_Next = Nothing + End Sub + + Private Sub IListRemove(ByVal value As Object) Implements IList.Remove + Dim index As Integer + index = IListIndexOf(value) + If index <> -1 Then + IListRemoveAt(index) + End If + 'No exception thrown if not found + End Sub + + Private Sub IListClear() Implements IList.Clear + Clear() + End Sub + + Private Property IListItem(ByVal index As Integer) As Object Implements IList.Item + Get + Dim Node As Node + + Node = m_ItemsList.Item(index) + Return Node.m_Value + End Get + + Set(ByVal value As Object) + Dim Node As Node = m_ItemsList.Item(index) + Node.m_Value = value + End Set + End Property + + Private Function IListContains(ByVal value As Object) As Boolean Implements IList.Contains + Return (IListIndexOf(value) <> -1) + End Function + + Private Function IListIndexOf(ByVal value As Object) As Integer Implements IList.IndexOf + Return m_ItemsList.IndexOfValue(value) + End Function + +#End Region + +#If Not TELESTO Then + Private m_DeserializationInfo As SerializationInfo + Private m_CultureInfo As CultureInfo 'The CultureInfo used for key comparisons +#End If + Private m_KeyedNodesHash As Generic.Dictionary(Of String, Node) 'Hashtable mapping key (string) -> Node, contains only items added to the collection with a key + Private m_ItemsList As FastList 'Doubly-linked list of Node containing all items in the collection +#If TELESTO Then + Private m_Iterators As List(Of Object) 'List of iterators currently iterating this collection +#Else + Private m_Iterators As ArrayList 'List of iterators currently iterating this collection +#End IF + End Class 'Collection +End Namespace + diff --git a/Microsoft.VisualBasic/runtime/msvbalib/Constants.vb b/Microsoft.VisualBasic/runtime/msvbalib/Constants.vb new file mode 100644 index 000000000..13b82a710 --- /dev/null +++ b/Microsoft.VisualBasic/runtime/msvbalib/Constants.vb @@ -0,0 +1,142 @@ +' Copyright (c) Microsoft Corporation. All rights reserved. + +Imports System.Globalization + +Namespace Microsoft.VisualBasic + + Public Module Constants + + Public Const vbCrLf As String = ChrW(13) & ChrW(10) +#If Not LATEBINDING Then + Public Const vbObjectError As Integer = &H80040000I + Public Const vbNewLine As String = ChrW(13) & ChrW(10) + Public Const vbCr As String = ChrW(13) + Public Const vbLf As String = ChrW(10) + Public Const vbBack As String = ChrW(8) + Public Const vbFormFeed As String = ChrW(12) + Public Const vbTab As String = ChrW(9) + Public Const vbVerticalTab As String = ChrW(11) + Public Const vbNullChar As String = ChrW(0) + Public Const vbNullString As String = Nothing +#If Not TELESTO Then + 'AppWinStyle + Public Const vbHide As AppWinStyle = AppWinStyle.Hide + Public Const vbNormalFocus As AppWinStyle = AppWinStyle.NormalFocus + Public Const vbMinimizedFocus As AppWinStyle = AppWinStyle.MinimizedFocus + Public Const vbMaximizedFocus As AppWinStyle = AppWinStyle.MaximizedFocus + Public Const vbNormalNoFocus As AppWinStyle = AppWinStyle.NormalNoFocus + Public Const vbMinimizedNoFocus As AppWinStyle = AppWinStyle.MinimizedNoFocus +#End If + 'vbCallType Enum values + Public Const vbMethod As CallType = CallType.Method + Public Const vbGet As CallType = CallType.Get + Public Const vbLet As CallType = CallType.Let + Public Const vbSet As CallType = CallType.Set + + 'vbCompareMethod enum values + Public Const vbBinaryCompare As CompareMethod = CompareMethod.Binary + Public Const vbTextCompare As CompareMethod = CompareMethod.Text + + 'vbDateTimeFormat + Public Const vbGeneralDate As DateFormat = DateFormat.GeneralDate + Public Const vbLongDate As DateFormat = DateFormat.LongDate + Public Const vbShortDate As DateFormat = DateFormat.ShortDate + Public Const vbLongTime As DateFormat = DateFormat.LongTime + Public Const vbShortTime As DateFormat = DateFormat.ShortTime + + 'vbDayOfWeek + Public Const vbUseSystemDayOfWeek As FirstDayOfWeek = FirstDayOfWeek.System + Public Const vbSunday As FirstDayOfWeek = FirstDayOfWeek.Sunday + Public Const vbMonday As FirstDayOfWeek = FirstDayOfWeek.Monday + Public Const vbTuesday As FirstDayOfWeek = FirstDayOfWeek.Tuesday + Public Const vbWednesday As FirstDayOfWeek = FirstDayOfWeek.Wednesday + Public Const vbThursday As FirstDayOfWeek = FirstDayOfWeek.Thursday + Public Const vbFriday As FirstDayOfWeek = FirstDayOfWeek.Friday + Public Const vbSaturday As FirstDayOfWeek = FirstDayOfWeek.Saturday +#If Not TELESTO Then + 'FileAttribute + Public Const vbNormal As FileAttribute = FileAttribute.Normal + Public Const vbReadOnly As FileAttribute = FileAttribute.ReadOnly + Public Const vbHidden As FileAttribute = FileAttribute.Hidden + Public Const vbSystem As FileAttribute = FileAttribute.System + Public Const vbVolume As FileAttribute = FileAttribute.Volume + Public Const vbDirectory As FileAttribute = FileAttribute.Directory + Public Const vbArchive As FileAttribute = FileAttribute.Archive +#End If + 'vbFirstWeekOfYear + Public Const vbUseSystem As FirstWeekOfYear = FirstWeekOfYear.System + Public Const vbFirstJan1 As FirstWeekOfYear = FirstWeekOfYear.Jan1 + Public Const vbFirstFourDays As FirstWeekOfYear = FirstWeekOfYear.FirstFourDays + Public Const vbFirstFullWeek As FirstWeekOfYear = FirstWeekOfYear.FirstFullWeek +#If Not TELESTO Then + 'vbStrConv + Public Const vbUpperCase As VbStrConv = VbStrConv.UpperCase + Public Const vbLowerCase As VbStrConv = VbStrConv.LowerCase + Public Const vbProperCase As VbStrConv = VbStrConv.ProperCase + Public Const vbWide As VbStrConv = VbStrConv.Wide + Public Const vbNarrow As VbStrConv = VbStrConv.Narrow + Public Const vbKatakana As VbStrConv = VbStrConv.Katakana + Public Const vbHiragana As VbStrConv = VbStrConv.Hiragana + Public Const vbSimplifiedChinese As VbStrConv = VbStrConv.SimplifiedChinese + Public Const vbTraditionalChinese As VbStrConv = VbStrConv.TraditionalChinese + Public Const vbLinguisticCasing As VbStrConv = VbStrConv.LinguisticCasing +#End If + 'vbTriState + Public Const vbUseDefault As TriState = TriState.UseDefault + Public Const vbTrue As TriState = TriState.True + Public Const vbFalse As TriState = TriState.False + + 'VariantType + Public Const vbEmpty As VariantType = VariantType.Empty + Public Const vbNull As VariantType = VariantType.Null + Public Const vbInteger As VariantType = VariantType.Integer + Public Const vbLong As VariantType = VariantType.Long + Public Const vbSingle As VariantType = VariantType.Single + Public Const vbDouble As VariantType = VariantType.Double + Public Const vbCurrency As VariantType = VariantType.Currency + Public Const vbDate As VariantType = VariantType.Date + Public Const vbString As VariantType = VariantType.String + Public Const vbObject As VariantType = VariantType.Object + Public Const vbBoolean As VariantType = VariantType.Boolean + Public Const vbVariant As VariantType = VariantType.Variant + Public Const vbDecimal As VariantType = VariantType.Decimal + Public Const vbByte As VariantType = VariantType.Byte + Public Const vbUserDefinedType As VariantType = VariantType.UserDefinedType + Public Const vbArray As VariantType = VariantType.Array +#If Not TELESTO Then + 'MsgBoxResult + Public Const vbOK As MsgBoxResult = MsgBoxResult.OK + Public Const vbCancel As MsgBoxResult = MsgBoxResult.Cancel + Public Const vbAbort As MsgBoxResult = MsgBoxResult.Abort + Public Const vbRetry As MsgBoxResult = MsgBoxResult.Retry + Public Const vbIgnore As MsgBoxResult = MsgBoxResult.Ignore + Public Const vbYes As MsgBoxResult = MsgBoxResult.Yes + Public Const vbNo As MsgBoxResult = MsgBoxResult.No + + 'MsgBoxStyle + 'You may BitOr one value from each group + Public Const vbOKOnly As MsgBoxStyle = MsgBoxStyle.OKOnly + Public Const vbOKCancel As MsgBoxStyle = MsgBoxStyle.OKCancel + Public Const vbAbortRetryIgnore As MsgBoxStyle = MsgBoxStyle.AbortRetryIgnore + Public Const vbYesNoCancel As MsgBoxStyle = MsgBoxStyle.YesNoCancel + Public Const vbYesNo As MsgBoxStyle = MsgBoxStyle.YesNo + Public Const vbRetryCancel As MsgBoxStyle = MsgBoxStyle.RetryCancel + Public Const vbCritical As MsgBoxStyle = MsgBoxStyle.Critical + Public Const vbQuestion As MsgBoxStyle = MsgBoxStyle.Question + Public Const vbExclamation As MsgBoxStyle = MsgBoxStyle.Exclamation + Public Const vbInformation As MsgBoxStyle = MsgBoxStyle.Information + Public Const vbDefaultButton1 As MsgBoxStyle = MsgBoxStyle.DefaultButton1 + Public Const vbDefaultButton2 As MsgBoxStyle = MsgBoxStyle.DefaultButton2 + Public Const vbDefaultButton3 As MsgBoxStyle = MsgBoxStyle.DefaultButton3 + Public Const vbApplicationModal As MsgBoxStyle = MsgBoxStyle.ApplicationModal + Public Const vbSystemModal As MsgBoxStyle = MsgBoxStyle.SystemModal + Public Const vbMsgBoxHelp As MsgBoxStyle = MsgBoxStyle.MsgBoxHelp + Public Const vbMsgBoxRight As MsgBoxStyle = MsgBoxStyle.MsgBoxRight + Public Const vbMsgBoxRtlReading As MsgBoxStyle = MsgBoxStyle.MsgBoxRtlReading + Public Const vbMsgBoxSetForeground As MsgBoxStyle = MsgBoxStyle.MsgBoxSetForeground +#End If 'Not TELESTO +#End If 'Not LATEBINDING + End Module + +End Namespace + diff --git a/Microsoft.VisualBasic/runtime/msvbalib/ControlChars.vb b/Microsoft.VisualBasic/runtime/msvbalib/ControlChars.vb new file mode 100644 index 000000000..6a9905cec --- /dev/null +++ b/Microsoft.VisualBasic/runtime/msvbalib/ControlChars.vb @@ -0,0 +1,21 @@ +' Copyright (c) Microsoft Corporation. All rights reserved. + +Namespace Microsoft.VisualBasic + + Public NotInheritable Class ControlChars + + Public Const CrLf As String = ChrW(13) & ChrW(10) + Public Const NewLine As String = ChrW(13) & ChrW(10) + Public Const Cr As Char = ChrW(13) + Public Const Lf As Char = ChrW(10) + Public Const Back As Char = ChrW(8) + Public Const FormFeed As Char = ChrW(12) + Public Const [Tab] As Char = ChrW(9) + Public Const VerticalTab As Char = ChrW(11) + Public Const NullChar As Char = ChrW(0) + Public Const Quote As Char = ChrW(34) + + End Class + +End Namespace + diff --git a/Microsoft.VisualBasic/runtime/msvbalib/Conversion.vb b/Microsoft.VisualBasic/runtime/msvbalib/Conversion.vb new file mode 100644 index 000000000..1b2664752 --- /dev/null +++ b/Microsoft.VisualBasic/runtime/msvbalib/Conversion.vb @@ -0,0 +1,1142 @@ +' Copyright (c) Microsoft Corporation. All rights reserved. + +Imports System +Imports System.Security +Imports System.Security.Permissions +Imports System.Text +Imports System.Globalization +Imports System.Runtime.Versioning + +Imports Microsoft.VisualBasic.CompilerServices +Imports Microsoft.VisualBasic.CompilerServices.ExceptionUtils +Imports Microsoft.VisualBasic.CompilerServices.Utils + +Namespace Microsoft.VisualBasic + + Public Module Conversion + + Private Const NUMPRS_LEADING_WHITE As Integer = &H1I + Private Const NUMPRS_TRAILING_WHITE As Integer = &H2I + Private Const NUMPRS_LEADING_PLUS As Integer = &H4I + Private Const NUMPRS_TRAILING_PLUS As Integer = &H8I + Private Const NUMPRS_LEADING_MINUS As Integer = &H10I + Private Const NUMPRS_TRAILING_MINUS As Integer = &H20I + Private Const NUMPRS_HEX_OCT As Integer = &H40I + Private Const NUMPRS_PARENS As Integer = &H80I + Private Const NUMPRS_DECIMAL As Integer = &H100I + Private Const NUMPRS_THOUSANDS As Integer = &H200I + Private Const NUMPRS_CURRENCY As Integer = &H400I + Private Const NUMPRS_EXPONENT As Integer = &H800I + Private Const NUMPRS_USE_ALL As Integer = &H1000I + Private Const NUMPRS_STD As Integer = &H1FFFI + + ' flags used by dwOutFlags only: + ' + Private Const NUMPRS_NEG As Integer = &H10000I + Private Const NUMPRS_INEXACT As Integer = &H20000I + + ' flags used by VarNumFromParseNum to indicate acceptable result types: + ' + Private Const VTBIT_EMPTY As Integer = &H0 + Private Const VTBIT_NULL As Integer = &H2 + Private Const VTBIT_I2 As Integer = &H4 + Private Const VTBIT_I4 As Integer = &H8 + Private Const VTBIT_R4 As Integer = &H10 + Private Const VTBIT_R8 As Integer = &H20 + Private Const VTBIT_CY As Integer = &H40 + Private Const VTBIT_DATE As Integer = &H80 + Private Const VTBIT_BSTR As Integer = &H100 + Private Const VTBIT_OBJECT As Integer = &H200 + Private Const VTBIT_ERROR As Integer = &H400 + Private Const VTBIT_BOOL As Integer = &H800 + Private Const VTBIT_VARIANT As Integer = &H1000 + Private Const VTBIT_DATAOBJECT As Integer = &H2000 + Private Const VTBIT_DECIMAL As Integer = &H4000 + Private Const VTBIT_BYTE As Integer = &H20000 + Private Const VTBIT_CHAR As Integer = &H40000 + Private Const VTBIT_LONG As Integer = &H100000 + + Private Const MAX_ERR_NUMBER As Integer = 65535 + Private Const LOCALE_NOUSEROVERRIDE As Integer = &H80000000I + Private Const LCID_US_ENGLISH As Integer = &H409I + Private Const PRSFLAGS As Integer _ + = (NUMPRS_LEADING_PLUS Or NUMPRS_LEADING_MINUS Or NUMPRS_HEX_OCT Or NUMPRS_DECIMAL Or NUMPRS_EXPONENT) + 'Private Const VTBITS As Integer = (VTBIT_I2 Or VTBIT_I4 Or VTBIT_R8 Or VTBIT_CY Or VTBIT_DECIMAL) + Private Const VTBITS As Integer = (VTBIT_I2 Or VTBIT_I4 Or VTBIT_R8 Or VTBIT_DECIMAL) + + Private Const TYPE_INDICATOR_INT16 As Char = "%"c + Private Const TYPE_INDICATOR_INT32 As Char = "&"c + Private Const TYPE_INDICATOR_SINGLE As Char = "!"c + Private Const TYPE_INDICATOR_DECIMAL As Char = "@"c + + + '============================================================================ + ' Error message functions. + '============================================================================ + Public Function ErrorToString() As String + Return Information.Err().Description + End Function + + + + Public Function ErrorToString(ByVal ErrorNumber As Integer) As String + If ErrorNumber >= MAX_ERR_NUMBER Then + Throw New ArgumentException(GetResourceString(ResID.MaxErrNumber)) + End If + + If ErrorNumber > 0 Then + ErrorNumber = (SEVERITY_ERROR Or FACILITY_CONTROL Or ErrorNumber) + End If + + If (ErrorNumber And SCODE_FACILITY) = FACILITY_CONTROL Then + ErrorNumber = ErrorNumber And &HFFFFI + Return GetResourceString(CType(ErrorNumber, vbErrors)) + ElseIf ErrorNumber <> 0 Then + Return GetResourceString(vbErrors.UserDefined) + Else + Return "" + End If + End Function + + + '============================================================================ + ' Numeric functions. + '============================================================================ + + Public Function Fix(ByVal Number As Short) As Short + Return Number + End Function + + Public Function Fix(ByVal Number As Integer) As Integer + Return Number + End Function + + Public Function Fix(ByVal Number As Long) As Long + Return Number + End Function + + Public Function Fix(ByVal Number As Double) As Double + If Number >= 0 Then + Return System.Math.Floor(Number) + Else + Return -System.Math.Floor(-Number) + End If + End Function + + Public Function Fix(ByVal Number As Single) As Single + If Number >= 0 Then + Return CSng(System.Math.Floor(CDbl(Number))) + Else + Return CSng(-System.Math.Floor(CDbl(-Number))) + End If + End Function + + Public Function Fix(ByVal Number As Decimal) As Decimal + If System.Decimal.op_LessThan(Number, System.Decimal.Zero) Then + Return System.Decimal.Negate(System.Decimal.Floor(System.Decimal.Negate(Number))) + Else + Return System.Decimal.Floor(Number) + End If + End Function + + Public Function Fix(ByVal Number As Object) As Object + If Number Is Nothing Then + Throw New ArgumentNullException(GetResourceString(ResID.Argument_InvalidNullValue1, "Number")) + End If + + Dim ValueInterface As IConvertible + + ValueInterface = TryCast(Number, IConvertible) + + If Not ValueInterface Is Nothing Then + + Select Case ValueInterface.GetTypeCode() + + Case TypeCode.SByte, _ + TypeCode.Byte, _ + TypeCode.Int16, _ + TypeCode.UInt16, _ + TypeCode.Int32, _ + TypeCode.UInt32, _ + TypeCode.Int64, _ + TypeCode.UInt64 + + Return Number + + Case TypeCode.Single + Return Fix(ValueInterface.ToSingle(Nothing)) + + Case TypeCode.Double + Return Fix(ValueInterface.ToDouble(Nothing)) + + Case TypeCode.Decimal + Return Fix(ValueInterface.ToDecimal(Nothing)) + + Case TypeCode.Boolean + Return ValueInterface.ToInt32(Nothing) + + Case TypeCode.String + Return Fix(CDbl(ValueInterface.ToString(Nothing))) + + Case Else + 'TypeCode.Char + 'TypeCode.DateTime + ' Fall through to error + + End Select + + End If + + Throw VbMakeException(New ArgumentException(GetResourceString(ResID.Argument_NotNumericType2, "Number", Number.GetType().FullName)), vbErrors.TypeMismatch) + End Function + + + Public Function Int(ByVal Number As Short) As Short + Return Number + End Function + + Public Function Int(ByVal Number As Integer) As Integer + Return Number + End Function + + Public Function Int(ByVal Number As Long) As Long + Return Number + End Function + + Public Function Int(ByVal Number As Double) As Double + Return System.Math.Floor(Number) + End Function + + Public Function Int(ByVal Number As Single) As Single + Return CSng(System.Math.Floor(CDbl(Number))) + End Function + + Public Function Int(ByVal Number As Decimal) As Decimal + Return System.Decimal.Floor(Number) + End Function + + Public Function Int(ByVal Number As Object) As Object + If Number Is Nothing Then + Throw New ArgumentNullException(GetResourceString(ResID.Argument_InvalidNullValue1, "Number")) + End If + + Dim ValueInterface As IConvertible + + ValueInterface = TryCast(Number, IConvertible) + + If Not ValueInterface Is Nothing Then + + Select Case ValueInterface.GetTypeCode() + + Case TypeCode.SByte, _ + TypeCode.Byte, _ + TypeCode.Int16, _ + TypeCode.UInt16, _ + TypeCode.Int32, _ + TypeCode.UInt32, _ + TypeCode.Int64, _ + TypeCode.UInt64 + + Return Number + + Case TypeCode.Single + Return Int(ValueInterface.ToSingle(Nothing)) + + Case TypeCode.Double + Return Int(ValueInterface.ToDouble(Nothing)) + + Case TypeCode.Decimal + Return Int(ValueInterface.ToDecimal(Nothing)) + + Case TypeCode.Boolean + Return ValueInterface.ToInt32(Nothing) + + Case TypeCode.String + Return Int(CDbl(ValueInterface.ToString(Nothing))) + + Case Else + 'TypeCode.Char + 'TypeCode.DateTime + ' Fall through to error + End Select + End If + + Throw VbMakeException(New ArgumentException(GetResourceString(ResID.Argument_NotNumericType2, "Number", Number.GetType().FullName)), vbErrors.TypeMismatch) + End Function + + + + '============================================================================ + ' Number to string conversion + '============================================================================ + _ + Public Function Hex(ByVal Number As SByte) As String + Return Number.ToString("X") + End Function + + Public Function Hex(ByVal Number As Byte) As String + Return Number.ToString("X") + End Function + + Public Function Hex(ByVal Number As Short) As String + Return Number.ToString("X") + End Function + + _ + Public Function Hex(ByVal Number As UShort) As String + Return Number.ToString("X") + End Function + + Public Function Hex(ByVal Number As Integer) As String + Return Number.ToString("X") + End Function + + _ + Public Function Hex(ByVal Number As UInteger) As String + Return Number.ToString("X") + End Function + + Public Function Hex(ByVal Number As Long) As String + Return Number.ToString("X") + End Function + + _ + Public Function Hex(ByVal Number As ULong) As String + Return Number.ToString("X") + End Function + + Public Function Hex(ByVal Number As Object) As String + Dim LongValue As Long + + If Number Is Nothing Then + Throw New ArgumentNullException(GetResourceString(ResID.Argument_InvalidNullValue1, "Number")) + End If + + Dim ValueInterface As IConvertible + + ValueInterface = TryCast(Number, IConvertible) + + If Not ValueInterface Is Nothing Then + + Select Case ValueInterface.GetTypeCode() + + Case TypeCode.SByte + Return Hex(ValueInterface.ToSByte(Nothing)) + + Case TypeCode.Byte + Return Hex(ValueInterface.ToByte(Nothing)) + + Case TypeCode.Int16 + Return Hex(ValueInterface.ToInt16(Nothing)) + + Case TypeCode.UInt16 + Return Hex(ValueInterface.ToUInt16(Nothing)) + + Case TypeCode.Int32 + Return Hex(ValueInterface.ToInt32(Nothing)) + + Case TypeCode.UInt32 + Return Hex(ValueInterface.ToUInt32(Nothing)) + + Case TypeCode.Int64, _ + TypeCode.Single, _ + TypeCode.Double, _ + TypeCode.Decimal + LongValue = ValueInterface.ToInt64(Nothing) + GoTo RangeCheck + + Case TypeCode.UInt64 + Return Hex(ValueInterface.ToUInt64(Nothing)) + + Case TypeCode.String + Try + LongValue = CLng(ValueInterface.ToString(Nothing)) + Catch ex As OverflowException + 'If the conversion to Long overflows, we can try ULong. + Return Hex(CULng(ValueInterface.ToString(Nothing))) + End Try +RangeCheck: + 'Optimization case + If LongValue = 0 Then + Return "0" + End If + + If (LongValue > 0) Then + Return Hex(LongValue) + Else + 'For VB6 compatability, format as Int32 value + ' unless it overflows into an Int64 + If (LongValue >= System.Int32.MinValue) Then + Return Hex(CInt(LongValue)) + End If + Return Hex(LongValue) + End If + + Case TypeCode.Boolean, _ + TypeCode.Char, _ + TypeCode.DateTime + ' Fall through to error + + Case Else + ' Fall through to error + End Select + End If + + Throw New ArgumentException(GetResourceString(ResID.Argument_InvalidValueType2, "Number", VBFriendlyName(Number))) + End Function + +#If Not TELESTO Then + _ + Public Function Oct(ByVal Number As SByte) As String + Return OctFromLong(CLng(Number) And &HFFL) + End Function + + Public Function Oct(ByVal Number As Byte) As String + Return OctFromULong(CULng(Number)) + End Function + + Public Function Oct(ByVal Number As Short) As String + Return OctFromLong(CLng(Number) And &HFFFFL) + End Function + + _ + Public Function Oct(ByVal Number As UShort) As String + Return OctFromULong(CULng(Number)) + End Function + + Public Function Oct(ByVal Number As Integer) As String + Return OctFromLong(CLng(Number) And &HFFFFFFFFL) + End Function + + _ + Public Function Oct(ByVal Number As UInteger) As String + Return OctFromULong(CULng(Number)) + End Function + + Public Function Oct(ByVal Number As Long) As String + Return OctFromLong(Number) + End Function + + _ + Public Function Oct(ByVal Number As ULong) As String + Return OctFromULong(Number) + End Function + + Public Function Oct(ByVal Number As Object) As String + Dim LongValue As Long + + If Number Is Nothing Then + Throw New ArgumentNullException(GetResourceString(ResID.Argument_InvalidNullValue1, "Number")) + End If + + Dim ValueInterface As IConvertible + + ValueInterface = TryCast(Number, IConvertible) + + If Not ValueInterface Is Nothing Then + + Select Case ValueInterface.GetTypeCode() + + Case TypeCode.SByte + Return Oct(ValueInterface.ToSByte(Nothing)) + Case TypeCode.Byte + Return Oct(ValueInterface.ToByte(Nothing)) + Case TypeCode.Int16 + Return Oct(ValueInterface.ToInt16(Nothing)) + Case TypeCode.UInt16 + Return Oct(ValueInterface.ToUInt16(Nothing)) + Case TypeCode.Int32 + Return Oct(ValueInterface.ToInt32(Nothing)) + Case TypeCode.UInt32 + Return Oct(ValueInterface.ToUInt32(Nothing)) + + Case TypeCode.Int64, _ + TypeCode.Single, _ + TypeCode.Double, _ + TypeCode.Decimal + LongValue = ValueInterface.ToInt64(Nothing) + GoTo RangeCheck + + Case TypeCode.UInt64 + Return Oct(ValueInterface.ToUInt64(Nothing)) + + Case TypeCode.String + Try + LongValue = CLng(ValueInterface.ToString(Nothing)) + Catch ex As OverflowException + 'If the conversion to Long overflows, we can try ULong. + Return Oct(CULng(ValueInterface.ToString(Nothing))) + End Try +RangeCheck: + 'Optimization case + If LongValue = 0 Then + Return "0" + End If + + If (LongValue > 0) Then + Return Oct(LongValue) + Else + 'For VB6 compatability, format as Int32 value + ' unless it overflows into an Int64 + If (LongValue >= System.Int32.MinValue) Then + Return Oct(CInt(LongValue)) + End If + Return Oct(LongValue) + End If + + Case TypeCode.Boolean, _ + TypeCode.Char, _ + TypeCode.DateTime + ' Fall through to error + + Case Else + ' Fall through to error + End Select + End If + + Throw New ArgumentException(GetResourceString(ResID.Argument_InvalidValueType2, "Number", VBFriendlyName(Number))) + End Function +#End If 'not TELESTO + + Public Function Str(ByVal Number As Object) As String + Dim s As String + + If Number Is Nothing Then + Throw New ArgumentNullException(GetResourceString(ResID.Argument_InvalidNullValue1, "Number")) + End If + + Dim ValueInterface As IConvertible + Dim ValueTypeCode As TypeCode + + ValueInterface = TryCast(Number, IConvertible) + + If ValueInterface Is Nothing Then + Throw New InvalidCastException(GetResourceString(ResID.ArgumentNotNumeric1, "Number")) + End If + + ValueTypeCode = ValueInterface.GetTypeCode() + Select Case ValueTypeCode + + Case TypeCode.DBNull + Return "Null" + + Case TypeCode.Boolean + If ValueInterface.ToBoolean(Nothing) Then + Return "True" + Else + Return "False" + End If + + Case TypeCode.SByte, _ + TypeCode.Byte, _ + TypeCode.Int16, _ + TypeCode.UInt16, _ + TypeCode.Int32, _ + TypeCode.UInt32, _ + TypeCode.Int64, _ + TypeCode.UInt64, _ + TypeCode.Single, _ + TypeCode.Double, _ + TypeCode.Decimal + s = CStr(Number) + + Case Else + If ValueTypeCode = TypeCode.String Then + Try + s = CStr(CDbl(ValueInterface.ToString(Nothing))) + GoTo FormatAndExit + Catch ex As StackOverflowException + Throw ex + Catch ex As OutOfMemoryException + Throw ex + Catch ex As System.Threading.ThreadAbortException + Throw ex + Catch + 'Throw our own exception below + End Try + End If + Throw New InvalidCastException(GetResourceString(ResID.ArgumentNotNumeric1, "Number")) + End Select + +FormatAndExit: + If s.Length > 0 AndAlso s.Chars(0) <> "-"c Then + Return " " & StdFormat(s) + Else + Return StdFormat(s) + End If + End Function + + Private Function HexOrOctValue(ByVal InputStr As String, ByVal i As Integer) As Double + Dim digits As Integer = 0 + Dim ch As Char + Dim iLen As Integer + Dim ivalue As Long + Dim digitValue As Integer + + Const asc0 As Integer = AscW("0"c) + Const ascUpperAoffset As Integer = AscW("A"c) - 10 + Const ascLowerAoffset As Integer = AscW("a"c) - 10 + + iLen = InputStr.Length + + ch = InputStr.Chars(i) + i += 1 + + If ch = "H"c OrElse ch = "h"c Then + 'Loop for octal + Do While (i < iLen AndAlso digits < 17) + ch = InputStr.Chars(i) + i += 1 + Select Case ch + Case ControlChars.Tab, ControlChars.Lf, ControlChars.Cr, ChrW(32), ChrW(&H3000S) + GoTo NextHexCharacter + + Case "0"c + If digits = 0 Then + 'leading zeros do not affect type + GoTo NextHexCharacter + End If + digitValue = 0 + + Case "1"c To "9"c + digitValue = AscW(ch) - asc0 + + Case "A"c To "F"c + digitValue = AscW(ch) - ascUpperAoffset + + Case "a"c To "f"c + digitValue = AscW(ch) - ascLowerAoffset + + Case Else + Exit Do + End Select +AddHexDigit: + ' If digits = 15 AndAlso ivalue >= &H800000000000000L Then + If digits = 15 AndAlso ivalue > &H7FFFFFFFFFFFFFFL Then + 'This will overflow because we don't have a shift operator + 'and must do multiplication + ivalue = (ivalue And &H7FFFFFFFFFFFFFFL) * 16 + ivalue = ivalue Or &H8000000000000000L + Else + ivalue = ivalue * 16 + End If + ivalue += digitValue + digits += 1 +NextHexCharacter: + + Loop + + If digits = 16 Then + i += 1 + If i < iLen Then + 'We fell out of the loop before getting the typechar + ch = InputStr.Chars(i) + End If + End If + + If digits > 8 Then + 'leave ivalue unchanged + + ElseIf digits > 4 OrElse ch = TYPE_INDICATOR_INT32 Then + If ivalue > &H7FFFFFFFL Then + ivalue = Int32.MinValue + (ivalue And &H7FFFFFFFL) + End If + + ElseIf digits > 2 OrElse ch = TYPE_INDICATOR_INT16 Then + If ivalue > &H7FFFL Then + ivalue = Int16.MinValue + (ivalue And &H7FFFL) + End If + + End If + + If ch = TYPE_INDICATOR_INT16 Then + ivalue = CShort(ivalue) + ElseIf ch = TYPE_INDICATOR_INT32 Then + ivalue = CInt(ivalue) + End If + Return ivalue + + ElseIf ch = "O"c OrElse ch = "o"c Then + + 'Loop for octal + Do While (i < iLen AndAlso digits < 22) + ch = InputStr.Chars(i) + i += 1 + + Select Case ch + Case ControlChars.Tab, ControlChars.Lf, ControlChars.Cr, ChrW(32), ChrW(&H3000S) + GoTo NextOctCharacter + + Case "0"c + If digits = 0 Then + 'leading zeros do not affect type + GoTo NextOctCharacter + End If + digitValue = 0 + + Case "1"c To "7"c + digitValue = AscW(ch) - asc0 + + Case Else + Exit Do + + End Select + +AddOctDigit: + If ivalue >= &O100000000000000000000L Then + 'This will overflow because we don't have a shift operator + 'and must do multiplication + ivalue = (ivalue And &O77777777777777777777L) * 8 + ivalue = ivalue Or &O100000000000000000000L + Else + ivalue = ivalue * 8 + End If + ivalue += digitValue + digits += 1 +NextOctCharacter: + + Loop + + If digits = 22 Then + i += 1 + If i < iLen Then + 'We fell out of the loop before getting the typechar + ch = InputStr.Chars(i) + End If + End If + + If ivalue > &H100000000L Then + 'leave ivalue unchanged + + ElseIf ivalue > &HFFFFL OrElse ch = TYPE_INDICATOR_INT32 Then + If ivalue > &H7FFFFFFFL Then + ivalue = Int32.MinValue + (ivalue And &H7FFFFFFFL) + End If + + ElseIf ivalue > &HFFL OrElse ch = TYPE_INDICATOR_INT16 Then + If ivalue > &H7FFFL Then + ivalue = Int16.MinValue + (ivalue And &H7FFFL) + End If + + End If + + If ch = TYPE_INDICATOR_INT16 Then + ivalue = CShort(ivalue) + ElseIf ch = TYPE_INDICATOR_INT32 Then + ivalue = CInt(ivalue) + End If + Return ivalue + Else + 'input is invalid + Return 0 + End If + + End Function + + 'CONSIDER: (VSW#395733) Should this function be extended and versioned to handle ULONG values? + Public Function Val(ByVal InputStr As String) As Double + + Dim ch As Char + Dim i As Integer + Dim iLen As Integer + Dim digits As Integer + Dim digitsAfterDecimal, digitsBeforeDecimal As Integer + + Const asc0 As Integer = AscW("0"c) + + If InputStr Is Nothing Then + iLen = 0 + Else + iLen = InputStr.Length + End If + + i = 0 + 'Skip over leading whitespace + Do While (i < iLen) + ch = InputStr.Chars(i) + Select Case ch + Case ControlChars.Tab, ControlChars.Lf, ControlChars.Cr, ChrW(32), ChrW(&H3000S) + i += 1 + Case Else + Exit Do + End Select + Loop + + If i >= iLen Then + Return 0 + End If + + ch = InputStr.Chars(i) + If ch = "&"c Then 'We are dealing with hex or octal numbers + Return HexOrOctValue(InputStr, i + 1) + + Else 'we are dealing with base 10 decimal + Dim value As Double + Dim afterdecimal As Boolean = False + Dim aftere As Boolean = False + Dim negative As Boolean = False + Dim eval As Double = 0 + + 'Check for negative + ch = InputStr.Chars(i) + If ch = "-"c Then + negative = True + i += 1 + ElseIf ch = "+"c Then + i += 1 + End If + + 'check for numbers before a decimal or E + Do While (i < iLen) + ch = InputStr.Chars(i) + Select Case ch + Case ControlChars.Tab, ControlChars.Lf, ControlChars.Cr, ChrW(32), ChrW(&H3000S) + i += 1 + + Case "0"c + If digits <> 0 OrElse afterdecimal Then + value = value * 10 + AscW(ch) - asc0 + i += 1 + digits += 1 + Else + i += 1 + 'don't count as digit + End If + + Case "1"c To "9"c + value = value * 10 + AscW(ch) - asc0 + i += 1 + digits += 1 + + Case "."c + i += 1 + If afterdecimal = False Then + afterdecimal = True + digitsBeforeDecimal = digits + Else + 'handle "1..1" or "1.2.1" + Exit Do + End If + + Case "e"c, "E"c, "d"c, "D"c + aftere = True + i += 1 + Exit Do + + Case Else + Exit Do + End Select + Loop + + If afterdecimal Then + digitsAfterDecimal = digits - digitsBeforeDecimal + End If + + If aftere Then + Dim afterplusminus As Boolean = False + Dim enegative As Boolean = False + Do While (i < iLen) + ch = InputStr.Chars(i) + Select Case ch + Case ControlChars.Tab, ControlChars.Lf, ControlChars.Cr, ChrW(32), ChrW(&H3000S) + i += 1 + + Case "0"c To "9"c + eval = eval * 10 + AscW(ch) - asc0 + i += 1 + + Case "+"c + If Not afterplusminus Then + afterplusminus = True + i += 1 + Else + Exit Do + End If + + Case "-"c + If Not afterplusminus Then + afterplusminus = True + enegative = True + i += 1 + Else + Exit Do + End If + + Case Else + Exit Do + End Select + Loop + + If enegative Then + eval += digitsAfterDecimal + value = value * (10 ^ (-eval)) + Else + eval -= digitsAfterDecimal + value = value * (10 ^ (eval)) + End If + Else + If afterdecimal AndAlso digitsAfterDecimal <> 0 Then + 'Need to adjust for decimal + value = value / (10 ^ digitsAfterDecimal) + End If + End If + + If System.Double.IsInfinity(value) Then + Throw VbMakeException(vbErrors.Overflow) + End If + + If negative Then + value = -value + End If + + Select Case ch + + Case TYPE_INDICATOR_INT16 + If digitsAfterDecimal > 0 Then + Throw VbMakeException(vbErrors.TypeMismatch) + End If + value = CShort(value) + + Case TYPE_INDICATOR_INT32 + If digitsAfterDecimal > 0 Then + Throw VbMakeException(vbErrors.TypeMismatch) + End If + value = CInt(value) + + Case TYPE_INDICATOR_SINGLE + value = CSng(value) + + Case TYPE_INDICATOR_DECIMAL + value = CDec(value) + + Case Else + + End Select + + Return value + End If + End Function + + Public Function Val(ByVal Expression As Char) As Integer + 'Val only handles Ascii decimal chars '0' to '9' + Dim CharValue As Integer + + CharValue = AscW(Expression) 'CType(Expression, IConvertible).ToInt32(Nothing) + If CharValue >= AscW("1"c) AndAlso CharValue <= AscW("9"c) Then + Return CharValue - AscW("0") + End If + Return 0 + End Function + + Public Function Val(ByVal Expression As Object) As Double + + Dim StringExpression As String = TryCast(Expression, String) + + If StringExpression IsNot Nothing Then + Return Val(StringExpression) + + ElseIf TypeOf Expression Is Char Then + Return Val(DirectCast(Expression, Char)) + + ElseIf CompilerServices.Versioned.IsNumeric(Expression) Then + Return CDbl(Expression) + + Else + Dim sValue As String + Try + sValue = CStr(Expression) + Catch ex As StackOverflowException + Throw ex + Catch ex As OutOfMemoryException + Throw ex + Catch ex As System.Threading.ThreadAbortException + Throw ex + Catch + Throw VbMakeException(New ArgumentException(GetResourceString(ResID.Argument_InvalidValueType2, "Expression", VBFriendlyName(Expression))), vbErrors.OLENoPropOrMethod) + End Try + + Return Val(sValue) + End If + + End Function +#If Not TELESTO Then + 'CONSIDER: Move to VB6InputFile where it is used + _ + _ + _ + Friend Function ParseInputField(ByVal Value As Object, ByVal vtInput As VariantType) As Object + Dim numprsPtr() As Byte + Dim vtSuffix As Integer + Dim cDecMax As Integer + Dim StringValue As String = CStr(Value) + Dim DigitArray() As Byte + Dim pd As ProjectData + Dim cchUsed As Int32 + Dim nPwr10 As Int32 + Dim chTypeChar As Char + Dim dwOutFlags As Int32 + Dim nBaseShift As Int32 + + Const INTEGER_SIZE As Integer = 4 + Const INFLAGS_OFFSET As Integer = 4 + + If ((vtInput = VariantType.Empty) AndAlso ((Value Is Nothing) OrElse Len(CStr(Value)) = 0)) Then + Return Nothing + End If + + pd = ProjectData.GetProjectData() + numprsPtr = pd.m_numprsPtr + DigitArray = pd.m_DigitArray + + 'numprsPtr is actually a struct. The first two fields are cDig (the size of the digits array) + 'and dwInFlags which we set to PRSFLAGS + + 'Init NUMPARSE.cDig + Array.Copy(BitConverter.GetBytes(Convert.ToInt32(DigitArray.Length)), 0, numprsPtr, 0, INTEGER_SIZE) + 'Init NUMPARSE.dwInFlags + Array.Copy(BitConverter.GetBytes(Convert.ToInt32(PRSFLAGS)), 0, numprsPtr, INFLAGS_OFFSET, INTEGER_SIZE) + + ' For file interchangeability, we always use US decimal. + If UnsafeNativeMethods.VarParseNumFromStr(StringValue, LCID_US_ENGLISH, LOCALE_NOUSEROVERRIDE, numprsPtr, DigitArray) < 0 Then + If (vtInput <> VariantType.Empty) Then + ' Just return 0 if we don't understand the number + Return 0 + End If + Return StringValue + End If + + ' Look for type character following string + dwOutFlags = BitConverter.ToInt32(numprsPtr, 8) + cchUsed = BitConverter.ToInt32(numprsPtr, 12) + nBaseShift = BitConverter.ToInt32(numprsPtr, 16) + nPwr10 = BitConverter.ToInt32(numprsPtr, 20) + + If cchUsed < StringValue.Length Then + chTypeChar = StringValue.Chars(cchUsed) + End If + + Select Case (chTypeChar) + Case "%"c + vtSuffix = VariantType.Short + cDecMax = 0 + Case "&"c + vtSuffix = VariantType.Integer + cDecMax = 0 + Case "@"c + 'Convert currency to Decimal + 'vtSuffix = VariantType.Currency + vtSuffix = VariantType.Decimal + cDecMax = 4 + Case "!"c + If (vtInput = VariantType.Double) Then + vtSuffix = VariantType.Double + Else + vtSuffix = VariantType.Single + End If + cDecMax = System.Int32.MaxValue + Case "#"c + vtSuffix = VariantType.Double + cDecMax = System.Int32.MaxValue + Case Else + ' No type suffix. + If (vtInput = VariantType.Empty) Then + ' no indication of type, either from suffix or defined + ' by type we're inputting to. + Dim dwVtBits As Integer = VTBITS + + If (dwOutFlags And NUMPRS_EXPONENT) <> 0 Then + ' if exponent specified, result is R8 only. + dwVtBits = VTBIT_R8 + End If + + Return UnsafeNativeMethods.VarNumFromParseNum(numprsPtr, DigitArray, dwVtBits) + End If + + If (nBaseShift <> 0) Then + Dim Int32Value As Integer + + ' Have a hex/octal number. Sign extend if short. + Value = UnsafeNativeMethods.VarNumFromParseNum(numprsPtr, DigitArray, VTBIT_I4) + Int32Value = CInt(Value) + + If ((Int32Value And &HFFFF0000I) = 0) Then + ' Sign extend if short. + Int32Value = CShort(Int32Value) + End If + + UnsafeNativeMethods.VariantChangeType(Value, Value, 0, CType(vtInput, Int16)) + Return Value + End If + + Return UnsafeNativeMethods.VarNumFromParseNum(numprsPtr, DigitArray, ShiftVTBits(vtInput)) + End Select + + ' Have a type character suffix. Convert to that type. + If (-nPwr10 > cDecMax) Then + Throw VbMakeException(vbErrors.TypeMismatch) + End If + + Value = UnsafeNativeMethods.VarNumFromParseNum(numprsPtr, DigitArray, ShiftVTBits(vtSuffix)) + + If (vtInput = VariantType.Empty) Then + Return Value + End If + + UnsafeNativeMethods.VariantChangeType(Value, Value, 0, CType(vtInput, Int16)) + Return Value + End Function + + Private Function ShiftVTBits(ByVal vt As Integer) As Integer + Select Case vt + 'Case VariantType.Empty + 'Fall through VTBIT_EMPTY + 'Case VariantType.Null + 'Fall through VTBIT_NULL + Case VariantType.Short + Return VTBIT_I2 + Case VariantType.Integer + Return VTBIT_I4 + Case VariantType.Single + Return VTBIT_R4 + Case VariantType.Double + Return VTBIT_R8 + Case VariantType.Decimal, VariantType.Currency + Return VTBIT_DECIMAL + Case VariantType.Date + Return VTBIT_DATE + Case VariantType.String + Return VTBIT_BSTR + Case VariantType.Object + Return VTBIT_OBJECT + Case VariantType.Error + Return VTBIT_ERROR + Case VariantType.Boolean + Return VTBIT_BOOL + Case VariantType.Variant + Return VTBIT_VARIANT + Case VariantType.DataObject + Return VTBIT_DATAOBJECT + Case VariantType.Decimal + Return VTBIT_DECIMAL + Case VariantType.Byte + Return VTBIT_BYTE + Case VariantType.Char + Return VTBIT_CHAR + Case VariantType.Long + Return VTBIT_LONG + Case Else + 'CONSIDER: Add debug assert + Return 0 + End Select + End Function +#End If 'not TELESTO + Public Function CTypeDynamic(ByVal Expression As Object, ByVal TargetType As System.Type) As Object + return Conversions.ChangeType(Expression, TargetType, True) + End Function + + Public Function CTypeDynamic(Of TargetType)(ByVal Expression As Object) As TargetType + return DirectCast(Conversions.ChangeType(Expression, GetType(TargetType), True), TargetType) + End Function + End Module +End Namespace + diff --git a/Microsoft.VisualBasic/runtime/msvbalib/DateAndTime.vb b/Microsoft.VisualBasic/runtime/msvbalib/DateAndTime.vb new file mode 100644 index 000000000..c42857176 --- /dev/null +++ b/Microsoft.VisualBasic/runtime/msvbalib/DateAndTime.vb @@ -0,0 +1,694 @@ +' Copyright (c) Microsoft Corporation. All rights reserved. + + +Imports System +Imports System.Globalization +Imports System.Security.Permissions +Imports System.Runtime.Versioning + +Imports Microsoft.VisualBasic.CompilerServices +Imports Microsoft.VisualBasic.CompilerServices.ExceptionUtils +Imports Microsoft.VisualBasic.CompilerServices.Utils + +Namespace Microsoft.VisualBasic + + Public Module DateAndTime + + + Private AcceptedDateFormatsDBCS() As String = {"yyyy-M-d", "y-M-d", "yyyy/M/d", "y/M/d"} + Private AcceptedDateFormatsSBCS() As String = {"M-d-yyyy", "M-d-y", "M/d/yyyy", "M/d/y"} + + + '============================================================================ + ' Date/Time Properties + '============================================================================ +#If Not TELESTO Then + Public Property Today() As DateTime +#Else 'We don't allow you to set the date on Telesto + ReadOnly Property Today() As DateTime +#End If + Get + Return DateTime.Today + End Get +#If Not TELESTO Then + _ + _ + _ + Set(ByVal Value As DateTime) + SetDate(Value) + End Set +#End If + End Property + + + + Public ReadOnly Property Now() As DateTime + Get + Return DateTime.Now + End Get + End Property + + +#If Not TELESTO Then + Public Property TimeOfDay() As DateTime +#Else + ReadOnly Property TimeOfDay() As DateTime +#End If + Get + Dim Ticks As Int64 = DateTime.Now.TimeOfDay.Ticks + + 'Truncate to the nearest second + Return New DateTime(Ticks - Ticks Mod TimeSpan.TicksPerSecond) + End Get +#If Not TELESTO Then + _ + _ + _ + Set(ByVal Value As DateTime) + SetTime(Value) + End Set +#End If + End Property + + + + ' TimeString (replaces Time$) +#If Not TELESTO Then + Public Property TimeString() As String +#Else + Public ReadOnly Property TimeString() As String +#End If + 'Locale agnostic, Always returns 24hr clock + Get + Return (New DateTime(DateTime.Now.TimeOfDay.Ticks)).ToString("HH:mm:ss", GetInvariantCultureInfo()) + End Get +#If Not TELESTO Then + _ + _ + _ + Set(ByVal Value As String) + Dim dt As Date + + Try + dt = CompilerServices.DateType.FromString(Value, GetInvariantCultureInfo()) + Catch ex As StackOverflowException + Throw ex + Catch ex As OutOfMemoryException + Throw ex + Catch ex As System.Threading.ThreadAbortException + Throw ex + Catch + Throw VbMakeException(New InvalidCastException(GetResourceString(ResID.InvalidCast_FromStringTo, Left(Value, 32), "Date")), vbErrors.IllegalFuncCall) + End Try + + SetTime(dt) + End Set +#End If + End Property + + + Private Function IsDBCSCulture() As Boolean +#If Not TELESTO Then + 'This function is apparently trying to determine a different default for East Asian systems. Why only East Asia + 'would get a separate default is a mystery. Vb6 compatability? SystemMaxDBCSCharSize is not available on Telesto. Telesto + 'doesn't have ANSI code page data, and since Telesto runs on the MAC it wouldn't be available there, anyway. + + 'The international PMs say that this is a broken way of determining what action to take for formatting, etc. + If System.Runtime.InteropServices.Marshal.SystemMaxDBCSCharSize = 1 Then + Return False + End If + Return True +#Else + ' Emulate IsDBCSCulture of .NET 3.5 using CultureInfo + Dim langName As String = System.Threading.Thread.CurrentThread.CurrentCulture.TwoLetterISOLanguageName + Return String.Equals(langName, "zh", StringComparison.OrdinalIgnoreCase) OrElse _ + String.Equals(langName, "ko", StringComparison.OrdinalIgnoreCase) OrElse _ + String.Equals(langName, "ja", StringComparison.OrdinalIgnoreCase) +#End If + End Function + +#If Not TELESTO Then + Public Property DateString() As String +#Else + Public ReadOnly Property DateString() As String +#End If + ' DateString (replaces Date$) + 'Returns yyyy-MM-dd for DBCS locale + 'Returns MM-dd-yyyy for non-DBCS locale + Get + If IsDBCSCulture() Then + Return DateTime.Today.ToString("yyyy\-MM\-dd", GetInvariantCultureInfo()) + Else + Return DateTime.Today.ToString("MM\-dd\-yyyy", GetInvariantCultureInfo()) + End If + End Get +#If Not TELESTO Then + _ + _ + _ + Set(ByVal Value As String) + Dim NewDate As Date + + Try + Dim TmpValue As String = ToHalfwidthNumbers(Value, GetCultureInfo()) + If IsDBCSCulture() Then + NewDate = DateTime.ParseExact(TmpValue, AcceptedDateFormatsDBCS, GetInvariantCultureInfo(), DateTimeStyles.AllowWhiteSpaces) + Else + NewDate = DateTime.ParseExact(TmpValue, AcceptedDateFormatsSBCS, GetInvariantCultureInfo(), DateTimeStyles.AllowWhiteSpaces) + End If + Catch ex As StackOverflowException + Throw ex + Catch ex As OutOfMemoryException + Throw ex + Catch ex As System.Threading.ThreadAbortException + Throw ex + Catch + Throw VbMakeException(New InvalidCastException(GetResourceString(ResID.InvalidCast_FromStringTo, Left(Value, 32), "Date")), vbErrors.IllegalFuncCall) + End Try + + SetDate(NewDate) + + End Set +#End If + End Property + + + + Public ReadOnly Property Timer() As Double + Get + 'Returns number of seconds past Midnight + Return (System.DateTime.Now.Ticks Mod System.TimeSpan.TicksPerDay) / _ + (TimeSpan.TicksPerMillisecond * 1000) + End Get + End Property + + + + Private ReadOnly Property CurrentCalendar() As Calendar + Get + Return Threading.Thread.CurrentThread.CurrentCulture.Calendar + End Get + End Property + + + + '============================================================================ + ' Date manipulation functions. + '============================================================================ + Public Function DateAdd(ByVal Interval As DateInterval, _ + ByVal Number As Double, _ + ByVal DateValue As DateTime) As DateTime + Dim lNumber As Integer + + lNumber = CInt(Fix(Number)) + + Select Case Interval + Case DateInterval.Year + Return CurrentCalendar.AddYears(DateValue, lNumber) + Case DateInterval.Month + Return CurrentCalendar.AddMonths(DateValue, lNumber) + Case DateInterval.Day, _ + DateInterval.DayOfYear, _ + DateInterval.Weekday + Return DateValue.AddDays(lNumber) + Case DateInterval.WeekOfYear + Return DateValue.AddDays(lNumber * 7.0#) + Case DateInterval.Hour + Return DateValue.AddHours(lNumber) + Case DateInterval.Minute + Return DateValue.AddMinutes(lNumber) + Case DateInterval.Second + Return DateValue.AddSeconds(lNumber) + Case DateInterval.Quarter + Return DateValue.AddMonths(lNumber * 3) + End Select + + Throw New ArgumentException(GetResourceString(ResID.Argument_InvalidValue1, "Interval")) + End Function + + + + Public Function DateDiff(ByVal Interval As DateInterval, _ + ByVal Date1 As DateTime, _ + ByVal Date2 As DateTime, _ + Optional ByVal DayOfWeek As FirstDayOfWeek = FirstDayOfWeek.Sunday, _ + Optional ByVal WeekOfYear As FirstWeekOfYear = FirstWeekOfYear.Jan1) As Long + + Dim tm As TimeSpan + Dim cal As Calendar + + tm = Date2.Subtract(Date1) + + Select Case Interval + Case DateInterval.Year + cal = CurrentCalendar + Return cal.GetYear(Date2) - cal.GetYear(Date1) + Case DateInterval.Month + cal = CurrentCalendar + Return (cal.GetYear(Date2) - cal.GetYear(Date1)) * 12 + cal.GetMonth(Date2) - cal.GetMonth(Date1) + Case DateInterval.Day, _ + DateInterval.DayOfYear + Return CLng(Fix(tm.TotalDays())) + Case DateInterval.Hour + Return CLng(Fix(tm.TotalHours())) + Case DateInterval.Minute + Return CLng(Fix(tm.TotalMinutes())) + Case DateInterval.Second + Return CLng(Fix(tm.TotalSeconds())) + Case DateInterval.WeekOfYear + Date1 = Date1.AddDays(-GetDayOfWeek(Date1, DayOfWeek)) + Date2 = Date2.AddDays(-GetDayOfWeek(Date2, DayOfWeek)) + tm = Date2.Subtract(Date1) + Return CLng(Fix(tm.TotalDays())) \ 7 + Case DateInterval.Weekday + Return CLng(Fix(tm.TotalDays())) \ 7 + Case DateInterval.Quarter + cal = CurrentCalendar + Return (cal.GetYear(Date2) - cal.GetYear(Date1)) * 4 + (cal.GetMonth(Date2) - 1) \ 3 - (cal.GetMonth(Date1) - 1) \ 3 + End Select + + Throw New ArgumentException(GetResourceString(ResID.Argument_InvalidValue1, "Interval")) + End Function + + + + Private Function GetDayOfWeek(ByVal dt As Date, ByVal weekdayFirst As FirstDayOfWeek) As Integer + If (weekdayFirst < FirstDayOfWeek.System OrElse weekdayFirst > FirstDayOfWeek.Saturday) Then + Throw VbMakeException(vbErrors.IllegalFuncCall) + End If + + ' If FirstWeekDay is 0, get offset from NLS. + If (weekdayFirst = FirstDayOfWeek.System) Then + weekdayFirst = CType(GetDateTimeFormatInfo().FirstDayOfWeek + 1, FirstDayOfWeek) + End If + + Return (dt.DayOfWeek - weekdayFirst + 8) Mod 7 + 1 + End Function + + + + Public Function DatePart(ByVal Interval As DateInterval, ByVal DateValue As DateTime, _ + Optional ByVal FirstDayOfWeekValue As FirstDayOfWeek = vbSunday, _ + Optional ByVal FirstWeekOfYearValue As FirstWeekOfYear = vbFirstJan1) As Integer + + 'Get the part asked for + Select Case Interval + Case DateInterval.Year + Return CurrentCalendar.GetYear(DateValue) + Case DateInterval.Month + Return CurrentCalendar.GetMonth(DateValue) + Case DateInterval.Day + Return CurrentCalendar.GetDayOfMonth(DateValue) + Case DateInterval.Hour + Return CurrentCalendar.GetHour(DateValue) + Case DateInterval.Minute + Return CurrentCalendar.GetMinute(DateValue) + Case DateInterval.Second + Return CurrentCalendar.GetSecond(DateValue) + Case DateInterval.Weekday + Return Weekday(DateValue, FirstDayOfWeekValue) + Case DateInterval.WeekOfYear + Dim WeekRule As CalendarWeekRule + Dim Day As DayOfWeek + + If FirstDayOfWeekValue = vbUseSystemDayOfWeek Then + Day = GetCultureInfo().DateTimeFormat.FirstDayOfWeek + Else + Day = CType(FirstDayOfWeekValue - 1, DayOfWeek) + End If + + Select Case FirstWeekOfYearValue + Case vbUseSystem + WeekRule = GetCultureInfo().DateTimeFormat.CalendarWeekRule + Case vbFirstJan1 + WeekRule = CalendarWeekRule.FirstDay + Case vbFirstFourDays + WeekRule = CalendarWeekRule.FirstFourDayWeek + Case vbFirstFullWeek + WeekRule = CalendarWeekRule.FirstFullWeek + End Select + + Return CurrentCalendar.GetWeekOfYear(DateValue, WeekRule, Day) + Case DateInterval.Quarter + Return ((DateValue.Month - 1) \ 3) + 1 + Case DateInterval.DayOfYear + Return CurrentCalendar.GetDayOfYear(DateValue) + End Select + + Throw New ArgumentException(GetResourceString(ResID.Argument_InvalidValue1, "Interval")) + End Function + + + + Public Function DateAdd(ByVal Interval As String, _ + ByVal Number As Double, _ + ByVal DateValue As Object) As DateTime + + Dim dt1 As Date + + Try + dt1 = CDate(DateValue) + Catch ex As StackOverflowException + Throw ex + Catch ex As OutOfMemoryException + Throw ex + Catch ex As System.Threading.ThreadAbortException + Throw ex + Catch + Throw New InvalidCastException(GetResourceString(ResID.Argument_InvalidDateValue1, "DateValue")) + End Try + + Return DateAdd(DateIntervalFromString(Interval), Number, dt1) + End Function + + + + Public Function DateDiff(ByVal Interval As String, _ + ByVal Date1 As Object, _ + ByVal Date2 As Object, _ + Optional ByVal DayOfWeek As FirstDayOfWeek = FirstDayOfWeek.Sunday, _ + Optional ByVal WeekOfYear As FirstWeekOfYear = FirstWeekOfYear.Jan1) As Long + + Dim dt1, dt2 As Date + + Try + dt1 = CDate(Date1) + Catch ex As StackOverflowException + Throw ex + Catch ex As OutOfMemoryException + Throw ex + Catch ex As System.Threading.ThreadAbortException + Throw ex + Catch + Throw New InvalidCastException(GetResourceString(ResID.Argument_InvalidDateValue1, "Date1")) + End Try + Try + dt2 = CDate(Date2) + Catch ex As StackOverflowException + Throw ex + Catch ex As OutOfMemoryException + Throw ex + Catch ex As System.Threading.ThreadAbortException + Throw ex + Catch + Throw New InvalidCastException(GetResourceString(ResID.Argument_InvalidDateValue1, "Date2")) + End Try + + Return DateDiff(DateIntervalFromString(Interval), dt1, dt2, DayOfWeek, WeekOfYear) + End Function + + + + Public Function DatePart(ByVal Interval As String, ByVal DateValue As Object, _ + Optional ByVal DayOfWeek As FirstDayOfWeek = FirstDayOfWeek.Sunday, _ + Optional ByVal WeekOfYear As FirstWeekOfYear = FirstWeekOfYear.Jan1) As Integer + + Dim dt1 As Date + + Try + dt1 = CDate(DateValue) + Catch ex As StackOverflowException + Throw ex + Catch ex As OutOfMemoryException + Throw ex + Catch ex As System.Threading.ThreadAbortException + Throw ex + Catch + Throw New InvalidCastException(GetResourceString(ResID.Argument_InvalidDateValue1, "DateValue")) + End Try + + Return DatePart(DateIntervalFromString(Interval), dt1, DayOfWeek, WeekOfYear) + End Function + + + + Private Function DateIntervalFromString(ByVal Interval As String) As DateInterval + If Interval IsNot Nothing Then +#If TELESTO Then + Interval = Interval.ToUpper(CultureInfo.InvariantCulture) 'Replace ToUpperInvariant() with this because these are equivalent on Telesto/Desktop +#Else + Interval = Interval.ToUpperInvariant() +#End If + End If + + Select Case Interval + Case "YYYY" + Return DateInterval.Year + Case "Y" + Return DateInterval.DayOfYear + Case "M" + Return DateInterval.Month + Case "D" + Return DateInterval.Day + Case "H" + Return DateInterval.Hour + Case "N" + Return DateInterval.Minute + Case "S" + Return DateInterval.Second + Case "WW" + Return DateInterval.WeekOfYear + Case "W" + Return DateInterval.Weekday + Case "Q" + Return DateInterval.Quarter + Case Else + Throw New ArgumentException(GetResourceString(ResID.Argument_InvalidValue1, "Interval")) + End Select + End Function + + + + '============================================================================ + ' Date value functions. + '============================================================================ + Public Function DateSerial(ByVal [Year] As Integer, ByVal [Month] As Integer, ByVal [Day] As Integer) As DateTime + 'We have to handle negative months and days + ' so we start with the year and add months and days + Dim cal As Calendar = CurrentCalendar + Dim Result As DateTime + + If Year < 0 Then + Year = cal.GetYear(System.DateTime.Today) + Year + ElseIf Year < 100 Then + Year = cal.ToFourDigitYear(Year) + End If + + '*** BEGIN PERFOPT *** + '*** Gregorian Calendar perf optimization + '*** The AddMonths/AddDays require excessive conversion to/from ticks + '*** so we special case + If TypeOf cal Is GregorianCalendar Then + If (Month >= 1 AndAlso Month <= 12) AndAlso (Day >= 1 AndAlso Day <= 28) Then + 'Uses 28 so we don't have to use the calendar to obtain + ' the number of days in the month, which is the cause of the + ' extra overhead we are trying to avoid + Return New DateTime(Year, Month, Day) + End If + End If + '*** END PERFOPT *** + + Try + Result = cal.ToDateTime(Year, 1, 1, 0, 0, 0, 0) + Catch ex As StackOverflowException + Throw ex + Catch ex As OutOfMemoryException + Throw ex + Catch ex As System.Threading.ThreadAbortException + Throw ex + Catch + Throw VbMakeException(New ArgumentException(GetResourceString(ResID.Argument_InvalidValue1, "Year")), vbErrors.IllegalFuncCall) + End Try + + Try + Result = cal.AddMonths(Result, Month - 1) + Catch ex As StackOverflowException + Throw ex + Catch ex As OutOfMemoryException + Throw ex + Catch ex As System.Threading.ThreadAbortException + Throw ex + Catch + Throw VbMakeException(New ArgumentException(GetResourceString(ResID.Argument_InvalidValue1, "Month")), vbErrors.IllegalFuncCall) + End Try + + Try + Result = cal.AddDays(Result, Day - 1) + Catch ex As StackOverflowException + Throw ex + Catch ex As OutOfMemoryException + Throw ex + Catch ex As System.Threading.ThreadAbortException + Throw ex + Catch + Throw VbMakeException(New ArgumentException(GetResourceString(ResID.Argument_InvalidValue1, "Day")), vbErrors.IllegalFuncCall) + End Try + + Return Result + End Function + + + + Public Function TimeSerial(ByVal Hour As Integer, ByVal Minute As Integer, ByVal Second As Integer) As DateTime + Const SecondsInDay As Integer = (24 * 60 * 60) + + Dim TotalSeconds As Integer = (Hour * 60 * 60) + (Minute * 60) + Second + + If TotalSeconds < 0 Then + 'Wrap clock + TotalSeconds += SecondsInDay + End If + + Return (New DateTime(TotalSeconds * TimeSpan.TicksPerSecond)) + End Function + + + + Public Function DateValue(ByVal [StringDate] As String) As DateTime + + Return CDate([StringDate]).Date + + End Function + + + + Public Function TimeValue(ByVal [StringTime] As String) As DateTime + + Return New DateTime(CDate([StringTime]).Ticks Mod TimeSpan.TicksPerDay) + + End Function + + + + '============================================================================ + ' Date/time part functions. + '============================================================================ + Public Function Year(ByVal DateValue As DateTime) As Integer + Return CurrentCalendar.GetYear(DateValue) + End Function + + + + Public Function Month(ByVal DateValue As DateTime) As Integer + Return CurrentCalendar.GetMonth(DateValue) + End Function + + + + Public Function Day(ByVal DateValue As DateTime) As Integer + Return CurrentCalendar.GetDayOfMonth(DateValue) + End Function + + + + Public Function Hour(ByVal [TimeValue] As DateTime) As Integer + Return CurrentCalendar.GetHour([TimeValue]) + End Function + + + + Public Function Minute(ByVal [TimeValue] As DateTime) As Integer + Return CurrentCalendar.GetMinute([TimeValue]) + End Function + + + + Public Function Second(ByVal [TimeValue] As DateTime) As Integer + Return CurrentCalendar.GetSecond([TimeValue]) + End Function + + + + Public Function Weekday(ByVal DateValue As DateTime, Optional ByVal DayOfWeek As FirstDayOfWeek = FirstDayOfWeek.Sunday) As Integer + If DayOfWeek = FirstDayOfWeek.System Then + ' + DayOfWeek = CType(DateTimeFormatInfo.CurrentInfo.FirstDayOfWeek + 1, FirstDayOfWeek) + + ElseIf (DayOfWeek < FirstDayOfWeek.Sunday) OrElse (DayOfWeek > FirstDayOfWeek.Saturday) Then + Throw New ArgumentException(GetResourceString(ResID.Argument_InvalidValue1, "DayOfWeek")) + End If + + 'Get the day from the date + Dim iDayOfWeek As Integer + + iDayOfWeek = CurrentCalendar.GetDayOfWeek(DateValue) + 1 ' System.DateTime uses Sunday = 0 thru Satuday = 6 + Return ((iDayOfWeek - DayOfWeek + 7) Mod 7) + 1 + End Function + + + + '============================================================================ + ' Date name functions. + '============================================================================ + + Public Function MonthName(ByVal Month As Integer, Optional ByVal Abbreviate As Boolean = False) As String + Dim Result As String + + If Month < 1 OrElse Month > 13 Then + Throw New ArgumentException(GetResourceString(ResID.Argument_InvalidValue1, "Month")) + End If + + If Abbreviate Then + Result = GetDateTimeFormatInfo().GetAbbreviatedMonthName(Month) + Else + Result = GetDateTimeFormatInfo().GetMonthName(Month) + End If + + If Result.Length = 0 Then + Throw New ArgumentException(GetResourceString(ResID.Argument_InvalidValue1, "Month")) + End If + + Return Result + End Function + + + + Public Function WeekdayName(ByVal Weekday As Integer, Optional ByVal Abbreviate As Boolean = False, Optional ByVal FirstDayOfWeekValue As FirstDayOfWeek = FirstDayOfWeek.System) As String + 'COM+ uses 0-6 while VB uses 1-7. + 'COM+ is not reacting to the FirstDayOfWeekSetting. If that gets fixed later, can call Clone to get a Read/Write dtfi, then + 'set the FirstDayOfWeek property (offset by 1) and simplify the calculation to "WeekdayName = dtfi.GetDayName(Weekday-1)". + Dim dtfi As DateTimeFormatInfo + Dim Result As String + + If (Weekday < 1) OrElse (Weekday > 7) Then + Throw New ArgumentException(GetResourceString(ResID.Argument_InvalidValue1, "Weekday")) + End If + + If (FirstDayOfWeekValue < 0) OrElse (FirstDayOfWeekValue > 7) Then + Throw New ArgumentException(GetResourceString(ResID.Argument_InvalidValue1, "FirstDayOfWeekValue")) + End If + + dtfi = CType(GetCultureInfo().GetFormat(GetType(System.Globalization.DateTimeFormatInfo)), DateTimeFormatInfo) 'Returns a read-only object + + If FirstDayOfWeekValue = 0 Then + FirstDayOfWeekValue = CType(CInt(dtfi.FirstDayOfWeek) + 1, FirstDayOfWeek) + End If + + Try + If Abbreviate Then + Result = dtfi.GetAbbreviatedDayName(CType((Weekday + FirstDayOfWeekValue - 2) Mod 7, System.DayOfWeek)) + Else + Result = dtfi.GetDayName(CType((Weekday + FirstDayOfWeekValue - 2) Mod 7, System.DayOfWeek)) + End If + Catch ex As StackOverflowException + Throw ex + Catch ex As OutOfMemoryException + Throw ex + Catch ex As System.Threading.ThreadAbortException + Throw ex + Catch + Throw New ArgumentException(GetResourceString(ResID.Argument_InvalidValue1, "Weekday")) + End Try + + If Result.Length = 0 Then + Throw New ArgumentException(GetResourceString(ResID.Argument_InvalidValue1, "Weekday")) + End If + + Return Result + End Function + + + End Module + +End Namespace + diff --git a/Microsoft.VisualBasic/runtime/msvbalib/Devices/Audio.vb b/Microsoft.VisualBasic/runtime/msvbalib/Devices/Audio.vb new file mode 100644 index 000000000..595bbe3e3 --- /dev/null +++ b/Microsoft.VisualBasic/runtime/msvbalib/Devices/Audio.vb @@ -0,0 +1,233 @@ +' Copyright (c) Microsoft Corporation. All rights reserved. +Option Explicit On +Option Strict On + +Imports System +Imports System.Collections +Imports System.Windows.Forms +Imports System.IO +Imports System.Security +Imports System.Security.Permissions +Imports System.Diagnostics + +Imports Microsoft.VisualBasic +Imports Microsoft.VisualBasic.CompilerServices +Imports Microsoft.VisualBasic.CompilerServices.ExceptionUtils + +Namespace Microsoft.VisualBasic + + '**************************************************************************** + ''';AudioPlayMode + ''' + ''' Enum for three ways to play a .wav file + ''' + ''' + Public Enum AudioPlayMode + '!!!!!!!!!!!!!!!! Any changes to this enum must be reflected in ValidateAudioPlayModeEnum() + WaitToComplete = 0 'Synchronous + Background = 1 'Asynchronous + BackgroundLoop = 2 'Asynchronous and looping + End Enum + + Namespace Devices + + '''************************************************************************** + ''';Audio + ''' + ''' An object that makes it easy to play wav files + ''' + ''' + _ + Public Class Audio + + '* PUBLIC ************************************************************** + + '''********************************************************************** + ''';New + ''' + ''' Creates a new Audio object + ''' + ''' + Public Sub New() + End Sub + + '''********************************************************************** + ''';Play + ''' + ''' Plays a .wav file in background mode + ''' + ''' The name of the file + ''' + Public Sub Play(ByVal location As String) + Play(location, AudioPlayMode.Background) + End Sub + + '''********************************************************************** + ''';Play + ''' + ''' Plays a .wav file in the passed in mode + ''' + ''' The name of the file + ''' + ''' An enum value representing the mode, Background (async), + ''' WaitToComplete (sync) or BackgroundLoop + ''' + ''' + Public Sub Play(ByVal location As String, ByVal playMode As AudioPlayMode) + ValidateAudioPlayModeEnum(playMode, "playMode") + Dim safeFilename As String = ValidateFilename(location) + Dim sound As System.Media.SoundPlayer = New System.Media.SoundPlayer(safeFilename) + Play(sound, playMode) + End Sub + + '''********************************************************************** + ''';Play + ''' + ''' Plays a Byte array representation of a .wav file in the passed in mode + ''' + ''' The array representing the .wav file + ''' The mode in which the array should be played + ''' + Public Sub Play(ByVal data() As Byte, ByVal playMode As AudioPlayMode) + If data Is Nothing Then + Throw GetArgumentNullException("data") + End If + ValidateAudioPlayModeEnum(playMode, "playMode") + + Dim soundStream As IO.MemoryStream = New IO.MemoryStream(data) + Play(soundStream, playMode) + soundStream.Close() + End Sub + + '''********************************************************************** + ''';Play + ''' + ''' Plays a stream representation of a .wav file in the passed in mode + ''' + ''' The stream representing the .wav file + ''' The mode in which the stream should be played + ''' + Public Sub Play(ByVal stream As IO.Stream, ByVal playMode As AudioPlayMode) + ValidateAudioPlayModeEnum(playMode, "playMode") + If stream Is Nothing Then + Throw GetArgumentNullException("stream") + End If + + Play(New System.Media.SoundPlayer(stream), playMode) + End Sub + + '''********************************************************************** + ''';PlaySystemSound + ''' + ''' Plays a system messageBeep sound. + ''' + ''' The sound to be played + ''' Plays the sound asysnchronously + Public Sub PlaySystemSound(ByVal systemSound As System.Media.SystemSound) + If systemSound Is Nothing Then + Throw GetArgumentNullException("systemSound") + End If + + systemSound.Play() + + End Sub + + '''********************************************************************** + ''';Stop + ''' + ''' Stops the play of any playing sound + ''' + ''' + Public Sub [Stop]() + Dim sound As New System.Media.SoundPlayer() + InternalStop(sound) + End Sub + + '* PRIVATE ************************************************************* + + ''' + ''' Plays the passed in SoundPlayer in the passed in mode + ''' + ''' The SoundPlayer to play + ''' The mode in which to play the sound + ''' + Private Sub Play(ByVal sound As System.Media.SoundPlayer, ByVal mode As AudioPlayMode) + + Debug.Assert(sound IsNot Nothing, "There's no SoundPlayer") + Debug.Assert([Enum].IsDefined(GetType(AudioPlayMode), mode), "Enum value is out of range") + + ' Stopping the sound ensures it's safe to dispose it. This could happen when we change the value of m_Sound below + If m_Sound IsNot Nothing Then + InternalStop(m_Sound) + End If + + m_Sound = sound + + Select Case mode + Case AudioPlayMode.WaitToComplete + m_Sound.PlaySync() + Case AudioPlayMode.Background + m_Sound.Play() + Case AudioPlayMode.BackgroundLoop + m_Sound.PlayLooping() + Case Else + Debug.Fail("Unknown AudioPlayMode") + End Select + + End Sub + + '''********************************************************************** + ''';InternalStop + ''' + ''' SoundPlayer.Stop requires unmanaged code permissions. This method allows us to wrap calls to SoundPlayer.Stop + ''' with the appropriate Demand/Assert + ''' + ''' + ''' + _ + Private Shared Sub InternalStop(ByVal sound As System.Media.SoundPlayer) + + ' Stop requires unmanaged code permission. Stop demands SafeSubWindows permissions, so we don't need to do it here + Call New System.Security.Permissions.SecurityPermission(System.Security.Permissions.SecurityPermissionFlag.UnmanagedCode).Assert() + Try + sound.Stop() + Finally + System.Security.CodeAccessPermission.RevertAssert() + End Try + End Sub + + '''********************************************************************** + ''';ValidateFilename + ''' + ''' Gets the full name and path for the file. Throws if unable to get full name and path + ''' + ''' The filename being tested + ''' A full name and path of the file + ''' + Private Function ValidateFilename(ByVal location As String) As String + If location = "" Then + Throw GetArgumentNullException("location") + End If + + Return location + End Function + + '''************************************************************************** + ''' ;ValidateAudioPlayModeEnum + ''' + ''' Validates that the value being passed as an AudioPlayMode enum is a legal value + ''' + ''' + ''' + Private Sub ValidateAudioPlayModeEnum(ByVal value As AudioPlayMode, ByVal paramName As String) + If value < AudioPlayMode.WaitToComplete OrElse value > AudioPlayMode.BackgroundLoop Then + Throw New System.ComponentModel.InvalidEnumArgumentException(paramName, DirectCast(value, Integer), GetType(AudioPlayMode)) + End If + End Sub + + ' Object that plays the sounds. We use a private member so we can ensure we have a reference for async plays + Private m_Sound As System.Media.SoundPlayer + + End Class 'Audio + End Namespace +End Namespace diff --git a/Microsoft.VisualBasic/runtime/msvbalib/Devices/Clock.vb b/Microsoft.VisualBasic/runtime/msvbalib/Devices/Clock.vb new file mode 100644 index 000000000..f1e4a0660 --- /dev/null +++ b/Microsoft.VisualBasic/runtime/msvbalib/Devices/Clock.vb @@ -0,0 +1,67 @@ +' Copyright (c) Microsoft Corporation. All rights reserved. + +Option Strict On +Option Explicit On + +Imports System.ComponentModel +Imports System +Imports System.Security.Permissions + +Namespace Microsoft.VisualBasic.Devices + + '''************************************************************************** + ''' ;Clock + ''' + ''' A wrapper object that acts as a discovery mechanism to quickly find out + ''' the current local time of the machine and the GMT time. + ''' + _ + Public Class Clock + + '* PUBLIC ************************************************************* + + '''************************************************************************** + ''' ;LocalTime + ''' + ''' Gets a DateTime that is the current local date and time on this computer. + ''' + ''' A DateTime whose value is the current date and time. + Public ReadOnly Property LocalTime() As DateTime + Get + Return DateTime.Now + End Get + End Property + + '''************************************************************************** + ''' ;GmtTime + ''' + ''' Gets a DateTime that is the current local date and time on this + ''' computer expressed as GMT time. + ''' + ''' A DateTime whose value is the current date and time expressed as GMT time. + ''' CONSIDER: Name of this property UtcTime for consistency with FX? + Public ReadOnly Property GmtTime() As DateTime + Get + Return DateTime.UtcNow + End Get + End Property + + '''************************************************************************** + ''' ;TickCount + ''' + ''' This property wraps the Environment.TickCount property to get the + ''' number of milliseconds elapsed since the system started. + ''' + ''' An Integer containing the amount of time in milliseconds. + Public ReadOnly Property TickCount() As Integer + Get + Return System.Environment.TickCount + End Get + End Property + + '* FRIEND ************************************************************* + + '* PRIVATE ************************************************************ + + End Class 'Clock +End Namespace diff --git a/Microsoft.VisualBasic/runtime/msvbalib/Devices/Computer.vb b/Microsoft.VisualBasic/runtime/msvbalib/Devices/Computer.vb new file mode 100644 index 000000000..ae46048d7 --- /dev/null +++ b/Microsoft.VisualBasic/runtime/msvbalib/Devices/Computer.vb @@ -0,0 +1,139 @@ +' Copyright (c) Microsoft Corporation. All rights reserved. +Option Strict On +Option Explicit On + +Imports System +Imports Microsoft.VisualBasic.MyServices +Imports System.ComponentModel +Imports System.Security.Permissions +Imports Microsoft.VisualBasic + +Namespace Microsoft.VisualBasic.Devices + + '''************************************************************************** + ''' ;Computer + ''' + ''' A RAD object representing the 'computer' that serves as a discovery + ''' mechanism for finding principle abstractions in the system that you can + ''' code against such as the file system, the clipboard, performance + ''' counters, etc. It also provides functionality you would expect to see + ''' associated with the computer such as playing sound, timers, access to + ''' environment variables, etc. This class represent a general computer + ''' available from a Windows Application, Web app, Dll library, etc. + ''' + _ + Public Class Computer : Inherits ServerComputer + + '= PUBLIC ============================================================= + + 'NOTE: The .Net design guidelines state that access to Instance members does not have to be thread-safe. Access to Shared members does have to be thread-safe. + 'Since My.Computer creates the instance of Computer in a thread-safe way, access to the Computer will necessarily be thread-safe. + 'There is nothing to prevent a user from passing our computer object across threads or creating their own instance and then getting into trouble. + ' But that is completely consistent with the rest of the FX design. It is MY.* that is thread safe and leads to best practice access to these objects. + ' If you dim them up yourself, you are responsible for managing the threading. + + '''************************************************************************** + ''' ;Audio + ''' + ''' Get an Audio object which can play sound files or resources. + ''' + ''' A sound object. + Public ReadOnly Property Audio() As Audio + Get + If m_Audio IsNot Nothing Then Return m_Audio + m_Audio = New Audio() + Return m_Audio + End Get + End Property + + '''************************************************************************** + ''' ;Clipboard + ''' + ''' A thin wrapper for System.Windows.Forms.Clipboard + ''' + ''' An object representing the clipboard + Public ReadOnly Property Clipboard() As ClipboardProxy + Get + If m_Clipboard Is Nothing Then + m_Clipboard = New ClipboardProxy() + End If + + Return m_Clipboard + End Get + End Property + + '''************************************************************************** + ''' ;Ports + ''' + ''' Gets a port object which gives access to the ports on the local machine + ''' + ''' A collection of serial ports on the machine. + Public ReadOnly Property Ports() As Ports + Get + If m_Ports Is Nothing Then + m_Ports = New Ports() + End If + + Return m_Ports + End Get + End Property + + '''************************************************************************** + ''' ;Mouse + ''' + ''' This property returns the Mouse object containing information about + ''' the physical mouse installed to the machine. + ''' + ''' An instance of the Mouse class. + Public ReadOnly Property Mouse() As Mouse + Get + If m_Mouse IsNot Nothing Then Return m_Mouse + m_Mouse = New Mouse + Return m_Mouse + End Get + End Property + + '''************************************************************************** + ''' ;Keyboard + ''' + ''' This property returns the Keyboard object representing some + ''' keyboard properties and a send keys method + ''' + ''' An instance of the System.Windows.Forms.Keyboard class. + Public ReadOnly Property Keyboard() As Keyboard + Get + If m_KeyboardInstance IsNot Nothing Then Return m_KeyboardInstance + m_KeyboardInstance = New Keyboard + Return m_KeyboardInstance + End Get + End Property + + '''************************************************************************** + ''' ;Screen + ''' + ''' This property returns the primary display screen. + ''' + ''' A System.Windows.Forms.Screen object as the primary screen. + Public ReadOnly Property Screen() As System.Windows.Forms.Screen + Get + 'Don't cache this. The Screen class responds to display resolution changes by nulling out AllScreens, which + 'PrimaryScreen relies on to find the primary. So we always need to access the latest PrimaryScreen so we + 'will get the current resolution reported. + Return System.Windows.Forms.Screen.PrimaryScreen + End Get + End Property + + '= FRIENDS ============================================================ + + '= PROTECTED ========================================================== + + '= PRIVATE ============================================================ + + Private m_Audio As Audio 'Lazy initialized cache for the Audio class. + Private m_Ports As Ports 'Lazy initialized cache for the Ports class + Private Shared m_Clipboard As ClipboardProxy 'Lazy initialized cacche for the clipboard class. (proxies can be shared - they have no state) + Private Shared m_Mouse As Mouse 'Lazy initialized cache for the Mouse class. SHARED because Mouse behaves as a readonly singleton class + Private Shared m_KeyboardInstance As Keyboard 'Lazy initialized cache for the Keyboard class. SHARED because Keyboard behaves as a readonly singleton class + + End Class 'Computer +End Namespace diff --git a/Microsoft.VisualBasic/runtime/msvbalib/Devices/ComputerInfo.vb b/Microsoft.VisualBasic/runtime/msvbalib/Devices/ComputerInfo.vb new file mode 100644 index 000000000..0f1c1fcdc --- /dev/null +++ b/Microsoft.VisualBasic/runtime/msvbalib/Devices/ComputerInfo.vb @@ -0,0 +1,393 @@ +' Copyright (c) Microsoft Corporation. All rights reserved. + +Imports System +Imports System.Collections +Imports System.Diagnostics +Imports System.Management +Imports System.Runtime.InteropServices +Imports System.Security +Imports System.Security.Permissions +Imports System.Runtime.Versioning +Imports Microsoft.VisualBasic.CompilerServices + +Namespace Microsoft.VisualBasic.Devices + + '''************************************************************************* + ''' ;ComputerInfo + ''' + ''' Provides configuration information about the current computer and the current process. + ''' + _ + _ + Public Class ComputerInfo + + '!!!!!!!!!!!!!! Keep the debugger proxy current as you change this class - see the nested ComputerInfoDebugView below !!!!!!!!!!!!!!!!! + + '= PUBLIC ============================================================= + + '''****************************************************************************** + ''' ;New + ''' + ''' Default ctor + ''' + _ + Sub New() + End Sub + + '''****************************************************************************** + ''' ;TotalPhysicalMemory + ''' + ''' Gets the total size of physical memory on the machine. + ''' + ''' A 64-bit unsigned integer containing the size of total physical memory on the machine, in bytes. + ''' If we are unable to obtain the memory status. + _ + Public ReadOnly Property TotalPhysicalMemory() As UInt64 + _ + Get + Return MemoryStatus.TotalPhysicalMemory + End Get + End Property + + '''****************************************************************************** + ''' ;AvailablePhysicalMemory + ''' + ''' Gets the total size of free physical memory on the machine. + ''' + ''' A 64-bit unsigned integer containing the size of free physical memory on the machine, in bytes. + ''' If we are unable to obtain the memory status. + _ + Public ReadOnly Property AvailablePhysicalMemory() As UInt64 + _ + Get + Return MemoryStatus.AvailablePhysicalMemory + End Get + End Property + + '''****************************************************************************** + ''' ;TotalVirtualMemory + ''' + ''' Gets the total size of user potion of virtual address space for calling process. + ''' + ''' A 64-bit unsigned integer containing the size of user potion of virtual address space for calling process, + ''' in bytes. + ''' If we are unable to obtain the memory status. + _ + Public ReadOnly Property TotalVirtualMemory() As UInt64 + _ + Get + Return MemoryStatus.TotalVirtualMemory + End Get + End Property + + '''****************************************************************************** + ''' ;AvailableVirtualMemory + ''' + ''' Gets the total size of free user potion of virtual address space for calling process. + ''' + ''' A 64-bit unsigned integer containing the size of free user potion of virtual address space for calling process, + ''' in bytes. + ''' If we are unable to obtain the memory status. + _ + Public ReadOnly Property AvailableVirtualMemory() As UInt64 + _ + Get + Return MemoryStatus.AvailableVirtualMemory + End Get + End Property + + '''****************************************************************************** + ''' ;InstalledUICulture + ''' + ''' Gets the current UICulture installed on the machine. + ''' + ''' A CultureInfo object represents the UI culture installed on the machine. + Public ReadOnly Property InstalledUICulture() As Globalization.CultureInfo + Get + Return Globalization.CultureInfo.InstalledUICulture + End Get + End Property + + '''****************************************************************************** + ''' ;OSFullName + ''' + ''' Gets the full operating system name. This method requires full trust and WMI installed. + ''' + ''' A string contains the operating system name. + ''' If the immediate caller does not have full trust. + ''' If we cannot obtain the query object from WMI. + ''' Since this property depends on WMI, we have OSPlatform property that does not require WMI. + Public ReadOnly Property OSFullName() As String + _ + Get + Try + ' There is no PInvoke Call for this purpose, have to use WMI. + ' The result from WMI is 'MS Windows xxx|C:\WINNT\Device\Harddisk0\Partition1. + ' We only show the first part. NOTE: This is fragile. + Dim PropertyName As String = "Name" + Dim Separator As Char = "|"c + + Dim Result As String = CStr(OSManagementBaseObject.Properties(PropertyName).Value) + If Result.Contains(Separator) Then + Return Result.Substring(0, Result.IndexOf(Separator)) + Else + Return Result + End If + Catch ex As System.Runtime.InteropServices.COMException ' VSWhidbey 214588 + Return OSPlatform + End Try + End Get + End Property + + '''************************************************************************** + ''' ;OSPlatform + ''' + ''' Gets the platform OS name. + ''' + ''' A string containing a Platform ID like "Win32NT", "Win32S", "Win32Windows". See PlatformID enum. + ''' If cannot obtain the OS Version information. + Public ReadOnly Property OSPlatform() As String + Get + Return Environment.OSVersion.Platform.ToString + End Get + End Property + + '''****************************************************************************** + ''' ;OSVersion + ''' + ''' Get the current version number of the operating system. + ''' + ''' A string contains the current version number of the operating system. + ''' If cannot obtain the OS Version information. + Public ReadOnly Property OSVersion() As String + Get + Return Environment.OSVersion.Version.ToString + End Get + End Property + + '= FRIEND ============================================================= + + '''****************************************************************************** + ''' ;ComputerInfoDebugView + ''' + ''' Debugger proxy for the ComputerInfo class. The problem is that OSFullName can time out the debugger + ''' so we offer a view that doesn't have that field. + ''' + ''' + Friend NotInheritable Class ComputerInfoDebugView + Public Sub New(ByVal RealClass As ComputerInfo) + m_InstanceBeingWatched = RealClass + End Sub + + _ + Public ReadOnly Property TotalPhysicalMemory() As UInt64 + Get + Return m_InstanceBeingWatched.TotalPhysicalMemory + End Get + End Property + + _ + Public ReadOnly Property AvailablePhysicalMemory() As UInt64 + Get + Return m_InstanceBeingWatched.AvailablePhysicalMemory + End Get + End Property + + _ + Public ReadOnly Property TotalVirtualMemory() As UInt64 + Get + Return m_InstanceBeingWatched.TotalVirtualMemory + End Get + End Property + + _ + Public ReadOnly Property AvailableVirtualMemory() As UInt64 + Get + Return m_InstanceBeingWatched.AvailableVirtualMemory + End Get + End Property + + _ + Public ReadOnly Property InstalledUICulture() As Globalization.CultureInfo + Get + Return m_InstanceBeingWatched.InstalledUICulture + End Get + End Property + + _ + Public ReadOnly Property OSPlatform() As String + Get + Return m_InstanceBeingWatched.OSPlatform + End Get + End Property + + _ + Public ReadOnly Property OSVersion() As String + Get + Return m_InstanceBeingWatched.OSVersion + End Get + End Property + + Private m_InstanceBeingWatched As ComputerInfo + End Class + + '= PRIVATE ============================================================ + + '''****************************************************************************** + ''' ;MemoryStatus + ''' + ''' Get the whole memory information details. + ''' + ''' An InternalMemoryStatus class. + Private ReadOnly Property MemoryStatus() As InternalMemoryStatus + Get + If m_InternalMemoryStatus Is Nothing Then + m_InternalMemoryStatus = New InternalMemoryStatus + End If + Return m_InternalMemoryStatus + End Get + End Property + + '''****************************************************************************** + ''' ;OSManagementBaseObject + ''' + ''' Get the management object used in WMI to query for the operating system name. + ''' + ''' A ManagementBaseObject represents the result of "Win32_OperatingSystem" query. + ''' If the immediate caller does not have full trust. + ''' If we cannot obtain the query object from WMI. + Private ReadOnly Property OSManagementBaseObject() As ManagementBaseObject + _ + Get + ' Query string to get the OperatingSystem information. + Dim QueryString As String = "Win32_OperatingSystem" + + ' Assumption: Each thread will have its own instance of App class so no need to SyncLock this. + If m_OSManagementObject Is Nothing Then + ' Build a query for enumeration of Win32_OperatingSystem instances + Dim Query As New SelectQuery(QueryString) + + ' Instantiate an object searcher with this query + Dim Searcher As New ManagementObjectSearcher(Query) + + Dim ManagementObjCollection As ManagementObjectCollection = Searcher.Get + + If ManagementObjCollection.Count > 0 Then + Debug.Assert(ManagementObjCollection.Count = 1, "Should find 1 instance only!!!") + + Dim ManagementObjEnumerator As ManagementObjectCollection.ManagementObjectEnumerator = _ + ManagementObjCollection.GetEnumerator + ManagementObjEnumerator.MoveNext() + m_OSManagementObject = ManagementObjEnumerator.Current + Else + Throw ExceptionUtils.GetInvalidOperationException(ResID.MyID.DiagnosticInfo_FullOSName) + End If + End If + + Debug.Assert(m_OSManagementObject IsNot Nothing, "Null management object!!!") + Return m_OSManagementObject + End Get + End Property + + _ + Private m_OSManagementObject As ManagementBaseObject = Nothing ' Cache the management object gotten from WMI. + Private m_InternalMemoryStatus As InternalMemoryStatus = Nothing ' Cache our InternalMemoryStatus + + '''****************************************************************************** + ''' ;InternalMemoryStatus + ''' + ''' This class makes the right call to GlobalMemoryStatus or GlobalMemoryStatusEx depending on Windows OS + ''' and returns the correct value. + ''' + ''' + ''' VSWhidbey 304259: Need to call GlobalMemoryStatus on OS lower than W2k. + ''' This table comes from http://support.microsoft.com/default.aspx?scid=kb;en-us;304283. + '''+--------------------------------------------------------------+ + '''| |Windows|Windows|Windows|Windows NT|Windows|Windows| + '''| | 95 | 98 | Me | 4.0 | 2000 | XP | + '''+--------------------------------------------------------------+ + '''|PlatformID | 1 | 1 | 1 | 2 | 2 | 2 | + '''+--------------------------------------------------------------+ + '''|Major | | | | | | | + '''| version | 4 | 4 | 4 | 4 | 5 | 5 | + '''+--------------------------------------------------------------+ + '''|Minor | | | | | | | + '''| version | 0 | 10 | 90 | 0 | 0 | 1 | + '''+--------------------------------------------------------------+ + ''' + Private Class InternalMemoryStatus + Friend Sub New() + End Sub + + Friend ReadOnly Property TotalPhysicalMemory() As UInt64 + _ + Get + Refresh() + If m_IsOldOS Then + Return CType(m_MemoryStatus.dwTotalPhys, UInt64) + Else + Return m_MemoryStatusEx.ullTotalPhys + End If + End Get + End Property + + Friend ReadOnly Property AvailablePhysicalMemory() As UInt64 + _ + Get + Refresh() + If m_IsOldOS Then + Return CType(m_MemoryStatus.dwAvailPhys, UInt64) + Else + Return m_MemoryStatusEx.ullAvailPhys + End If + End Get + End Property + + Friend ReadOnly Property TotalVirtualMemory() As UInt64 + _ + Get + Refresh() + If m_IsOldOS Then + Return CType(m_MemoryStatus.dwTotalVirtual, UInt64) + Else + Return m_MemoryStatusEx.ullTotalVirtual + End If + End Get + End Property + + Friend ReadOnly Property AvailableVirtualMemory() As UInt64 + _ + Get + Refresh() + If m_IsOldOS Then + Return CType(m_MemoryStatus.dwAvailVirtual, UInt64) + Else + Return m_MemoryStatusEx.ullAvailVirtual + End If + End Get + End Property + + _ + _ + _ + Private Sub Refresh() + If (m_IsOldOS) Then + m_MemoryStatus = New NativeMethods.MEMORYSTATUS + NativeMethods.GlobalMemoryStatus(m_MemoryStatus) + Else + m_MemoryStatusEx = New NativeMethods.MEMORYSTATUSEX + m_MemoryStatusEx.Init() + If (Not NativeMethods.GlobalMemoryStatusEx(m_MemoryStatusEx)) Then + Throw ExceptionUtils.GetWin32Exception(ResID.MyID.DiagnosticInfo_Memory) + End If + End If + End Sub + + ' Are we on Windows with Major Version < 5 (NT4.0, Me, 98, 95)? + Private m_IsOldOS As Boolean = System.Environment.OSVersion.Version.Major < 5 + Private m_MemoryStatus As NativeMethods.MEMORYSTATUS + Private m_MemoryStatusEx As NativeMethods.MEMORYSTATUSEX + End Class + End Class + +End Namespace diff --git a/Microsoft.VisualBasic/runtime/msvbalib/Devices/Keyboard.vb b/Microsoft.VisualBasic/runtime/msvbalib/Devices/Keyboard.vb new file mode 100644 index 000000000..4042fea86 --- /dev/null +++ b/Microsoft.VisualBasic/runtime/msvbalib/Devices/Keyboard.vb @@ -0,0 +1,155 @@ +' Copyright (c) Microsoft Corporation. All rights reserved. + +Option Explicit On +Option Strict On + +Imports System +Imports System.Diagnostics +Imports System.Windows.Forms +Imports System.ComponentModel +Imports System.Security +Imports System.Security.Permissions +Imports System.Runtime.Versioning +Imports Microsoft.VisualBasic.CompilerServices + +Namespace Microsoft.VisualBasic.Devices + + '***************************************************************************** + ';Keyboard + ' + 'Remarks: A class representing a computer keyboard. Enables discovery of key + ' state information for the most common scenarios and enables SendKeys + '***************************************************************************** + _ + Public Class Keyboard + + '''***************************************************************************** + ''' ;SendKeys + ''' + ''' Sends keys to the active window as if typed as keyboard with wait = false. + ''' + ''' A string containing the keys to be sent (typed). + ''' VSWhidbey 404397. + Public Sub SendKeys(ByVal keys As String) + SendKeys(keys, False) + End Sub + + '***************************************************************************** + ';SendKeys + ' + 'Summary: Sends keys to the active window as if typed at keyboard. This overloaded + ' version uses the same conventions as the VB6 SendKeys. + ' Param: Keys - A string containing the keys to be sent (typed). + ' Param: Wait - Wait for messages to be processed before returning. + '***************************************************************************** + Public Sub SendKeys(ByVal keys As String, ByVal wait As Boolean) + If wait Then + System.Windows.Forms.SendKeys.SendWait(keys) + Else + System.Windows.Forms.SendKeys.Send(keys) + End If + End Sub + + '***************************************************************************** + ';ShiftDown + ' + 'Summary: Gets the state (up or down) of the Shift key. + 'Returns: True if the key is down otherwise false. + '***************************************************************************** + Public ReadOnly Property ShiftKeyDown() As Boolean + Get + Dim Keys As Keys = Control.ModifierKeys + Return CType(Keys And Keys.Shift, Boolean) + End Get + End Property + + '***************************************************************************** + ';AltDown + ' + 'Summary: Gets the state (up or down) of the Alt key. + 'Returns: True if the key is down otherwise false. + '***************************************************************************** + Public ReadOnly Property AltKeyDown() As Boolean + Get + Dim Keys As Keys = Control.ModifierKeys + Return CType(Keys And Keys.Alt, Boolean) + End Get + End Property + + '***************************************************************************** + ';CtrlDown + ' + 'Summary: Gets the state (up or down) of the Ctrl key. + 'Returns: True if the key is down otherwise false. + '***************************************************************************** + Public ReadOnly Property CtrlKeyDown() As Boolean + Get + Dim Keys As Keys = Control.ModifierKeys + Return CType(Keys And Keys.Control, Boolean) + End Get + End Property + + '***************************************************************************** + ';CapsLock + ' + 'Summary: Gets the toggle state of the Caps Lock key. + 'Returns: True if the key is on otherwise false. + '***************************************************************************** + Public ReadOnly Property CapsLock() As Boolean + _ + _ + _ + Get + 'Security Note: Only the state of the Caps Lock is returned + + 'The low order byte of the return value from GetKeyState is 1 if the key is + 'toggled on. + Return CType((UnsafeNativeMethods.GetKeyState(Keys.CapsLock) And 1), Boolean) + End Get + End Property + + '***************************************************************************** + ';NumLock + ' + 'Summary: Gets the toggle state of the Num Lock key. + 'Returns: True if the key is on otherwise false. + '***************************************************************************** + Public ReadOnly Property NumLock() As Boolean + _ + _ + _ + Get + 'Security Note: Only the state of the Num Lock is returned + + 'The low order byte of the return value from GetKeyState is 1 if the key is + 'toggled on. + Return CType((UnsafeNativeMethods.GetKeyState(Keys.NumLock) And 1), Boolean) + End Get + End Property + + '***************************************************************************** + ';ScrollLock + ' + 'Summary: Gets the toggle state of the Scroll Lock key. + 'Returns: True if the key is on otherwise false. + '***************************************************************************** + Public ReadOnly Property ScrollLock() As Boolean + _ + _ + _ + Get + 'Security Note: Only the state of the Scroll Lock is returned + + 'The low order byte of the return value from GetKeyState is 1 if the key is + 'toggled on. + Return CType((UnsafeNativeMethods.GetKeyState(Keys.Scroll) And 1), Boolean) + End Get + End Property + + '* FRIEND ************************************************************************** + + '* PRIVATE ************************************************************************** + + End Class + +End Namespace diff --git a/Microsoft.VisualBasic/runtime/msvbalib/Devices/Mouse.vb b/Microsoft.VisualBasic/runtime/msvbalib/Devices/Mouse.vb new file mode 100644 index 000000000..f12beaa23 --- /dev/null +++ b/Microsoft.VisualBasic/runtime/msvbalib/Devices/Mouse.vb @@ -0,0 +1,88 @@ +' Copyright (c) Microsoft Corporation. All rights reserved. + +Option Strict On +Option Explicit On + +Imports System.ComponentModel +Imports System.Security.Permissions +Imports System.Windows.Forms +Imports Microsoft.VisualBasic.CompilerServices +Imports Microsoft.VisualBasic.CompilerServices.ExceptionUtils + +Namespace Microsoft.VisualBasic.Devices + + '''************************************************************************** + ''' ;Mouse + ''' + ''' A wrapper object that acts as a discovery mechanism for finding + ''' information about the mouse on your computer such as whether the mouse + ''' exists, the number of buttons, wheelscrolls details. + ''' + ''' This class is a Singleton Class. See Common.Computer for details. + ''' + ''' + _ + Public Class Mouse + + '= PUBLIC ============================================================= + + '''************************************************************************** + ''' ;ButtonsSwapped + ''' + ''' Gets a value indicating whether the functions of the left and right + ''' mouses buttons have been swapped. + ''' + ''' + ''' true if the functions of the left and right mouse buttons are swapped. false otherwise. + ''' + ''' If no mouse is installed. + Public ReadOnly Property ButtonsSwapped() As Boolean + Get + If System.Windows.Forms.SystemInformation.MousePresent Then + Return SystemInformation.MouseButtonsSwapped + Else + Throw GetInvalidOperationException(ResID.MyID.Mouse_NoMouseIsPresent) + End If + End Get + End Property + + '''************************************************************************** + ''' ;WheelExists + ''' + ''' Gets a value indicating whether a mouse with a mouse wheel is installed + ''' + ''' true if a mouse with a mouse wheel is installed, false otherwise. + ''' If no mouse is installed. + Public ReadOnly Property WheelExists() As Boolean + Get + If System.Windows.Forms.SystemInformation.MousePresent Then + Return SystemInformation.MouseWheelPresent + Else + Throw GetInvalidOperationException(ResID.MyID.Mouse_NoMouseIsPresent) + End If + End Get + End Property + + '''************************************************************************** + ''' ;WheelScrollLines + ''' + ''' Gets the number of lines to scroll when the mouse wheel is rotated. + ''' + ''' The number of lines to scroll. + ''' if no mouse is installed or no wheels exists. + Public ReadOnly Property WheelScrollLines() As Integer + Get + If WheelExists Then + Return SystemInformation.MouseWheelScrollLines + Else + Throw GetInvalidOperationException(ResID.MyID.Mouse_NoWheelIsPresent) + End If + End Get + End Property + + '= FRIEND ============================================================= + + '= PRIVATE ============================================================ + + End Class 'Mouse +End Namespace diff --git a/Microsoft.VisualBasic/runtime/msvbalib/Devices/Network.vb b/Microsoft.VisualBasic/runtime/msvbalib/Devices/Network.vb new file mode 100644 index 000000000..f34d9d819 --- /dev/null +++ b/Microsoft.VisualBasic/runtime/msvbalib/Devices/Network.vb @@ -0,0 +1,961 @@ +' Copyright (c) Microsoft Corporation. All rights reserved. +Option Explicit On +Option Strict On + +Imports System +Imports System.ComponentModel +Imports System.Diagnostics +Imports System.Net +Imports NetInfoAlias = System.Net.NetworkInformation +Imports System.Security +Imports System.Security.Permissions +Imports System.Threading +Imports Microsoft.VisualBasic +Imports Microsoft.VisualBasic.FileIO +Imports Microsoft.VisualBasic.CompilerServices +Imports Microsoft.VisualBasic.CompilerServices.ExceptionUtils +Imports Microsoft.VisualBasic.CompilerServices.Utils +Imports Microsoft.VisualBasic.MyServices.Internal + +Namespace Microsoft.VisualBasic.Devices + + '''************************************************************************** + ''';NetworkAvailableEventArgs + ''' + ''' Used to pass network connectivity status. + ''' + ''' + Public Class NetworkAvailableEventArgs + Inherits EventArgs + + Public Sub New(ByVal networkAvailable As Boolean) + m_NetworkAvailable = networkAvailable + End Sub + + Public ReadOnly Property IsNetworkAvailable() As Boolean + Get + Return m_NetworkAvailable + End Get + End Property + + Private m_NetworkAvailable As Boolean + + End Class + + _ + Public Delegate Sub NetworkAvailableEventHandler(ByVal sender As Object, ByVal e As NetworkAvailableEventArgs) + + '''************************************************************************** + ''';Network + ''' + ''' An object that allows easy access to some simple network properties and functionality. + ''' + ''' + _ + Public Class Network + + '* PUBLIC ************************************************************* + + '''********************************************************************* + ''';NetworkAvailability + ''' + ''' Event fired when connected to the network + ''' + ''' Has no meaning for this event + ''' Has no meaning for this event + ''' + Public Custom Event NetworkAvailabilityChanged As NetworkAvailableEventHandler + 'This is a custom event because we want to hook up the NetworkAvailabilityChanged event only if the user writes a handler for it. + 'The reason being that it is very expensive to handle and kills our application startup perf. + AddHandler(ByVal handler As Global.Microsoft.VisualBasic.Devices.NetworkAvailableEventHandler) + + ' Set the current state of connectedness, swallow known exceptions since user won't be able to correct problem + Try + m_Connected = Me.IsAvailable + Catch ex As System.Security.SecurityException + Return + Catch ex As System.PlatformNotSupportedException + Return + End Try + SyncLock m_SyncObject 'we don't want our event firing before we've finished setting up the infrastructure. Also, need to assure there are no races in here so we don't hook up the OS listener twice, etc. + If m_NetworkAvailabilityEventHandlers Is Nothing Then m_NetworkAvailabilityEventHandlers = New System.Collections.ArrayList + m_NetworkAvailabilityEventHandlers.Add(handler) + + 'Only setup the event marshalling infrastructure once + If m_NetworkAvailabilityEventHandlers.Count = 1 Then + m_NetworkAvailabilityChangedCallback = New Threading.SendOrPostCallback(AddressOf NetworkAvailabilityChangedHandler) 'the async operation posts to this delegate + If AsyncOperationManager.SynchronizationContext IsNot Nothing Then + m_SynchronizationContext = AsyncOperationManager.SynchronizationContext 'We need to hang on to the syncronization context associated with the thread the network object is created on + Try ' UNDONE: Exceptions are thrown if the user isn't an admin. This try/catch is a temporary solution for beta1. For beta2 we need to find a way to enable these events for a non admin. + AddHandler NetInfoAlias.NetworkChange.NetworkAddressChanged, New NetInfoAlias.NetworkAddressChangedEventHandler(AddressOf Me.OS_NetworkAvailabilityChangedListener) 'listen to the OS event + Catch ex As System.PlatformNotSupportedException + Catch ex As NetInfoAlias.NetworkInformationException + End Try + End If + End If + End SyncLock + End AddHandler + + RemoveHandler(ByVal handler As Global.Microsoft.VisualBasic.Devices.NetworkAvailableEventHandler) + If m_NetworkAvailabilityEventHandlers IsNot Nothing AndAlso m_NetworkAvailabilityEventHandlers.Count > 0 Then + m_NetworkAvailabilityEventHandlers.Remove(handler) + 'Last one to leave, turn out the lights... + If m_NetworkAvailabilityEventHandlers.Count = 0 Then + RemoveHandler NetInfoAlias.NetworkChange.NetworkAddressChanged, New NetInfoAlias.NetworkAddressChangedEventHandler(AddressOf Me.OS_NetworkAvailabilityChangedListener) 'listen to the OS event + DisconnectListener() 'Stop listening to network change events since nobody is listening to us anymore + End If + End If + End RemoveHandler + + RaiseEvent(ByVal sender As Object, ByVal e As Global.Microsoft.VisualBasic.Devices.NetworkAvailableEventArgs) + If m_NetworkAvailabilityEventHandlers IsNot Nothing Then + For Each handler As Global.Microsoft.VisualBasic.Devices.NetworkAvailableEventHandler In m_NetworkAvailabilityEventHandlers + If handler IsNot Nothing Then handler.Invoke(sender, e) + Next + End If + End RaiseEvent + End Event + + '''********************************************************************** + ''';New - Constructor + ''' + ''' Creates class and hooks up events + ''' + ''' + Public Sub New() + End Sub + + '''********************************************************************** + ''';IsAvailable + ''' + ''' Indicates whether or not the local machine is connected to an IP network. + ''' + ''' True if connected, otherwise False + ''' + Public ReadOnly Property IsAvailable() As Boolean + Get + + Return NetInfoAlias.NetworkInterface.GetIsNetworkAvailable() + + End Get + End Property + + '''************************************************************************** + ''';Ping + ''' + ''' Sends and receives a packet to and from the passed in address. + ''' + ''' + ''' True if ping was successful, otherwise False + ''' + Public Function Ping(ByVal hostNameOrAddress As String) As Boolean + Return Ping(hostNameOrAddress, DEFAULT_PING_TIMEOUT) + End Function + + '''************************************************************************** + ''';Ping + ''' + ''' Sends and receives a packet to and from the passed in Uri. + ''' + ''' A Uri representing the host + ''' True if ping was successful, otherwise False + ''' + Public Function Ping(ByVal address As Uri) As Boolean + ' We're safe from Ping(Nothing, ...) due to overload failure (Ping(String,...) v.s Ping(Uri,...)). + ' However, it is good practice to verify address before calling address.Host. + If address Is Nothing Then + Throw ExceptionUtils.GetArgumentNullException("address") + End If + Return Ping(address.Host, DEFAULT_PING_TIMEOUT) + End Function + + '''************************************************************************** + ''';Ping + ''' + ''' Sends and receives a packet to and from the passed in address. + ''' + ''' The name of the host as a Url or IP Address + ''' Time to wait before aborting ping + ''' True if ping was successful, otherwise False + ''' + Public Function Ping(ByVal hostNameOrAddress As String, ByVal timeout As Integer) As Boolean + + ' Make sure a network is available + If Not Me.IsAvailable Then + Throw ExceptionUtils.GetInvalidOperationException(ResID.MyID.Network_NetworkNotAvailable) + End If + + Dim PingMaker As New NetInfoAlias.Ping + Dim Reply As NetInfoAlias.PingReply = PingMaker.Send(hostNameOrAddress, timeout, Me.PingBuffer) + If Reply.Status = NetworkInformation.IPStatus.Success Then + Return True + End If + Return False + End Function + + '''************************************************************************** + ''';Ping + ''' + ''' Sends and receives a packet to and from the passed in Uri. + ''' + ''' A Uri representing the host + ''' Time to wait before aborting ping + ''' True if ping was successful, otherwise False + ''' + Public Function Ping(ByVal address As Uri, ByVal timeout As Integer) As Boolean + ' We're safe from Ping(Nothing, ...) due to overload failure (Ping(String,...) v.s Ping(Uri,...)). + ' However, it is good practice to verify address before calling address.Host. + If address Is Nothing Then + Throw ExceptionUtils.GetArgumentNullException("address") + End If + Return Ping(address.Host, timeout) + End Function + + '''*************************************************************************** + ''';DownloadFile + ''' + ''' Downloads a file from the network to the specified path + ''' + ''' Address to the remote file, http, ftp etc... + ''' Name and path of file where download is saved + ''' + Public Sub DownloadFile(ByVal address As String, ByVal destinationFileName As String) + DownloadFile(address, destinationFileName, DEFAULT_USERNAME, DEFAULT_PASSWORD, False, DEFAULT_TIMEOUT, False) + End Sub + + '''*************************************************************************** + ''';DownloadFile + ''' + ''' Downloads a file from the network to the specified path + ''' + ''' Uri to the remote file + ''' Name and path of file where download is saved + ''' + Public Sub DownloadFile(ByVal address As Uri, ByVal destinationFileName As String) + DownloadFile(address, destinationFileName, DEFAULT_USERNAME, DEFAULT_PASSWORD, False, DEFAULT_TIMEOUT, False) + End Sub + + '''*************************************************************************** + ''';DownloadFile + ''' + ''' Downloads a file from the network to the specified path + ''' + ''' Address to the remote file, http, ftp etc... + ''' Name and path of file where download is saved + ''' The name of the user performing the download + ''' The user's password + ''' + Public Sub DownloadFile(ByVal address As String, ByVal destinationFileName As String, ByVal userName As String, ByVal password As String) + DownloadFile(address, destinationFileName, userName, password, False, DEFAULT_TIMEOUT, False) + End Sub + + '''*************************************************************************** + ''';DownloadFile + ''' + ''' Downloads a file from the network to the specified path + ''' + ''' Uri to the remote file + ''' Name and path of file where download is saved + ''' The name of the user performing the download + ''' The user's password + ''' + Public Sub DownloadFile(ByVal address As Uri, ByVal destinationFileName As String, ByVal userName As String, ByVal password As String) + DownloadFile(address, destinationFileName, userName, password, False, DEFAULT_TIMEOUT, False) + End Sub + + '''*************************************************************************** + ''';DownloadFile + ''' + ''' Downloads a file from the network to the specified path + ''' + ''' Address to the remote file, http, ftp etc... + ''' Name and path of file where download is saved + ''' The name of the user performing the download + ''' The user's password + ''' Indicates whether or not to show a progress bar + ''' Time alloted before giving up on a connection + ''' Indicates whether or not the file should be overwritten if local file already exists + ''' + Public Sub DownloadFile(ByVal address As String, _ + ByVal destinationFileName As String, _ + ByVal userName As String, _ + ByVal password As String, _ + ByVal showUI As Boolean, _ + ByVal connectionTimeout As Integer, _ + ByVal overwrite As Boolean) + + DownloadFile(address, destinationFileName, userName, password, showUI, connectionTimeout, overwrite, UICancelOption.ThrowException) + End Sub + + '''*************************************************************************** + ''';DownloadFile + ''' + ''' Downloads a file from the network to the specified path + ''' + ''' Address to the remote file, http, ftp etc... + ''' Name and path of file where download is saved + ''' The name of the user performing the download + ''' The user's password + ''' Indicates whether or not to show a progress bar + ''' Time alloted before giving up on a connection + ''' Indicates whether or not the file should be overwritten if local file already exists + ''' Indicates what to do if user cancels dialog (either throw or do nothing) + ''' + Public Sub DownloadFile(ByVal address As String, _ + ByVal destinationFileName As String, _ + ByVal userName As String, _ + ByVal password As String, _ + ByVal showUI As Boolean, _ + ByVal connectionTimeout As Integer, _ + ByVal overwrite As Boolean, _ + ByVal onUserCancel As UICancelOption) + + ' We're safe from DownloadFile(Nothing, ...) due to overload failure (DownloadFile(String,...) v.s DownloadFile(Uri,...)). + ' However, it is good practice to verify address before calling Trim. + If String.IsNullOrEmpty(address) OrElse address.Trim() = "" Then + Throw ExceptionUtils.GetArgumentNullException("address") + End If + + Dim addressUri As Uri = GetUri(address.Trim()) + + ' Get network credentials + Dim networkCredentials As ICredentials = GetNetworkCredentials(userName, password) + + DownloadFile(addressUri, destinationFileName, networkCredentials, showUI, connectionTimeout, overwrite, onUserCancel) + End Sub + + '''*************************************************************************** + ''';DownloadFile + ''' + ''' Downloads a file from the network to the specified path + ''' + ''' Uri to the remote file + ''' Name and path of file where download is saved + ''' The name of the user performing the download + ''' The user's password + ''' Indicates whether or not to show a progress bar + ''' Time alloted before giving up on a connection + ''' Indicates whether or not the file should be overwritten if local file already exists + ''' + Sub DownloadFile(ByVal address As Uri, _ + ByVal destinationFileName As String, _ + ByVal userName As String, _ + ByVal password As String, _ + ByVal showUI As Boolean, _ + ByVal connectionTimeout As Integer, _ + ByVal overwrite As Boolean) + + DownloadFile(address, destinationFileName, userName, password, showUI, connectionTimeout, overwrite, UICancelOption.ThrowException) + End Sub + + '''*************************************************************************** + ''';DownloadFile + ''' + ''' Downloads a file from the network to the specified path + ''' + ''' Uri to the remote file + ''' Name and path of file where download is saved + ''' The name of the user performing the download + ''' The user's password + ''' Indicates whether or not to show a progress bar + ''' Time alloted before giving up on a connection + ''' Indicates whether or not the file should be overwritten if local file already exists + ''' Indicates what to do if user cancels dialog (either throw or do nothing) + ''' + Sub DownloadFile(ByVal address As Uri, _ + ByVal destinationFileName As String, _ + ByVal userName As String, _ + ByVal password As String, _ + ByVal showUI As Boolean, _ + ByVal connectionTimeout As Integer, _ + ByVal overwrite As Boolean, _ + ByVal onUserCancel As UICancelOption) + + ' Get network credentials + Dim networkCredentials As ICredentials = GetNetworkCredentials(userName, password) + + DownloadFile(address, destinationFileName, networkCredentials, showUI, connectionTimeout, overwrite, onUserCancel) + End Sub + + '''*************************************************************************** + ''';DownloadFile + ''' + ''' Downloads a file from the network to the specified path + ''' + ''' Uri to the remote file + ''' Name and path of file where download is saved + ''' The credentials of the user performing the download + ''' Indicates whether or not to show a progress bar + ''' Time alloted before giving up on a connection + ''' Indicates whether or not the file should be overwritten if local file already exists + ''' Calls to all the other overloads will come through here + Sub DownloadFile(ByVal address As Uri, _ + ByVal destinationFileName As String, _ + ByVal networkCredentials As System.Net.ICredentials, _ + ByVal showUI As Boolean, _ + ByVal connectionTimeout As Integer, _ + ByVal overwrite As Boolean) + + DownloadFile(address, destinationFileName, networkCredentials, showUI, connectionTimeout, overwrite, UICancelOption.ThrowException) + + End Sub + + '''*************************************************************************** + ''';DownloadFile + ''' + ''' Downloads a file from the network to the specified path + ''' + ''' Uri to the remote file + ''' Name and path of file where download is saved + ''' The credentials of the user performing the download + ''' Indicates whether or not to show a progress bar + ''' Time alloted before giving up on a connection + ''' Indicates whether or not the file should be overwritten if local file already exists + ''' Indicates what to do if user cancels dialog (either throw or do nothing) + ''' Calls to all the other overloads will come through here + _ + Sub DownloadFile(ByVal address As Uri, _ + ByVal destinationFileName As String, _ + ByVal networkCredentials As System.Net.ICredentials, _ + ByVal showUI As Boolean, _ + ByVal connectionTimeout As Integer, _ + ByVal overwrite As Boolean, _ + ByVal onUserCancel As UICancelOption) + + If connectionTimeout <= 0 Then + Throw GetArgumentExceptionWithArgName("connectionTimeOut", ResID.MyID.Network_BadConnectionTimeout) + End If + + If address Is Nothing Then + Throw ExceptionUtils.GetArgumentNullException("address") + End If + + Using client As New WebClientExtended + client.Timeout = connectionTimeout + + ' Don't use passive mode if we're showing UI + client.UseNonPassiveFtp = showUI + + 'Construct the local file. This will validate the full name and path + Dim fullFilename As String = FileIO.FileSystem.NormalizeFilePath(destinationFileName, "destinationFileName") + + ' Sometime a path that can't be parsed is normalized to the current directory. This makes sure we really + ' have a file and path + If System.IO.Directory.Exists(fullFilename) Then + Throw ExceptionUtils.GetInvalidOperationException(ResID.MyID.Network_DownloadNeedsFilename) + End If + + 'Throw if the file exists and the user doesn't want to overwrite + If IO.File.Exists(fullFilename) And Not overwrite Then + Throw New IO.IOException(GetResourceString(ResID.MyID.IO_FileExists_Path, destinationFileName)) + End If + + ' Set credentials if we have any + If networkCredentials IsNot Nothing Then + client.Credentials = networkCredentials + End If + + Dim dialog As ProgressDialog = Nothing + If showUI AndAlso System.Environment.UserInteractive Then + ' Do UI demand here rather than waiting for form.show so that exception is thrown as early as possible + Dim UIPermission As New UIPermission(UIPermissionWindow.SafeSubWindows) + UIPermission.Demand() + + dialog = New ProgressDialog() + dialog.Text = GetResourceString(ResID.MyID.ProgressDialogDownloadingTitle, address.AbsolutePath) + dialog.LabelText = GetResourceString(ResID.MyID.ProgressDialogDownloadingLabel, address.AbsolutePath, fullFilename) + End If + + 'Check to see if the target directory exists. If it doesn't, create it + Dim targetDirectory As String = System.IO.Path.GetDirectoryName(fullFilename) + + ' Make sure we have a meaningful directory. If we don't, the destinationFileName is suspect + If targetDirectory = "" Then + Throw ExceptionUtils.GetInvalidOperationException(ResID.MyID.Network_DownloadNeedsFilename) + End If + + If Not IO.Directory.Exists(targetDirectory) Then + IO.Directory.CreateDirectory(targetDirectory) + End If + + 'Create the copier + Dim copier As New WebClientCopy(client, dialog) + + + 'Download the file + copier.DownloadFile(address, fullFilename) + + 'Handle a dialog cancel + If showUI AndAlso System.Environment.UserInteractive Then + If onUserCancel = UICancelOption.ThrowException And dialog.UserCanceledTheDialog Then + Throw New OperationCanceledException() + End If + End If + + End Using + + End Sub + + '''*************************************************************************** + ''';UploadFile + ''' + ''' Uploads a file from the local machine to the specified host + ''' + ''' The file to be uploaded + ''' The full name and path of the host destination + ''' + Public Sub UploadFile(ByVal sourceFileName As String, ByVal address As String) + UploadFile(sourceFileName, address, DEFAULT_USERNAME, DEFAULT_PASSWORD, False, DEFAULT_TIMEOUT) + End Sub + + '''*************************************************************************** + ''';UploadFile + ''' + ''' Uploads a file from the local machine to the specified host + ''' + ''' The file to be uploaded + ''' Uri representing the destination + ''' + Public Sub UploadFile(ByVal sourceFileName As String, ByVal address As Uri) + UploadFile(sourceFileName, address, DEFAULT_USERNAME, DEFAULT_PASSWORD, False, DEFAULT_TIMEOUT) + End Sub + + '''*************************************************************************** + ''';UploadFile + ''' + ''' Uploads a file from the local machine to the specified host + ''' + ''' The file to be uploaded + ''' The full name and path of the host destination + ''' The name of the user performing the upload + ''' The user's password + ''' + Public Sub UploadFile(ByVal sourceFileName As String, ByVal address As String, ByVal userName As String, ByVal password As String) + UploadFile(sourceFileName, address, userName, password, False, DEFAULT_TIMEOUT) + End Sub + + '''*************************************************************************** + ''';UploadFile + ''' + ''' Uploads a file from the local machine to the specified host + ''' + ''' The file to be uploaded + ''' Uri representing the destination + ''' The name of the user performing the upload + ''' The user's password + ''' + Public Sub UploadFile(ByVal sourceFileName As String, ByVal address As Uri, ByVal userName As String, ByVal password As String) + UploadFile(sourceFileName, address, userName, password, False, DEFAULT_TIMEOUT) + End Sub + + '''*************************************************************************** + ''';UploadFile + ''' + ''' Uploads a file from the local machine to the specified host + ''' + ''' The file to be uploaded + ''' The full name and path of the host destination + ''' The name of the user performing the upload + ''' The user's password + ''' Indicates whether or not to show a progress bar + ''' Time alloted before giving up on a connection + ''' + Public Sub UploadFile(ByVal sourceFileName As String, _ + ByVal address As String, _ + ByVal userName As String, _ + ByVal password As String, _ + ByVal showUI As Boolean, _ + ByVal connectionTimeout As Integer) + + UploadFile(sourceFileName, address, userName, password, showUI, connectionTimeout, UICancelOption.ThrowException) + End Sub + + + '''*************************************************************************** + ''';UploadFile + ''' + ''' Uploads a file from the local machine to the specified host + ''' + ''' The file to be uploaded + ''' The full name and path of the host destination + ''' The name of the user performing the upload + ''' The user's password + ''' Indicates whether or not to show a progress bar + ''' Time alloted before giving up on a connection + ''' Indicates what to do if user cancels dialog (either throw or do nothing) + ''' + Public Sub UploadFile(ByVal sourceFileName As String, _ + ByVal address As String, _ + ByVal userName As String, _ + ByVal password As String, _ + ByVal showUI As Boolean, _ + ByVal connectionTimeout As Integer, _ + ByVal onUserCancel As UICancelOption) + + ' We're safe from UploadFile(Nothing, ...) due to overload failure (UploadFile(String,...) v.s UploadFile(Uri,...)). + ' However, it is good practice to verify address before calling address.Trim. + If String.IsNullOrEmpty(address) OrElse address.Trim() = "" Then + Throw ExceptionUtils.GetArgumentNullException("address") + End If + + ' Getting a uri will validate the form of the host address + Dim addressUri As Uri = GetUri(address.Trim()) + + ' For uploads, we need to make sure the address includes the filename + If System.IO.Path.GetFileName(addressUri.AbsolutePath) = "" Then + Throw ExceptionUtils.GetInvalidOperationException(ResID.MyID.Network_UploadAddressNeedsFilename) + End If + + UploadFile(sourceFileName, addressUri, userName, password, showUI, connectionTimeout, onUserCancel) + + End Sub + + '''*************************************************************************** + ''';UploadFile + ''' + ''' Uploads a file from the local machine to the specified host + ''' + ''' The file to be uploaded + ''' Uri representing the destination + ''' The name of the user performing the upload + ''' The user's password + ''' Indicates whether or not to show a progress bar + ''' Time alloted before giving up on a connection + ''' + Public Sub UploadFile(ByVal sourceFileName As String, _ + ByVal address As Uri, _ + ByVal userName As String, _ + ByVal password As String, _ + ByVal showUI As Boolean, _ + ByVal connectionTimeout As Integer) + + UploadFile(sourceFileName, address, userName, password, showUI, connectionTimeout, UICancelOption.ThrowException) + End Sub + + '''*************************************************************************** + ''';UploadFile + ''' + ''' Uploads a file from the local machine to the specified host + ''' + ''' The file to be uploaded + ''' Uri representing the destination + ''' The name of the user performing the upload + ''' The user's password + ''' Indicates whether or not to show a progress bar + ''' Time alloted before giving up on a connection + ''' Indicates what to do if user cancels dialog (either throw or do nothing) + ''' + Public Sub UploadFile(ByVal sourceFileName As String, _ + ByVal address As Uri, _ + ByVal userName As String, _ + ByVal password As String, _ + ByVal showUI As Boolean, _ + ByVal connectionTimeout As Integer, _ + ByVal onUserCancel As UICancelOption) + + ' Get network credentials + Dim networkCredentials As ICredentials = GetNetworkCredentials(userName, password) + + UploadFile(sourceFileName, address, networkCredentials, showUI, connectionTimeout, onUserCancel) + + End Sub + + '''*************************************************************************** + ''';UploadFile + ''' + ''' Uploads a file from the local machine to the specified host + ''' + ''' The file to be uploaded + ''' Uri representing the destination + ''' The credentials of the user performing the upload + ''' Indicates whether or not to show a progress bar + ''' Time alloted before giving up on a connection + ''' + Public Sub UploadFile(ByVal sourceFileName As String, _ + ByVal address As Uri, _ + ByVal networkCredentials As ICredentials, _ + ByVal showUI As Boolean, _ + ByVal connectionTimeout As Integer) + + UploadFile(sourceFileName, address, networkCredentials, showUI, connectionTimeout, UICancelOption.ThrowException) + End Sub + + '''*************************************************************************** + ''';UploadFile + ''' + ''' Uploads a file from the local machine to the specified host + ''' + ''' The file to be uploaded + ''' Uri representing the destination + ''' The credentials of the user performing the upload + ''' Indicates whether or not to show a progress bar + ''' Time alloted before giving up on a connection + ''' Indicates what to do if user cancels dialog (either throw or do nothing) + ''' + Public Sub UploadFile(ByVal sourceFileName As String, _ + ByVal address As Uri, _ + ByVal networkCredentials As ICredentials, _ + ByVal showUI As Boolean, _ + ByVal connectionTimeout As Integer, _ + ByVal onUserCancel As UICancelOption) + + sourceFileName = FileIO.FileSystem.NormalizeFilePath(sourceFileName, "sourceFileName") + + 'Make sure the file exists + If Not IO.File.Exists(sourceFileName) Then + Throw New IO.FileNotFoundException(GetResourceString(ResID.MyID.IO_FileNotFound_Path, sourceFileName)) + End If + + If connectionTimeout <= 0 Then + Throw GetArgumentExceptionWithArgName("connectionTimeout", ResID.MyID.Network_BadConnectionTimeout) + End If + + If address Is Nothing Then + Throw ExceptionUtils.GetArgumentNullException("address") + End If + + Using client As New WebClientExtended() + client.Timeout = connectionTimeout + + ' Set credentials if we have any + If networkCredentials IsNot Nothing Then + client.Credentials = networkCredentials + End If + + Dim Dialog As ProgressDialog = Nothing + If showUI AndAlso System.Environment.UserInteractive Then + Dialog = New ProgressDialog + Dialog.Text = GetResourceString(ResID.MyID.ProgressDialogUploadingTitle, sourceFileName) + Dialog.LabelText = GetResourceString(ResID.MyID.ProgressDialogUploadingLabel, sourceFileName, address.AbsolutePath) + End If + + 'Create the copier + Dim copier As New WebClientCopy(client, dialog) + + 'Download the file + copier.UploadFile(sourceFileName, address) + + 'Handle a dialog cancel + If showUI AndAlso System.Environment.UserInteractive Then + If onUserCancel = UICancelOption.ThrowException And Dialog.UserCanceledTheDialog Then + Throw New OperationCanceledException() + End If + End If + End Using + + End Sub + + '* FRIEND ************************************************************** + + 'UNDONE - see VSWHIDBEY #343374 + Friend Sub DisconnectListener() + RemoveHandler NetInfoAlias.NetworkChange.NetworkAddressChanged, New NetInfoAlias.NetworkAddressChangedEventHandler(AddressOf Me.OS_NetworkAvailabilityChangedListener) + End Sub + + + '* PRIVATE ************************************************************* + + 'Listens to the AddressChanged event from the OS which comes in on an arbitrary thread + Private Sub OS_NetworkAvailabilityChangedListener(ByVal sender As Object, ByVal e As EventArgs) + SyncLock m_SyncObject 'Ensure we don't handle events until after we've finished setting up the event marshalling infrastructure + 'Don't call AsyncOperationManager.OperationSynchronizationContext.Post. The reason we want to go through m_SynchronizationContext is that + 'the OperationSyncronizationContext is thread static. Since we are getting called on some random thread, the context that was + 'in place when the Network object was created won't be available (it is on the original thread). To hang on to the original + 'context associated with the thread that the network object is created on, I use m_SynchronizationContext. + m_SynchronizationContext.Post(m_NetworkAvailabilityChangedCallback, Nothing) + End SyncLock + End Sub + + 'Listens to the AddressChanged event which will come on the same thread that this class was created on (AsyncEventManager is responsible for getting the event here) + Private Sub NetworkAvailabilityChangedHandler(ByVal state As Object) + Dim Connected As Boolean = Me.IsAvailable + ' Fire an event only if the connected state has changed + If m_Connected <> Connected Then + m_Connected = Connected + RaiseEvent NetworkAvailabilityChanged(Me, New NetworkAvailableEventArgs(Connected)) + End If + End Sub + + + '''********************************************************************** + ''';PingBuffer + ''' + ''' A buffer for pinging. This immitates the buffer used by Ping.Exe + ''' + ''' A buffer + ''' + Private ReadOnly Property PingBuffer() As Byte() + Get + If m_PingBuffer Is Nothing Then + ReDim m_PingBuffer(BUFFER_SIZE - 1) + For i As Integer = 0 To BUFFER_SIZE - 1 + 'This is the same logic Ping.exe uses to fill it's buffer + m_PingBuffer(i) = System.Convert.ToByte(Asc("a"c) + i Mod 23, System.Globalization.CultureInfo.InvariantCulture) + Next + End If + + Return m_PingBuffer + End Get + End Property + + '''********************************************************************** + ''';GetUri + ''' + ''' Gets a Uri from a uri string. We also use this function to validate the UriString (remote file address) + ''' + ''' The remote file address + ''' A Uri if successful, otherwise it throws an exception + ''' + Private Function GetUri(ByVal address As String) As Uri + Try + Return New Uri(address) + Catch ex As UriFormatException + 'Throw an exception with an error message more appropriate to our API + Throw GetArgumentExceptionWithArgName("address", ResID.MyID.Network_InvalidUriString, address) + End Try + End Function + + '''******************************************************************** + ''';GetNetworkCredentials + ''' + ''' Gets network credentials from a userName and password + ''' + ''' The name of the user + ''' The password of the user + ''' A NetworkCredentials + ''' + Private Function GetNetworkCredentials(ByVal userName As String, ByVal password As String) As ICredentials + + ' Make sure all nulls are empty strings + If userName Is Nothing Then + userName = "" + End If + + If password Is Nothing Then + password = "" + End If + + If userName = "" And password = "" Then + Return Nothing + End If + + Return New NetworkCredential(userName, password) + End Function + + + 'Holds the buffer for pinging. We lazy initialize on first use + Private m_PingBuffer() As Byte + + 'Size of Ping.exe buffer + Private Const BUFFER_SIZE As Integer = 32 + + ' Default timeout value + Private Const DEFAULT_TIMEOUT As Integer = 100000 + + ' Defalt timeout for Ping + Private Const DEFAULT_PING_TIMEOUT As Integer = 1000 + + ' UserName used in overloads where there is no userName parameter + Private Const DEFAULT_USERNAME As String = "" + + ' Password used in overloads where there is no password parameter + Private Const DEFAULT_PASSWORD As String = "" + + ' Indicates last known connection state + Private m_Connected As Boolean + + ' Object for syncing + Private m_SyncObject As New Object() + + Private m_NetworkAvailabilityEventHandlers As System.Collections.ArrayList 'Holds the listeners to our NetworkAvailability changed event + + Private m_SynchronizationContext As System.Threading.SynchronizationContext + Private m_NetworkAvailabilityChangedCallback As Threading.SendOrPostCallback 'Used for marshalling the network address changed event to the foreground thread + End Class + + '''**************************************************************************************************** + ''';WebClientExtended + ''' + ''' Temporary class used to provide WebClient with a timeout property. + ''' + ''' This class will be deleted when Timeout is added to WebClient + Friend Class WebClientExtended + Inherits WebClient + + '* PUBLIC ***************************************************************************************** + + '''************************************************************************************************ + ''';Timeout + ''' + ''' Sets or indicates the timeout used by WebRequest used by WebClient + ''' + ''' + ''' + Public WriteOnly Property Timeout() As Integer + Set(ByVal value As Integer) + Debug.Assert(value > 0, "illegal value for timeout") + m_Timeout = value + End Set + End Property + '''************************************************************************************************ + ''';UseNonPassiveFtp + ''' + ''' Enables switching the server to non passive mode. + ''' + ''' + ''' We need this in order for the progress UI on a download to work + Public WriteOnly Property UseNonPassiveFtp() As Boolean + Set(ByVal value As Boolean) + m_UseNonPassiveFtp = value + End Set + End Property + + '* PROTECTED ***************************************************************************************** + + '''*************************************************************************************************** + ''';GetWebRequest + ''' + ''' Makes sure that the timeout value for WebRequests (used for all Download and Upload methods) is set + ''' to the Timeout value + ''' + ''' + ''' + ''' + Protected Overrides Function GetWebRequest(ByVal address As System.Uri) As System.Net.WebRequest + Dim request As WebRequest = MyBase.GetWebRequest(address) + + Debug.Assert(request IsNot Nothing, "Unable to get WebRequest from base class") + If request IsNot Nothing Then + request.Timeout = m_Timeout + If m_UseNonPassiveFtp Then + Dim ftpRequest As FtpWebRequest = TryCast(request, FtpWebRequest) + If ftpRequest IsNot Nothing Then + ftpRequest.UsePassive = False + End If + End If + + Dim httpRequest As HttpWebRequest = TryCast(request, HttpWebRequest) + If httpRequest IsNot Nothing Then + httpRequest.AllowAutoRedirect = False + End If + + End If + + Return request + End Function + + '* FRIEND ****************************************************************************************** + + Friend Sub New() + End Sub + + '* PRIVATE ***************************************************************************************** + + ' The Timeout value to be used by Webclient's WebRequest for Downloading or Uploading a file + Private m_Timeout As Integer = 100000 + + ' Flag used to indicate whether or not we should use passive mode when ftp downloading + Private m_UseNonPassiveFtp As Boolean + End Class + +End Namespace diff --git a/Microsoft.VisualBasic/runtime/msvbalib/Devices/Ports.vb b/Microsoft.VisualBasic/runtime/msvbalib/Devices/Ports.vb new file mode 100644 index 000000000..afc5c21c5 --- /dev/null +++ b/Microsoft.VisualBasic/runtime/msvbalib/Devices/Ports.vb @@ -0,0 +1,155 @@ +' Copyright (c) Microsoft Corporation. All rights reserved. +Option Explicit On +Option Strict On + +Imports System +Imports System.Collections +Imports System.Collections.Generic +Imports System.Collections.ObjectModel +Imports System.Diagnostics +Imports System.IO.Ports +Imports System.Security.Permissions +Imports System.Text +Imports Microsoft.VisualBasic.CompilerServices + +Namespace Microsoft.VisualBasic.Devices + + '''************************************************************************* + ''';Ports + ''' + ''' Gives access to Ports on the local machine + ''' + ''' Only serial ports are supported at present, but this class may expand in the future + _ + Public Class Ports + + '==PUBLIC************************************************************** + + '''********************************************************************* + ''';New + ''' + ''' Constructor + ''' + ''' + Public Sub New() + End Sub + + '''********************************************************************* + ''';OpenSerialPort + ''' + ''' Creates an opens a SerialPort object + ''' + ''' The name of the port to open + ''' An opened SerialPort + ''' + ''' We delegate all validation to the fx SerialPort class. We open the port so exceptions will + ''' be thrown as quickly as possible + ''' + Public Function OpenSerialPort(ByVal portName As String) As SerialPort + Dim Port As New SerialPort(portName) + Port.Open() + Return Port + End Function + + '''********************************************************************* + ''';OpenSerialPort + ''' + ''' Creates an opens a SerialPort object + ''' + ''' The name of the port to open + ''' The baud rate of the port + ''' An opened SerialPort + ''' + ''' We delegate all validation to the fx SerialPort class. We open the port so exceptions will + ''' be thrown as quickly as possible + ''' + Public Function OpenSerialPort(ByVal portName As String, ByVal baudRate As Integer) As SerialPort + Dim Port As New SerialPort(portName, baudRate) + Port.Open() + Return Port + End Function + + '''********************************************************************* + ''';OpenSerialPort + ''' + ''' Creates an opens a SerialPort object + ''' + ''' The name of the port to open + ''' The baud rate of the port + ''' The parity of the port + ''' An opened SerialPort + ''' + ''' We delegate all validation to the fx SerialPort class. We open the port so exceptions will + ''' be thrown as quickly as possible + ''' + Public Function OpenSerialPort(ByVal portName As String, ByVal baudRate As Integer, ByVal parity As Parity) As SerialPort + Dim Port As New SerialPort(portName, baudRate, parity) + Port.Open() + Return Port + End Function + + '''********************************************************************* + ''';OpenSerialPort + ''' + ''' Creates an opens a SerialPort object + ''' + ''' The name of the port to open + ''' The baud rate of the port + ''' The parity of the port + ''' The data bits of the port + ''' An opened SerialPort + ''' + ''' We delegate all validation to the fx SerialPort class. We open the port so exceptions will + ''' be thrown as quickly as possible + ''' + Public Function OpenSerialPort(ByVal portName As String, ByVal baudRate As Integer, ByVal parity As Parity, ByVal dataBits As Integer) As SerialPort + Dim Port As New SerialPort(portName, baudRate, parity, dataBits) + Port.Open() + Return Port + End Function + + '''********************************************************************* + ''';OpenSerialPort + ''' + ''' Creates an opens a SerialPort object + ''' + ''' The name of the port to open + ''' The baud rate of the port + ''' The parity of the port + ''' The data bits of the port + ''' The stop bit setting of the port + ''' An opened SerialPort + ''' + ''' We delegate all validation to the fx SerialPort class. We open the port so exceptions will + ''' be thrown as quickly as possible + ''' + Public Function OpenSerialPort(ByVal portName As String, ByVal baudRate As Integer, ByVal parity As Parity, ByVal dataBits As Integer, ByVal stopBits As StopBits) As SerialPort + Dim Port As New SerialPort(portName, baudRate, parity, dataBits, stopBits) + Port.Open() + Return Port + End Function + + '''********************************************************************* + ''';SerialPortNames + ''' + ''' Returns the names of the serial ports on the local machine + ''' + ''' A collection of the names of the serial ports + ''' + Public ReadOnly Property SerialPortNames() As ReadOnlyCollection(Of String) + Get + Dim names() As String = SerialPort.GetPortNames() + Dim namesList As New List(Of String) + + For Each portName As String in names + namesList.Add(portName) + Next + + Return New ReadOnlyCollection(Of String)(namesList) + End Get + End Property + + '==PRIVATE************************************************************* + + End Class +End Namespace diff --git a/Microsoft.VisualBasic/runtime/msvbalib/Devices/ServerComputer.vb b/Microsoft.VisualBasic/runtime/msvbalib/Devices/ServerComputer.vb new file mode 100644 index 000000000..67a5d3834 --- /dev/null +++ b/Microsoft.VisualBasic/runtime/msvbalib/Devices/ServerComputer.vb @@ -0,0 +1,133 @@ +' Copyright (c) Microsoft Corporation. All rights reserved. +Option Strict On +Option Explicit On + +Imports System +Imports System.ComponentModel +Imports System.Security.Permissions +Imports Microsoft.VisualBasic +Imports Microsoft.VisualBasic.MyServices + +Namespace Microsoft.VisualBasic.Devices + + '''************************************************************************** + ''' ;ServerComputer + ''' + ''' A RAD object representing the server 'computer' for the web/Windows Services + ''' that serves as a discovery mechanism for finding principle abstractions in + ''' the system that you can code against + ''' + _ + Public Class ServerComputer + + '= PUBLIC ============================================================= + + 'NOTE: The .Net design guidelines state that access to Instance members does not have to be thread-safe. Access to Shared members does have to be thread-safe. + 'Since My.Computer creates the instance of Computer in a thread-safe way, access to the Computer will necessarily be thread-safe. + 'There is nothing to prevent a user from passing our computer object across threads or creating their own instance and then getting into trouble. + 'But that is completely consistent with the rest of the FX design. It is MY.* that is thread safe and leads to best practice access to these objects. + 'If you dim them up yourself, you are responsible for managing the threading. + + '''************************************************************************** + ''' ;Clock + ''' + ''' Returns the Clock object which contains the LocalTime and GMTTime. + ''' + Public ReadOnly Property Clock() As Clock + Get + If m_Clock IsNot Nothing Then Return m_Clock + m_Clock = New Clock + Return m_Clock + End Get + End Property + + '''************************************************************************** + ''' ;FileSystem + ''' + ''' Gets the object representing the file system of the computer. + ''' + ''' A System.IO.FileSystem object. + ''' The instance returned by this property is lazy initialized and cached. + Public ReadOnly Property FileSystem() As FileSystemProxy + Get + If m_FileIO Is Nothing Then + m_FileIO = New FileSystemProxy + End If + Return m_FileIO + End Get + End Property + + '''************************************************************************** + ''' ;Info + ''' + ''' Gets the object representing information about the computer's state + ''' + ''' A Microsoft.VisualBasic.MyServices.ComputerInfo object. + ''' The instance returned by this property is lazy initialized and cached. + Public ReadOnly Property Info() As ComputerInfo + Get + If m_ComputerInfo Is Nothing Then + m_ComputerInfo = New ComputerInfo + End If + Return m_ComputerInfo + End Get + End Property + + '''************************************************************************** + ''' ;Network + ''' + ''' This property returns the Network object containing information about + ''' the network the machine is part of. + ''' + ''' An instance of the Network.Network class. + Public ReadOnly Property Network() As Network + Get + If m_Network IsNot Nothing Then Return m_Network + m_Network = New Network + Return m_Network + End Get + End Property + + '''************************************************************************** + ''' ;Name + ''' + ''' This property wraps the System.Environment.MachineName property + ''' in the .NET framework to return the name of the computer. + ''' + ''' A string containing the name of the computer. + Public ReadOnly Property Name() As String + Get + Return System.Environment.MachineName + End Get + End Property + + '''************************************************************************** + ''' ;Registry + ''' + ''' Get the Registry object, which can be used to read, set and + ''' enumerate keys and values in the system registry. + ''' + ''' An instance of the RegistryProxy object + ''' + Public ReadOnly Property Registry() As RegistryProxy + Get + If m_RegistryInstance IsNot Nothing Then Return m_RegistryInstance + m_RegistryInstance = New RegistryProxy + Return m_RegistryInstance + End Get + End Property + + '= FRIENDS ============================================================ + + '= PROTECTED ========================================================== + + '= PRIVATE ============================================================ + + Private m_ComputerInfo As ComputerInfo 'Lazy initialized cache for ComputerInfo + Private m_FileIO As FileSystemProxy 'Lazy initialized cache for the FileSystem. + Private m_Network As Network 'Lazy initialized cache for the Network class. + Private m_RegistryInstance As RegistryProxy 'Lazy initialized cache for the Registry class + Private Shared m_Clock As Clock 'Lazy initialized cache for the Clock class. SHARED because Clock behaves as a readonly singleton class + + End Class 'MyServerComputer +End Namespace diff --git a/Microsoft.VisualBasic/runtime/msvbalib/ErrObject.vb b/Microsoft.VisualBasic/runtime/msvbalib/ErrObject.vb new file mode 100644 index 000000000..2d3cf339d --- /dev/null +++ b/Microsoft.VisualBasic/runtime/msvbalib/ErrObject.vb @@ -0,0 +1,671 @@ +' Copyright (c) Microsoft Corporation. All rights reserved. + +Imports Microsoft.VisualBasic.CompilerServices +Imports Microsoft.VisualBasic.CompilerServices.Utils +Imports Microsoft.VisualBasic.CompilerServices.ExceptionUtils + +Imports System +#If TELESTO Then +Imports System.Diagnostics +#End If +Imports System.Runtime.InteropServices +Imports System.Security +Imports System.Security.Permissions +Imports System.Runtime.ConstrainedExecution + +Namespace Microsoft.VisualBasic + + Public NotInheritable Class ErrObject + + ' Error object private values + Private m_curException As Exception + Private m_curErl As Integer + Private m_curNumber As Integer + Private m_curDescription As String + Private m_NumberIsSet As Boolean + Private m_ClearOnCapture As Boolean + Private m_DescriptionIsSet As Boolean + +#If Not TELESTO Then + Private m_curSource As String + Private m_SourceIsSet As Boolean + Private m_curHelpFile As String + Private m_curHelpContext As Integer + Private m_HelpFileIsSet As Boolean + Private m_HelpContextIsSet As Boolean +#End If + + Friend Sub New() + Me.Clear() 'need to do this so the fields are set to Empty string, not Nothing + End Sub + + + '============================================================================ + ' ErrObject functions. + '============================================================================ + Public ReadOnly Property Erl() As Integer + Get + Return m_curErl + End Get + End Property + + + + Public Property Number() As Integer + Get + If m_NumberIsSet Then + Return m_curNumber + End If + + If Not m_curException Is Nothing Then + Me.Number = MapExceptionToNumber(m_curException) + Return m_curNumber + Else + 'The default case. NOTE: falling into the default does not "Set" the property. + 'We only get here if the Err object was previously cleared. + Return 0 + End If + End Get + + Set(ByVal Value As Integer) + m_curNumber = MapErrorNumber(Value) + m_NumberIsSet = True + End Set + End Property + + +#If Not TELESTO Then 'There is no concept of a Source on exceptions in Telesto + Public Property Source() As String + Get + 'Return the current Source if we've already calculated it. + If m_SourceIsSet Then + Return m_curSource + End If + + + If Not m_curException Is Nothing Then + Me.Source = m_curException.Source + Return m_curSource + Else + 'The default case. NOTE: falling into the default does not "Set" the property. + 'We only get here if the Err object was previously cleared. + ' + Return "" + End If + End Get + + Set(ByVal Value As String) + m_curSource = Value + m_SourceIsSet = True + End Set + End Property +#End If 'not TELESTO + + ''' + ''' Determines what the correct error description should be. + ''' If we don't have an exception that we are responding to then + ''' we don't do anything to the message. + ''' If we do have an exception pending, we morph the description + ''' to match the corresponding VB error. + ''' We also special case HRESULT exceptions to map to a VB description + ''' if we have one. + ''' + ''' + ''' + ''' + Private Function FilterDefaultMessage(ByVal Msg As String) As String + Dim NewMsg As String + + 'This is one of the default messages, + If m_curException Is Nothing Then + 'Leave message as is + Return Msg + End If + + Dim tmpNumber As Integer = Me.Number + + If Msg Is Nothing OrElse Msg.Length = 0 Then + Msg = GetResourceString("ID" & CStr(tmpNumber)) + ElseIf System.String.CompareOrdinal("Exception from HRESULT: 0x", 0, Msg, 0, Math.Min(Msg.Length, 26)) = 0 Then + NewMsg = GetResourceString("ID" & CStr(m_curNumber), False) + If Not NewMsg Is Nothing Then + Msg = NewMsg + End If + End If + + Return Msg + End Function + + + Public Property Description() As String + Get + If m_DescriptionIsSet Then + Return m_curDescription + End If + + If Not m_curException Is Nothing Then + Me.Description = FilterDefaultMessage(m_curException.Message) + Return m_curDescription + Else + 'The default case. NOTE: falling into the default does not "Set" the property. + 'We only get here if the Err object was previously cleared. + Return "" + End If + End Get + + Set(ByVal Value As String) + m_curDescription = Value + m_DescriptionIsSet = True + End Set + End Property + +#If Not TELESTO Then 'There is no concept of a HelpFile on exceptions in Telesto + Public Property HelpFile() As String + Get + If m_HelpFileIsSet Then + Return m_curHelpFile + End If + + If Not m_curException Is Nothing Then + ParseHelpLink(m_curException.HelpLink) + Return m_curHelpFile + Else + 'The default case. NOTE: falling into the default does not "Set" the property. + 'We only get here if the Err object was previously cleared. + ' + Return "" + End If + End Get + + Set(ByVal Value As String) + m_curHelpFile = Value + + m_HelpFileIsSet = True + End Set + End Property + + + Private Function MakeHelpLink(ByVal HelpFile As String, ByVal HelpContext As Integer) As String + Return HelpFile & "#" & CStr(HelpContext) + End Function + + Private Sub ParseHelpLink(ByVal HelpLink As String) + + Diagnostics.Debug.Assert((Not m_HelpContextIsSet) OrElse (Not m_HelpFileIsSet), "Why is this getting called?") + + If HelpLink Is Nothing OrElse HelpLink.Length = 0 Then + + If Not m_HelpContextIsSet Then + Me.HelpContext = 0 + End If + If Not m_HelpFileIsSet Then + Me.HelpFile = "" + End If + + Else + + Dim iContext As Integer = m_InvariantCompareInfo.IndexOf(HelpLink, "#", Globalization.CompareOptions.Ordinal) + + If iContext <> -1 Then + If Not m_HelpContextIsSet Then + If iContext < HelpLink.Length Then + Me.HelpContext = CInt(HelpLink.Substring(iContext + 1)) + Else + Me.HelpContext = 0 + End If + End If + If Not m_HelpFileIsSet Then + Me.HelpFile = HelpLink.Substring(0, iContext) + End If + Else + If Not m_HelpContextIsSet Then + Me.HelpContext = 0 + End If + If Not m_HelpFileIsSet Then + Me.HelpFile = HelpLink + End If + End If + + End If + + End Sub + + + + Public Property HelpContext() As Integer + Get + If m_HelpContextIsSet Then + Return m_curHelpContext + End If + + If Not m_curException Is Nothing Then + ParseHelpLink(m_curException.HelpLink) + Return m_curHelpContext + + Else + 'The default case. NOTE: falling into the default does not "Set" the property. + 'We only get here if the Err object was previously cleared. + ' + Return 0 + End If + + Return m_curHelpContext + End Get + + Set(ByVal Value As Integer) + m_curHelpContext = Value + m_HelpContextIsSet = True + End Set + End Property +#End If 'not TELESTO + + Public Function GetException() As Exception + Return m_curException + End Function + + ''' + ''' VB calls clear whenever it executes any type of Resume statement, Exit Sub, Exit funcion, exit Property, or + ''' any On Error statement. + ''' + ''' +#If TELESTO Then + Public Sub Clear() +#Else + _ + _ + Public Sub Clear() + + ' The Try/Finally and constrained regions calls guarantee success under high + ' stress conditions by enabling eager jitting of the finally block + + System.Runtime.CompilerServices.RuntimeHelpers.PrepareConstrainedRegions() +#End If + Try + Finally + 'CONSIDER: do we even care about CLEARING the fields if clearing the flags are enough (aside from m_curException)? + m_curException = Nothing + m_curNumber = 0 +#If Not TELESTO Then + m_curSource = "" + m_curHelpFile = "" + m_curHelpContext = 0 + m_SourceIsSet = False + m_HelpFileIsSet = False + m_HelpContextIsSet = False +#End If + m_curDescription = "" + m_curErl = 0 + m_NumberIsSet = False + m_DescriptionIsSet = False + m_ClearOnCapture = True + End Try + End Sub + +#If TELESTO Then + ''' + ''' This function is called when the Raise code command is executed + ''' + ''' The error code being raised + ''' If not supplied, we try to look one up based on the error code being raised + ''' + Public Sub Raise(ByVal Number As Integer, Optional ByVal Description As Object = Nothing) +#Else + ''' + ''' This function is called when the Raise code command is executed + ''' + ''' The error code being raised + ''' If not supplied we take the name from the assembly + ''' If not supplied, we try to look one up based on the error code being raised + ''' + ''' + ''' + Public Sub Raise(ByVal Number As Integer, _ + Optional ByVal Source As Object = Nothing, _ + Optional ByVal Description As Object = Nothing, _ + Optional ByVal HelpFile As Object = Nothing, _ + Optional ByVal HelpContext As Object = Nothing) +#End If + + If Number = 0 Then + 'This is only called by Raise, so Raise(0) should give the following exception + Throw New ArgumentException(GetResourceString(ResID.Argument_InvalidValue1, "Number")) + End If + Me.Number = Number + +#If Not TELESTO Then 'Telesto doesn't have the notion of Source or Help file links + If Not Source Is Nothing Then + Me.Source = CStr(Source) + Else + Dim vbhost As CompilerServices.IVbHost + vbhost = CompilerServices.HostServices.VBHost + If vbhost Is Nothing Then + Dim FullName As String + Dim CommaPos As Integer + + FullName = System.Reflection.Assembly.GetCallingAssembly().FullName + CommaPos = InStr(FullName, ",") + If CommaPos < 1 Then + Me.Source = FullName + Else + Me.Source = Left(FullName, CommaPos - 1) + End If + Else + Me.Source = vbhost.GetWindowTitle() + End If + End If + + If Not HelpFile Is Nothing Then + Me.HelpFile = CStr(HelpFile) + End If + + If Not HelpContext Is Nothing Then + Me.HelpContext = CInt(HelpContext) + End If +#End If 'not TELESTO + + If Not Description Is Nothing Then + Me.Description = CStr(Description) + ElseIf Not m_DescriptionIsSet Then + 'Set the Description here so the exception object contains the right message + Me.Description = GetResourceString(CType(m_curNumber, vbErrors)) + End If + + Dim e As Exception + e = MapNumberToException(m_curNumber, m_curDescription) +#If Not TELESTO Then + e.Source = m_curSource + e.HelpLink = MakeHelpLink(m_curHelpFile, m_curHelpContext) +#End If + m_ClearOnCapture = False + Throw e + End Sub + +#If Not TELESTO Then ' DevDiv Bugs 117407 - Remove LastDllError from Silverlight. + ReadOnly Property LastDllError() As Integer + _ + Get + Return Marshal.GetLastWin32Error() + End Get + End Property +#End If + + Friend Sub SetUnmappedError(ByVal Number As Integer) + Me.Clear() + Me.Number = Number + m_ClearOnCapture = False + End Sub + + + + 'a function like this that can be used by the runtime to generate errors which will also do a clear would be nice. + Friend Function CreateException(ByVal Number As Integer, ByVal Description As String) As System.Exception + Me.Clear() + Me.Number = Number + + If Number = 0 Then + 'This is only called by Error xxxx, zero is not a valid exception number + Throw New ArgumentException(GetResourceString(ResID.Argument_InvalidValue1, "Number")) + End If + + Dim e As Exception = MapNumberToException(m_curNumber, Description) + m_ClearOnCapture = False + Return e + End Function + +#If TELESTO Then + 'No ReliabilityContract defined on Telesto + _ + Friend Overloads Sub CaptureException(ByVal ex As Exception) +#Else + _ + _ + Friend Overloads Sub CaptureException(ByVal ex As Exception) + + ' The Try/Finally and constrained regions calls guarantee success under high + ' stress conditions by enabling eager jitting of the finally block + System.Runtime.CompilerServices.RuntimeHelpers.PrepareConstrainedRegions() +#End If + + Try + Finally + 'if we've already captured this exception, then we're done + If ex IsNot m_curException Then + If m_ClearOnCapture Then + Me.Clear() + Else + m_ClearOnCapture = True 'False only used once - set this flag back to the default + End If + m_curException = ex + End If + End Try + End Sub + +#If TELESTO Then + 'No ReliabilityContract defined on Telesto + _ + Friend Overloads Sub CaptureException(ByVal ex As Exception, ByVal lErl As Integer) +#Else + _ + _ + Friend Overloads Sub CaptureException(ByVal ex As Exception, ByVal lErl As Integer) + + ' The Try/Finally and constrained regions calls guarantee success under high stress conditions by enabling eager jitting of the finally block + System.Runtime.CompilerServices.RuntimeHelpers.PrepareConstrainedRegions() +#End If + Try + Finally + CaptureException(ex) + m_curErl = lErl 'This is the only place where the line number can be set + End Try + End Sub + + + Private Function MapExceptionToNumber(ByVal e As Exception) As Integer +#If TELESTO Then + Debug.Assert(e IsNot Nothing, "Exception shouldn't be Nothing") +#Else + Diagnostics.Debug.Assert(e IsNot Nothing, "Exception shouldn't be Nothing") +#End If + Dim typ As Type = e.GetType() + + If typ Is GetType(System.IndexOutOfRangeException) Then + Return vbErrors.OutOfBounds + ElseIf typ Is GetType(System.RankException) Then + Return vbErrors.OutOfBounds + ElseIf typ Is GetType(System.DivideByZeroException) Then + Return vbErrors.DivByZero + ElseIf typ Is GetType(System.OverflowException) Then + Return vbErrors.Overflow + ElseIf typ Is GetType(System.NotFiniteNumberException) Then + Dim exNotFiniteNumber As NotFiniteNumberException = CType(e, NotFiniteNumberException) +#If Not TELESTO Then + If exNotFiniteNumber.OffendingNumber = 0 Then + Return vbErrors.DivByZero + Else + Return vbErrors.Overflow + End If +#Else + 'Telesto doesn't have the OffendingNumbermember on System.NotFiniteNumberException. + 'Which seems like a good thing because the value is zero by default if the exception isn't initially constructed + 'by passing in the offending value. And we already have a divide by zero exception... So I think it is a 'code improvement' + 'to get rid of logic based on what is in OffendingNumber. + Return vbErrors.Overflow +#End If + ElseIf typ Is GetType(System.NullReferenceException) Then + Return vbErrors.ObjNotSet + ElseIf TypeOf e Is System.AccessViolationException Then + Return vbErrors.AccessViolation + ElseIf typ Is GetType(System.InvalidCastException) Then + Return vbErrors.TypeMismatch + ElseIf typ Is GetType(System.NotSupportedException) Then + Return vbErrors.TypeMismatch +#If Not TELESTO Then + ElseIf typ Is GetType(System.Runtime.InteropServices.COMException) Then + Dim comex As COMException = CType(e, COMException) + Return CompilerServices.Utils.MapHRESULT(comex.ErrorCode) +#End If + ElseIf typ Is GetType(System.Runtime.InteropServices.SEHException) Then + Return vbErrors.DLLCallException + ElseIf typ Is GetType(System.DllNotFoundException) Then + Return vbErrors.FileNotFound + ElseIf typ Is GetType(System.EntryPointNotFoundException) Then + Return vbErrors.InvalidDllFunctionName + ' + 'Must fall after EntryPointNotFoundException because of inheritance + ' + ElseIf typ Is GetType(System.TypeLoadException) Then + Return vbErrors.CantCreateObject + ElseIf typ Is GetType(System.OutOfMemoryException) Then + Return vbErrors.OutOfMemory + ElseIf typ Is GetType(System.FormatException) Then + Return vbErrors.TypeMismatch + ElseIf typ Is GetType(System.IO.DirectoryNotFoundException) Then + Return vbErrors.PathNotFound + ElseIf typ Is GetType(System.IO.IOException) Then + Return vbErrors.IOError + ElseIf typ Is GetType(System.IO.FileNotFoundException) Then + Return vbErrors.FileNotFound + ElseIf TypeOf e Is MissingMemberException Then + Return vbErrors.OLENoPropOrMethod +#If Not TELESTO Then + ElseIf TypeOf e Is Runtime.InteropServices.InvalidOleVariantTypeException Then + Return vbErrors.InvalidTypeLibVariable +#End If + Else + Return vbErrors.IllegalFuncCall 'Generic error + End If + + End Function + + + + Private Function MapNumberToException(ByVal Number As Integer, _ + ByVal Description As String) As System.Exception + Return ExceptionUtils.BuildException(Number, Description, False) + End Function + + + + Friend Function MapErrorNumber(ByVal Number As Integer) As Integer + If Number > 65535 Then + ' Number cannot be greater than 65535. + Throw New ArgumentException(GetResourceString(ResID.Argument_InvalidValue1, "Number")) + End If + + If Number >= 0 Then + Return Number + End If + + 'strip off top two bytes if FACILITY_CONTROL is set + If (Number And SCODE_FACILITY) = FACILITY_CONTROL Then + Return (Number And &HFFFFI) + End If + + Select Case Number + + ' FACILITY_NULL errors + Case E_NOTIMPL : Return vbErrors.ActionNotSupported '&H80004001 + Case E_NOINTERFACE : Return vbErrors.OLENotSupported '&H80004002 + Case E_POINTER : Return vbErrors.AccessViolation '&H80004003 + Case E_ABORT : Return vbErrors.Abort '&H80004004 + + ' FACILITY_DISPATCH - IDispatch errors. + Case DISP_E_UNKNOWNINTERFACE : Return vbErrors.OLENoPropOrMethod '&H80020001 + Case DISP_E_MEMBERNOTFOUND : Return vbErrors.OLENoPropOrMethod '&H80020003 + Case DISP_E_PARAMNOTFOUND : Return vbErrors.NamedParamNotFound '&H80020004 + Case DISP_E_TYPEMISMATCH : Return vbErrors.TypeMismatch '&H80020005 + Case DISP_E_UNKNOWNNAME : Return vbErrors.OLENoPropOrMethod '&H80020006 + Case DISP_E_NONAMEDARGS : Return vbErrors.NamedArgsNotSupported '&H80020007 + Case DISP_E_BADVARTYPE : Return vbErrors.InvalidTypeLibVariable '&H80020008 + Case DISP_E_OVERFLOW : Return vbErrors.Overflow '&H8002000A + Case DISP_E_BADINDEX : Return vbErrors.OutOfBounds '&H8002000B + Case DISP_E_UNKNOWNLCID : Return vbErrors.LocaleSettingNotSupported '&H8002000C + Case DISP_E_ARRAYISLOCKED : Return vbErrors.ArrayLocked '&H8002000D + Case DISP_E_BADPARAMCOUNT : Return vbErrors.FuncArityMismatch '&H8002000E + Case DISP_E_PARAMNOTOPTIONAL : Return vbErrors.ParameterNotOptional '&H8002000F + Case DISP_E_NOTACOLLECTION : Return vbErrors.NotEnum '&H80020011 + Case DISP_E_DIVBYZERO : Return vbErrors.DivByZero '&H80020012 + + ' FACILITY_DISPATCH - Typelib errors + Case TYPE_E_BUFFERTOOSMALL : Return vbErrors.BufferTooSmall '&H80028016 + Case &H80028017I : Return vbErrors.IdentNotMember '&H80028017 + Case TYPE_E_INVDATAREAD : Return vbErrors.InvDataRead '&H80028018 + Case TYPE_E_UNSUPFORMAT : Return vbErrors.UnsupFormat '&H80028019 + Case TYPE_E_REGISTRYACCESS : Return vbErrors.RegistryAccess '&H8002801C + Case TYPE_E_LIBNOTREGISTERED : Return vbErrors.LibNotRegistered '&H8002801D + Case TYPE_E_UNDEFINEDTYPE : Return vbErrors.UndefinedType '&H80028027 + Case TYPE_E_QUALIFIEDNAMEDISALLOWED : Return vbErrors.QualifiedNameDisallowed '&H80028028 + Case TYPE_E_INVALIDSTATE : Return vbErrors.InvalidState '&H80028029 + Case TYPE_E_WRONGTYPEKIND : Return vbErrors.WrongTypeKind '&H8002802A + Case TYPE_E_ELEMENTNOTFOUND : Return vbErrors.ElementNotFound '&H8002802B + Case TYPE_E_AMBIGUOUSNAME : Return vbErrors.AmbiguousName '&H8002802C + Case TYPE_E_NAMECONFLICT : Return vbErrors.ModNameConflict '&H8002802D + Case TYPE_E_UNKNOWNLCID : Return vbErrors.UnknownLcid '&H8002802E + Case TYPE_E_DLLFUNCTIONNOTFOUND : Return vbErrors.InvalidDllFunctionName '&H8002802F + Case TYPE_E_BADMODULEKIND : Return vbErrors.BadModuleKind '&H800288BD + Case TYPE_E_SIZETOOBIG : Return vbErrors.SizeTooBig '&H800288C5 + Case TYPE_E_TYPEMISMATCH : Return vbErrors.TypeMismatch '&H80028CA0 + Case TYPE_E_OUTOFBOUNDS : Return vbErrors.OutOfBounds '&H80028CA1 + Case TYPE_E_IOERROR : Return vbErrors.IOError '&H80028CA2 + Case TYPE_E_CANTCREATETMPFILE : Return vbErrors.CantCreateTmpFile '&H80028CA3 + Case TYPE_E_CANTLOADLIBRARY : Return vbErrors.DLLLoadErr '&H80029C4A + Case TYPE_E_INCONSISTENTPROPFUNCS : Return vbErrors.InconsistentPropFuncs '&H80029C83 + Case TYPE_E_CIRCULARTYPE : Return vbErrors.CircularType '&H80029C84 + + ' FACILITY_STORAGE errors + Case STG_E_INVALIDFUNCTION : Return vbErrors.BadFunctionId '&H80030001 + Case STG_E_FILENOTFOUND : Return vbErrors.FileNotFound '&H80030002 + Case STG_E_PATHNOTFOUND : Return vbErrors.PathNotFound '&H80030003 + Case STG_E_TOOMANYOPENFILES : Return vbErrors.TooManyFiles '&H80030004 + Case STG_E_ACCESSDENIED : Return vbErrors.PermissionDenied '&H80030005 + Case STG_E_INVALIDHANDLE : Return vbErrors.ReadFault '&H80030006 + Case STG_E_INSUFFICIENTMEMORY : Return vbErrors.OutOfMemory '&H80030008 + Case STG_E_NOMOREFILES : Return vbErrors.TooManyFiles '&H80030012 + Case STG_E_DISKISWRITEPROTECTED : Return vbErrors.PermissionDenied '&H80030013 + Case STG_E_SEEKERROR : Return vbErrors.SeekErr '&H80030019 + Case STG_E_WRITEFAULT : Return vbErrors.WriteFault '&H8003001D + Case STG_E_READFAULT : Return vbErrors.ReadFault '&H8003001E + Case STG_E_SHAREVIOLATION : Return vbErrors.PathFileAccess '&H80030020 + Case STG_E_LOCKVIOLATION : Return vbErrors.PermissionDenied '&H80030021 + Case STG_E_FILEALREADYEXISTS : Return vbErrors.FileAlreadyExists '&H80030050 + Case STG_E_MEDIUMFULL : Return vbErrors.DiskFull '&H80030070 + Case STG_E_INVALIDHEADER : Return vbErrors.InvDataRead '&H800300FB + Case STG_E_INVALIDNAME : Return vbErrors.FileNotFound '&H800300FC + Case STG_E_UNKNOWN : Return vbErrors.InvDataRead '&H800300FD + Case STG_E_UNIMPLEMENTEDFUNCTION : Return vbErrors.NotYetImplemented '&H800300FE + Case STG_E_INUSE : Return vbErrors.PermissionDenied '&H80030100 + Case STG_E_NOTCURRENT : Return vbErrors.PermissionDenied '&H80030101 + Case STG_E_REVERTED : Return vbErrors.WriteFault '&H80030102 + Case STG_E_CANTSAVE : Return vbErrors.IOError '&H80030103 + Case STG_E_OLDFORMAT : Return vbErrors.UnsupFormat '&H80030104 + Case STG_E_OLDDLL : Return vbErrors.UnsupFormat '&H80030105 + Case STG_E_SHAREREQUIRED : Return vbErrors.ShareRequired '&H80030106 + Case STG_E_NOTFILEBASEDSTORAGE : Return vbErrors.UnsupFormat '&H80030107 + Case STG_E_EXTANTMARSHALLINGS : Return vbErrors.UnsupFormat '&H80030108 + + ' FACILITY_ITF errors. + Case CLASS_E_NOTLICENSED : Return vbErrors.CantCreateObject '&H80040112 + Case REGDB_E_CLASSNOTREG : Return vbErrors.CantCreateObject '&H80040154 + Case MK_E_UNAVAILABLE : Return vbErrors.CantCreateObject '&H800401E3 + Case MK_E_INVALIDEXTENSION : Return vbErrors.OLEFileNotFound '&H800401E6 + Case MK_E_CANTOPENFILE : Return vbErrors.OLEFileNotFound '&H800401EA + Case CO_E_CLASSSTRING : Return vbErrors.CantCreateObject '&H800401F3 + Case CO_E_APPNOTFOUND : Return vbErrors.CantCreateObject '&H800401F5 + Case CO_E_APPDIDNTREG : Return vbErrors.CantCreateObject '&H800401FE + + ' FACILITY_WIN32 errors + Case E_ACCESSDENIED : Return vbErrors.PermissionDenied '&H80070005 + Case E_OUTOFMEMORY : Return vbErrors.OutOfMemory '&H8007000E + Case E_INVALIDARG : Return vbErrors.IllegalFuncCall '&H80070057 + Case &H800706BAI : Return vbErrors.ServerNotFound '&H800706BA + + ' FACILITY_WINDOWS - I don't know why this differs from FACILITY_WIN32 + Case CO_E_SERVER_EXEC_FAILURE : Return vbErrors.CantCreateObject '&H80080005 + + Case Else + Return Number + End Select + End Function + + End Class +End Namespace diff --git a/Microsoft.VisualBasic/runtime/msvbalib/FileIO/FileSystem.vb b/Microsoft.VisualBasic/runtime/msvbalib/FileIO/FileSystem.vb new file mode 100644 index 000000000..b70af3930 --- /dev/null +++ b/Microsoft.VisualBasic/runtime/msvbalib/FileIO/FileSystem.vb @@ -0,0 +1,2797 @@ +' Copyright (c) Microsoft Corporation. All rights reserved. +Option Strict On +Option Explicit On + +Imports System +Imports System.Collections +Imports System.Collections.Specialized +Imports System.ComponentModel +Imports System.Diagnostics +Imports System.Globalization +Imports System.Security.Permissions +Imports System.Security +Imports System.Runtime.InteropServices +Imports System.Runtime.Versioning +Imports System.Text + +Imports Microsoft.VisualBasic +Imports Microsoft.VisualBasic.CompilerServices +Imports Microsoft.VisualBasic.CompilerServices.Utils +Imports Microsoft.VisualBasic.CompilerServices.NativeMethods +Imports Microsoft.VisualBasic.CompilerServices.NativeTypes +Imports ExUtils = Microsoft.VisualBasic.CompilerServices.ExceptionUtils + +'''' IMPORTANT: Changes made to public interface of FileSystem should be reflected in FileSystemProxy.vb. + +Namespace Microsoft.VisualBasic.FileIO + + + '''****************************************************************************** + ''' ;FileSystem + ''' + ''' This class represents the file system on a computer. It allows browsing the existing drives, special directories; + ''' and also contains some commonly use methods for IO tasks. + ''' + _ + Public Class FileSystem + + '= PUBLIC ============================================================= + + '== PROPERTIES ======================================================== + + + '''****************************************************************************** + ''' ;Drives + ''' + ''' Return the names of all available drives on the computer. + ''' + ''' A ReadOnlyCollection(Of DriveInfo) containing all the current drives' names. + Public Shared ReadOnly Property Drives() As ObjectModel.ReadOnlyCollection(Of System.IO.DriveInfo) + Get + ' NOTE: Don't cache the collection since it may change without us knowing. + ' The performance hit will be small since it's a small collection. + ' CONSIDER: : Create a read-only collection from an array? + Dim DriveInfoCollection As New ObjectModel.Collection(Of System.IO.DriveInfo) + For Each DriveInfo As System.IO.DriveInfo In IO.DriveInfo.GetDrives() + DriveInfoCollection.Add(DriveInfo) + Next + Return New ObjectModel.ReadOnlyCollection(Of System.IO.DriveInfo)(DriveInfoCollection) + End Get + End Property + + + '''************************************************************************** + ''' ;CurrentDirectory + ''' + ''' Get or set the current working directory. + ''' + ''' A String containing the path to the directory. + Public Shared Property CurrentDirectory() As String + Get + Return NormalizePath(IO.Directory.GetCurrentDirectory()) + End Get + Set(ByVal value As String) + IO.Directory.SetCurrentDirectory(value) + End Set + End Property + + + '== FUNCTIONS ========================================================= + + + '''************************************************************************** + ''' ;CombinePath + ''' + ''' Combines two path strings by adding a path separator. + ''' + ''' The first part of the path. + ''' The second part of the path, must be a relative path. + ''' A String contains the combined path. + Public Shared Function CombinePath(ByVal baseDirectory As String, ByVal relativePath As String) As String + + ' VSWhidbey 258686. + If baseDirectory = "" Then + Throw ExUtils.GetArgumentNullException("baseDirectory", ResID.MyID.General_ArgumentEmptyOrNothing_Name, "baseDirectory") + End If + If relativePath = "" Then + Return baseDirectory + End If + + baseDirectory = IO.Path.GetFullPath(baseDirectory) ' Throw exceptions if BaseDirectoryPath is invalid. + + Return NormalizePath(IO.Path.Combine(baseDirectory, relativePath)) + End Function + + + '''************************************************************************** + ''' ;DirectoryExists + ''' + ''' Determines whether the given path refers to an existing directory on disk. + ''' + ''' The path to verify. + ''' True if DirectoryPath refers to an existing directory. Otherwise, False. + Public Shared Function DirectoryExists(ByVal directory As String) As Boolean + Return IO.Directory.Exists(directory) + End Function + + + '''************************************************************************** + ''' ;FileExists + ''' + ''' Determines whether the given path refers to an existing file on disk. + ''' + ''' The path to verify. + ''' True if FilePath refers to an existing file on disk. Otherwise, False. + Public Shared Function FileExists(ByVal file As String) As Boolean + If Not String.IsNullOrEmpty(file) AndAlso _ + (file.EndsWith(IO.Path.DirectorySeparatorChar, StringComparison.OrdinalIgnoreCase) Or _ + file.EndsWith(IO.Path.AltDirectorySeparatorChar, StringComparison.OrdinalIgnoreCase)) Then + Return False + End If + + Return IO.File.Exists(file) + End Function + + + '''************************************************************************** + ''' ;FindInFiles + ''' + ''' Find files in the given folder that contain the given text. + ''' + ''' The folder path to start from. + ''' The text to be found in file. + ''' True to ignore case. Otherwise, False. + ''' SearchAllSubDirectories to find recursively. Otherwise, SearchTopLevelOnly. + ''' A string array containing the files that match the search condition. + Public Shared Function FindInFiles(ByVal directory As String, _ + ByVal containsText As String, ByVal ignoreCase As Boolean, ByVal searchType As SearchOption) As ObjectModel.ReadOnlyCollection(Of String) + Return FindInFiles(directory, containsText, ignoreCase, searchType, Nothing) + End Function + + '''************************************************************************** + ''' ;FindInFiles + ''' + ''' Find files in the given folder that contain the given text. + ''' + ''' The folder path to start from. + ''' The text to be found in file. + ''' True to ignore case. Otherwise, False. + ''' SearchAllSubDirectories to find recursively. Otherwise, SearchTopLevelOnly. + ''' The search patterns to use for the file name ("*.*") + ''' A string array containing the files that match the search condition. + ''' If one of the pattern is Null, Empty or all-spaces string. + Public Shared Function FindInFiles(ByVal directory As String, ByVal containsText As String, ByVal ignoreCase As Boolean, _ + ByVal searchType As SearchOption, ByVal ParamArray fileWildcards() As String) As ObjectModel.ReadOnlyCollection(Of String) + + ' Find the files with matching name. + Dim NameMatchFiles As ObjectModel.ReadOnlyCollection(Of String) = FindFilesOrDirectories( _ + FileOrDirectory.File, directory, searchType, fileWildcards) + + ' Find the files containing the given text. + If containsText <> "" Then + Dim ContainTextFiles As New ObjectModel.Collection(Of String) + For Each FilePath As String In NameMatchFiles + If (FileContainsText(FilePath, containsText, ignoreCase)) Then + ContainTextFiles.Add(FilePath) + End If + Next + Return New ObjectModel.ReadOnlyCollection(Of String)(ContainTextFiles) + Else + Return NameMatchFiles + End If + End Function + + + '''************************************************************************** + ''' ;GetDirectories + ''' + ''' Return the paths of sub directories found directly under a directory. + ''' + ''' The directory to find the sub directories inside. + ''' A ReadOnlyCollection(Of String) containing the matched directories' paths. + Public Shared Function GetDirectories(ByVal directory As String) As ObjectModel.ReadOnlyCollection(Of String) + + Return FindFilesOrDirectories(FileOrDirectory.Directory, directory, SearchOption.SearchTopLevelOnly, Nothing) + End Function + + '''************************************************************************** + ''' ;GetDirectories + ''' + ''' Return the paths of sub directories found under a directory with the specified name patterns. + ''' + ''' The directory to find the sub directories inside. + ''' SearchAllSubDirectories to find recursively. Otherwise, SearchTopLevelOnly. + ''' The wildcards for the file name, for example "*.bmp", "*.txt" + ''' A ReadOnlyCollection(Of String) containing the matched directories' paths. + Public Shared Function GetDirectories(ByVal directory As String, ByVal searchType As SearchOption, _ + ByVal ParamArray wildcards() As String) As ObjectModel.ReadOnlyCollection(Of String) + + Return FindFilesOrDirectories(FileOrDirectory.Directory, directory, searchType, wildcards) + End Function + + + '''************************************************************************** + ''' ;GetDirectoryInfo + ''' + ''' Returns the information object about the specifed directory. + ''' + ''' The path to the directory. + ''' A DirectoryInfo object containing the information about the specified directory. + Public Shared Function GetDirectoryInfo(ByVal directory As String) As System.IO.DirectoryInfo + Return New System.IO.DirectoryInfo(directory) + End Function + + + '''************************************************************************** + ''' ;GetDriveInfo + ''' + ''' Return the information about the specified drive. + ''' + ''' The path to the drive. + ''' A DriveInfo object containing the information about the specified drive. + Public Shared Function GetDriveInfo(ByVal drive As String) As System.IO.DriveInfo + Return New System.IO.DriveInfo(drive) + End Function + + + '''************************************************************************** + ''' ;GetFileInfo + ''' + ''' Returns the information about the specified file. + ''' + ''' The path to the file. + ''' A FileInfo object containing the information about the specified file. + Public Shared Function GetFileInfo(ByVal file As String) As System.IO.FileInfo + file = NormalizeFilePath(file, "file") + Return New System.IO.FileInfo(file) + End Function + + + '''************************************************************************** + ''' ;GetFiles + ''' + ''' Return a collection of file paths found directly under a directory. + ''' + ''' The directory to find the files inside. + ''' A ReadOnlyCollection(Of String) containing the matched files' paths. + Public Shared Function GetFiles(ByVal directory As String) As ObjectModel.ReadOnlyCollection(Of String) + Return FindFilesOrDirectories(FileOrDirectory.File, directory, SearchOption.SearchTopLevelOnly, Nothing) + End Function + + '''************************************************************************** + ''' ;GetFiles + ''' + ''' Return a collection of file paths found under a directory with the specified name patterns and containing the specified text. + ''' + ''' The directory to find the files inside. + ''' SearchAllSubDirectories to find recursively. Otherwise, SearchTopLevelOnly. + ''' The wildcards for the file name, for example "*.bmp", "*.txt" + ''' A ReadOnlyCollection(Of String) containing the matched files' paths. + Public Shared Function GetFiles(ByVal directory As String, ByVal searchType As SearchOption, _ + ByVal ParamArray wildcards() As String) As ObjectModel.ReadOnlyCollection(Of String) + + Return FindFilesOrDirectories(FileOrDirectory.File, directory, searchType, wildcards) + End Function + + + '''************************************************************************** + ''' ;GetName + ''' + ''' Return the name (and extension) from the given path string. + ''' + ''' The path string from which to obtain the file name (and extension). + ''' A String containing the name of the file or directory. + ''' path contains one or more of the invalid characters defined in InvalidPathChars. + Public Shared Function GetName(ByVal path As String) As String + Return IO.Path.GetFileName(path) + End Function + + + '''************************************************************************** + ''' ;GetParentPath + ''' + ''' Returns the parent directory's path from a specified path. + ''' + ''' The path to a file or directory, this can be absolute or relative. + ''' + ''' The path to the parent directory of that file or directory (whether absolute or relative depends on the input), + ''' or an empty string if Path is a root directory. + ''' + ''' See IO.Path.GetFullPath: If path is an invalid path. + ''' + ''' The path will be normalized (for example: C:\Dir1////\\\Dir2 will become C:\Dir1\Dir2) + ''' but will not be resolved (for example: C:\Dir1\Dir2\..\Dir3 WILL NOT become C:\Dir1\Dir3). Use CombinePath. + ''' + Public Shared Function GetParentPath(ByVal path As String) As String + ' Call IO.Path.GetFullPath to handle exception cases. Don't use the full path returned. + IO.Path.GetFullPath(path) + + If IsRoot(path) Then + Throw ExUtils.GetArgumentExceptionWithArgName("path", ResID.MyID.IO_GetParentPathIsRoot_Path, path) + Else + Return IO.Path.GetDirectoryName(path.TrimEnd( _ + IO.Path.DirectorySeparatorChar, IO.Path.AltDirectorySeparatorChar)) + End If + End Function + + + '''************************************************************************** + ''' ;GetTempFileName + ''' + ''' Create a uniquely named zero-byte temporary file on disk and return the full path to that file. + ''' + ''' A String containing the name of the temporary file. + Public Shared Function GetTempFileName() As String + Return System.IO.Path.GetTempFileName() + End Function + + + '''************************************************************************** + ''' ;OpenTextFieldParser + ''' + ''' Return an instance of a TextFieldParser for the given file. + ''' + ''' The path to the file to parse. + ''' An instance of a TextFieldParser. + Public Shared Function OpenTextFieldParser(ByVal file As String) As TextFieldParser + Return New TextFieldParser(file) + End Function + + '''************************************************************************** + ''' ;OpenTextFieldParser + ''' + ''' Return an instance of a TextFieldParser for the given file using the given delimiters. + ''' + ''' The path to the file to parse. + ''' A list of delimiters. + ''' An instance of a TextFieldParser + Public Shared Function OpenTextFieldParser(ByVal file As String, ByVal ParamArray delimiters As String()) As TextFieldParser + Dim Result As New TextFieldParser(file) + Result.SetDelimiters(delimiters) + Result.TextFieldType = FieldType.Delimited + Return Result + End Function + + '''************************************************************************** + ''' ;OpenTextFieldParser + ''' + ''' Return an instance of a TextFieldParser for the given file using the given field widths. + ''' + ''' The path to the file to parse. + ''' A list of field widths. + ''' An instance of a TextFieldParser + Public Shared Function OpenTextFieldParser(ByVal file As String, ByVal ParamArray fieldWidths As Integer()) As TextFieldParser + Dim Result As New TextFieldParser(file) + Result.SetFieldWidths(fieldWidths) + Result.TextFieldType = FieldType.FixedWidth + Return Result + End Function + + + '''************************************************************************** + ''' ;OpenTextFieldParser + ''' + ''' Return a StreamReader for reading the given file using UTF-8 as prefered encoding. + ''' + ''' The file to open the StreamReader on. + ''' An instance of System.IO.StreamReader opened on the file (with FileShare.Read). + Public Shared Function OpenTextFileReader(ByVal file As String) As IO.StreamReader + Return OpenTextFileReader(file, Encoding.UTF8) + End Function + + '''************************************************************************** + ''' ;OpenTextFileReader + ''' + ''' Return a StreamReader for reading the given file using the given encoding as prefered encoding. + ''' + ''' The file to open the StreamReader on. + ''' The prefered encoding that will be used if the encoding of the file could not be detected. + ''' An instance of System.IO.StreamReader opened on the file (with FileShare.Read). + Public Shared Function OpenTextFileReader(ByVal file As String, ByVal encoding As Encoding) As IO.StreamReader + + file = NormalizeFilePath(file, "file") + Return New IO.StreamReader(file, encoding, detectEncodingFromByteOrderMarks:=True) + End Function + + + '''************************************************************************** + ''' ;OpenTextFileWriter + ''' + ''' Return a StreamWriter for writing to the given file using UTF-8 encoding. + ''' + ''' The file to write to. + ''' True to append to the content of the file. False to overwrite the content of the file. + ''' An instance of StreamWriter opened on the file (with FileShare.Read). + Public Shared Function OpenTextFileWriter(ByVal file As String, ByVal append As Boolean) As IO.StreamWriter + Return OpenTextFileWriter(file, append, Encoding.UTF8) + End Function + + '''************************************************************************** + ''' ;OpenTextFileWriter + ''' + ''' Return a StreamWriter for writing to the given file using the given encoding. + ''' + ''' The file to write to. + ''' True to append to the content of the file. False to overwrite the content of the file. + ''' The encoding to use to write to the file. + ''' An instance of StreamWriter opened on the file (with FileShare.Read). + Public Shared Function OpenTextFileWriter(ByVal file As String, ByVal append As Boolean, _ + ByVal encoding As Encoding) As IO.StreamWriter + + file = NormalizeFilePath(file, "file") + Return New IO.StreamWriter(file, append, encoding) + End Function + + + '''************************************************************************** + ''' ;ReadAllBytes + ''' + ''' Read the whole content of a file into a byte array. + ''' + ''' The path to the file. + ''' A byte array contains the content of the file. + ''' If the length of the file is larger than Integer.MaxValue (~2GB). + ''' See FileStream constructor and Read: for other exceptions. + Public Shared Function ReadAllBytes(ByVal file As String) As Byte() + Return IO.File.ReadAllBytes(file) + End Function + + + '''************************************************************************** + ''' ;ReadAllText + ''' + ''' Read the whole content of a text file into a string using UTF-8 encoding. + ''' + ''' The path to the text file. + ''' A String contains the content of the given file. + ''' See StreamReader constructor and ReadToEnd. + Public Shared Function ReadAllText(ByVal file As String) As String + Return IO.File.ReadAllText(file) + End Function + + '''************************************************************************** + ''' ;ReadAllText + ''' + ''' Read the whole content of a text file into a string using the given encoding. + ''' + ''' The path to the text file. + ''' The character encoding to use if the encoding was not detected. + ''' A String contains the content of the given file. + ''' See StreamReader constructor and ReadToEnd. + Public Shared Function ReadAllText(ByVal file As String, ByVal encoding As Encoding) As String + Return IO.File.ReadAllText(file, encoding) + End Function + + + '== METHODS =========================================================== + + + '''************************************************************************** + ''' ;CopyDirectory + ''' + ''' Copy an existing directory to a new directory, + ''' throwing exception if there are existing files with the same name. + ''' + ''' The path to the source directory, can be relative or absolute. + ''' The path to the target directory, can be relative or absolute. Parent directory will always be created. + _ + _ + Public Shared Sub CopyDirectory(ByVal sourceDirectoryName As String, ByVal destinationDirectoryName As String) + CopyOrMoveDirectory(CopyOrMove.Copy, sourceDirectoryName, destinationDirectoryName, _ + False, UIOptionInternal.NoUI, UICancelOption.ThrowException) + End Sub + + '''************************************************************************** + ''' ;CopyDirectory + ''' + ''' Copy an existing directory to a new directory, + ''' overwriting existing files with the same name if specified. + ''' + ''' The path to the source directory, can be relative or absolute. + ''' The path to the target directory, can be relative or absolute. Parent directory will always be created. + ''' True to overwrite existing files with the same name. Otherwise False. + _ + _ + Public Shared Sub CopyDirectory(ByVal sourceDirectoryName As String, ByVal destinationDirectoryName As String, ByVal overwrite As Boolean) + CopyOrMoveDirectory(CopyOrMove.Copy, sourceDirectoryName, destinationDirectoryName, _ + overwrite, UIOptionInternal.NoUI, UICancelOption.ThrowException) + End Sub + + '''************************************************************************** + ''' ;CopyDirectory + ''' + ''' Copy an existing directory to a new directory, + ''' displaying progress dialog and confirmation dialogs if specified, + ''' throwing exception if user cancels the operation (only applies if displaying progress dialog and confirmation dialogs). + ''' + ''' The path to the source directory, can be relative or absolute. + ''' The path to the target directory, can be relative or absolute. Parent directory will always be created. + ''' ShowDialogs to display progress and confirmation dialogs. Otherwise HideDialogs. + _ + _ + Public Shared Sub CopyDirectory(ByVal sourceDirectoryName As String, ByVal destinationDirectoryName As String, ByVal showUI As UIOption) + CopyOrMoveDirectory(CopyOrMove.Copy, sourceDirectoryName, destinationDirectoryName, _ + False, ToUIOptionInternal(showUI), UICancelOption.ThrowException) + End Sub + + '''************************************************************************** + ''' ;CopyDirectory + ''' + ''' Copy an existing directory to a new directory, + ''' displaying progress dialog and confirmation dialogs if specified, + ''' throwing exception if user cancels the operation if specified. (only applies if displaying progress dialog and confirmation dialogs). + ''' + ''' The path to the source directory, can be relative or absolute. + ''' The path to the target directory, can be relative or absolute. Parent directory will always be created. + ''' ShowDialogs to display progress and confirmation dialogs. Otherwise HideDialogs. + ''' ThrowException to throw exception if user cancels the operation. Otherwise DoNothing. + _ + _ + Public Shared Sub CopyDirectory(ByVal sourceDirectoryName As String, ByVal destinationDirectoryName As String, ByVal showUI As UIOption, ByVal onUserCancel As UICancelOption) + CopyOrMoveDirectory(CopyOrMove.Copy, sourceDirectoryName, destinationDirectoryName, _ + False, ToUIOptionInternal(showUI), onUserCancel) + End Sub + + + '''************************************************************************** + ''' ;CopyFile + ''' + ''' Copy an existing file to a new file. Overwriting a file of the same name is not allowed. + ''' + ''' The path to the source file, can be relative or absolute. + ''' The path to the destination file, can be relative or absolute. Parent directory will always be created. + _ + _ + Public Shared Sub CopyFile(ByVal sourceFileName As String, ByVal destinationFileName As String) + CopyOrMoveFile(CopyOrMove.Copy, sourceFileName, destinationFileName, _ + False, UIOptionInternal.NoUI, UICancelOption.ThrowException) + End Sub + + '''************************************************************************** + ''' ;CopyFile + ''' + ''' Copy an existing file to a new file. Overwriting a file of the same name if specified. + ''' + ''' The path to the source file, can be relative or absolute. + ''' The path to the destination file, can be relative or absolute. Parent directory will always be created. + ''' True to overwrite existing file with the same name. Otherwise False. + _ + _ + Public Shared Sub CopyFile(ByVal sourceFileName As String, ByVal destinationFileName As String, ByVal overwrite As Boolean) + CopyOrMoveFile(CopyOrMove.Copy, sourceFileName, destinationFileName, _ + overwrite, UIOptionInternal.NoUI, UICancelOption.ThrowException) + End Sub + + '''************************************************************************** + ''' ;CopyFile + ''' + ''' Copy an existing file to a new file, + ''' displaying progress dialog and confirmation dialogs if specified, + ''' will throw exception if user cancels the operation. + ''' + ''' The path to the source file, can be relative or absolute. + ''' The path to the destination file, can be relative or absolute. Parent directory will always be created. + ''' ShowDialogs to display progress and confirmation dialogs. Otherwise HideDialogs. + _ + _ + Public Shared Sub CopyFile(ByVal sourceFileName As String, ByVal destinationFileName As String, ByVal showUI As UIOption) + CopyOrMoveFile(CopyOrMove.Copy, sourceFileName, destinationFileName, _ + False, ToUIOptionInternal(showUI), UICancelOption.ThrowException) + End Sub + + '''************************************************************************** + ''' ;CopyFile + ''' + ''' Copy an existing file to a new file, + ''' displaying progress dialog and confirmation dialogs if specified, + ''' will throw exception if user cancels the operation if specified. + ''' + ''' The path to the source file, can be relative or absolute. + ''' The path to the destination file, can be relative or absolute. Parent directory will always be created. + ''' ShowDialogs to display progress and confirmation dialogs. Otherwise HideDialogs. + ''' ThrowException to throw exception if user cancels the operation. Otherwise DoNothing. + ''' onUserCancel will be ignored if showUI = HideDialogs. + _ + _ + Public Shared Sub CopyFile(ByVal sourceFileName As String, ByVal destinationFileName As String, ByVal showUI As UIOption, ByVal onUserCancel As UICancelOption) + CopyOrMoveFile(CopyOrMove.Copy, sourceFileName, destinationFileName, _ + False, ToUIOptionInternal(showUI), onUserCancel) + End Sub + + + '''************************************************************************** + ''' ;CreateDirectory + ''' + ''' Creates a directory from the given path (including all parent directories). + ''' + ''' The path to create the directory at. + Public Shared Sub CreateDirectory(ByVal directory As String) + ' Get the full path. GetFullPath will throw if invalid path. + directory = IO.Path.GetFullPath(directory) + + If IO.File.Exists(directory) Then ' CONSIDER: : Pending on VSWhidbey 104049. + Throw ExUtils.GetIOException(ResID.MyID.IO_FileExists_Path, directory) + End If + + ' CreateDirectory will create the full structure and not throw if directory exists. + System.IO.Directory.CreateDirectory(directory) + End Sub + + + '''************************************************************************** + ''' ;DeleteDirectory + ''' + ''' Delete the given directory, with options to recursively delete. + ''' + ''' The path to the directory. + ''' DeleteAllContents to delete everything. ThrowIfDirectoryNonEmpty to throw exception if the directory is not empty. + _ + _ + Public Shared Sub DeleteDirectory(ByVal directory As String, ByVal onDirectoryNotEmpty As DeleteDirectoryOption) + DeleteDirectoryInternal(directory, onDirectoryNotEmpty, _ + UIOptionInternal.NoUI, RecycleOption.DeletePermanently, UICancelOption.ThrowException) + End Sub + + '''************************************************************************** + ''' ;DeleteDirectory + ''' + ''' Delete the given directory, with options to recursively delete, show progress UI, send file to Recycle Bin; throwing exception if user cancels. + ''' + ''' The path to the directory. + ''' True to shows progress window. Otherwise, False. + ''' SendToRecycleBin to delete to Recycle Bin. Otherwise DeletePermanently. + _ + _ + Public Shared Sub DeleteDirectory(ByVal directory As String, ByVal showUI As UIOption, ByVal recycle As RecycleOption) + DeleteDirectoryInternal(directory, DeleteDirectoryOption.DeleteAllContents, _ + ToUIOptionInternal(showUI), recycle, UICancelOption.ThrowException) + End Sub + + '''************************************************************************** + ''' ;DeleteDirectory + ''' + ''' Delete the given directory, with options to recursively delete, show progress UI, send file to Recycle Bin, and whether to throw exception if user cancels. + ''' + ''' The path to the directory. + ''' ShowDialogs to display progress and confirmation dialogs. Otherwise HideDialogs. + ''' SendToRecycleBin to delete to Recycle Bin. Otherwise DeletePermanently. + ''' Throw exception when user cancel the UI operation or not. + _ + _ + Public Shared Sub DeleteDirectory(ByVal directory As String, _ + ByVal showUI As UIOption, ByVal recycle As RecycleOption, ByVal onUserCancel As UICancelOption) + DeleteDirectoryInternal(directory, DeleteDirectoryOption.DeleteAllContents, _ + ToUIOptionInternal(showUI), recycle, onUserCancel) + End Sub + + + '''************************************************************************** + ''' ;DeleteFile + ''' + ''' Delete the given file. + ''' + ''' The path to the file. + _ + _ + Public Shared Sub DeleteFile(ByVal file As String) + DeleteFileInternal(file, UIOptionInternal.NoUI, RecycleOption.DeletePermanently, UICancelOption.ThrowException) + End Sub + + '''************************************************************************** + ''' ;DeleteFile + ''' + ''' Delete the given file, with options to show progress UI, delete to recycle bin. + ''' + ''' The path to the file. + ''' ShowDialogs to display progress and confirmation dialogs. Otherwise HideDialogs. + ''' SendToRecycleBin to delete to Recycle Bin. Otherwise DeletePermanently. + _ + _ + Public Shared Sub DeleteFile(ByVal file As String, ByVal showUI As UIOption, ByVal recycle As RecycleOption) + DeleteFileInternal(file, ToUIOptionInternal(showUI), recycle, UICancelOption.ThrowException) + End Sub + + '''************************************************************************** + ''' ;DeleteFile + ''' + ''' Delete the given file, with options to show progress UI, delete to recycle bin, and whether to throw exception if user cancels. + ''' + ''' The path to the file. + ''' ShowDialogs to display progress and confirmation dialogs. Otherwise HideDialogs. + ''' SendToRecycleBin to delete to Recycle Bin. Otherwise DeletePermanently. + ''' Throw exception when user cancel the UI operation or not. + ''' IO.Path.GetFullPath() exceptions: if FilePath is invalid. + ''' if a file does not exist at FilePath + _ + _ + Public Shared Sub DeleteFile(ByVal file As String, ByVal showUI As UIOption, ByVal recycle As RecycleOption, _ + ByVal onUserCancel As UICancelOption) + + DeleteFileInternal(file, ToUIOptionInternal(showUI), recycle, onUserCancel) + End Sub + + + '''************************************************************************** + ''' ;MoveDirectory + ''' + ''' Move an existing directory to a new directory, + ''' throwing exception if there are existing files with the same name. + ''' + ''' The path to the source directory, can be relative or absolute. + ''' The path to the target directory, can be relative or absolute. Parent directory will always be created. + _ + _ + Public Shared Sub MoveDirectory(ByVal sourceDirectoryName As String, ByVal destinationDirectoryName As String) + CopyOrMoveDirectory(CopyOrMove.Move, sourceDirectoryName, destinationDirectoryName, _ + False, UIOptionInternal.NoUI, UICancelOption.ThrowException) + End Sub + + '''************************************************************************** + ''' ;MoveDirectory + ''' + ''' Move an existing directory to a new directory, + ''' overwriting existing files with the same name if specified. + ''' + ''' The path to the source directory, can be relative or absolute. + ''' The path to the target directory, can be relative or absolute. Parent directory will always be created. ''' True to overwrite existing files with the same name. Otherwise False. + _ + _ + Public Shared Sub MoveDirectory(ByVal sourceDirectoryName As String, ByVal destinationDirectoryName As String, ByVal overwrite As Boolean) + CopyOrMoveDirectory(CopyOrMove.Move, sourceDirectoryName, destinationDirectoryName, _ + overwrite, UIOptionInternal.NoUI, UICancelOption.ThrowException) + End Sub + + '''************************************************************************** + ''' ;MoveDirectory + ''' + ''' Move an existing directory to a new directory, + ''' displaying progress dialog and confirmation dialogs if specified, + ''' throwing exception if user cancels the operation (only applies if displaying progress dialog and confirmation dialogs). + ''' + ''' The path to the source directory, can be relative or absolute. + ''' The path to the target directory, can be relative or absolute. Parent directory will always be created. + ''' ShowDialogs to display progress and confirmation dialogs. Otherwise HideDialogs. + _ + _ + Public Shared Sub MoveDirectory(ByVal sourceDirectoryName As String, ByVal destinationDirectoryName As String, ByVal showUI As UIOption) + CopyOrMoveDirectory(CopyOrMove.Move, sourceDirectoryName, destinationDirectoryName, _ + False, ToUIOptionInternal(showUI), UICancelOption.ThrowException) + End Sub + + '''************************************************************************** + ''' ;MoveDirectory + ''' + ''' Move an existing directory to a new directory, + ''' displaying progress dialog and confirmation dialogs if specified, + ''' throwing exception if user cancels the operation if specified. (only applies if displaying progress dialog and confirmation dialogs). + ''' + ''' The path to the source directory, can be relative or absolute. + ''' The path to the target directory, can be relative or absolute. Parent directory will always be created. + ''' ShowDialogs to display progress and confirmation dialogs. Otherwise HideDialogs. + ''' ThrowException to throw exception if user cancels the operation. Otherwise DoNothing. + _ + _ + Public Shared Sub MoveDirectory(ByVal sourceDirectoryName As String, ByVal destinationDirectoryName As String, ByVal showUI As UIOption, ByVal onUserCancel As UICancelOption) + CopyOrMoveDirectory(CopyOrMove.Move, sourceDirectoryName, destinationDirectoryName, _ + False, ToUIOptionInternal(showUI), onUserCancel) + End Sub + + + '''************************************************************************** + ''' ;MoveFile + ''' + ''' Move an existing file to a new file. Overwriting a file of the same name is not allowed. + ''' + ''' The path to the source file, can be relative or absolute. + ''' The path to the destination file, can be relative or absolute. Parent directory will always be created. + _ + _ + Public Shared Sub MoveFile(ByVal sourceFileName As String, ByVal destinationFileName As String) + CopyOrMoveFile(CopyOrMove.Move, sourceFileName, destinationFileName, _ + False, UIOptionInternal.NoUI, UICancelOption.ThrowException) + End Sub + + '''************************************************************************** + ''' ;MoveFile + ''' + ''' Move an existing file to a new file. Overwriting a file of the same name if specified. + ''' + ''' The path to the source file, can be relative or absolute. + ''' The path to the destination file, can be relative or absolute. Parent directory will always be created. + ''' True to overwrite existing file with the same name. Otherwise False. + _ + _ + Public Shared Sub MoveFile(ByVal sourceFileName As String, ByVal destinationFileName As String, ByVal overwrite As Boolean) + CopyOrMoveFile(CopyOrMove.Move, sourceFileName, destinationFileName, _ + overwrite, UIOptionInternal.NoUI, UICancelOption.ThrowException) + End Sub + + '''************************************************************************** + ''' ;MoveFile + ''' + ''' Move an existing file to a new file, + ''' displaying progress dialog and confirmation dialogs if specified, + ''' will throw exception if user cancels the operation. + ''' + ''' The path to the source file, can be relative or absolute. + ''' The path to the destination file, can be relative or absolute. Parent directory will always be created. + ''' ShowDialogs to display progress and confirmation dialogs. Otherwise HideDialogs. + _ + _ + Public Shared Sub MoveFile(ByVal sourceFileName As String, ByVal destinationFileName As String, ByVal showUI As UIOption) + CopyOrMoveFile(CopyOrMove.Move, sourceFileName, destinationFileName, _ + False, ToUIOptionInternal(showUI), UICancelOption.ThrowException) + End Sub + + '''************************************************************************** + ''' ;MoveFile + ''' + ''' Move an existing file to a new file, + ''' displaying progress dialog and confirmation dialogs if specified, + ''' will throw exception if user cancels the operation if specified. + ''' + ''' The path to the source file, can be relative or absolute. + ''' The path to the destination file, can be relative or absolute. Parent directory will always be created. + ''' ShowDialogs to display progress and confirmation dialogs. Otherwise HideDialogs. + ''' ThrowException to throw exception if user cancels the operation. Otherwise DoNothing. + ''' onUserCancel will be ignored if showUI = HideDialogs. + _ + _ + Public Shared Sub MoveFile(ByVal sourceFileName As String, ByVal destinationFileName As String, ByVal showUI As UIOption, ByVal onUserCancel As UICancelOption) + CopyOrMoveFile(CopyOrMove.Move, sourceFileName, destinationFileName, _ + False, ToUIOptionInternal(showUI), onUserCancel) + End Sub + + + '''************************************************************************** + ''' ;RenameDirectory + ''' + ''' Rename a directory, does not act like a move. + ''' + ''' The path of the directory to be renamed. + ''' The new name to change to. This must not contain path information. + ''' IO.Path.GetFullPath exceptions: If directory is invalid. + ''' If newName is Nothing or Empty String or contains path information. + ''' If directory does not point to an existing directory. + ''' If directory points to a root directory. + ''' Or if there's an existing directory or an existing file with the same name. + Public Shared Sub RenameDirectory(ByVal directory As String, ByVal newName As String) + ' Get the full path. This will handle invalid path exceptions. + directory = IO.Path.GetFullPath(directory) + ' Throw if device path. + ThrowIfDevicePath(directory) + + ' Directory is a root directory. This does not require IO access so it's cheaper up front. + If IsRoot(directory) Then + Throw ExUtils.GetIOException(ResID.MyID.IO_DirectoryIsRoot_Path, directory) + End If + + ' Throw if directory does not exist. + If Not IO.Directory.Exists(directory) Then + Throw ExUtils.GetDirectoryNotFoundException(ResID.MyID.IO_DirectoryNotFound_Path, directory) + End If + + ' Verify newName is not null. + If newName = "" Then + Throw ExUtils.GetArgumentNullException( _ + "newName", ResID.MyID.General_ArgumentEmptyOrNothing_Name, "newName") + End If + + ' Calculate new path. GetFullPathFromNewName will verify newName is only a name. + Dim FullNewPath As String = GetFullPathFromNewName(GetParentPath(directory), newName, "newName") + Debug.Assert(GetParentPath(FullNewPath).Equals(GetParentPath(directory), _ + StringComparison.OrdinalIgnoreCase), "Invalid FullNewPath!!!") + + ' Verify that the new path does not conflict. + EnsurePathNotExist(FullNewPath) + + IO.Directory.Move(directory, FullNewPath) + End Sub + + + '''************************************************************************** + ''' ;RenameFile + ''' + ''' Renames a file, does not change the file location. + ''' + ''' The path to the file. + ''' The new name to change to. This must not contain path information. + ''' IO.Path.GetFullPath exceptions: If file is invalid. + ''' If newName is Nothing or Empty String or contains path information. + ''' If file does not point to an existing file. + ''' If there's an existing directory or an existing file with the same name. + Public Shared Sub RenameFile(ByVal file As String, ByVal newName As String) + ' Get the full path. This will handle invalid path exceptions. + file = NormalizeFilePath(file, "file") + ' Throw if device path. + ThrowIfDevicePath(file) + + ' Throw if file does not exist. + If Not IO.File.Exists(file) Then + Throw ExUtils.GetFileNotFoundException(file, ResID.MyID.IO_FileNotFound_Path, file) + End If + + ' Verify newName is not null. + If newName = "" Then + Throw ExUtils.GetArgumentNullException( _ + "newName", ResID.MyID.General_ArgumentEmptyOrNothing_Name, "newName") + End If + + ' Calculate new path. GetFullPathFromNewName will verify that newName is only a name. + Dim FullNewPath As String = GetFullPathFromNewName(GetParentPath(file), newName, "newName") + Debug.Assert(GetParentPath(FullNewPath).Equals(GetParentPath(file), _ + StringComparison.OrdinalIgnoreCase), "Invalid FullNewPath!!!") + + ' Verify that the new path does not conflict. + EnsurePathNotExist(FullNewPath) + + IO.File.Move(file, FullNewPath) + End Sub + + + '''************************************************************************** + ''' ;WriteAllBytes + ''' + ''' Overwrites or appends the specified byte array to the specified file, + ''' creating the file if it does not exist. + ''' + ''' The path to the file. + ''' The byte array to write to the file. + ''' True to append the text to the existing content. False to overwrite the existing content. + ''' See FileStream constructor and Write: For other exceptions. + Public Shared Sub WriteAllBytes(ByVal file As String, ByVal data() As Byte, ByVal append As Boolean) + + ' VSWhidbey 445570: Cannot call through IO.File.WriteAllBytes (since they don't support append) + ' so only check for trailing separator as specified in VSWhidbey 372980. + CheckFilePathTrailingSeparator(file, "file") + + Dim FileStream As IO.FileStream = Nothing + Try + Dim IOFileMode As IO.FileMode + If append Then + IOFileMode = IO.FileMode.Append + Else + IOFileMode = IO.FileMode.Create ' CreateNew or Truncate. + End If + + FileStream = New IO.FileStream(file, _ + Mode:=IOFileMode, access:=IO.FileAccess.Write, share:=IO.FileShare.Read) + FileStream.Write(data, 0, data.Length) + Finally + If Not FileStream Is Nothing Then + FileStream.Close() + End If + End Try + End Sub + + + '''************************************************************************** + ''' ;WriteAllText + ''' + ''' Overwrites or appends the given text using UTF-8 encoding to the given file, + ''' creating the file if it does not exist. + ''' + ''' The path to the file. + ''' The text to write to the file. + ''' True to append the text to the existing content. False to overwrite the existing content. + ''' See StreamWriter constructor and Write: For other exceptions. + Public Shared Sub WriteAllText(ByVal file As String, ByVal text As String, ByVal append As Boolean) + WriteAllText(file, text, append, Encoding.UTF8) + End Sub + + '''************************************************************************** + ''' ;WriteAllText + ''' + ''' Overwrites or appends the given text using the given encoding to the given file, + ''' creating the file if it does not exist. + ''' + ''' The path to the file. + ''' The text to write to the file. + ''' True to append the text to the existing content. False to overwrite the existing content. + ''' The encoding to use. + ''' See StreamWriter constructor and Write: For other exceptions. + Public Shared Sub WriteAllText(ByVal file As String, ByVal text As String, ByVal append As Boolean, _ + ByVal encoding As Encoding) + + ' VSWhidbey 445570: Cannot call through IO.File.WriteAllText (since they don't support: append, prefer current encoding than specified one) + ' so only check for trailing separator as specified in VSWhidbey 372980. + CheckFilePathTrailingSeparator(file, "file") + + Dim StreamWriter As IO.StreamWriter = Nothing + Try + ' If appending to a file and it exists, attempt to detect the current encoding and use it (VSWhidbey 199224). + If append AndAlso IO.File.Exists(file) Then + Dim StreamReader As IO.StreamReader = Nothing + Try + StreamReader = New IO.StreamReader(file, encoding, detectEncodingFromByteOrderMarks:=True) + Dim Chars(10 - 1) As Char + StreamReader.Read(Chars, 0, 10) ' Read the next 10 characters to activate auto detect encoding. + encoding = StreamReader.CurrentEncoding ' Set encoding to the detected encoding. + Catch ex As IO.IOException + ' Ignore IOException. + Finally + If StreamReader IsNot Nothing Then + StreamReader.Close() + End If + End Try + End If + + ' StreamWriter uses FileShare.Read by default. + StreamWriter = New IO.StreamWriter(file, append, encoding) + StreamWriter.Write(text) + Finally + If Not StreamWriter Is Nothing Then + StreamWriter.Close() + End If + End Try + End Sub + + + '= FRIEND ============================================================= + + '''************************************************************************** + ''' ;NormalizeFilePath + ''' + ''' Normalize the path, but throw exception if the path ends with separator. + ''' + ''' The input path. + ''' The parameter name to include in the exception if one is raised. + ''' The normalized path. + ''' VSWhidbey 372980. + Friend Shared Function NormalizeFilePath(ByVal Path As String, ByVal ParamName As String) As String + CheckFilePathTrailingSeparator(Path, ParamName) + Return NormalizePath(Path) + End Function + + '''************************************************************************** + ''' ;NormalizePath + ''' + ''' Get full path, get long format, and remove any pending separator. + ''' + ''' The path to be normalized. + ''' The normalized path. + ''' See IO.Path.GetFullPath for possible exceptions. + ''' Keep this function since we might change the implementaion / behavior later. + Friend Shared Function NormalizePath(ByVal Path As String) As String + Return GetLongPath(RemoveEndingSeparator(IO.Path.GetFullPath(Path))) + End Function + + '''************************************************************************** + ''' ;CheckFilePathTrailingSeparator + ''' + ''' Throw ArgumentException if the file path ends with a separator. (VSWhidbey 372980). + ''' + ''' The file path. + ''' The parameter name to include in ArgumentException. + Friend Shared Sub CheckFilePathTrailingSeparator(ByVal path As String, ByVal paramName As String) + If path = "" Then ' Check for argument null - VSWhidbey 452078. + Throw ExUtils.GetArgumentNullException(paramName) + End If + If path.EndsWith(IO.Path.DirectorySeparatorChar, StringComparison.Ordinal) Or _ + path.EndsWith(IO.Path.AltDirectorySeparatorChar, StringComparison.Ordinal) Then + Throw ExUtils.GetArgumentExceptionWithArgName(paramName, ResID.MyID.IO_FilePathException) + End If + End Sub + + '= PRIVATE ============================================================ + + + ''' ************************************************************************** + ''' ;AddToStringCollection + ''' + ''' Add an array of string into a Generic Collection of String. + ''' + Private Shared Sub AddToStringCollection(ByVal StrCollection As ObjectModel.Collection(Of String), ByVal StrArray() As String) + ' CONSIDER: : BCL to support adding an array of string directly into a generic string collection? + Debug.Assert(StrCollection IsNot Nothing, "StrCollection is NULL!!!") + + If StrArray IsNot Nothing Then + For Each Str As String In StrArray + If Not StrCollection.Contains(Str) Then + StrCollection.Add(Str) + End If + Next + End If + End Sub + + + '''************************************************************************** + ''' ;CopyOrMoveDirectory + ''' + ''' Handles exception cases and calls shell or framework to copy / move directory. + ''' + ''' select Copy or Move operation. + ''' the source directory + ''' the target directory + ''' overwrite files + ''' calls into shell to copy / move directory + ''' throw exception if user cancels the operation or not. + ''' IO.Path.GetFullPath exceptions: If SourceDirectoryPath or TargetDirectoryPath is invalid. + ''' Or if NewName contains path information. + ''' If Source or Target is device path (\\.\). + ''' Source directory does not exist as a directory. + ''' If NewName = "". + ''' SourceDirectoryPath and TargetDirectoryPath are the same. + ''' IOException: Target directory is under source directory - cyclic operation. + ''' IOException: TargetDirectoryPath points to an existing file. + ''' IOException: Some files and directories can not be copied. + _ + _ + _ + Private Shared Sub CopyOrMoveDirectory(ByVal operation As CopyOrMove, _ + ByVal sourceDirectoryName As String, ByVal destinationDirectoryName As String, _ + ByVal overwrite As Boolean, ByVal showUI As UIOptionInternal, ByVal onUserCancel As UICancelOption) + + Debug.Assert(System.Enum.IsDefined(GetType(CopyOrMove), operation), "Invalid Operation!!!") + + ' Verify enums. + VerifyUICancelOption("onUserCancel", onUserCancel) + + ' Get the full path and remove any separators at the end. This will handle invalid path exceptions. + ' IMPORTANT: sourceDirectoryName and destinationDirectoryName should be used for exception throwing ONLY. + Dim SourceDirectoryFullPath As String = NormalizePath(sourceDirectoryName) + Dim TargetDirectoryFullPath As String = NormalizePath(destinationDirectoryName) + + ' Demand FileIOPermission on the given path. See CopyOrMoveFile for reason why we don't wait for Framework to demand. + Dim sourceAccess As FileIOPermissionAccess = FileIOPermissionAccess.Read + If operation = CopyOrMove.Move Then + sourceAccess = sourceAccess Or FileIOPermissionAccess.Write + End If + DemandDirectoryPermission(SourceDirectoryFullPath, sourceAccess) + ' Copy / Move Directory will create the target directory. + ' Therefore we also demand Read permission on target path. This will also fix VSWhidbey 421158. + DemandDirectoryPermission(TargetDirectoryFullPath, FileIOPermissionAccess.Read Or FileIOPermissionAccess.Write) + + ' Throw if device path. + ThrowIfDevicePath(SourceDirectoryFullPath) + ThrowIfDevicePath(TargetDirectoryFullPath) + + ' Throw if source directory does not exist. + If Not IO.Directory.Exists(SourceDirectoryFullPath) Then + Throw ExUtils.GetDirectoryNotFoundException(ResID.MyID.IO_DirectoryNotFound_Path, sourceDirectoryName) + End If + + ' Throw if source directory is a root directory. + If IsRoot(SourceDirectoryFullPath) Then + Throw ExUtils.GetIOException(ResID.MyID.IO_DirectoryIsRoot_Path, sourceDirectoryName) + End If + + ' Throw if there's a file at TargetDirectoryFullPath. + If IO.File.Exists(TargetDirectoryFullPath) Then + Throw ExUtils.GetIOException(ResID.MyID.IO_FileExists_Path, destinationDirectoryName) + End If + + ' Throw if source and target are the same. + If TargetDirectoryFullPath.Equals(SourceDirectoryFullPath, StringComparison.OrdinalIgnoreCase) Then + Throw ExUtils.GetIOException(ResID.MyID.IO_SourceEqualsTargetDirectory) + End If + + ' Throw if cyclic operation (target is under source). A sample case is + ' Source = C:\Dir1\Dir2 + ' Target = C:\Dir1\Dir2\Dir3\Dir4. + ' NOTE: Do not use StartWith since it does not allow specifying InvariantCultureIgnoreCase. + If TargetDirectoryFullPath.Length > SourceDirectoryFullPath.Length AndAlso _ + TargetDirectoryFullPath.Substring(0, SourceDirectoryFullPath.Length).Equals( _ + SourceDirectoryFullPath, StringComparison.OrdinalIgnoreCase) Then + Debug.Assert(TargetDirectoryFullPath.Length > SourceDirectoryFullPath.Length, "Target path should be longer!!!") + + ' Bug fix VSWhidbey 74249: don't throw in case Target = C:\Dir1\Dir2SomethingElse. + If TargetDirectoryFullPath.Chars(SourceDirectoryFullPath.Length) = IO.Path.DirectorySeparatorChar Then + Throw ExUtils.GetInvalidOperationException(ResID.MyID.IO_CyclicOperation) + End If + End If + + ' NOTE: Decision to create target directory is different for Shell and Framework call. + + If showUI <> UIOptionInternal.NoUI AndAlso Environment.UserInteractive Then + ' If ShowUI AND UserInteractive (VSWhidbey 230265), attempt to call Shell function. + ShellCopyOrMove(operation, FileOrDirectory.Directory, SourceDirectoryFullPath, TargetDirectoryFullPath, showUI, onUserCancel) + Else + ' Otherwise, copy the directory using System.IO. + FxCopyOrMoveDirectory(operation, SourceDirectoryFullPath, TargetDirectoryFullPath, overwrite) + End If + End Sub + + '''****************************************************************************** + ''' ;FxCopyOrMoveDirectory + ''' + ''' Copies or moves the directory using Framework. + ''' + ''' Copy or Move. + ''' Source path - must be full path. + ''' Target path - must be full path. + ''' True to overwrite the files. Otherwise, False. + ''' Some files or directories cannot be copied or moved. + _ + _ + Private Shared Sub FxCopyOrMoveDirectory(ByVal operation As CopyOrMove, _ + ByVal sourceDirectoryPath As String, ByVal targetDirectoryPath As String, ByVal overwrite As Boolean) + + Debug.Assert(System.Enum.IsDefined(GetType(CopyOrMove), operation), "Invalid Operation!!!") + Debug.Assert(sourceDirectoryPath <> "" And IO.Path.IsPathRooted(sourceDirectoryPath), "Invalid Source!!!") + Debug.Assert(targetDirectoryPath <> "" And IO.Path.IsPathRooted(targetDirectoryPath), "Invalid Target!!!") + + ' Special case for moving: If target directory does not exist, AND both directories are on same drive, + ' use IO.Directory.Move for performance gain (not copying). + If operation = CopyOrMove.Move And Not IO.Directory.Exists(targetDirectoryPath) And _ + IsOnSameDrive(sourceDirectoryPath, targetDirectoryPath) Then + + ' Create the target's parent. IO.Directory.CreateDirectory won't throw if it exists. + IO.Directory.CreateDirectory(GetParentPath(targetDirectoryPath)) + + Try + IO.Directory.Move(sourceDirectoryPath, targetDirectoryPath) + Exit Sub + Catch ex As IO.IOException + Catch ex As UnauthorizedAccessException + ' VSWhidbey 289537: Ignore IO.Directory.Move specific exceptions here. Try to do as much as possible later. + End Try + End If + + ' Create the target, create the root node, and call the recursive function. + System.IO.Directory.CreateDirectory(targetDirectoryPath) + Debug.Assert(IO.Directory.Exists(targetDirectoryPath), "Should be able to create Target Directory!!!") + + Dim SourceDirectoryNode As New DirectoryNode(sourceDirectoryPath, targetDirectoryPath) + Dim Exceptions As New ListDictionary + CopyOrMoveDirectoryNode(operation, SourceDirectoryNode, overwrite, Exceptions) + + ' Throw the final exception if there were exceptions during copy / move. + If Exceptions.Count > 0 Then + Dim IOException As New IO.IOException(GetResourceString(ResID.MyID.IO_CopyMoveRecursive)) + For Each Entry As DictionaryEntry In Exceptions + IOException.Data.Add(Entry.Key, Entry.Value) + Next + Throw IOException + End If + End Sub + + '''****************************************************************************** + ''' ;CopyOrMoveDirectoryNode + ''' + ''' Given a directory node, copy or move that directory tree. + ''' + ''' Specify whether to move or copy the directories. + ''' The source node. Only copy / move directories contained in the source node. + ''' True to overwrite sub-files. Otherwise False. + ''' The list of accumulated exceptions while doing the copy / move + _ + _ + Private Shared Sub CopyOrMoveDirectoryNode(ByVal Operation As CopyOrMove, _ + ByVal SourceDirectoryNode As DirectoryNode, ByVal Overwrite As Boolean, ByVal Exceptions As ListDictionary) + + Debug.Assert(System.Enum.IsDefined(GetType(CopyOrMove), Operation), "Invalid Operation!!!") + Debug.Assert(Exceptions IsNot Nothing, "Null exception list!!!") + Debug.Assert(SourceDirectoryNode IsNot Nothing, "Null source node!!!") + + ' Create the target directory. If we encounter known exceptions, add the exception to the exception list and quit. + Try + If Not IO.Directory.Exists(SourceDirectoryNode.TargetPath) Then + IO.Directory.CreateDirectory(SourceDirectoryNode.TargetPath) + End If + Catch ex As Exception + If (TypeOf ex Is IO.IOException OrElse TypeOf ex Is UnauthorizedAccessException OrElse _ + TypeOf ex Is IO.DirectoryNotFoundException OrElse TypeOf ex Is NotSupportedException OrElse _ + TypeOf ex Is SecurityException) Then + Exceptions.Add(SourceDirectoryNode.Path, ex.Message) + Exit Sub + Else + Throw + End If + End Try + Debug.Assert(IO.Directory.Exists(SourceDirectoryNode.TargetPath), "TargetPath should have existed or exception should be thrown!!!") + If Not IO.Directory.Exists(SourceDirectoryNode.TargetPath) Then + Exceptions.Add(SourceDirectoryNode.TargetPath, ExUtils.GetDirectoryNotFoundException(ResID.MyID.IO_DirectoryNotFound_Path, SourceDirectoryNode.TargetPath)) + Exit Sub + End If + + ' Copy / move all the files under this directory to target directory. + For Each SubFilePath As String In IO.Directory.GetFiles(SourceDirectoryNode.Path) + Try + CopyOrMoveFile(Operation, SubFilePath, IO.Path.Combine(SourceDirectoryNode.TargetPath, IO.Path.GetFileName(SubFilePath)), _ + Overwrite, UIOptionInternal.NoUI, UICancelOption.ThrowException) + Catch ex As Exception + If (TypeOf ex Is IO.IOException OrElse TypeOf ex Is UnauthorizedAccessException OrElse _ + TypeOf ex Is SecurityException OrElse TypeOf ex Is NotSupportedException) Then + Exceptions.Add(SubFilePath, ex.Message) + Else + Throw + End If + End Try + Next + + ' Copy / move all the sub directories under this directory to target directory. + For Each SubDirectoryNode As DirectoryNode In SourceDirectoryNode.SubDirs + CopyOrMoveDirectoryNode(Operation, SubDirectoryNode, Overwrite, Exceptions) + Next + + ' If this is a move, try to delete the current directory. + ' Using recursive:=False since we expect the content should be emptied by now. + If Operation = CopyOrMove.Move Then + Try + IO.Directory.Delete(SourceDirectoryNode.Path, recursive:=False) + Catch ex As Exception + If (TypeOf ex Is IO.IOException OrElse TypeOf ex Is UnauthorizedAccessException OrElse _ + TypeOf ex Is SecurityException OrElse TypeOf ex Is IO.DirectoryNotFoundException) Then + Exceptions.Add(SourceDirectoryNode.Path, ex.Message) + Else + Throw + End If + End Try + End If + End Sub + + + '''************************************************************************** + ''' ;CopyOrMoveFile + ''' + ''' Copies or move files. This will be called from CopyFile and MoveFile. + ''' + ''' Copy or Move. + ''' Path to source file. + ''' Path to target file. + ''' True = Overwrite. This flag will be ignored if ShowUI. + ''' Hide or show the UIDialogs. + ''' Throw exception in case user cancel using UI or not. + ''' + ''' IO.Path.GetFullPath exceptions: If SourceFilePath or TargetFilePath is invalid. + ''' ArgumentException: If Source or Target is device path (\\.\). + ''' FileNotFoundException: If SourceFilePath does not exist (including pointing to an existing directory). + ''' IOException: If TargetFilePath points to an existing directory. + ''' ArgumenNullException: If NewName = "". + ''' ArgumentException: If NewName contains path information. + ''' + _ + _ + _ + Private Shared Sub CopyOrMoveFile(ByVal operation As CopyOrMove, _ + ByVal sourceFileName As String, ByVal destinationFileName As String, _ + ByVal overwrite As Boolean, ByVal showUI As UIOptionInternal, ByVal onUserCancel As UICancelOption _ + ) + Debug.Assert(System.Enum.IsDefined(GetType(CopyOrMove), operation), "Invalid Operation!!!") + + ' Verify enums. + VerifyUICancelOption("onUserCancel", onUserCancel) + + ' Get the full path and remove any separator at the end. This will handle invalid path exceptions. + ' IMPORTANT: sourceFileName and destinationFileName should be used for throwing user exceptions ONLY. + Dim sourceFileFullPath As String = NormalizeFilePath(sourceFileName, "sourceFileName") + Dim destinationFileFullPath As String = NormalizeFilePath(destinationFileName, "destinationFileName") + + ' Demand FileIOPermission on the given paths. (CONSIDER: Performance?) + ' We do it here instead of waiting for System.IO.File.Copy/Move to demand because + ' - Framework's code demand FileIOPermission firstly. + ' - We add the ability to move with overwrite flag, which leads to different behavior + ' if we don't demand (VSWhidbey 421322). + ' - Security reason. + ' SECURITY NOTE: We assert UnmanagedCodePermission to call MoveFileEx later if needed so make sure + ' we demand the appropriate FileIOPermission. + Dim sourceAccess As FileIOPermissionAccess = FileIOPermissionAccess.Read + If operation = CopyOrMove.Move Then + sourceAccess = sourceAccess Or FileIOPermissionAccess.Write + End If + Call (New FileIOPermission(sourceAccess, sourceFileFullPath)).Demand() + Call (New FileIOPermission(FileIOPermissionAccess.Write, destinationFileFullPath)).Demand() + + ' Throw if device path. + ThrowIfDevicePath(sourceFileFullPath) + ThrowIfDevicePath(destinationFileFullPath) + + ' Throw exception if SourceFilePath does not exist. + If Not IO.File.Exists(sourceFileFullPath) Then + Throw ExUtils.GetFileNotFoundException(sourceFileName, ResID.MyID.IO_FileNotFound_Path, sourceFileName) + End If + + ' Throw exception if TargetFilePath is an existing directory. + If IO.Directory.Exists(destinationFileFullPath) Then + Throw ExUtils.GetIOException(ResID.MyID.IO_DirectoryExists_Path, destinationFileName) + End If + + ' Always create the target's parent directory(s). + IO.Directory.CreateDirectory(GetParentPath(destinationFileFullPath)) + + ' If ShowUI, attempt to call Shell function. + If showUI <> UIOptionInternal.NoUI AndAlso System.Environment.UserInteractive Then + ShellCopyOrMove(Operation, FileOrDirectory.File, sourceFileFullPath, destinationFileFullPath, showUI, onUserCancel) + Exit Sub + End If + + ' Use Framework. + If operation = CopyOrMove.Copy OrElse _ + sourceFileFullPath.Equals(destinationFileFullPath, StringComparison.OrdinalIgnoreCase) Then + ' Call IO.File.Copy if this is a copy operation. + ' In addition, if sourceFileFullPath is the same as destinationFileFullPath, + ' IO.File.Copy will throw, IO.File.Move will not (VSWhidbey334759). + ' Whatever overwrite flag is passed in, IO.File.Move should throw exception, + ' so call IO.File.Copy to get the exception as well. + IO.File.Copy(sourceFileFullPath, destinationFileFullPath, overwrite) + + Else ' MoveFile with support for overwrite flag. + If overwrite Then ' User wants to overwrite destination. + ' Why not checking for destination existence: user may not have read permission / ACL, + ' but have write permission / ACL thus cannot see but can delete / overwrite destination. + + If Environment.OSVersion.Platform = PlatformID.Win32NT Then ' Platforms supporting MoveFileEx. + ' SECURITY NOTE: We already demand permission at the start. + Call (New SecurityPermission(SecurityPermissionFlag.UnmanagedCode)).Assert() + Try + Dim succeed As Boolean = NativeMethods.MoveFileEx( _ + sourceFileFullPath, destinationFileFullPath, m_MOVEFILEEX_FLAGS) + ' GetLastWin32Error has to be close to PInvoke call. FxCop rule. + If Not succeed Then + ThrowWinIOError(System.Runtime.InteropServices.Marshal.GetLastWin32Error()) + End If + Catch + Throw + Finally + CodeAccessPermission.RevertAssert() + End Try + + Else ' Win95, Win98, WinME. + ' IO.File.Delete will not throw if destinationFileFullPath does not exist + ' (user may not have permission to discover this, but have permission to overwrite), + ' so always delete the destination. + IO.File.Delete(destinationFileFullPath) + + IO.File.Move(sourceFileFullPath, destinationFileFullPath) + End If + + Else ' Overwrite = False, call Framework. + IO.File.Move(sourceFileFullPath, destinationFileFullPath) + End If ' Overwrite + End If + End Sub + + '''************************************************************************** + ''' ;DeleteDirectory + ''' + ''' Delete the given directory, with options to recursively delete, show progress UI, send file to Recycle Bin, and whether to throw exception if user cancels. + ''' + ''' The path to the directory. + ''' DeleteAllContents to delete everything. ThrowIfDirectoryNonEmpty to throw exception if the directory is not empty. + ''' ShowDialogs to display progress and confirmation dialogs. Otherwise HideDialogs. + ''' SendToRecycleBin to delete to Recycle Bin. Otherwise DeletePermanently. + ''' Throw exception when user cancel the UI operation or not. + ''' If user wants shell features, onDirectoryNotEmpty is ignored. (VSWhidbey 283409) + _ + _ + _ + Private Shared Sub DeleteDirectoryInternal(ByVal directory As String, ByVal onDirectoryNotEmpty As DeleteDirectoryOption, _ + ByVal showUI As UIOptionInternal, ByVal recycle As RecycleOption, ByVal onUserCancel As UICancelOption) + + ' Verify enums. VSWhidbey 522083. + VerifyDeleteDirectoryOption("onDirectoryNotEmpty", onDirectoryNotEmpty) + VerifyRecycleOption("recycle", recycle) + VerifyUICancelOption("onUserCancel", onUserCancel) + + ' Get the full path. This will handle invalid paths exceptions. + Dim directoryFullPath As String = IO.Path.GetFullPath(directory) + + ' Demand Write permission for security reason (see CopyOrMoveFile / CopyOrMoveDirectory). + DemandDirectoryPermission(directoryFullPath, FileIOPermissionAccess.Write) + + ' Throw if device path. + ThrowIfDevicePath(directoryFullPath) + + If Not IO.Directory.Exists(directoryFullPath) Then + Throw ExUtils.GetDirectoryNotFoundException(ResID.MyID.IO_DirectoryNotFound_Path, directory) + End If + + ' VsWhidbey 319809: Throw exception if deleting root directory. + If IsRoot(directoryFullPath) Then + Throw ExUtils.GetIOException(ResID.MyID.IO_DirectoryIsRoot_Path, directory) + End If + + ' If user want shell features (Progress, Recycle Bin), call shell operation. + ' We don't need to consider onDirectoryNotEmpty here (VSWhidbey 283409). + If (showUI <> UIOptionInternal.NoUI) AndAlso Environment.UserInteractive Then + ShellDelete(directoryFullPath, showUI, recycle, onUserCancel, FileOrDirectory.Directory) + Exit Sub + End If + + ' Otherwise, call Framework's method. + IO.Directory.Delete(directoryFullPath, onDirectoryNotEmpty = DeleteDirectoryOption.DeleteAllContents) + End Sub + + + '''************************************************************************** + ''' ;DeleteFileInternal + ''' + ''' Delete the given file, with options to show progress UI, send file to Recycle Bin, throw exception if user cancels. + ''' + ''' the path to the file + ''' AllDialogs, OnlyErrorDialogs, or NoUI + ''' DeletePermanently or SendToRecycleBin + ''' DoNothing or ThrowException + ''' + _ + _ + _ + Private Shared Sub DeleteFileInternal(ByVal file As String, ByVal showUI As UIOptionInternal, ByVal recycle As RecycleOption, _ + ByVal onUserCancel As UICancelOption) + + ' Verify enums + VerifyRecycleOption("recycle", recycle) + VerifyUICancelOption("onUserCancel", onUserCancel) + + ' Get the full path. This will handle invalid path exceptions. + Dim fileFullPath As String = NormalizeFilePath(file, "file") + + ' Demand Write permission for security reason (see CopyOrMoveFile / CopyOrMoveDirectory). + Call (New FileIOPermission(FileIOPermissionAccess.Write, fileFullPath)).Demand() + + ' Throw if device path. + ThrowIfDevicePath(fileFullPath) + + If Not IO.File.Exists(fileFullPath) Then + Throw ExUtils.GetFileNotFoundException(file, ResID.MyID.IO_FileNotFound_Path, file) + End If + + ' If user want shell features (Progress, Recycle Bin), call shell operation. + If (showUI <> UIOptionInternal.NoUI) AndAlso Environment.UserInteractive Then + ShellDelete(fileFullPath, showUI, recycle, onUserCancel, FileOrDirectory.File) + Exit Sub + End If + + IO.File.Delete(fileFullPath) + End Sub + + + '''************************************************************************** + ''' ;DemandDirectoryPermission + ''' + ''' Given a full directory path, demand the given access using FileIOPermission. + ''' + ''' The full path to the directory. This must be normalized. + ''' FileIOPermissionAccess value. + ''' We add a \ to the end if needed to demand permission on the entire directory. + _ + Private Shared Sub DemandDirectoryPermission(ByVal fullDirectoryPath As String, ByVal access As FileIOPermissionAccess) + Debug.Assert(NormalizePath(fullDirectoryPath).Equals(fullDirectoryPath, StringComparison.OrdinalIgnoreCase), _ + "fullDirectoryPath must be normalized before calling this method.") + + ' Add a directory separator character to the end if needed to demand permission on the whole directory. + If Not (fullDirectoryPath.EndsWith(IO.Path.DirectorySeparatorChar, StringComparison.Ordinal) Or _ + fullDirectoryPath.EndsWith(IO.Path.AltDirectorySeparatorChar, StringComparison.Ordinal)) Then + + fullDirectoryPath &= IO.Path.DirectorySeparatorChar + End If + + Dim fileIOPerm As New FileIOPermission(access, fullDirectoryPath) + fileIOPerm.Demand() + End Sub + + '''****************************************************************************** + ''' ;EnsurePathNotExist + ''' + ''' Verify that a path does not refer to an existing directory or file. Throw exception otherwise. + ''' + ''' The path to verify. + ''' This is used for RenameFile and RenameDirectory. + Private Shared Sub EnsurePathNotExist(ByVal Path As String) + If IO.File.Exists(Path) Then + Throw ExUtils.GetIOException(ResID.MyID.IO_FileExists_Path, Path) + End If + + If IO.Directory.Exists(Path) Then + Throw ExUtils.GetIOException(ResID.MyID.IO_DirectoryExists_Path, Path) + End If + End Sub + + '''****************************************************************************** + ''' ;FileContainsText + ''' + ''' Determines if the given file in the path contains the given text. + ''' + ''' The file to check for. + ''' The text to searh for. + ''' True if the file contains the text. Otherwise False. + Private Shared Function FileContainsText(ByVal FilePath As String, ByVal Text As String, ByVal IgnoreCase As Boolean) _ + As Boolean + + Debug.Assert(FilePath <> "" AndAlso IO.Path.IsPathRooted(FilePath), FilePath) + Debug.Assert(Text <> "", "Empty text!!!") + + ' To support different encoding (UTF-8, ASCII). + ' Read the file in byte, then use Decoder classes to get a string from those bytes and compare. + ' Decoder class maintains state between the conversion, allowing it to correctly decode + ' byte sequences that span adjacent blocks. (sources\ndp\clr\src\BCL\System\Text\Decoder.cs). + + Dim DEFAULT_BUFFER_SIZE As Integer = 1024 ' default buffer size to read each time. + Dim FileStream As IO.FileStream = Nothing + + Try + ' Open the file with ReadWrite share, least possibility to fail. + FileStream = New IO.FileStream(FilePath, IO.FileMode.Open, IO.FileAccess.Read, IO.FileShare.ReadWrite) + + ' Read in a byte buffer with default size, then open a StreamReader to detect the encoding of the file. + Dim DetectedEncoding As System.Text.Encoding = System.Text.Encoding.Default ' Use Default encoding as the fall back. + Dim ByteBuffer(DEFAULT_BUFFER_SIZE - 1) As Byte + Dim ByteCount As Integer = 0 + ByteCount = FileStream.Read(ByteBuffer, 0, ByteBuffer.Length) + If ByteCount > 0 Then + ' Only take the number of bytes returned to avoid false detection. VSWhidbey 518893. + Dim MemoryStream As New IO.MemoryStream(ByteBuffer, 0, ByteCount) + Dim StreamReader As New IO.StreamReader(MemoryStream, DetectedEncoding, detectEncodingFromByteOrderMarks:=True) + StreamReader.ReadLine() + DetectedEncoding = StreamReader.CurrentEncoding + End If + + ' Calculate the real buffer size to read in each time to ensure read in at least a character array + ' as long as or longer than the given text. + ' 1. Calculate the maximum number of bytes required to encode the given text in the detected encoding. + ' 2. If it's larger than DEFAULT_BUFFER_SIZE, use it. Otherwise, use DEFAULT_BUFFER_SIZE. + Dim MaxByteDetectedEncoding As Integer = DetectedEncoding.GetMaxByteCount(Text.Length) + Dim BufferSize As Integer = Math.Max(MaxByteDetectedEncoding, DEFAULT_BUFFER_SIZE) + + ' Dim up the byte buffer and the search helpers (See TextSearchHelper). + Dim SearchHelper As New TextSearchHelper(DetectedEncoding, Text, IgnoreCase) + + ' If the buffer size is larger than DEFAULT_BUFFER_SIZE, read more from the file stream + ' to fill up the byte buffer. + If BufferSize > DEFAULT_BUFFER_SIZE Then + ReDim Preserve ByteBuffer(BufferSize - 1) + ' Read maximum ByteBuffer.Length - ByteCount (from the initial read) bytes from the stream + ' into the ByteBuffer, starting at ByteCount position. + Dim AdditionalByteCount As Integer = FileStream.Read(ByteBuffer, ByteCount, ByteBuffer.Length - ByteCount) + ByteCount += AdditionalByteCount ' The total byte count now is ByteCount + AdditionalByteCount + Debug.Assert(ByteCount <= ByteBuffer.Length) + End If + + ' Start the search and read until end of file. + Do + If ByteCount > 0 Then + If SearchHelper.IsTextFound(ByteBuffer, ByteCount) Then + Return True + End If + End If + ByteCount = FileStream.Read(ByteBuffer, 0, ByteBuffer.Length) + Loop While (ByteCount > 0) + + Return False + Catch ex As Exception + + ' We don't expect the following types of exceptions, so we'll rethrow it together with Yukon's exceptions. + Debug.Assert(Not (TypeOf ex Is ArgumentException Or TypeOf ex Is ArgumentOutOfRangeException Or _ + TypeOf ex Is ArgumentNullException Or TypeOf ex Is IO.DirectoryNotFoundException Or _ + TypeOf ex Is IO.FileNotFoundException Or TypeOf ex Is ObjectDisposedException Or _ + TypeOf ex Is RankException Or TypeOf ex Is ArrayTypeMismatchException Or _ + TypeOf ex Is InvalidCastException), "Unexpected exception: " & ex.ToString()) + + ' These exceptions may happen and we'll return False here. + If TypeOf ex Is IO.IOException Or _ + TypeOf ex Is NotSupportedException Or _ + TypeOf ex Is SecurityException Or _ + TypeOf ex Is UnauthorizedAccessException Then + + Return False + Else + ' Rethrow Yukon's exceptions, PathTooLong exception (linked directory) and others. + Throw + End If + Finally + If FileStream IsNot Nothing Then + FileStream.Close() + End If + End Try + End Function + + + '''************************************************************************** + ''' ;FindFilesOrDirectories + ''' + ''' Find files or directories in a directory and return them in a string collection. + ''' + ''' Specify to search for file or directory. + ''' The directory path to start from. + ''' SearchAllSubDirectories to find recursively. Otherwise, SearchTopLevelOnly. + ''' The search patterns to use for the file name ("*.*") + ''' A ReadOnlyCollection(Of String) containing the files that match the search condition. + ''' ArgumentNullException: If one of the pattern is Null, Empty or all-spaces string. + Private Shared Function FindFilesOrDirectories(ByVal FileOrDirectory As FileOrDirectory, ByVal directory As String, _ + ByVal searchType As SearchOption, ByVal wildcards() As String) As ObjectModel.ReadOnlyCollection(Of String) + + Dim Results As New ObjectModel.Collection(Of String) + FindFilesOrDirectories(FileOrDirectory, directory, searchType, wildcards, Results) + + Return New ObjectModel.ReadOnlyCollection(Of String)(Results) + End Function + + '''************************************************************************** + ''' ;FindFilesOrDirectories + ''' + ''' Find files or directories in a directory and return them in a string collection. + ''' + ''' Specify to search for file or directory. + ''' The directory path to start from. + ''' SearchAllSubDirectories to find recursively. Otherwise, SearchTopLevelOnly. + ''' The search patterns to use for the file name ("*.*") + ''' A ReadOnlyCollection(Of String) containing the files that match the search condition. + Private Shared Sub FindFilesOrDirectories(ByVal FileOrDirectory As FileOrDirectory, ByVal directory As String, _ + ByVal searchType As SearchOption, ByVal wildcards() As String, ByVal Results As ObjectModel.Collection(Of String)) + Debug.Assert(Results IsNot Nothing, "Results is NULL!!!") + + ' Verify enums. + VerifySearchOption("searchType", searchType) + + directory = NormalizePath(directory) + + ' Verify wild cards. Only TrimEnd since empty space is allowed at the start of file / directory name. + If wildcards IsNot Nothing Then + For Each wildcard As String In wildcards + ' Throw if empty string or Nothing. + If wildcard.TrimEnd() = "" Then + Throw ExUtils.GetArgumentNullException("wildcards", ResID.MyID.IO_GetFiles_NullPattern) + End If + Next + End If + + ' Search for files / directories directly under given directory (based on wildcards). + If wildcards Is Nothing OrElse wildcards.Length = 0 Then + AddToStringCollection(Results, FindPaths(FileOrDirectory, directory, Nothing)) + Else + For Each wildcard As String In wildcards + AddToStringCollection(Results, FindPaths(FileOrDirectory, directory, wildcard)) + Next + End If + + ' Search in sub directories if specified. + If searchType = SearchOption.SearchAllSubDirectories Then + For Each SubDirectoryPath As String In IO.Directory.GetDirectories(directory) + FindFilesOrDirectories(FileOrDirectory, SubDirectoryPath, searchType, wildcards, Results) + Next + End If + End Sub + + ''' ************************************************************************** + ''' ;FindPaths + ''' + ''' Given a directory, a pattern, find the files or directories directly under the given directory that match the pattern. + ''' + ''' Specify whether to find files or directories. + ''' The directory to look under. + ''' *.bmp, *.txt, ... Nothing to search for every thing. + ''' An array of String containing the paths found. + Private Shared Function FindPaths(ByVal FileOrDirectory As FileOrDirectory, ByVal directory As String, ByVal wildCard As String) As String() + If FileOrDirectory = FileSystem.FileOrDirectory.Directory Then + If wildCard = "" Then + Return IO.Directory.GetDirectories(directory) + Else + Return IO.Directory.GetDirectories(directory, wildCard) + End If + Else + If wildCard = "" Then + Return IO.Directory.GetFiles(directory) + Else + Return IO.Directory.GetFiles(directory, wildCard) + End If + End If + End Function + + + '''****************************************************************************** + ''' ;GetFullPathFromNewName + ''' + ''' Returns the fullpath from a directory path and a new name. Throws exception if the new name contains path information. + ''' + ''' The directory path. + ''' The new name to combine to the directory path. + ''' The argument name to throw in the exception. + ''' A String contains the full path. + ''' This function is used for CopyFile, RenameFile and RenameDirectory. + Private Shared Function GetFullPathFromNewName(ByVal Path As String, _ + ByVal NewName As String, ByVal ArgumentName As String) As String + Debug.Assert(Path <> "" AndAlso IO.Path.IsPathRooted(Path), Path) + Debug.Assert(Path.Equals(IO.Path.GetFullPath(Path)), Path) + Debug.Assert(NewName <> "", "Null NewName!!!") + Debug.Assert(ArgumentName <> "", "Null argument name!!!") + + ' In copy file, rename file and rename directory, the new name must be a name only. + ' Enforce that by combine the path, normalize it, then compare the new parent directory with the old parent directory. + ' These two directories must be the same. + + ' Throw exception if NewName contains any separator characters. + If NewName.IndexOfAny(m_SeparatorChars) >= 0 Then + Throw ExUtils.GetArgumentExceptionWithArgName(ArgumentName, ResID.MyID.IO_ArgumentIsPath_Name_Path, ArgumentName, NewName) + End If + + ' Call GetFullPath again to catch invalid characters in NewName. + Dim FullPath As String = RemoveEndingSeparator(IO.Path.GetFullPath(IO.Path.Combine(Path, NewName))) + + ' If the new parent directory path does not equal the parent directory passed in, throw exception. + ' Use this to check for cases like "..", checking for separators will not block this case. + If Not GetParentPath(FullPath).Equals(Path, StringComparison.OrdinalIgnoreCase) Then + Throw ExUtils.GetArgumentExceptionWithArgName(ArgumentName, ResID.MyID.IO_ArgumentIsPath_Name_Path, ArgumentName, NewName) + End If + + Return FullPath + End Function + + + '''****************************************************************************** + ''' ;GetLongPath + ''' + ''' Returns the given path in long format (v.s 8.3 format) if the path exists. + ''' + ''' The path to resolve to long format. + ''' The given path in long format if the path exists. + ''' + ''' GetLongPathName is a PInvoke call and requires unmanaged code permission. + ''' Use DirectoryInfo.GetFiles and GetDirectories (which call FindFirstFile) so that we always have permission. + ''' + Private Shared Function GetLongPath(ByVal FullPath As String) As String + Debug.Assert(Not FullPath = "" AndAlso IO.Path.IsPathRooted(FullPath), "Must be full path!!!") + + Try + ' If root path, return itself. UNC path do not recognize 8.3 format in root path, so this is fine. + If IsRoot(FullPath) Then + Return FullPath + End If + + ' DirectoryInfo.GetFiles and GetDirectories call FindFirstFile which resolves 8.3 path. + ' Get the DirectoryInfo (user must have code permission or access permission). + Dim DInfo As New IO.DirectoryInfo(GetParentPath(FullPath)) + + If IO.File.Exists(FullPath) Then + Debug.Assert(DInfo.GetFiles(IO.Path.GetFileName(FullPath)).Length = 1, "Must found exactly 1!!!") + Return DInfo.GetFiles(IO.Path.GetFileName(FullPath))(0).FullName + ElseIf IO.Directory.Exists(FullPath) Then + Debug.Assert(DInfo.GetDirectories(IO.Path.GetFileName(FullPath)).Length = 1, _ + "Must found exactly 1!!!") + Return DInfo.GetDirectories(IO.Path.GetFileName(FullPath))(0).FullName + Else + Return FullPath ' Path does not exist, cannot resolve. + End If + Catch ex As Exception + ' Ignore these type of exceptions and return FullPath. These type of exceptions should either be caught by calling functions + ' or indicate that caller does not have enough permission and should get back the 8.3 path. + If TypeOf ex Is ArgumentException OrElse _ + TypeOf ex Is ArgumentNullException OrElse _ + TypeOf ex Is IO.PathTooLongException OrElse _ + TypeOf ex Is NotSupportedException OrElse _ + TypeOf ex Is IO.DirectoryNotFoundException OrElse _ + TypeOf ex Is SecurityException OrElse _ + TypeOf ex Is UnauthorizedAccessException Then + + Debug.Assert(Not (TypeOf ex Is ArgumentException OrElse _ + TypeOf ex Is ArgumentNullException OrElse _ + TypeOf ex Is IO.PathTooLongException OrElse _ + TypeOf ex Is NotSupportedException), "These exceptions should be caught above!!!") + + Return FullPath + Else + Throw + End If + End Try + End Function + + + '''****************************************************************************** + ''' ;IsOnSameDrive + ''' + ''' Checks to see if the two paths is on the same drive. + ''' + ''' + ''' + ''' True if the 2 paths is on the same drive. False otherwise. + ''' Just a string comparison. + Private Shared Function IsOnSameDrive(ByVal Path1 As String, ByVal Path2 As String) As Boolean + ' Remove any separators at the end for the same reason in IsRoot. + Path1 = Path1.TrimEnd(IO.Path.DirectorySeparatorChar, IO.Path.AltDirectorySeparatorChar) + Path2 = Path2.TrimEnd(IO.Path.DirectorySeparatorChar, IO.Path.AltDirectorySeparatorChar) + Return String.Compare(IO.Path.GetPathRoot(Path1), IO.Path.GetPathRoot(Path2), _ + StringComparison.OrdinalIgnoreCase) = 0 + End Function + + + '''************************************************************************** + ''' ;IsRoot + ''' + ''' Checks if the full path is a root path. + ''' + ''' The path to check. + ''' True if FullPath is a root path, False otherwise. + ''' + ''' IO.Path.GetPathRoot: C: -> C:, C:\ -> C:\, \\machine\share -> \\machine\share, + ''' BUT \\machine\share\ -> \\machine\share (No separator here). + ''' Therefore, remove any separators at the end to have correct result. + ''' + Private Shared Function IsRoot(ByVal Path As String) As Boolean + ' This function accepts a relative path since GetParentPath will call this, + ' and GetParentPath accept relative paths. + If Not IO.Path.IsPathRooted(Path) Then + Return False + End If + + Path = Path.TrimEnd(IO.Path.DirectorySeparatorChar, IO.Path.AltDirectorySeparatorChar) + Return String.Compare(Path, IO.Path.GetPathRoot(Path), _ + StringComparison.OrdinalIgnoreCase) = 0 + End Function + + + '''************************************************************************** + ''' ;RemoveEndingSeparator + ''' + ''' Removes all directory separators at the end of a path. + ''' + ''' a full or relative path. + ''' If Path is a root path, the same value. Otherwise, removes any directory separators at the end. + ''' We decided not to return path with separators at the end (VsWhidbey 54741). + Private Shared Function RemoveEndingSeparator(ByVal Path As String) As String + If IO.Path.IsPathRooted(Path) Then + ' If the path is rooted, attempt to check if it is a root path. + ' Note: IO.Path.GetPathRoot: C: -> C:, C:\ -> C:\, \\myshare\mydir -> \\myshare\mydir + ' BUT \\myshare\mydir\ -> \\myshare\mydir!!! This function will remove the ending separator of + ' \\myshare\mydir\ as well. Do not use IsRoot here. + If Path.Equals(IO.Path.GetPathRoot(Path), StringComparison.OrdinalIgnoreCase) Then + Return Path + End If + End If + + ' Otherwise, remove all separators at the end. + Return Path.TrimEnd(IO.Path.DirectorySeparatorChar, IO.Path.AltDirectorySeparatorChar) + End Function + + + '''****************************************************************************** + ''' ;ShellCopyOrMove + ''' + ''' Sets relevant flags on the SHFILEOPSTRUCT and calls SHFileOperation to copy move file / directory. + ''' + ''' Copy or move. + ''' The target is a file or directory? + ''' Full path to source directory / file. + ''' Full path to target directory / file. + ''' Show all dialogs or just the error dialogs. + ''' Throw exception or ignore if user cancels the operation. + ''' + ''' Copy/MoveFile will call this directly. Copy/MoveDirectory will call ShellCopyOrMoveDirectory first + ''' to change the path if needed. + ''' !!!!! SECURITY WARNING !!!! + ''' Demand appropriate FileIOPermission on FullSource and FullTarget before calling into this method. + ''' + _ + _ + _ + Private Shared Sub ShellCopyOrMove(ByVal Operation As CopyOrMove, ByVal TargetType As FileOrDirectory, _ + ByVal FullSourcePath As String, ByVal FullTargetPath As String, ByVal ShowUI As UIOptionInternal, ByVal OnUserCancel As UICancelOption) + + Debug.Assert(System.Enum.IsDefined(GetType(CopyOrMove), Operation)) + Debug.Assert(System.Enum.IsDefined(GetType(FileOrDirectory), TargetType)) + Debug.Assert(FullSourcePath <> "" And IO.Path.IsPathRooted(FullSourcePath), "Invalid FullSourcePath!!!") + Debug.Assert(FullTargetPath <> "" And IO.Path.IsPathRooted(FullTargetPath), "Invalid FullTargetPath!!!") + Debug.Assert(ShowUI <> UIOptionInternal.NoUI, "Why call ShellDelete if ShowUI is NoUI???") + + ' Set operation type. + Dim OperationType As SHFileOperationType + If Operation = CopyOrMove.Copy Then + OperationType = SHFileOperationType.FO_COPY + Else + OperationType = SHFileOperationType.FO_MOVE + End If + + ' Set operation details. + Dim OperationFlags As ShFileOperationFlags = GetOperationFlags(ShowUI) + + ' *** Special action for Directory only. *** + Dim FinalSourcePath As String = FullSourcePath + If TargetType = FileOrDirectory.Directory Then + ' Shell behavior: If target does not exist, create target and copy / move source CONTENT into target. + ' If target exists, copy / move source into target. + ' To have our behavior: + ' If target does not exist, create target parent (or shell will throw) and call ShellCopyOrMove. + ' If target exists, attach "\*" to FullSourcePath and call ShellCopyOrMove. + ' In case of Move, since moving the directory, just create the target parent. + If IO.Directory.Exists(FullTargetPath) Then + FinalSourcePath = IO.Path.Combine(FullSourcePath, "*") + Else + IO.Directory.CreateDirectory(GetParentPath(FullTargetPath)) + End If + End If + + ' Call into ShellFileOperation. + ShellFileOperation(OperationType, OperationFlags, FinalSourcePath, FullTargetPath, OnUserCancel, TargetType) + + ' *** Special action for Directory only. *** + ' In case target does exist, and it's a move, we actually move content and leave the source directory. + ' Clean up here. + If Operation = CopyOrMove.Move And TargetType = FileOrDirectory.Directory Then + If IO.Directory.Exists(FullSourcePath) Then + If IO.Directory.GetDirectories(FullSourcePath).Length = 0 _ + AndAlso IO.Directory.GetFiles(FullSourcePath).Length = 0 Then + IO.Directory.Delete(FullSourcePath, recursive:=False) + End If + End If + End If + + End Sub + + '''************************************************************************** + ''' ;ShellDelete + ''' + ''' Sets relevant flags on the SHFILEOPSTRUCT and calls into SHFileOperation to delete file / directory. + ''' + ''' Full path to the file / directory. + ''' ShowDialogs to display progress and confirmation dialogs. Otherwise HideDialogs. + ''' SendToRecycleBin to delete to Recycle Bin. Otherwise DeletePermanently. + ''' Throw exception or not if the operation was canceled (by user or errors in the system). + ''' + ''' We don't need to consider Recursive flag here since we already verify that in DeleteDirectory. + ''' !!!!! SECURITY WARNING !!!! + ''' Demand appropriate FileIOPermission on FullSource and FullTarget before calling into this method. + ''' + _ + _ + _ + Private Shared Sub ShellDelete(ByVal FullPath As String, _ + ByVal ShowUI As UIOptionInternal, ByVal recycle As RecycleOption, ByVal OnUserCancel As UICancelOption, ByVal FileOrDirectory As FileOrDirectory) + + Debug.Assert(FullPath <> "" And IO.Path.IsPathRooted(FullPath), "FullPath must be a full path!!!") + Debug.Assert(ShowUI <> UIOptionInternal.NoUI, "Why call ShellDelete if ShowUI is NoUI???") + + ' Set fFlags to control the operation details. + Dim OperationFlags As ShFileOperationFlags = GetOperationFlags(ShowUI) + If (recycle = RecycleOption.SendToRecycleBin) Then + OperationFlags = OperationFlags Or ShFileOperationFlags.FOF_ALLOWUNDO + End If + + ShellFileOperation(SHFileOperationType.FO_DELETE, OperationFlags, FullPath, Nothing, OnUserCancel, FileOrDirectory) + End Sub + + '''************************************************************************** + ''' ;ShellFileOperation + ''' + ''' Calls NativeMethods.SHFileOperation with the given SHFILEOPSTRUCT, notifies the shell of change, + ''' and throw exceptions if needed. + ''' + ''' Value from SHFileOperationType, specifying Copy / Move / Delete + ''' Value from ShFileOperationFlags, specifying overwrite, recycle bin, etc... + ''' The full path to the source. + ''' The full path to the target. Nothing if this is a Delete operation. + ''' Value from UICancelOption, specifying to throw or not when user cancels the operation. + ''' + ''' !!!!! SECURITY WARNING !!!! + ''' Demand appropriate FileIOPermission on FullSource and FullTarget before calling into this method. + ''' + _ + _ + _ + _ + Private Shared Sub ShellFileOperation(ByVal OperationType As SHFileOperationType, ByVal OperationFlags As ShFileOperationFlags, _ + ByVal FullSource As String, ByVal FullTarget As String, ByVal OnUserCancel As UICancelOption, ByVal FileOrDirectory As FileOrDirectory) + + ' Apply HostProtectionAttribute(UI = true) to indicate this function belongs to UI type. + ' http://devdiv/SpecTool/Documents/Whidbey/CLR/CurrentSpecs/SQLHost/hPA%20Guidance.doc + + Debug.Assert(System.Enum.IsDefined(GetType(SHFileOperationType), OperationType)) + Debug.Assert(OperationType <> SHFileOperationType.FO_RENAME, "Don't call Shell to rename!!!") + Debug.Assert(FullSource <> "" And IO.Path.IsPathRooted(FullSource), "Invalid FullSource path!!!") + Debug.Assert(OperationType = SHFileOperationType.FO_DELETE OrElse (FullTarget <> "" And IO.Path.IsPathRooted(FullTarget)), "Invalid FullTarget path!!!") + + ' Demand the neccessary permissions: UIPermission. + Dim UIPermission As New UIPermission(UIPermissionWindow.SafeSubWindows) + UIPermission.Demand() + + ' Demand FileIOPermission, defense in depth. VSWhidbey 427776. CONSIDER: Perf hit? + ' Demand permission on source path. Get the correct permission access based on operation type. + Dim SourceIOPermissionAccess As FileIOPermissionAccess = FileIOPermissionAccess.NoAccess + If OperationType = SHFileOperationType.FO_COPY Then + SourceIOPermissionAccess = FileIOPermissionAccess.Read + ElseIf OperationType = SHFileOperationType.FO_MOVE Then + SourceIOPermissionAccess = FileIOPermissionAccess.Read Or FileIOPermissionAccess.Write + ElseIf OperationType = SHFileOperationType.FO_DELETE Then + SourceIOPermissionAccess = FileIOPermissionAccess.Write + End If + ' FullSource might end with '\*' (for copying and moving) so normalize the path to the correct format to demand the permission. + Dim CheckPermissionPath As String = FullSource + If (OperationType = SHFileOperationType.FO_COPY OrElse OperationType = SHFileOperationType.FO_MOVE) _ + AndAlso CheckPermissionPath.EndsWith("*", StringComparison.Ordinal) Then + CheckPermissionPath = RemoveEndingSeparator(FullSource.TrimEnd("*"c)) + End If + ' Demand the permission on source file or directory. + If FileOrDirectory = FileSystem.FileOrDirectory.Directory Then + DemandDirectoryPermission(CheckPermissionPath, SourceIOPermissionAccess) + Else + Call (New FileIOPermission(SourceIOPermissionAccess, CheckPermissionPath)).Demand() + End If + ' Demand permission on target file or directory. Only in copy / move. + If OperationType <> SHFileOperationType.FO_DELETE Then + If FileOrDirectory = FileSystem.FileOrDirectory.Directory Then + DemandDirectoryPermission(FullTarget, FileIOPermissionAccess.Write) + Else + Call (New FileIOPermission(FileIOPermissionAccess.Write, FullTarget)).Demand() + End If + End If + + ' Get the SHFILEOPSTRUCT + Dim OperationInfo As SHFILEOPSTRUCT = GetShellOperationInfo(OperationType, OperationFlags, FullSource, FullTarget) + + Dim Result As Integer + + ' Assert UnmanagedCodePermission to call Win32 methods. + Call (New SecurityPermission(SecurityPermissionFlag.UnmanagedCode)).Assert() + + Try + Result = NativeMethods.SHFileOperation(OperationInfo) + ' Notify the shell in case some changes happened. + NativeMethods.SHChangeNotify(SHChangeEventTypes.SHCNE_DISKEVENTS, _ + SHChangeEventParameterFlags.SHCNF_DWORD, IntPtr.Zero, IntPtr.Zero) + Catch + Throw + Finally + CodeAccessPermission.RevertAssert() + End Try + + ' If the operation was canceled, check OnUserCancel and throw OperationCanceledException if needed. + ' Otherwise, check the result and throw the appropriate exception if there is an error code. + ' VSWhidbey 253382, 468577. + If OperationInfo.fAnyOperationsAborted Then + If OnUserCancel = UICancelOption.ThrowException Then + Throw New OperationCanceledException() + End If + ElseIf Result <> 0 Then + ThrowWinIOError(Result) + End If + End Sub + + '''****************************************************************************** + ''' ;GetShellOperationInfo + ''' + ''' Returns an SHFILEOPSTRUCT used by SHFileOperation based on the given parameters. + ''' + ''' One of the SHFileOperationType value: copy, move or delete. + ''' Combination SHFileOperationFlags values: details of the operation. + ''' The source file / directory path. + ''' The target file / directory path. Nothing in case of delete. + ''' A fully initialized SHFILEOPSTRUCT. + _ + Private Shared Function GetShellOperationInfo( _ + ByVal OperationType As SHFileOperationType, ByVal OperationFlags As ShFileOperationFlags, _ + ByVal SourcePath As String, Optional ByVal TargetPath As String = Nothing) As SHFILEOPSTRUCT + Debug.Assert(SourcePath <> "" And IO.Path.IsPathRooted(SourcePath), "Invalid SourcePath!!!") + + Return GetShellOperationInfo(OperationType, OperationFlags, New String() {SourcePath}, TargetPath) + End Function + + '''****************************************************************************** + ''' ;GetShellOperationInfo + ''' + ''' Returns an SHFILEOPSTRUCT used by SHFileOperation based on the given parameters. + ''' + ''' One of the SHFileOperationType value: copy, move or delete. + ''' Combination SHFileOperationFlags values: details of the operation. + ''' A string array containing the paths of source files. Must not be empty. + ''' The target file / directory path. Nothing in case of delete. + ''' A fully initialized SHFILEOPSTRUCT. + _ + Private Shared Function GetShellOperationInfo( _ + ByVal OperationType As SHFileOperationType, ByVal OperationFlags As ShFileOperationFlags, _ + ByVal SourcePaths() As String, Optional ByVal TargetPath As String = Nothing) As SHFILEOPSTRUCT + Debug.Assert(System.Enum.IsDefined(GetType(SHFileOperationType), OperationType), "Invalid OperationType!!!") + Debug.Assert(TargetPath = "" Or IO.Path.IsPathRooted(TargetPath), "Invalid TargetPath!!!") + Debug.Assert(SourcePaths IsNot Nothing AndAlso SourcePaths.Length > 0, "Invalid SourcePaths!!!") + + Dim OperationInfo As SHFILEOPSTRUCT + + ' Set wFunc - the operation. + OperationInfo.wFunc = CType(OperationType, UInteger) + + ' Set fFlags - the operation details. + OperationInfo.fFlags = CType(OperationFlags, UShort) + + ' Set pFrom and pTo - the paths. + OperationInfo.pFrom = GetShellPath(SourcePaths) + If TargetPath Is Nothing Then + OperationInfo.pTo = Nothing + Else + OperationInfo.pTo = GetShellPath(TargetPath) + End If + + ' Set other fields. + OperationInfo.hNameMappings = IntPtr.Zero + ' Try to set hwnd to the process's MainWindowHandle. If exception occurs, use IntPtr.Zero, which is desktop. + Try + OperationInfo.hwnd = Process.GetCurrentProcess.MainWindowHandle + Catch ex As Exception + If TypeOf (ex) Is SecurityException OrElse _ + TypeOf (ex) Is InvalidOperationException OrElse _ + TypeOf (ex) Is NotSupportedException Then + ' GetCurrentProcess can throw SecurityException. MainWindowHandle can throw InvalidOperationException or NotSupportedException. + OperationInfo.hwnd = IntPtr.Zero + Else + Throw + End If + End Try + OperationInfo.lpszProgressTitle = String.Empty ' We don't set this since we don't have any FOF_SIMPLEPROGRESS. + + Return OperationInfo + End Function + + '''****************************************************************************** + ''' ;GetOperationFlags + ''' + ''' Return the ShFileOperationFlags based on the ShowUI option. + ''' + ''' UIOptionInternal value. + Private Shared Function GetOperationFlags(ByVal ShowUI As UIOptionInternal) As ShFileOperationFlags + Dim OperationFlags As ShFileOperationFlags = m_SHELL_OPERATION_FLAGS_BASE + If (ShowUI = UIOptionInternal.OnlyErrorDialogs) Then + OperationFlags = OperationFlags Or m_SHELL_OPERATION_FLAGS_HIDE_UI + End If + Return OperationFlags + End Function + + '''****************************************************************************** + ''' ;GetShellPath + ''' + ''' Returns the special path format required for pFrom and pTo of SHFILEOPSTRUCT. See NativeMethod. + ''' + ''' The full path to be converted. + ''' A string in the required format. + Private Shared Function GetShellPath(ByVal FullPath As String) As String + Debug.Assert(FullPath <> "" And IO.Path.IsPathRooted(FullPath), "Must be full path!!!") + + Return GetShellPath(New String() {FullPath}) + End Function + + '''****************************************************************************** + ''' ;GetShellPath + ''' + ''' Returns the special path format required for pFrom and pTo of SHFILEOPSTRUCT. See NativeMethod. + ''' + ''' A string array containing the paths for the operation. + ''' A string in the required format. + Private Shared Function GetShellPath(ByVal FullPaths() As String) As String +#If DEBUG Then + Debug.Assert(FullPaths IsNot Nothing, "FullPaths is NULL!!!") + Debug.Assert(FullPaths.Length > 0, "FullPaths() is empty array!!!") + For Each FullPath As String In FullPaths + Debug.Assert(FullPath <> "" And IO.Path.IsPathRooted(FullPath), FullPath) + Next +#End If + + ' Each path will end with a Null character. + Dim MultiString As New StringBuilder() + For Each FullPath As String In FullPaths + MultiString.Append(FullPath & ControlChars.NullChar) + Next + ' Don't need to append another Null character since String always end with Null character by default. + Debug.Assert(MultiString.ToString.EndsWith(ControlChars.NullChar, StringComparison.Ordinal)) + + Return MultiString.ToString() + End Function + + + '''************************************************************************** + ''' ;ThrowIfDevicePath + ''' + ''' Throw an argument exception if the given path starts with "\\.\" (device path). + ''' + ''' The path to check. + ''' + ''' VSWhidbey 230286. + ''' FileStream already throws exception with device path, so our code only check for device path in Copy / Move / Delete / Rename. + ''' + Private Shared Sub ThrowIfDevicePath(ByVal path As String) + If path.StartsWith("\\.\", StringComparison.Ordinal) Then + Throw ExceptionUtils.GetArgumentExceptionWithArgName("path", ResID.MyID.IO_DevicePath) + End If + End Sub + + + '''************************************************************************** + ''' ;ThrowWinIOError + ''' + ''' Given an error code from winerror.h, throw the appropriate exception. + ''' + ''' An error code from winerror.h. + ''' + ''' - This method is based on sources\ndp\clr\src\BCL\System\IO\_Error.cs::WinIOError, except the following. + ''' - Exception message does not contain the path since at this point it is normalized. + ''' - Instead of using PInvoke of GetMessage and MakeHRFromErrorCode, use managed code. + ''' + _ + Private Shared Sub ThrowWinIOError(ByVal errorCode As Integer) + Select Case errorCode + Case NativeTypes.ERROR_FILE_NOT_FOUND + Throw New IO.FileNotFoundException() + Case NativeTypes.ERROR_PATH_NOT_FOUND + Throw New IO.DirectoryNotFoundException() + Case NativeTypes.ERROR_ACCESS_DENIED + Throw New UnauthorizedAccessException() + Case NativeTypes.ERROR_FILENAME_EXCED_RANGE + Throw New IO.PathTooLongException() + Case NativeTypes.ERROR_INVALID_DRIVE + Throw New IO.DriveNotFoundException() + Case NativeTypes.ERROR_OPERATION_ABORTED, NativeTypes.ERROR_CANCELLED + Throw New OperationCanceledException() + Case Else + ' Including these from _Error.cs::WinIOError. + 'Case NativeTypes.ERROR_ALREADY_EXISTS + 'Case NativeTypes.ERROR_INVALID_PARAMETER + 'Case NativeTypes.ERROR_SHARING_VIOLATION + 'Case NativeTypes.ERROR_FILE_EXISTS + Throw New IO.IOException((New Win32Exception(errorCode)).Message, _ + System.Runtime.InteropServices.Marshal.GetHRForLastWin32Error()) + End Select + End Sub + + '''************************************************************************** + ''' ;ToUIOptionInternal + ''' + ''' Convert UIOption to UIOptionInternal to use internally. + ''' + ''' + ''' To fix common issues of VSWhidbey 474856, 499359; only accept valid UIOption values. + ''' + Private Shared Function ToUIOptionInternal(ByVal showUI As UIOption) As UIOptionInternal + Select Case showUI + Case FileIO.UIOption.AllDialogs + Return UIOptionInternal.AllDialogs + Case FileIO.UIOption.OnlyErrorDialogs + Return UIOptionInternal.OnlyErrorDialogs + Case Else + Throw New System.ComponentModel.InvalidEnumArgumentException("showUI", showUI, GetType(UIOption)) + End Select + End Function + + '''************************************************************************** + ''' ;VerifyDeleteDirectoryOption + ''' + ''' Verify that the given argument value is a valid DeleteDirectoryOption. If not, throw InvalidEnumArgumentException. + ''' + ''' The argument name. + ''' The argument value. + ''' VSWhidbey 522083. + Private Shared Sub VerifyDeleteDirectoryOption(ByVal argName As String, ByVal argValue As DeleteDirectoryOption) + If argValue = FileIO.DeleteDirectoryOption.DeleteAllContents OrElse _ + argValue = FileIO.DeleteDirectoryOption.ThrowIfDirectoryNonEmpty Then + Exit Sub + End If + + Throw New InvalidEnumArgumentException(argName, argValue, GetType(DeleteDirectoryOption)) + End Sub + + '''************************************************************************** + ''' ;VerifyRecycleOption + ''' + ''' Verify that the given argument value is a valid RecycleOption. If not, throw InvalidEnumArgumentException. + ''' + ''' The argument name. + ''' The argument value. + Private Shared Sub VerifyRecycleOption(ByVal argName As String, ByVal argValue As RecycleOption) + If argValue = RecycleOption.DeletePermanently OrElse _ + argValue = RecycleOption.SendToRecycleBin Then + Exit Sub + End If + + Throw New InvalidEnumArgumentException(argName, argValue, GetType(RecycleOption)) + End Sub + + '''************************************************************************** + ''' ;VerifySearchOption + ''' + ''' Verify that the given argument value is a valid SearchOption. If not, throw InvalidEnumArgumentException. + ''' + ''' The argument name. + ''' The argument value. + Private Shared Sub VerifySearchOption(ByVal argName As String, ByVal argValue As SearchOption) + If argValue = SearchOption.SearchAllSubDirectories OrElse _ + argValue = SearchOption.SearchTopLevelOnly Then + Exit Sub + End If + + Throw New InvalidEnumArgumentException(argName, argValue, GetType(SearchOption)) + End Sub + + '''************************************************************************** + ''' ;VerifyUICancelOption + ''' + ''' Verify that the given argument value is a valid UICancelOption. If not, throw InvalidEnumArgumentException. + ''' + ''' The argument name. + ''' The argument value. + Private Shared Sub VerifyUICancelOption(ByVal argName As String, ByVal argValue As UICancelOption) + If argValue = UICancelOption.DoNothing OrElse _ + argValue = UICancelOption.ThrowException Then + Exit Sub + End If + + Throw New InvalidEnumArgumentException(argName, argValue, GetType(UICancelOption)) + End Sub + + + ' Base operation flags used in shell IO operation. + ' - DON'T move connected files as a group. + ' - DON'T confirm directory creation - our silent copy / move do not. + Private Const m_SHELL_OPERATION_FLAGS_BASE As ShFileOperationFlags = _ + ShFileOperationFlags.FOF_NO_CONNECTED_ELEMENTS Or _ + ShFileOperationFlags.FOF_NOCONFIRMMKDIR + + ' Hide UI operation flags for Delete. + ' - DON'T show progress bar. + ' - DON'T confirm (answer yes to everything). NOTE: In exception cases (read-only file), shell still asks. + Private Const m_SHELL_OPERATION_FLAGS_HIDE_UI As ShFileOperationFlags = _ + ShFileOperationFlags.FOF_SILENT Or _ + ShFileOperationFlags.FOF_NOCONFIRMATION + + ' When calling MoveFileEx, set the following flags: + ' - Simulate CopyFile and DeleteFile if copied to a different volume. + ' - Replace contents of existing target with the contents of source file. + ' - Do not return until the file has actually been moved on the disk. + Private Const m_MOVEFILEEX_FLAGS As Integer = CInt( _ + MoveFileExFlags.MOVEFILE_COPY_ALLOWED Or _ + MoveFileExFlags.MOVEFILE_REPLACE_EXISTING Or _ + MoveFileExFlags.MOVEFILE_WRITE_THROUGH) + + ' Array containing all the path separator chars. Used to verify that input is a name, not a path. + Private Shared ReadOnly m_SeparatorChars() As Char = { _ + IO.Path.DirectorySeparatorChar, IO.Path.AltDirectorySeparatorChar, IO.Path.VolumeSeparatorChar} + + '''************************************************************************** + ''' ;CopyOrMove + ''' + ''' Private enumeration: The operation is a Copy or Move. + ''' + Private Enum CopyOrMove + Copy + Move + End Enum + + + '''************************************************************************** + ''' ;FileOrDirectory + ''' + ''' Private enumeration: Target of the operation is a File or Directory. + ''' + ''' + Private Enum FileOrDirectory + File + Directory + End Enum + + + '''************************************************************************** + ''' ;UIOptionInternal + ''' + ''' Private enumeration: Indicate the options of ShowUI to use internally. + ''' This includes NoUI so that we can base the decision on 1 variable. + ''' + ''' + Private Enum UIOptionInternal + OnlyErrorDialogs = UIOption.OnlyErrorDialogs + AllDialogs = UIOption.AllDialogs + NoUI + End Enum + + + '''************************************************************************** + ''' ;DirectoryNode + ''' + ''' A simple tree node to build up the directory structure used for a snapshot in Copy / Move Directory. + ''' + Private Class DirectoryNode + + '''************************************************************************** + ''' ;New + ''' + ''' Given a DirectoryPath, create the node and add the sub-directory nodes. + ''' + ''' Path to the directory. NOTE: must exist. + ''' Path to the target directory of the move / copy. NOTE: must be a full path. + Friend Sub New(ByVal DirectoryPath As String, ByVal TargetDirectoryPath As String) + Debug.Assert(IO.Directory.Exists(DirectoryPath), "Directory does not exist!!!") + Debug.Assert(TargetDirectoryPath <> "" And IO.Path.IsPathRooted(TargetDirectoryPath), "Invalid TargetPath!!!") + + m_Path = DirectoryPath + m_TargetPath = TargetDirectoryPath + m_SubDirs = New ObjectModel.Collection(Of DirectoryNode) + For Each SubDirPath As String In IO.Directory.GetDirectories(m_Path) + Dim SubTargetDirPath As String = IO.Path.Combine(m_TargetPath, IO.Path.GetFileName(SubDirPath)) + m_SubDirs.Add(New DirectoryNode(SubDirPath, SubTargetDirPath)) + Next + End Sub + + '''************************************************************************** + ''' ;Path + ''' + ''' Return the Path of the current node. + ''' + ''' A String containing the Path of the current node. + Friend ReadOnly Property Path() As String + Get + Return m_Path + End Get + End Property + + '''************************************************************************** + ''' ;TargetPath + ''' + ''' Return the TargetPath for copy / move. + ''' + ''' A String containing the copy / move target path of the current node. + Friend ReadOnly Property TargetPath() As String + Get + Return m_TargetPath + End Get + End Property + + '''************************************************************************** + ''' ;SubDirs + ''' + ''' Return the sub directories of the current node. + ''' + ''' A Collection(Of DirectoryNode) containing the sub-directory nodes. + Friend ReadOnly Property SubDirs() As ObjectModel.Collection(Of DirectoryNode) + Get + Return m_SubDirs + End Get + End Property + + Private m_Path As String + Private m_TargetPath As String + Private m_SubDirs As ObjectModel.Collection(Of DirectoryNode) + End Class 'Private Class DirectoryNode + + + '''************************************************************************** + ''' ;TextSearchHelper + ''' + ''' Helper class to search for text in an array of byte using a specific Decoder. + ''' + ''' + ''' To search for text that might exist in an encoding, construct this class with the text and Decoder. + ''' Then call IsTextFound() and pass in byte arrays. + ''' This class will take care of text spanning byte arrays by caching a part of the array and use it in + ''' the next IsTextFound() call. + ''' + Private Class TextSearchHelper + + '''************************************************************************** + ''' ;New + ''' + ''' Constructs a new helper with a given encoding and a text to search for. + ''' + ''' The Encoding to use to convert byte to text. + ''' The text to search for in subsequent byte array. + Friend Sub New(ByVal Encoding As Text.Encoding, ByVal Text As String, ByVal IgnoreCase As Boolean) + Debug.Assert(Encoding IsNot Nothing, "Null Decoder!!!") + Debug.Assert(Text <> "", "Empty Text!!!") + + m_Decoder = Encoding.GetDecoder + m_Preamble = Encoding.GetPreamble + m_IgnoreCase = IgnoreCase + + ' If use wants to ignore case, convert search text to lower case. + If m_IgnoreCase Then + m_SearchText = Text.ToUpper(CultureInfo.CurrentCulture) + Else + m_SearchText = Text + End If + End Sub + + '''************************************************************************** + ''' ;IsTextFound + ''' + ''' Determines whether the text is found in the given byte array. + ''' + ''' The byte array to find the text in + ''' The number of valid bytes in the byte array + ''' True if the text is found. Otherwise, False. + Friend Function IsTextFound(ByVal ByteBuffer() As Byte, ByVal Count As Integer) As Boolean + Debug.Assert(ByteBuffer IsNot Nothing, "Null ByteBuffer!!!") + Debug.Assert(Count > 0, Count.ToString(CultureInfo.InvariantCulture)) + Debug.Assert(m_Decoder IsNot Nothing, "Null Decoder!!!") + Debug.Assert(m_Preamble IsNot Nothing, "Null Preamble!!!") + + Dim ByteBufferStartIndex As Integer = 0 ' If need to handle BOM, ByteBufferStartIndex will increase. + + ' Check for the preamble the first time IsTextFound is called. If find it, shrink ByteBuffer. + If m_CheckPreamble Then + If BytesMatch(ByteBuffer, m_Preamble) Then + ByteBufferStartIndex = m_Preamble.Length + Count -= m_Preamble.Length ' Reduce the valid byte count if ByteBuffer was shrinked (VSWhidbey 361409). + End If + m_CheckPreamble = False + ' In case of an empty file with BOM at the beginning (VSWhidbey 518893), return FALSE. + If Count <= 0 Then + Return False + End If + End If + + ' Get the number of characters in the byte array. + Dim ExpectedCharCount As Integer = m_Decoder.GetCharCount(ByteBuffer, ByteBufferStartIndex, Count) + + ' The character buffer used to search will be a combination of the cached buffer and the current one. + Dim CharBuffer(m_PreviousCharBuffer.Length + ExpectedCharCount - 1) As Char + ' Start the buffer with the cached buffer. + Array.Copy(sourceArray:=m_PreviousCharBuffer, sourceIndex:=0, _ + destinationArray:=CharBuffer, destinationIndex:=0, length:=m_PreviousCharBuffer.Length) + ' And fill the rest with the ones from byte array. + Dim CharCount As Integer = m_Decoder.GetChars( _ + bytes:=ByteBuffer, byteIndex:=ByteBufferStartIndex, byteCount:=Count, _ + chars:=CharBuffer, charIndex:=m_PreviousCharBuffer.Length) + Debug.Assert(CharCount = ExpectedCharCount, "Should read all characters!!!") + + ' Refresh the cached buffer for the possible next search. + If CharBuffer.Length > m_SearchText.Length Then + If m_PreviousCharBuffer.Length <> m_SearchText.Length Then + ReDim m_PreviousCharBuffer(m_SearchText.Length - 1) + End If + Array.Copy(sourceArray:=CharBuffer, sourceIndex:=(CharBuffer.Length - m_SearchText.Length), _ + destinationArray:=m_PreviousCharBuffer, destinationIndex:=0, length:=m_SearchText.Length) + Else + m_PreviousCharBuffer = CharBuffer + End If + + ' If user wants to ignore case, convert new string to lower case. m_SearchText was converted in constructor. + If m_IgnoreCase Then + Return New String(CharBuffer).ToUpper(CultureInfo.CurrentCulture).Contains(m_SearchText) + Else + Return New String(CharBuffer).Contains(m_SearchText) + End If + End Function + + '''************************************************************************** + ''' ;New + ''' + ''' No default constructor. + ''' + Private Sub New() + End Sub + + '''************************************************************************** + ''' + ''' Returns whether the big buffer starts with the small buffer. + ''' + ''' + ''' + ''' True if BigBuffer starts with SmallBuffer.Otherwise, False. + Private Shared Function BytesMatch(ByVal BigBuffer() As Byte, ByVal SmallBuffer() As Byte) As Boolean + Debug.Assert(BigBuffer.Length > SmallBuffer.Length, "BigBuffer should be longer!!!") + If BigBuffer.Length < SmallBuffer.Length Or SmallBuffer.Length = 0 Then + Return False + End If + For i As Integer = 0 To SmallBuffer.Length - 1 + If BigBuffer(i) <> SmallBuffer(i) Then + Return False + End If + Next + Return True + End Function + + Private m_SearchText As String ' The text to search. + Private m_IgnoreCase As Boolean ' Should we ignore case? + Private m_Decoder As Text.Decoder ' The Decoder to use. + Private m_PreviousCharBuffer() As Char = {} ' The cached character array from previous call to IsTextExist. + Private m_CheckPreamble As Boolean = True ' True to check for preamble. False otherwise. + Private m_Preamble() As Byte ' The byte order mark we need to consider. + End Class 'Private Class TextSearchHelper + + + End Class 'Public Class FileSystem + + '''************************************************************************** + ''' ;DeleteDirectoryOption + ''' + ''' Specify the action to do when deleting a directory and it is not empty. + ''' + ''' + ''' Again, avoid Integer values that VB Compiler will convert Boolean to (0 and -1). VSWhidbey 522083. + ''' IMPORTANT: Change VerifyDeleteDirectoryOption if this enum is changed. + ''' Also, values in DeleteDirectoryOption must be different from UIOption. VSWhidbey 491042. + ''' + Public Enum DeleteDirectoryOption As Integer + ThrowIfDirectoryNonEmpty = 4 + DeleteAllContents = 5 + End Enum + + '''************************************************************************** + ''' ;RecycleOption + ''' + ''' Specify whether to delete a file / directory to Recycle Bin or not. + ''' + Public Enum RecycleOption As Integer + DeletePermanently = 2 + SendToRecycleBin = 3 + End Enum + + '''************************************************************************** + ''' ;SearchOption + ''' + ''' Specify whether to perform the search for files/directories recursively or not. + ''' + Public Enum SearchOption As Integer + SearchTopLevelOnly = 2 + SearchAllSubDirectories = 3 + End Enum + + '''************************************************************************** + ''' ;UICancelOption + ''' + ''' Defines option whether to throw exception when user cancels a UI operation or not. + ''' + Public Enum UICancelOption As Integer + DoNothing = 2 + ThrowException = 3 + End Enum + + '''************************************************************************** + ''' ;UIOption + ''' + ''' Specify which UI dialogs to show. + ''' + ''' + ''' To fix common issues of VSWhidbey 474856, 499359; avoid Integer values that VB Compiler + ''' will convert Boolean to (0 and -1). + ''' + Public Enum UIOption As Integer + OnlyErrorDialogs = 2 + AllDialogs = 3 + End Enum + +End Namespace + +' NOTE: +' - All path returned by us will NOT have the Directory Separator Character ('\') at the end. (VSWhidbey 54741). +' - All path accepted by us will NOT consider the meaning of Directory Separator Character ('\') at the end. +' - Parameter accepting path will accept both relative and absolute paths unless specified. +' Relative paths will be resolved using the current working directory. +' - IO.Path.GetFullPath is used to normalized the path. It will only throw in case of not well-formed path. +' - Hidden Files and Directories will be moved / copied by Framework code. +' +' - On both Read and Write, we use the default Share mode that FX uses for the StreamReader/Writer, which is Share.Read. +' Details on what share mode means: +' When a call is made to open the file, the share mode not only means that the caller wants to restrict every call +' afterwards, but also every call before as well, which means that the caller will fail if any calls before it +' already obtained a conflict right. +' For example: if this call succeeds, +' Open(FileA, OpenMode.Write, ShareMode.Read) +' Although it is sharing FileA for reading, if the 2nd call is +' Open(FileA, OpenMode.Read, ShareMode.Read) +' the 2nd call will fail since it wants to restrict everybody else to read only, but 1st caller has already obtained +' write access. +' So the default behavior is fine since novice Mort can't run into trouble using it. +' +' - All IO functions involving ShowUI have dependency on Windows Shell and sometimes have different behavior. +' - CopyDirectory will attempt to copy all the files in the directory. If there are files or sub-directories +' that cause exception, CopyDirectory will not stop, since that will leave the result in unknown state. +' Instead, an exception will be thrown at the end containing a list of exception files in Data property. +' - MoveDirectory behaves the same so MoveDirectory is not equal to calling CopyDirectory and DeleteDirectory. +' - Overwrite in directory case means overwrite sub files. Sub directories will always be merged. +' +' - 2004/08/09: Including the Overwrite option and ShowUI in one method is confusing +' since there are cases Shell methods will ask questions, even with NOCONFIRMATION flag on. +' We made changes to separate methods containing Overwrite and ShowUI. UE should notice this. + +' Shell behavior in exception cases: +' - Copy / Move File +' . Existing target: +' Overwrite = True: Overwrite target. +' Overwrite = False: Dialog Yes: Overwrite target. +' No: Error code 7. ERROR_ARENA_TRASHED +' . Existing target and Read-Only (Framework will throw). +' Always ask. No: Error code 7. ERROR_ARENA_TRASHED +' . OS access denied: Error code 1223. ERROR_CANCELLED +' - Copy / Move Directory Existing target: +' . Has an existing file: +' Overwrite = True: Overwrite file. +' Overwrite = False: Dialog Yes / Yes to all : Overwrite target. +' No: Leave and copy the rest. +' Cancel: Error code 2. ERROR_FILE_NOT_FOUND. +' . Has an existing file and Read-Only (Framework will throw). +' Behave as when Overwrite = False. +' . File in source same name with directory in target: +' * Copy: Error code 1223 ERROR_CANCELLED. +' * Move: Overwrite = True: Error code 183. ERROR_ALREADY_EXISTS. +' Overwrite = False: Ask question Yes: Error code 183. +' Cancel: Error code 2. +' . Directory in source same name with file in target: +' Error code 183 in all cases. +' +' NOTE: Some different behavior when deleting files / directories. +' ShowUI RecycleBin Normal file. Read-only file. +' F F Gone Exception. * +' T F Question + UI + Gone Question + UI + Gone +' F T Bin Question + Bin * +' T T Question + UI + Bin Question + UI + Bin diff --git a/Microsoft.VisualBasic/runtime/msvbalib/FileIO/MalformedLineException.vb b/Microsoft.VisualBasic/runtime/msvbalib/FileIO/MalformedLineException.vb new file mode 100644 index 000000000..d35d6d472 --- /dev/null +++ b/Microsoft.VisualBasic/runtime/msvbalib/FileIO/MalformedLineException.vb @@ -0,0 +1,163 @@ +' Copyright (c) Microsoft Corporation. All rights reserved. +Option Explicit On +Option Strict On + +Imports System +Imports System.ComponentModel +Imports System.Globalization +Imports System.Security +Imports System.Security.Permissions + +Imports Microsoft.VisualBasic.CompilerServices +Imports Microsoft.VisualBasic.CompilerServices.Utils + +Namespace Microsoft.VisualBasic.FileIO + + '''************************************************************************ + ''';MalformedLineException + ''' + ''' Indicates a line cannot be parsed into fields + ''' + ''' + _ + Public Class MalformedLineException + Inherits Exception + + '==PUBLIC************************************************************* + + '''******************************************************************** + ''';New + ''' + ''' Creates a new exception with no properties set + ''' + ''' + Public Sub New() + MyBase.New() + End Sub + + '''******************************************************************** + ''';New + ''' + ''' Creates a new exception, setting Message and LineNumber + ''' + ''' The message for the exception + ''' The number of the line that is malformed + ''' + Public Sub New(ByVal message As String, ByVal lineNumber As Long) + MyBase.New(message) + m_LineNumber = lineNumber + End Sub + + '''******************************************************************** + ''';New + ''' + ''' Creates a new exception, setting Message + ''' + ''' The message for the exception + ''' + Public Sub New(ByVal message As String) + MyBase.New(message) + End Sub + + '''******************************************************************** + ''';New + ''' + ''' Creates a new exception, setting Message, LineNumber, and InnerException + ''' + ''' The message for the exception + ''' The number of the line that is malformed + ''' The inner exception for the exception + ''' + Public Sub New(ByVal message As String, ByVal lineNumber As Long, ByVal innerException As Exception) + MyBase.New(message, innerException) + m_LineNumber = lineNumber + End Sub + + '''********************************************************************* + ''';New + ''' + ''' Creates a new exception, setting Message and InnerException + ''' + ''' The message for the exception + ''' The inner exception for the exception + ''' + Public Sub New(ByVal message As String, ByVal innerException As Exception) + MyBase.New(message, innerException) + End Sub + + '''********************************************************************* + ''';New + ''' + ''' Constructor used for serialization + ''' + ''' + ''' + ''' + _ + Protected Sub New(ByVal info As System.Runtime.Serialization.SerializationInfo, ByVal context As System.Runtime.Serialization.StreamingContext) + MyBase.New(info, context) + + If info IsNot Nothing Then ' Fix FxCop violation ValidateArgumentsOfPublicMethods. + m_LineNumber = info.GetInt32(LINE_NUMBER_PROPERTY) + Else + m_LineNumber = -1 + End If + End Sub + + '''******************************************************************** + ''';LineNumber + ''' + ''' The number of the offending line + ''' + ''' The line number + ''' + _ + Public Property LineNumber() As Long + Get + Return m_LineNumber + End Get + Set(ByVal value As Long) + m_LineNumber = value + End Set + End Property + + '''******************************************************************** + ''';GetObjectData + ''' + ''' Supports serialization + ''' + ''' + ''' + ''' + _ + _ + _ + Public Overrides Sub GetObjectData(ByVal info As System.Runtime.Serialization.SerializationInfo, ByVal context As System.Runtime.Serialization.StreamingContext) + If info IsNot Nothing Then ' Fix FxCop violation ValidateArgumentsOfPublicMethods. + info.AddValue(LINE_NUMBER_PROPERTY, m_LineNumber, GetType(Long)) + End If + + MyBase.GetObjectData(info, context) + End Sub + + '''*************************************************************** + ''';ToString + ''' + ''' Appends extra data to string so that it's available when the exception is caught as an Exception + ''' + ''' The base ToString plus the Line Number + ''' + Public Overrides Function ToString() As String + Return MyBase.ToString() & " " & GetResourceString(ResID.MyID.TextFieldParser_MalformedExtraData, LineNumber.ToString(CultureInfo.InvariantCulture)) + End Function + + '==PRIVATE************************************************************ + + ' Holds the line number + Private m_LineNumber As Long + + ' Name of property used for serialization + Private Const LINE_NUMBER_PROPERTY As String = "LineNumber" + + End Class +End Namespace diff --git a/Microsoft.VisualBasic/runtime/msvbalib/FileIO/SpecialDirectories.vb b/Microsoft.VisualBasic/runtime/msvbalib/FileIO/SpecialDirectories.vb new file mode 100644 index 000000000..a652dcfec --- /dev/null +++ b/Microsoft.VisualBasic/runtime/msvbalib/FileIO/SpecialDirectories.vb @@ -0,0 +1,195 @@ +' Copyright (c) Microsoft Corporation. All rights reserved. +Option Strict On +Option Explicit On + +Imports System +Imports System.Diagnostics +Imports System.Environment +Imports System.Security.Permissions + +Imports Microsoft.VisualBasic.CompilerServices +Imports Microsoft.VisualBasic.CompilerServices.Utils +Imports ExUtils = Microsoft.VisualBasic.CompilerServices.ExceptionUtils + +Namespace Microsoft.VisualBasic.FileIO + + '''************************************************************************** + ''' ;SpecialDirectories + ''' + ''' This class contains properties that will return the Special Directories + ''' specific to the current user (My Documents, My Music ...) and those specific + ''' to the current Application that a developer expects to be able to find quickly. + ''' + _ + Public Class SpecialDirectories + + '= PUBLIC ============================================================= + + '''************************************************************************** + ''' ;MyDocuments + ''' + ''' Return the directory that serves as a common repository for user's personal documents. + ''' + ''' A String containing the path to the user's personal documents. + ''' If the system does not have the notion of My Documents directory. + ''' This directory is usually: C:\Documents and Settings\[UserName]\My Documents. + Public Shared ReadOnly Property MyDocuments() As String + Get + Return GetDirectoryPath(Environment.GetFolderPath(SpecialFolder.Personal), ResID.MyID.IO_SpecialDirectory_MyDocuments) + End Get + End Property + + '''************************************************************************** + ''' ;MyMusic + ''' + ''' Return the "My Music" directory. + ''' + ''' A String containing the path to the user's "My Music" directory. + ''' If the system does not have the notion of My Music directory. + ''' This directory is C:\Documents and Settings\[UserName]\My Music + Public Shared ReadOnly Property MyMusic() As String + Get + Return GetDirectoryPath(Environment.GetFolderPath(SpecialFolder.MyMusic), ResID.MyID.IO_SpecialDirectory_MyMusic) + End Get + End Property + + '''************************************************************************** + ''' ;MyPictures + ''' + ''' Return the "My Pictures" directory. + ''' + ''' A String containing the path to the user's "My Pictures" directory. + ''' If the system does not have the notion of My Pictures directory. + ''' This directory is C:\Documents and Settings\[UserName]\My Pictures. + Public Shared ReadOnly Property MyPictures() As String + Get + Return GetDirectoryPath(Environment.GetFolderPath(SpecialFolder.MyPictures), ResID.MyID.IO_SpecialDirectory_MyPictures) + End Get + End Property + + '''************************************************************************** + ''' ;Desktop + ''' + ''' Return the current user's Desktop directory. + ''' + ''' A String containing the path to the current user's Desktop directory. + ''' This directory is C:\Document and Settings\[UserName]\Desktop. + Public Shared ReadOnly Property Desktop() As String + Get + Return GetDirectoryPath(Environment.GetFolderPath(SpecialFolder.Desktop), ResID.MyID.IO_SpecialDirectory_Desktop) + End Get + End Property + + '''************************************************************************** + ''' ;Programs + ''' + ''' Returns the directory used to store program shortcuts from Start Menu for current user. + ''' + ''' A String containing the path to the Start Menu \ Programs directory. + ''' This directory is C:\Document and Settings\[UserName]\Start Menu\Programs. + Public Shared ReadOnly Property Programs() As String + Get + Return GetDirectoryPath(Environment.GetFolderPath(SpecialFolder.Programs), ResID.MyID.IO_SpecialDirectory_Programs) + End Get + End Property + + '''************************************************************************** + ''' ;ProgramFiles + ''' + ''' Return the program files directory. + ''' + ''' A String containing the path to the default program directories. + ''' This directory is C:\Program Files. + Public Shared ReadOnly Property ProgramFiles() As String + Get + Return GetDirectoryPath(Environment.GetFolderPath(SpecialFolder.ProgramFiles), ResID.MyID.IO_SpecialDirectory_ProgramFiles) + End Get + End Property + + '''************************************************************************** + ''' ;Temp + ''' + ''' Return the directory that contain temporary files for the current user. + ''' + ''' A String containing the path to the temporary directory for the current user. + ''' + ''' According to Win32 API document, GetTempPath should always return a value even if TEMP and TMP = "". + ''' Also, this is not updated if TEMP or TMP is changed in Windows. The reason is + ''' each process has its own copy of the environment variables and this copy is not updated. + ''' + Public Shared ReadOnly Property Temp() As String + Get + Return GetDirectoryPath(IO.Path.GetTempPath(), ResID.MyID.IO_SpecialDirectory_Temp) + End Get + End Property + + '''************************************************************************** + ''' ;CurrentUserApplicationData + ''' + ''' Returns the directory that serves as a common repository for data files + ''' from your application used only by the current user. + ''' + ''' A String containing the path to the directory your application can use to store data for the current user. + ''' + ''' If a path does not exist, one is created in the following format + ''' C:\Documents and Settings\[UserName]\Application Data\[CompanyName]\[ProductName]\[ProductVersion] + ''' + ''' We choose to use System.Windows.Forms.Application.* instead of System.Environment.GetFolderPath(*) + ''' since the second function will only return the C:\Documents and Settings\[UserName]\Application Data.\ + ''' The first function separates applications by CompanyName, ProductName, ProductVersion. + ''' The only catch is that CompanyName, ProductName has to be specified in the AssemblyInfo.vb file, + ''' otherwise the name of the assembly will be used instead (which still has a level of separation). + ''' + ''' Also, we chose to use UserAppDataPath instead of LocalUserAppDataPath since this directory + ''' will work with Roaming User as well. + ''' + Public Shared ReadOnly Property CurrentUserApplicationData() As String + Get + Return GetDirectoryPath(System.Windows.Forms.Application.UserAppDataPath, ResID.MyID.IO_SpecialDirectory_UserAppData) + End Get + End Property + + '''************************************************************************** + ''' ;AllUsersApplicationData + ''' + ''' Returns the directory that serves as a common repository for data files + ''' from your application used by all users. + ''' + ''' A String containing the path to the directory your application can use to store data for all users. + ''' + ''' If a path does not exist, one is created in the following format + ''' C:\Documents and Settings\All Users\Application Data\[CompanyName]\[ProductName]\[ProductVersion] + ''' + ''' See above for reason why we don't use System.Environment.GetFolderPath(*). + ''' + Public Shared ReadOnly Property AllUsersApplicationData() As String + Get + Return GetDirectoryPath(System.Windows.Forms.Application.CommonAppDataPath, ResID.MyID.IO_SpecialDirectory_AllUserAppData) + End Get + End Property + + + '= FRIEND ============================================================= + + '= PROTECTED ========================================================== + + '= PRIVATE ============================================================ + + '''************************************************************************** + ''' ;GetDirectoryPath + ''' + ''' Return a normalized from a directory path and throw exception if directory path is "". + ''' + ''' The special directory's path got back from FX. "" if it does not exist. + ''' The resource ID of the special directory's localized name. + ''' A String containing the path to the special directory if success. + Private Shared Function GetDirectoryPath(ByVal Directory As String, ByVal DirectoryNameResID As String) As String + ' CONSIDER: create the directory if not exist. VSWhidbey 163316. + ' Only need to worry about Directory being "" since it comes from Framework. + If Directory = "" Then + Throw ExUtils.GetDirectoryNotFoundException(ResID.MyID.IO_SpecialDirectoryNotExist, GetResourceString(DirectoryNameResID)) + End If + Return FileSystem.NormalizePath(Directory) + End Function + End Class +End Namespace diff --git a/Microsoft.VisualBasic/runtime/msvbalib/FileIO/TextFieldParser.vb b/Microsoft.VisualBasic/runtime/msvbalib/FileIO/TextFieldParser.vb new file mode 100644 index 000000000..e0629ac10 --- /dev/null +++ b/Microsoft.VisualBasic/runtime/msvbalib/FileIO/TextFieldParser.vb @@ -0,0 +1,1860 @@ +' Copyright (c) Microsoft Corporation. All rights reserved. +Option Explicit On +Option Strict On + +Imports System +Imports System.ComponentModel +Imports System.Diagnostics +Imports System.Globalization +Imports System.IO +Imports System.Security.Permissions +Imports System.Text +Imports System.Text.RegularExpressions +Imports Microsoft.VisualBasic +Imports Microsoft.VisualBasic.CompilerServices +Imports Microsoft.VisualBasic.CompilerServices.ExceptionUtils +Imports Microsoft.VisualBasic.CompilerServices.Utils + +Namespace Microsoft.VisualBasic.FileIO + + '''************************************************************************* + ''';TextFieldParser + ''' + ''' Enables parsing very large delimited or fixed width field files + ''' + ''' + Public Class TextFieldParser + Implements IDisposable + + '==PUBLIC************************************************************** + + '''********************************************************************* + ''';New + ''' + ''' Creates a new TextFieldParser to parse the passed in file + ''' + ''' The path of the file to be parsed + ''' + _ + Public Sub New(ByVal path As String) + + ' Default to UTF-8 and detect encoding + InitializeFromPath(path, System.Text.Encoding.UTF8, True) + End Sub + + '''********************************************************************* + ''';New + ''' + ''' Creates a new TextFieldParser to parse the passed in file + ''' + ''' The path of the file to be parsed + ''' The decoding to default to if encoding isn't determined from file + ''' + _ + Public Sub New(ByVal path As String, ByVal defaultEncoding As System.Text.Encoding) + + ' Default to detect encoding + InitializeFromPath(path, defaultEncoding, True) + End Sub + + '''********************************************************************* + ''';New + ''' + ''' Creates a new TextFieldParser to parse the passed in file + ''' + ''' The path of the file to be parsed + ''' The decoding to default to if encoding isn't determined from file + ''' Indicates whether or not to try to detect the encoding from the BOM + ''' + _ + Public Sub New(ByVal path As String, ByVal defaultEncoding As System.Text.Encoding, ByVal detectEncoding As Boolean) + + InitializeFromPath(path, defaultEncoding, detectEncoding) + End Sub + + '''********************************************************************* + ''';New + ''' + ''' Creates a new TextFieldParser to parse a file represented by the passed in stream + ''' + ''' + ''' + _ + Public Sub New(ByVal stream As Stream) + + ' Default to UTF-8 and detect encoding + InitializeFromStream(stream, System.Text.Encoding.UTF8, True) + End Sub + + '''********************************************************************* + ''';New + ''' + ''' Creates a new TextFieldParser to parse a file represented by the passed in stream + ''' + ''' + ''' The decoding to default to if encoding isn't determined from file + ''' + _ + Public Sub New(ByVal stream As Stream, ByVal defaultEncoding As System.Text.Encoding) + + ' Default to detect encoding + InitializeFromStream(stream, defaultEncoding, True) + End Sub + + '''********************************************************************* + ''';New + ''' + ''' Creates a new TextFieldParser to parse a file represented by the passed in stream + ''' + ''' + ''' The decoding to default to if encoding isn't determined from file + ''' Indicates whether or not to try to detect the encoding from the BOM + ''' + _ + Public Sub New(ByVal stream As Stream, ByVal defaultEncoding As System.Text.Encoding, ByVal detectEncoding As Boolean) + + InitializeFromStream(stream, defaultEncoding, detectEncoding) + End Sub + + '''********************************************************************* + ''';New + ''' + ''' Creates a new TextFieldParser to parse a file represented by the passed in stream + ''' + ''' + ''' The decoding to default to if encoding isn't determined from file + ''' Indicates whether or not to try to detect the encoding from the BOM + ''' Indicates whether or not to leave the passed in stream open + ''' + _ + Public Sub New(ByVal stream As Stream, ByVal defaultEncoding As System.Text.Encoding, ByVal detectEncoding As Boolean, ByVal leaveOpen As Boolean) + + m_LeaveOpen = leaveOpen + InitializeFromStream(stream, defaultEncoding, detectEncoding) + End Sub + + '''********************************************************************* + ''';New + ''' + ''' Creates a new TextFieldParser to parse a stream or file represented by the passed in TextReader + ''' + ''' The TextReader that does the reading + ''' + _ + Public Sub New(ByVal reader As TextReader) + + If reader Is Nothing Then + Throw GetArgumentNullException("reader") + End If + + m_Reader = reader + + ReadToBuffer() + + End Sub + + '''********************************************************************** + ''';CommentTokens + ''' + ''' An array of the strings that indicate a line is a comment + ''' + ''' An array of comment indicators + ''' Returns an empty array if not set + _ + Public Property CommentTokens() As String() + Get + Return m_CommentTokens + End Get + Set(ByVal value As String()) + CheckCommentTokensForWhitespace(value) + m_CommentTokens = value + m_NeedPropertyCheck = True + End Set + End Property + + '''******************************************************************* + ''';EndOfData + ''' + ''' Indicates whether or not there is any data (non ignorable lines) left to read in the file + ''' + ''' True if there's more data to read, otherwise False + ''' Ignores comments and blank lines + Public ReadOnly Property EndOfData() As Boolean + Get + If m_EndOfData Then + Return m_EndOfData + End If + + ' Make sure we're not at end of file + If m_Reader Is Nothing Or m_Buffer Is Nothing Then + m_EndOfData = True + Return True + End If + + 'See if we can get a data line + If PeekNextDataLine() IsNot Nothing Then + Return False + End If + + m_EndOfData = True + Return True + End Get + End Property + + '''******************************************************************* + ''';LineNumber + ''' + ''' The line to the right of the cursor. + ''' + ''' The number of the line + ''' LineNumber returns the location in the file and has nothing to do with rows or fields + _ + Public ReadOnly Property LineNumber() As Long + Get + If m_LineNumber <> -1 Then + + ' See if we're at the end of file + If m_Reader.Peek = -1 And m_Position = m_CharsRead Then + CloseReader() + End If + End If + + Return m_LineNumber + End Get + End Property + + '''******************************************************************* + ''';ErrorLine + ''' + ''' Returns the last malformed line if there is one. + ''' + ''' The last malformed line + ''' + Public ReadOnly Property ErrorLine() As String + Get + Return m_ErrorLine + End Get + End Property + + '''******************************************************************* + ''';ErrorLineNumber + ''' + ''' Returns the line number of last malformed line if there is one. + ''' + ''' The last malformed line line number + ''' + Public ReadOnly Property ErrorLineNumber() As Long + Get + Return m_ErrorLineNumber + End Get + End Property + + '''******************************************************************* + ''';TextFieldType + ''' + ''' Indicates the type of file being read, either fixed width or delimited + ''' + ''' The type of fields in the file + ''' + Public Property TextFieldType() As FieldType + Get + Return m_TextFieldType + End Get + Set(ByVal value As FieldType) + ValidateFieldTypeEnumValue(value, "value") + m_TextFieldType = value + m_NeedPropertyCheck = True + End Set + End Property + + '''****************************************************************** + ''';FieldWidths + ''' + ''' Gets or sets the widths of the fields for reading a fixed width file + ''' + ''' An array of the widths + ''' + Public Property FieldWidths() As Integer() + Get + Return m_FieldWidths + End Get + Set(ByVal value As Integer()) + If value IsNot Nothing Then + ValidateFieldWidthsOnInput(value) + + ' Keep a copy so we can determine if the user changes elements of the array + m_FieldWidthsCopy = DirectCast(value.Clone(), Integer()) + Else + m_FieldWidthsCopy = Nothing + End If + + m_FieldWidths = value + m_NeedPropertyCheck = True + End Set + End Property + + '''******************************************************************** + ''';Delimiters + ''' + ''' Gets or sets the delimiters used in a file + ''' + ''' An array of the delimiters + ''' + Public Property Delimiters() As String() + Get + Return m_Delimiters + End Get + Set(ByVal value As String()) + If value IsNot Nothing Then + ValidateDelimiters(value) + + ' Keep a copy so we can determine if the user changes elements of the array + m_DelimitersCopy = DirectCast(value.Clone(), String()) + Else + m_DelimitersCopy = Nothing + End If + + m_Delimiters = value + + m_NeedPropertyCheck = True + + ' Force rebuilding of regex + m_BeginQuotesRegex = Nothing + + End Set + End Property + + '''******************************************************************* + ''';SetDelimiters + ''' + ''' Helper function to enable setting delimiters without diming an array + ''' + ''' A list of the delimiters + ''' + Public Sub SetDelimiters(ByVal ParamArray delimiters As String()) + Me.Delimiters = delimiters + End Sub + + '''******************************************************************* + ''';SetFieldWidths + ''' + ''' Helper function to enable setting field widths without diming an array + ''' + ''' A list of field widths + ''' + Public Sub SetFieldWidths(ByVal ParamArray fieldWidths As Integer()) + Me.FieldWidths = fieldWidths + End Sub + + '''******************************************************************* + ''';TrimWhiteSpace + ''' + ''' Indicates whether or not leading and trailing white space should be removed when returning a field + ''' + ''' True if white space should be removed, otherwise False + ''' + Public Property TrimWhiteSpace() As Boolean + Get + Return m_TrimWhiteSpace + End Get + Set(ByVal value As Boolean) + m_TrimWhiteSpace = value + End Set + End Property + + '''******************************************************************** + ''';ReadLine + ''' + ''' Reads and returns the next line from the file + ''' + ''' The line read or Nothing if at the end of the file + ''' This is data unaware method. It simply reads the next line in the file. + _ + Public Function ReadLine() As String + + If m_Reader Is Nothing Or m_Buffer Is Nothing Then + Return Nothing + End If + + Dim Line As String + + ' Set the method to be used when we reach the end of the buffer + Dim BufferFunction As New ChangeBufferFunction(AddressOf ReadToBuffer) + + Line = ReadNextLine(m_Position, BufferFunction) + + If Line Is Nothing Then + FinishReading() + Return Nothing + Else + m_LineNumber += 1 + Return Line.TrimEnd(Chr(13), Chr(10)) + End If + + End Function + + '''******************************************************************* + ''';ReadFields + ''' + ''' Reads a non ignorable line and parses it into fields + ''' + ''' The line parsed into fields + ''' This is a data aware method. Comments and blank lines are ignored. + Public Function ReadFields() As String() + + If m_Reader Is Nothing Or m_Buffer Is Nothing Then + Return Nothing + End If + + ValidateReadyToRead() + + Select Case m_TextFieldType + Case FieldType.FixedWidth + Return ParseFixedWidthLine() + Case FieldType.Delimited + Return ParseDelimitedLine() + Case Else + Debug.Fail("The TextFieldType is not supported") + End Select + Return Nothing + End Function + + '''******************************************************************** + ''';PeekChars + ''' + ''' Enables looking at the passed in number of characters of the next data line without reading the line + ''' + ''' + ''' A string consisting of the first NumberOfChars characters of the next line + ''' If numberOfChars is greater than the next line, only the next line is returned + Public Function PeekChars(ByVal numberOfChars As Integer) As String + + If numberOfChars <= 0 Then + Throw GetArgumentExceptionWithArgName("numberOfChars", ResID.MyID.TextFieldParser_NumberOfCharsMustBePositive, "numberOfChars") + End If + + If m_Reader Is Nothing Or m_Buffer Is Nothing Then + Return Nothing + End If + + ' If we know there's no more data return Nothing + If m_EndOfData Then + Return Nothing + End If + + ' Get the next line without reading it + Dim Line As String = PeekNextDataLine() + + If Line Is Nothing Then + m_EndOfData = True + Return Nothing + End If + + ' Strip of end of line chars + Line = Line.TrimEnd(Chr(13), Chr(10)) + + ' If the number of chars is larger than the line, return the whole line. Otherwise + ' return the NumberOfChars characters from the beginning of the line + If Line.Length < numberOfChars Then + Return Line + Else + Dim info As New StringInfo(Line) + Return info.SubstringByTextElements(0, numberOfChars) + End If + + End Function + + '''******************************************************************** + ''';ReadToEnd + ''' + ''' Reads the file starting at the current position and moving to the end of the file + ''' + ''' The contents of the file from the current position to the end of the file + ''' This is not a data aware method. Everything in the file from the current position to the end is read + _ + Public Function ReadToEnd() As String + + If m_Reader Is Nothing Or m_Buffer Is Nothing Then + Return Nothing + End If + + + Dim Builder As New System.Text.StringBuilder(m_Buffer.Length) + + ' Get the lines in the Buffer first + Builder.Append(m_Buffer, m_Position, m_CharsRead - m_Position) + + ' Add what we haven't read + Builder.Append(m_Reader.ReadToEnd()) + + FinishReading() + + Return Builder.ToString() + + End Function + + '''********************************************************************* + ''';HasFieldsEnclosedInQuotes + ''' + ''' Indicates whether or not to handle quotes in a csv friendly way + ''' + ''' True if we escape quotes otherwise false + ''' + _ + Public Property HasFieldsEnclosedInQuotes() As Boolean + Get + Return m_HasFieldsEnclosedInQuotes + End Get + Set(ByVal value As Boolean) + m_HasFieldsEnclosedInQuotes = value + End Set + End Property + + '''********************************************************************** + ''';Close + ''' + ''' Closes the StreamReader + ''' + ''' + Public Sub Close() + CloseReader() + End Sub + + '''********************************************************************** + ''';Dispose + ''' + ''' Closes the StreamReader + ''' + ''' + Public Sub Dispose() Implements System.IDisposable.Dispose + Dispose(True) + GC.SuppressFinalize(Me) + End Sub + + '==PROTECTED************************************************************** + + '''*********************************************************************** + ''' ;Dispose + ''' + ''' Standard implementation of IDisposable.Dispose for non sealed classes. Classes derived from + ''' TextFieldParser should override this method. After doing their own cleanup, they should call + ''' this method (MyBase.Dispose(disposing)) + ''' + ''' Indicates we are called by Dispose and not GC + ''' + Protected Overridable Sub Dispose(ByVal disposing As Boolean) + If disposing Then + If Not Me.m_Disposed Then + Close() + End If + Me.m_Disposed = True + End If + End Sub + + '''************************************************************************** + ''' ;ValidateFieldTypeEnumValue + ''' + ''' Validates that the value being passed as an AudioPlayMode enum is a legal value + ''' + ''' + ''' + Private Sub ValidateFieldTypeEnumValue(ByVal value As FieldType, ByVal paramName As String) + If value < FieldType.Delimited OrElse value > FieldType.FixedWidth Then + Throw New System.ComponentModel.InvalidEnumArgumentException(paramName, DirectCast(value, Integer), GetType(FieldType)) + End If + End Sub + + + '''******************************************************************************* + ''';Finalize + ''' + ''' Clean up following dispose pattern + ''' + ''' + Protected Overrides Sub Finalize() + ' Do not change this code. Put cleanup code in Dispose(ByVal disposing As Boolean) above. + Dispose(False) + MyBase.Finalize() + End Sub + + '==PRIVATE************************************************************** + + '''********************************************************************** + ''';CloseReader + ''' + ''' Closes the StreamReader + ''' + ''' + Private Sub CloseReader() + + FinishReading() + If m_Reader IsNot Nothing Then + If Not m_LeaveOpen Then + m_Reader.Close() + End If + m_Reader = Nothing + End If + End Sub + + '''********************************************************************** + ''';FinishReading + ''' + ''' Cleans up managed resources except the StreamReader and indicates reading is finished + ''' + ''' + Private Sub FinishReading() + + m_LineNumber = -1 + m_EndOfData = True + m_Buffer = Nothing + m_DelimiterRegex = Nothing + m_BeginQuotesRegex = Nothing + + End Sub + + ''';InitializeFromPath + ''' + ''' Creates a StreamReader for the passed in Path + ''' + ''' The passed in path + ''' The encoding to default to if encoding can't be detected + ''' Indicates whether or not to detect encoding from the BOM + ''' We validate the arguments here for the three Public constructors that take a Path + Private Sub InitializeFromPath(ByVal path As String, ByVal defaultEncoding As System.Text.Encoding, ByVal detectEncoding As Boolean) + + If path = "" Then + Throw GetArgumentNullException("path") + End If + + If defaultEncoding Is Nothing Then + Throw GetArgumentNullException("defaultEncoding") + End If + + Dim fullPath As String = ValidatePath(path) + Dim fileStreamTemp As New FileStream(fullPath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite) + m_Reader = New StreamReader(fileStreamTemp, defaultEncoding, detectEncoding) + + ReadToBuffer() + + End Sub + + '''********************************************************************* + ''';InitializeFromStream + ''' + ''' Creates a StreamReader for a passed in stream + ''' + ''' The passed in stream + ''' The encoding to default to if encoding can't be detected + ''' Indicates whether or not to detect encoding from the BOM + ''' We validate the arguments here for the three Public constructors that take a Stream + Private Sub InitializeFromStream(ByVal stream As Stream, ByVal defaultEncoding As System.Text.Encoding, ByVal detectEncoding As Boolean) + + If stream Is Nothing Then + Throw GetArgumentNullException("stream") + End If + + If Not stream.CanRead Then + Throw GetArgumentExceptionWithArgName("stream", ResID.MyID.TextFieldParser_StreamNotReadable, "stream") + End If + + If defaultEncoding Is Nothing Then + Throw GetArgumentNullException("defaultEncoding") + End If + + m_Reader = New StreamReader(stream, defaultEncoding, detectEncoding) + + ReadToBuffer() + End Sub + + + '''********************************************************************** + ''';ValidatePath + ''' + ''' Gets full name and path from passed in path. + ''' + ''' The path to be validated + ''' The full name and path + ''' Throws if the file doesn't exist or if the path is malformed + Private Function ValidatePath(ByVal path As String) As String + + ' Validate and get full path + Dim fullPath As String = FileSystem.NormalizeFilePath(path, "path") + + ' Make sure the file exists + If Not File.Exists(fullPath) Then + Throw New IO.FileNotFoundException(GetResourceString(ResID.MyID.IO_FileNotFound_Path, fullPath)) + End If + + Return fullPath + End Function + + '''*********************************************************************** + ''';IgnoreLine + ''' + ''' Indicates whether or not the passed in line should be ignored + ''' + ''' The line to be tested + ''' True if the line should be ignored, otherwise False + ''' Lines to ignore are blank lines and comments + Private Function IgnoreLine(ByVal line As String) As Boolean + + ' If the Line is Nothing, it has meaning (we've reached the end of the file) so don't + ' ignore it + If line Is Nothing Then + Return False + End If + + ' Ignore empty or whitespace lines + Dim TrimmedLine As String = line.Trim() + If TrimmedLine.Length = 0 Then + Return True + End If + + ' Ignore comments + If m_CommentTokens IsNot Nothing Then + For Each Token As String In m_CommentTokens + If Token = "" Then + Continue For + End If + + If TrimmedLine.StartsWith(Token, StringComparison.Ordinal) Then + Return True + End If + + ' Test original line in case whitespace char is a coment token + If line.StartsWith(Token, StringComparison.Ordinal) Then + Return True + End If + Next + End If + + Return False + + End Function + + '''*********************************************************************** + ''';ReadToBuffer + ''' + ''' Reads characters from the file into the buffer + ''' + ''' The number of Chars read. If no Chars are read, we're at the end of the file + ''' + Private Function ReadToBuffer() As Integer + + Debug.Assert(m_Buffer IsNot Nothing, "There's no buffer") + Debug.Assert(m_Reader IsNot Nothing, "There's no StreamReader") + + ' Set cursor to beginning of buffer + m_Position = 0 + Dim BufferLength As Integer = m_Buffer.Length + Debug.Assert(BufferLength >= DEFAULT_BUFFER_LENGTH, "Buffer shrunk to below default") + + ' If the buffer has grown, shrink it back to the default size + If BufferLength > DEFAULT_BUFFER_LENGTH Then + BufferLength = DEFAULT_BUFFER_LENGTH + ReDim m_Buffer(BufferLength - 1) + End If + + ' Read from the stream + m_CharsRead = m_Reader.Read(m_Buffer, 0, BufferLength) + + ' Return the number of Chars read + Return m_CharsRead + End Function + + '''************************************************************************ + ''';SlideCursorToStartOfBuffer + ''' + ''' Moves the cursor and all the data to the right of the cursor to the front of the buffer. It + ''' then fills the remainder of the buffer from the file + ''' + ''' The number of Chars read in filling the remainder of the buffer + ''' + ''' This should be called when we want to make maximum use of the space in the buffer. Characters + ''' to the left of the cursor have already been read and can be discarded. + ''' + Private Function SlideCursorToStartOfBuffer() As Integer + + Debug.Assert(m_Buffer IsNot Nothing, "There's no buffer") + Debug.Assert(m_Reader IsNot Nothing, "There's no StreamReader") + Debug.Assert(m_Position >= 0 And m_Position <= m_Buffer.Length, "The cursor is out of range") + + ' No need to slide if we're already at the beginning + If m_Position > 0 Then + Dim BufferLength As Integer = m_Buffer.Length + Dim TempArray(BufferLength - 1) As Char + Array.Copy(m_Buffer, m_Position, TempArray, 0, BufferLength - m_Position) + + ' Fill the rest of the buffer + Dim CharsRead As Integer = m_Reader.Read(TempArray, BufferLength - m_Position, m_Position) + m_CharsRead = m_CharsRead - m_Position + CharsRead + + m_Position = 0 + m_Buffer = TempArray + + Return CharsRead + End If + + Return 0 + End Function + + '''********************************************************************* + ''';IncreaseBufferSize + ''' + ''' Increases the size of the buffer. Used when we are at the end of the buffer, we need + ''' to read more data from the file, and we can't discard what we've already read. + ''' + ''' The number of characters read to fill the new buffer + ''' This is needed for PeekChars and EndOfData + Private Function IncreaseBufferSize() As Integer + + Debug.Assert(m_Buffer IsNot Nothing, "There's no buffer") + Debug.Assert(m_Reader IsNot Nothing, "There's no StreamReader") + + ' Set cursor + m_PeekPosition = m_CharsRead + + ' Create a larger buffer and copy our data into it + Dim BufferSize As Integer = m_Buffer.Length + DEFAULT_BUFFER_LENGTH + + ' Make sure the buffer hasn't grown too large + If BufferSize > m_MaxBufferSize Then + Throw GetInvalidOperationException(ResID.MyID.TextFieldParser_BufferExceededMaxSize) + End If + + Dim TempArray(BufferSize - 1) As Char + + Array.Copy(m_Buffer, TempArray, m_Buffer.Length) + Dim CharsRead As Integer = m_Reader.Read(TempArray, m_Buffer.Length, DEFAULT_BUFFER_LENGTH) + m_Buffer = TempArray + m_CharsRead += CharsRead + + Debug.Assert(m_CharsRead <= BufferSize, "We've read more chars than we have space for") + + Return CharsRead + End Function + + '''********************************************************************** + ''';ReadNextDataLine + ''' + ''' Returns the next line of data or nothing if there's no more data to be read + ''' + ''' The next line of data + ''' Moves the cursor past the line read + Private Function ReadNextDataLine() As String + + Dim Line As String + + ' Set function to use when we reach the end of the buffer + Dim BufferFunction As New ChangeBufferFunction(AddressOf ReadToBuffer) + + Do + Line = ReadNextLine(m_Position, BufferFunction) + m_LineNumber += 1 + Loop While IgnoreLine(Line) + + If Line Is Nothing Then + CloseReader() + End If + + Return Line + + End Function + + '''*********************************************************************** + ''';PeekNextDataLine + ''' + ''' Returns the next data line but doesn't move the cursor + ''' + ''' The next data line, or Nothing if there's no more data + ''' + Private Function PeekNextDataLine() As String + + Dim Line As String + + ' Set function to use when we reach the end of the buffer + Dim BufferFunction As New ChangeBufferFunction(AddressOf IncreaseBufferSize) + + ' Slide the data to the left so that we make maximum use of the buffer + SlideCursorToStartOfBuffer() + m_PeekPosition = 0 + + Do + Line = ReadNextLine(m_PeekPosition, BufferFunction) + Loop While IgnoreLine(Line) + + Return Line + End Function + + '''********************************************************************* + ''';ChangeBufferFunction + ''' + ''' Function to call when we're at the end of the buffer. We either re fill the buffer + ''' or change the size of the buffer + ''' + ''' + ''' + Private Delegate Function ChangeBufferFunction() As Integer + + '''********************************************************************** + ''';ReadNextLine + ''' + ''' Gets the next line from the file and moves the pased in cursor past the line + ''' + ''' Indicates the current position in the buffer + ''' Function to call when we've reached the end of the buffer + ''' The next line in the file + ''' Returns Nothing if we are at the end of the file + Private Function ReadNextLine(ByRef Cursor As Integer, ByVal ChangeBuffer As ChangeBufferFunction) As String + + Debug.Assert(m_Buffer IsNot Nothing, "There's no buffer") + Debug.Assert(Cursor >= 0 And Cursor <= m_CharsRead, "The cursor is out of range") + + ' Check to see if the cursor is at the end of the chars in the buffer. If it is, re fill the buffer + If Cursor = m_CharsRead Then + If ChangeBuffer() = 0 Then + + ' We're at the end of the file + Return Nothing + End If + End If + + Dim Builder As StringBuilder = Nothing + Do + ' Walk through buffer looking for the end of a line. End of line can be vbLf (\n), vbCr (\r) or vbCrLf (\r\n) + For i As Integer = Cursor To m_CharsRead - 1 + + Dim Character As Char = m_Buffer(i) + If Character = vbCr Or Character = vbLf Then + + ' We've found the end of a line so add everything we've read so far to the + ' builder. We include the end of line char because we need to know what it is + ' in case it's embedded in a field. + If Builder IsNot Nothing Then + Builder.Append(m_Buffer, Cursor, i - Cursor + 1) + Else + Builder = New StringBuilder(i + 1) + Builder.Append(m_Buffer, Cursor, i - Cursor + 1) + End If + Cursor = i + 1 + + ' See if vbLf should be added as well + If Character = vbCr Then + If Cursor < m_CharsRead Then + If m_Buffer(Cursor) = vbLf Then + Cursor += 1 + Builder.Append(vbLf) + End If + ElseIf ChangeBuffer() > 0 Then + If m_Buffer(Cursor) = vbLf Then + Cursor += 1 + Builder.Append(vbLf) + End If + End If + End If + + Return Builder.ToString() + End If + Next i + + ' We've searched the whole buffer and haven't found an end of line. Save what we have, and read more to the buffer. + Dim Size As Integer = m_CharsRead - Cursor + + If Builder Is Nothing Then + Builder = New StringBuilder(Size + DEFAULT_BUILDER_INCREASE) + End If + Builder.Append(m_Buffer, Cursor, Size) + + Loop While ChangeBuffer() > 0 + + Return Builder.ToString() + End Function + + '''*************************************************************** + ''';ParseDelimitedLine + ''' + ''' Gets the next data line and parses it with the delimiters + ''' + ''' An array of the fields in the line + ''' + Private Function ParseDelimitedLine() As String() + + Dim Line As String = ReadNextDataLine() + If Line Is Nothing Then + Return Nothing + End If + + ' The line number is that of the line just read + Dim CurrentLineNumber As Long = m_LineNumber - 1 + + Dim Index As Integer = 0 + Dim Fields As New System.Collections.Generic.List(Of String) + Dim Field As String + Dim LineEndIndex As Integer = GetEndOfLineIndex(Line) + + While Index <= LineEndIndex + + ' Is the field delimited in quotes? We only care about this if + ' EscapedQuotes is True + Dim MatchResult As Match = Nothing + Dim QuoteDelimited As Boolean = False + + If m_HasFieldsEnclosedInQuotes Then + MatchResult = BeginQuotesRegex.Match(Line, Index) + QuoteDelimited = MatchResult.Success + End If + + If QuoteDelimited Then + + 'Move the Index beyond quote + Index = MatchResult.Index + MatchResult.Length + ' Look for the closing " + Dim EndHelper As New QuoteDelimitedFieldBuilder(m_DelimiterWithEndCharsRegex, m_SpaceChars) + EndHelper.BuildField(Line, Index) + + If EndHelper.MalformedLine Then + m_ErrorLine = Line.TrimEnd(Chr(13), Chr(10)) + m_ErrorLineNumber = CurrentLineNumber + Throw New MalformedLineException(GetResourceString(ResID.MyID.TextFieldParser_MalFormedDelimitedLine, CurrentLineNumber.ToString(CultureInfo.InvariantCulture)), CurrentLineNumber) + End If + + If EndHelper.FieldFinished Then + Field = EndHelper.Field + Index = EndHelper.Index + EndHelper.DelimiterLength + Else + ' We may have an embedded line end character, so grab next line + Dim NewLine As String + Dim EndOfLine As Integer + + Do + EndOfLine = Line.Length + ' Get the next data line + NewLine = ReadNextDataLine() + + ' If we didn't get a new line, we're at the end of the file so our original line is mal formed + If NewLine Is Nothing Then + m_ErrorLine = Line.TrimEnd(Chr(13), Chr(10)) + m_ErrorLineNumber = CurrentLineNumber + Throw New MalformedLineException(GetResourceString(ResID.MyID.TextFieldParser_MalFormedDelimitedLine, CurrentLineNumber.ToString(CultureInfo.InvariantCulture)), CurrentLineNumber) + End If + + If Line.Length + NewLine.Length > m_MaxLineSize Then + m_ErrorLine = Line.TrimEnd(Chr(13), Chr(10)) + m_ErrorLineNumber = CurrentLineNumber + Throw New MalformedLineException(GetResourceString(ResID.MyID.TextFieldParser_MaxLineSizeExceeded, CurrentLineNumber.ToString(CultureInfo.InvariantCulture)), CurrentLineNumber) + End If + + Line &= NewLine + LineEndIndex = GetEndOfLineIndex(Line) + EndHelper.BuildField(Line, EndOfLine) + If EndHelper.MalformedLine Then + m_ErrorLine = Line.TrimEnd(Chr(13), Chr(10)) + m_ErrorLineNumber = CurrentLineNumber + Throw New MalformedLineException(GetResourceString(ResID.MyID.TextFieldParser_MalFormedDelimitedLine, CurrentLineNumber.ToString(CultureInfo.InvariantCulture)), CurrentLineNumber) + End If + Loop Until EndHelper.FieldFinished + + Field = EndHelper.Field + Index = EndHelper.Index + EndHelper.DelimiterLength + End If + + If m_TrimWhiteSpace Then + Field = Field.Trim() + End If + + Fields.Add(Field) + Else + ' Find the next delimiter + Dim DelimiterMatch As Match = m_DelimiterRegex.Match(Line, Index) + If DelimiterMatch.Success Then + Field = Line.Substring(Index, DelimiterMatch.Index - Index) + + If m_TrimWhiteSpace Then + Field = Field.Trim() + End If + + Fields.Add(Field) + + ' Move the index + Index = DelimiterMatch.Index + DelimiterMatch.Length + Else + ' We're at the end of the line so the field consists of all that's left of the line + ' minus the end of line chars + Field = Line.Substring(Index).TrimEnd(Chr(13), Chr(10)) + + If m_TrimWhiteSpace Then + Field = Field.Trim() + End If + Fields.Add(Field) + Exit While + End If + + End If + End While + + Return Fields.ToArray() + + End Function + + + '''**************************************************************** + ''';ParseFixedWidthLine + ''' + ''' Gets the next data line and parses into fixed width fields + ''' + ''' An array of the fields in the line + ''' + Private Function ParseFixedWidthLine() As String() + + Debug.Assert(m_FieldWidths IsNot Nothing, "No field widths") + + Dim Line As String = ReadNextDataLine() + + If Line Is Nothing Then + Return Nothing + End If + + ' Strip off trailing carriage return or line feed + Line = Line.TrimEnd(Chr(13), Chr(10)) + + Dim LineInfo As New StringInfo(Line) + ValidateFixedWidthLine(LineInfo, m_LineNumber - 1) + + Dim Index As Integer = 0 + Dim Bound As Integer = m_FieldWidths.Length - 1 + Dim Fields(Bound) As String + + For i As Integer = 0 To Bound + Fields(i) = GetFixedWidthField(LineInfo, Index, m_FieldWidths(i)) + Index += m_FieldWidths(i) + Next + + Return Fields + End Function + + '''***************************************************************** + ''';GetFixedWidthField + ''' + ''' Returns the field at the passed in index + ''' + ''' The string containing the fields + ''' The start of the field + ''' The length of the field + ''' The field + ''' + Private Function GetFixedWidthField(ByVal Line As StringInfo, ByVal Index As Integer, ByVal FieldLength As Integer) As String + + Dim Field As String + If FieldLength > 0 Then + Field = Line.SubstringByTextElements(Index, FieldLength) + Else + ' Make sure the index isn't past the string + If Index >= Line.LengthInTextElements Then + Field = String.Empty + Else + Field = Line.SubstringByTextElements(Index).TrimEnd(Chr(13), Chr(10)) + End If + End If + + If m_TrimWhiteSpace Then + Return Field.Trim() + Else + Return Field + End If + End Function + + + '''*************************************************************** + ''';GetEndOfLineIndex + ''' + ''' Gets the index of the first end of line character + ''' + ''' + ''' + ''' When there are no end of line characters, the index is the length (one past the end) + Private Function GetEndOfLineIndex(ByVal Line As String) As Integer + + Debug.Assert(Line IsNot Nothing, "We are parsing a Nothing") + + Dim Length As Integer = Line.Length + Debug.Assert(Length > 0, "A blank line shouldn't be parsed") + + If Length = 1 Then + Debug.Assert(Line(0) <> vbCr And Line(0) <> vbLf, "A blank line shouldn't be parsed") + Return Length + End If + + ' Check the next to last and last char for end line characters + If Line(Length - 2) = vbCr Or Line(Length - 2) = vbLf Then + Return Length - 2 + ElseIf Line(Length - 1) = vbCr Or Line(Length - 1) = vbLf Then + Return Length - 1 + Else + Return Length + End If + + End Function + + '''***************************************************************** + ''';ValidateFixedWidthLine + ''' + ''' Indicates whether or not a line is valid + ''' + ''' The line to be tested + ''' The line number, used for exception + ''' + Private Sub ValidateFixedWidthLine(ByVal Line As StringInfo, ByVal LineNumber As Long) + Debug.Assert(Line IsNot Nothing, "No Line sent") + + ' The only mal formed line for fixed length fields is one that's too short + If Line.LengthInTextElements < m_LineLength Then + m_ErrorLine = Line.String + m_ErrorLineNumber = m_LineNumber - 1 + Throw New MalformedLineException(GetResourceString(ResID.MyID.TextFieldParser_MalFormedFixedWidthLine, LineNumber.ToString(CultureInfo.InvariantCulture)), LineNumber) + End If + + End Sub + + '''**************************************************************** + ''';ValidateFieldWidths + ''' + ''' Determines whether or not the field widths are valid, and sets the size of a line + ''' + ''' + Private Sub ValidateFieldWidths() + + If m_FieldWidths Is Nothing Then + Throw GetInvalidOperationException(ResID.MyID.TextFieldParser_FieldWidthsNothing) + End If + + If m_FieldWidths.Length = 0 Then + Throw GetInvalidOperationException(ResID.MyID.TextFieldParser_FieldWidthsNothing) + End If + + Dim WidthBound As Integer = m_FieldWidths.Length - 1 + m_LineLength = 0 + + ' add all but the last element + For i As Integer = 0 To WidthBound - 1 + Debug.Assert(m_FieldWidths(i) > 0, "Bad field width, this should have been caught on input") + + m_LineLength += m_FieldWidths(i) + Next + + ' add the last field if it's greater than zero (ie not ragged). + If m_FieldWidths(WidthBound) > 0 Then + m_LineLength += m_FieldWidths(WidthBound) + End If + End Sub + + '''***************************************************************** + ''';ValidateFieldWidthsOnInput + ''' + ''' Checks the field widths at input. + ''' + ''' + ''' + ''' All field widths, except the last one, must be greater than zero. If the last width is + ''' less than one it indicates the last field is ragged + ''' + Private Sub ValidateFieldWidthsOnInput(ByVal Widths() As Integer) + + Debug.Assert(Widths IsNot Nothing, "There are no field widths") + + Dim Bound As Integer = Widths.Length - 1 + For i As Integer = 0 To Bound - 1 + If Widths(i) < 1 Then + Throw GetArgumentExceptionWithArgName("FieldWidths", ResID.MyID.TextFieldParser_FieldWidthsMustPositive, "FieldWidths") + End If + Next + End Sub + + '''*************************************************************** + ''';ValidateAndEscapeDelimiters + ''' + ''' Validates the delimiters and creates the Regex objects for finding delimiters or quotes followed + ''' by delimiters + ''' + ''' + Private Sub ValidateAndEscapeDelimiters() + If m_Delimiters Is Nothing Then + Throw GetArgumentExceptionWithArgName("Delimiters", ResID.MyID.TextFieldParser_DelimitersNothing, "Delimiters") + End If + + If m_Delimiters.Length = 0 Then + Throw GetArgumentExceptionWithArgName("Delimiters", ResID.MyID.TextFieldParser_DelimitersNothing, "Delimiters") + End If + + Dim Length As Integer = m_Delimiters.Length + + Dim Builder As StringBuilder = New StringBuilder() + Dim QuoteBuilder As StringBuilder = New StringBuilder() + + ' Add ending quote pattern. It will be followed by delimiters resulting in a string like: + ' "[ ]*(d1|d2|d3) + QuoteBuilder.Append(EndQuotePattern & "(") + For i As Integer = 0 To Length - 1 + If m_Delimiters(i) IsNot Nothing Then + + ' Make sure delimiter is legal + If m_HasFieldsEnclosedInQuotes Then + If m_Delimiters(i).IndexOf(""""c) > -1 Then + Throw GetInvalidOperationException(ResID.MyID.TextFieldParser_IllegalDelimiter) + End If + End If + + Dim EscapedDelimiter As String = Regex.Escape(m_Delimiters(i)) + + Builder.Append(EscapedDelimiter & "|") + QuoteBuilder.Append(EscapedDelimiter & "|") + Else + Debug.Fail("Delimiter element is empty. This should have been caught on input") + End If + Next + + m_SpaceChars = WhitespaceCharacters + + ' Get rid of trailing | and set regex + m_DelimiterRegex = New Regex(Builder.ToString(0, Builder.Length - 1), REGEX_OPTIONS) + Builder.Append(vbCr & "|" & vbLf) + m_DelimiterWithEndCharsRegex = New Regex(Builder.ToString(), REGEX_OPTIONS) + + ' Add end of line (either cr, ln, or nothing) and set regex + QuoteBuilder.Append(vbCr & "|" & vbLf & ")|""$") + End Sub + + + '''************************************************************* + ''';ValidateReadyToRead + ''' + ''' Checks property settings to ensure we're able to read fields. + ''' + ''' Throws if we're not able to read fields with current property settings + Private Sub ValidateReadyToRead() + + If m_NeedPropertyCheck Or ArrayHasChanged() Then + Select Case m_TextFieldType + Case FieldType.Delimited + + ValidateAndEscapeDelimiters() + Case FieldType.FixedWidth + + ' Check FieldWidths + ValidateFieldWidths() + + Case Else + Debug.Fail("Unknown TextFieldType") + End Select + + ' Check Comment Tokens + If m_CommentTokens IsNot Nothing Then + For Each Token As String In m_CommentTokens + If Token <> "" Then + If m_HasFieldsEnclosedInQuotes And m_TextFieldType = FieldType.Delimited Then + + If String.Compare(Token.Trim(), """", StringComparison.Ordinal) = 0 Then + Throw GetInvalidOperationException(ResID.MyID.TextFieldParser_InvalidComment) + End If + End If + End If + Next + End If + + m_NeedPropertyCheck = False + End If + End Sub + + '''************************************************************* + ''';ValidateDelimiters + ''' + ''' Thows if any of the delimiters contain line end characters + ''' + ''' A string array of delimiters + ''' + Private Sub ValidateDelimiters(ByVal delimiterArray() As String) + + If delimiterArray Is Nothing Then + Return + End If + For Each delimiter As String In delimiterArray + If delimiter = "" Then + Throw GetArgumentExceptionWithArgName("Delimiters", ResID.MyID.TextFieldParser_DelimiterNothing, "Delimiters") + End If + If delimiter.IndexOfAny(New Char() {Chr(13), Chr(10)}) > -1 Then + Throw GetArgumentExceptionWithArgName("Delimiters", ResID.MyID.TextFieldParser_EndCharsInDelimiter) + End If + Next + End Sub + + '''************************************************************* + ''';ArrayHasChanged + ''' + ''' Determines if the FieldWidths or Delimiters arrays have changed. + ''' + ''' If the array has changed, we need to re initialize before reading. + Private Function ArrayHasChanged() As Boolean + + Dim lowerBound As Integer = 0 + Dim upperBound As Integer = 0 + + Select Case m_TextFieldType + Case FieldType.Delimited + + Debug.Assert((m_DelimitersCopy Is Nothing And m_Delimiters Is Nothing) Or (m_DelimitersCopy IsNot Nothing And m_Delimiters IsNot Nothing), "Delimiters and copy are not both Nothing or both not Nothing") + + ' Check null cases + If m_Delimiters Is Nothing Then + Return False + End If + + lowerBound = m_DelimitersCopy.GetLowerBound(0) + upperBound = m_DelimitersCopy.GetUpperBound(0) + + For i As Integer = lowerBound To upperBound + If m_Delimiters(i) <> m_DelimitersCopy(i) Then + Return True + End If + Next i + + Case FieldType.FixedWidth + + Debug.Assert((m_FieldWidthsCopy Is Nothing And m_FieldWidths Is Nothing) Or (m_FieldWidthsCopy IsNot Nothing And m_FieldWidths IsNot Nothing), "FieldWidths and copy are not both Nothing or both not Nothing") + + ' Check null cases + If m_FieldWidths Is Nothing Then + Return False + End If + + lowerBound = m_FieldWidthsCopy.GetLowerBound(0) + upperBound = m_FieldWidthsCopy.GetUpperBound(0) + + For i As Integer = lowerBound To upperBound + If m_FieldWidths(i) <> m_FieldWidthsCopy(i) Then + Return True + End If + Next i + + Case Else + Debug.Fail("Unknown TextFieldType") + End Select + + Return False + End Function + + + '''************************************************************* + ''';CheckCommentTokensForWhitespace + ''' + ''' Thows if any of the comment tokens contain whitespace + ''' + ''' A string array of comment tokens + ''' + Private Sub CheckCommentTokensForWhitespace(ByVal tokens() As String) + If tokens Is Nothing Then + Return + End If + For Each token As String In tokens + If m_WhiteSpaceRegEx.IsMatch(token) Then + Throw GetArgumentExceptionWithArgName("CommentTokens", ResID.MyID.TextFieldParser_WhitespaceInToken) + End If + Next + End Sub + + '''************************************************************* + ''';BeginQuotesRegex + ''' + ''' Gets the appropriate regex for finding a field beginning with quotes + ''' + ''' The right regex + ''' + Private ReadOnly Property BeginQuotesRegex() As Regex + Get + If m_BeginQuotesRegex Is Nothing Then + ' Get the pattern + Dim pattern As String = String.Format(CultureInfo.InvariantCulture, BEGINS_WITH_QUOTE, WhitespacePattern) + m_BeginQuotesRegex = New Regex(pattern, REGEX_OPTIONS) + End If + + Return m_BeginQuotesRegex + End Get + End Property + + '''************************************************************* + ''';EndQuotePattern + ''' + ''' Gets the appropriate expression for finding ending quote of a field + ''' + ''' The expression + ''' + Private ReadOnly Property EndQuotePattern() As String + Get + Return String.Format(CultureInfo.InvariantCulture, ENDING_QUOTE, WhitespacePattern) + End Get + End Property + + '''************************************************************** + ''';WhitepaceCharacters + ''' + ''' Returns a string containing all the characters which are whitespace for parsing purposes + ''' + ''' + ''' + Private ReadOnly Property WhitespaceCharacters() As String + Get + Dim builder As New StringBuilder + For Each code As Integer In m_WhitespaceCodes + + Dim spaceChar As Char = ChrW(code) + If Not CharacterIsInDelimiter(spaceChar) Then + builder.Append(spaceChar) + End If + Next + + Return builder.ToString() + End Get + End Property + + '''************************************************************** + ''';WhitespacePattern + ''' + ''' Gets the character set of whitespaces to be used in a regex pattern + ''' + ''' + ''' + Private ReadOnly Property WhitespacePattern() As String + Get + Dim builder As New StringBuilder() + For Each code As Integer In m_WhitespaceCodes + Dim spaceChar As Char = ChrW(code) + If Not CharacterIsInDelimiter(spaceChar) Then + ' Gives us something like \u00A0 + builder.Append("\u" & code.ToString("X4", CultureInfo.InvariantCulture)) + End If + Next + + Return builder.ToString() + End Get + End Property + + '''************************************************************************************ + ''';CharacterIsInDelimiter + ''' + ''' Checks to see if the passed in character is in any of the delimiters + ''' + ''' The character to look for + ''' True if the character is found in a delimiter, otherwise false + ''' + Private Function CharacterIsInDelimiter(ByVal testCharacter As Char) As Boolean + + Debug.Assert(m_Delimiters IsNot Nothing, "No delimiters set!") + + For Each delimiter As String In m_Delimiters + If delimiter.IndexOf(testCharacter) > -1 Then + Return True + End If + Next + + Return False + End Function + + ' Indicates reader has been disposed + Private m_Disposed As Boolean + + ' The internal StreamReader that reads the file + Private m_Reader As TextReader + + ' An array holding the strings that indicate a line is a comment + Private m_CommentTokens() As String = New String() {} + + ' The line last read by either ReadLine or ReadFields + Private m_LineNumber As Long = 1 + + ' Flags whether or not there is data left to read. Assume there is at creation + Private m_EndOfData As Boolean = False + + ' Holds the last mal formed line + Private m_ErrorLine As String = "" + + ' Holds the line number of the last malformed line + Private m_ErrorLineNumber As Long = -1 + + ' Indicates what type of fields are in the file (fixed width or delimited) + Private m_TextFieldType As FieldType = FieldType.Delimited + + ' An array of the widths of the fields in a fixed width file + Private m_FieldWidths() As Integer + + ' An array of the delimiters used for the fields in the file + Private m_Delimiters() As String + + ' Holds a copy of the field widths last set so we can respond to changes in the array + Private m_FieldWidthsCopy() As Integer + + ' Holds a copy of the field widths last set so we can respond to changes in the array + Private m_DelimitersCopy() As String + + ' Regular expression used to find delimiters + Private m_DelimiterRegex As Regex + + ' Regex used with BuildField + Private m_DelimiterWithEndCharsRegex As Regex + + ' Options used for regular expressions + Private Const REGEX_OPTIONS As RegexOptions = RegexOptions.CultureInvariant + + ' Codes for whitespace as used by String.Trim excluding line end chars as those are handled separately + Private m_WhitespaceCodes() As Integer = {&H9, &HB, &HC, &H20, &H85, &HA0, &H1680, &H2000, &H2001, &H2002, &H2003, &H2004, &H2005, &H2006, &H2007, &H2008, &H2009, &H200A, &H200B, &H2028, &H2029, &H3000, &HFEFF} + + ' Regualr expression used to find beginning quotes ignore spaces and tabs + Private m_BeginQuotesRegex As Regex + + ' Regular expression for whitespace + Private m_WhiteSpaceRegEx As Regex = New Regex("\s", REGEX_OPTIONS) + + ' Indicates whether or not white space should be removed from a returned field + Private m_TrimWhiteSpace As Boolean = True + + ' The position of the cursor in the buffer + Private m_Position As Integer = 0 + + ' The position of the peek cursor + Private m_PeekPosition As Integer = 0 + + ' The number of chars in the buffer + Private m_CharsRead As Integer = 0 + + ' Indicates that the user has changed properties so that we need to validate before a read + Private m_NeedPropertyCheck As Boolean = True + + ' The default size for the buffer + Private Const DEFAULT_BUFFER_LENGTH As Integer = 4096 + + ' This is a guess as to how much larger the string builder should be beyond the size of what + ' we've already read + Private Const DEFAULT_BUILDER_INCREASE As Integer = 10 + + ' Buffer used to hold data read from the file. It holds data that must be read + ' ahead of the cursor (for PeekChars and EndOfData) + Private m_Buffer(DEFAULT_BUFFER_LENGTH - 1) As Char + + ' The minimum length for a valid fixed width line + Private m_LineLength As Integer + + ' Indicates whether or not we handle quotes in a csv appropriate way + Private m_HasFieldsEnclosedInQuotes As Boolean = True + + ' A string of the chars that count as spaces (used for csv format). The norm is spaces and tabs. + Private m_SpaceChars As String + + ' The largest size a line can be. + Private m_MaxLineSize As Integer = 10000000 + + ' The largest size the buffer can be + Private m_MaxBufferSize As Integer = 10000000 + + ' Regex pattern to determine if field begins with quotes + Private Const BEGINS_WITH_QUOTE As String = "\G[{0}]*""" + + ' Regex pattern to find a quote before a delimiter + Private Const ENDING_QUOTE As String = """[{0}]*" + + ' Indicates passed in stream should be not be closed + Private m_LeaveOpen As Boolean = False + + End Class + + '''************************************************************************ + ''';FieldType + ''' + ''' Enum used to indicate the kind of file being read, either delimited or fixed length + ''' + ''' + Public Enum FieldType As Integer + '!!!!!!!!!! Changes to this enum must be reflected in ValidateFieldTypeEnumValue() + Delimited + FixedWidth + End Enum + + + '''*********************************************************************** + ''';QuoteDelimitedFieldBuilder + ''' + ''' Helper class that when passed a line and an index to a quote delimited field + ''' will build the field and handle escaped quotes + ''' + ''' + Friend Class QuoteDelimitedFieldBuilder + + '==PUBLIC************************************************************ + + ''' + ''' Creates an intance of the class and sets some properties + ''' + ''' The regex used to find any of the delimiters + ''' Characters treated as space (usually space and tab) + ''' + Public Sub New(ByVal DelimiterRegex As Regex, ByVal SpaceChars As String) + m_DelimiterRegex = DelimiterRegex + m_SpaceChars = SpaceChars + End Sub + + '''******************************************************************* + ''';FieldFinished + ''' + ''' Indicates whether or not the field has been built. + ''' + ''' True if the field has been built, otherwise False + ''' If the Field has been built, the Field property will return the entire field + Public ReadOnly Property FieldFinished() As Boolean + Get + Return m_FieldFinished + End Get + End Property + + '''******************************************************************* + ''';Field + ''' + ''' The field being built + ''' + ''' The field + ''' + Public ReadOnly Property Field() As String + Get + Return m_Field.ToString() + End Get + End Property + + '''******************************************************************* + ''';Index + ''' + ''' The current index on the line. Used to indicate how much of the line was used to build the field + ''' + ''' The current position on the line + ''' + Public ReadOnly Property Index() As Integer + Get + Return m_Index + End Get + End Property + + '''******************************************************************* + ''';DelimiterLength + ''' + ''' The length of the closing delimiter if one was found + ''' + ''' The length of the delimiter + ''' + Public ReadOnly Property DelimiterLength() As Integer + Get + Return m_DelimiterLength + End Get + End Property + + '''******************************************************************* + ''';MalformedLine + ''' + ''' Indicates that the current field breaks the subset of csv rules we enforce + ''' + ''' True if the line is malformed, otherwise False + ''' + ''' The rules we enforce are: + ''' Embedded quotes must be escaped + ''' Only space characters can occur between a delimiter and a quote + ''' + Public ReadOnly Property MalformedLine() As Boolean + Get + Return m_MalformedLine + End Get + End Property + + '''******************************************************************* + ''';BuildField + ''' + ''' Builds a field by walking through the passed in line starting at StartAt + ''' + ''' The line containing the data + ''' The index at which we start building the field + ''' + Public Sub BuildField(ByVal Line As String, ByVal StartAt As Integer) + + m_Index = StartAt + Dim Length As Integer = Line.Length + + While m_Index < Length + + If Line(m_Index) = """"c Then + + ' Are we at the end of the file? + If m_Index + 1 = Length Then + ' We've found the end of the field + m_FieldFinished = True + m_DelimiterLength = 1 + + ' Move index past end of line + m_Index += 1 + Return + End If + ' Check to see if this is an escaped quote + If m_Index + 1 < Line.Length And Line(m_Index + 1) = """"c Then + m_Field.Append(""""c) + m_Index += 2 + Continue While + End If + + ' Find the next delimiter and make sure everything between the quote and + ' the delimiter is ignorable + Dim Limit As Integer + Dim DelimiterMatch As Match = m_DelimiterRegex.Match(Line, m_Index + 1) + If Not DelimiterMatch.Success Then + Limit = Length - 1 + Else + Limit = DelimiterMatch.Index - 1 + End If + + For i As Integer = m_Index + 1 To Limit + If m_SpaceChars.IndexOf(Line(i)) < 0 Then + m_MalformedLine = True + Return + End If + Next + + ' The length of the delimiter is the length of the closing quote (1) + any spaces + the length + ' of the delimiter we matched if any + m_DelimiterLength = 1 + Limit - m_Index + If DelimiterMatch.Success Then + m_DelimiterLength += DelimiterMatch.Length + End If + + m_FieldFinished = True + Return + Else + m_Field.Append(Line(m_Index)) + m_Index += 1 + End If + End While + End Sub + + ' String builder holding the field + Private m_Field As New StringBuilder + + ' Indicates m_Field contains the entire field + Private m_FieldFinished As Boolean + + ' The current index on the field + Private m_Index As Integer + + ' The length of the closing delimiter if one is found + Private m_DelimiterLength As Integer + + ' The regular expression used to find the next delimiter + Private m_DelimiterRegex As Regex + + ' Chars that should be counted as space (and hence ignored if occurring before or after a delimiter + Private m_SpaceChars As String + + ' Indicates the line breaks the csv rules we enforce + Private m_MalformedLine As Boolean + + End Class + +End Namespace diff --git a/Microsoft.VisualBasic/runtime/msvbalib/FileSystem.vb b/Microsoft.VisualBasic/runtime/msvbalib/FileSystem.vb new file mode 100644 index 000000000..f310bb668 --- /dev/null +++ b/Microsoft.VisualBasic/runtime/msvbalib/FileSystem.vb @@ -0,0 +1,1708 @@ +' Copyright (c) Microsoft Corporation. All rights reserved. + +Imports System +Imports System.Diagnostics +Imports System.IO +Imports System.Security +Imports System.Security.Permissions +Imports System.Text +Imports System.Globalization +Imports System.Runtime.Versioning + +Imports Microsoft.VisualBasic.CompilerServices +Imports Microsoft.VisualBasic.CompilerServices.ExceptionUtils +Imports Microsoft.VisualBasic.CompilerServices.Utils +Imports Microsoft.VisualBasic.CompilerServices.IOUtils + +Namespace Microsoft.VisualBasic + + _ + _ + Public Module FileSystem + + Private Const ERROR_ACCESS_DENIED As Integer = 5 + Private Const ERROR_FILE_NOT_FOUND As Integer = 2 + Private Const ERROR_BAD_NETPATH As Integer = 53 + Private Const ERROR_INVALID_PARAMETER As Integer = 87 + Private Const ERROR_WRITE_PROTECT As Integer = 19 + Private Const ERROR_FILE_EXISTS As Integer = 80 + Private Const ERROR_ALREADY_EXISTS As Integer = 183 + Private Const ERROR_INVALID_ACCESS As Integer = 12 + Private Const ERROR_NOT_SAME_DEVICE As Integer = 17 + + Friend Enum vbFileType + vbPrintFile = 0 + vbWriteFile = 1 + End Enum + 'FILESYSTEM function vars + + Friend Const FIRST_LOCAL_CHANNEL As Integer = 1 + Friend Const LAST_LOCAL_CHANNEL As Integer = 255 + + Private Const A_NORMAL As Integer = &H0I + Private Const A_RDONLY As Integer = &H1I + Private Const A_HIDDEN As Integer = &H2I + Private Const A_SYSTEM As Integer = &H4I + Private Const A_VOLID As Integer = &H8I + Private Const A_SUBDIR As Integer = &H10I + Private Const A_ARCH As Integer = &H20I + Private Const A_ALLBITS As Integer = (A_NORMAL Or A_RDONLY Or A_HIDDEN Or A_SYSTEM Or A_VOLID Or A_SUBDIR Or A_ARCH) + + Friend Const sTimeFormat As String = "T" + Friend Const sDateFormat As String = "d" + Friend Const sDateTimeFormat As String = "F" + + Friend ReadOnly m_WriteDateFormatInfo As DateTimeFormatInfo = InitializeWriteDateFormatInfo() ' Call static initializer due to FxCop InitializeReferenceTypeStaticFieldsInline. + Private Function InitializeWriteDateFormatInfo() As DateTimeFormatInfo + Dim dfi As New DateTimeFormatInfo + dfi.DateSeparator = "-" + dfi.ShortDatePattern = "\#yyyy-MM-dd\#" + dfi.LongTimePattern = "\#HH:mm:ss\#" + dfi.FullDateTimePattern = "\#yyyy-MM-dd HH:mm:ss\#" + Return dfi + End Function + + '============================================================================ + ' Directory/drive functions. + '============================================================================ + + Public Sub ChDir(ByVal Path As String) + Debug.Assert(Not System.Reflection.Assembly.GetCallingAssembly() Is Utils.VBRuntimeAssembly, _ + "Methods in Microsoft.VisualBasic should not call FileSystem public method. VSWhidbey 476783.") + + Path = RTrim(Path) 'VB6 accepted things like "\ ", so need to trim the trailing spaces + + If (Path Is Nothing) OrElse (Path.Length = 0) Then + Throw VbMakeException(New ArgumentException(GetResourceString(ResID.Argument_PathNullOrEmpty)), vbErrors.BadFileNameOrNumber) + End If + + ' Do this since System.IO.Directory does not accept "\" + If Path = "\" Then + Path = Directory.GetDirectoryRoot(Directory.GetCurrentDirectory()) + End If + + Try + System.IO.Directory.SetCurrentDirectory(Path) + Catch ex As System.IO.FileNotFoundException + Throw VbMakeException(New FileNotFoundException(GetResourceString(ResID.FileSystem_PathNotFound1, Path)), vbErrors.PathNotFound) + End Try + + End Sub + + Public Sub ChDrive(ByVal Drive As Char) + Drive = System.Char.ToUpper(Drive, CultureInfo.InvariantCulture) + + If (Drive < chLetterA) OrElse (Drive > chLetterZ) Then + Throw New ArgumentException(GetResourceString(ResID.Argument_InvalidValue1, "Drive")) + End If + + If Not UnsafeValidDrive(Drive) Then + Throw VbMakeException(New IOException(GetResourceString(ResID.FileSystem_DriveNotFound1, CStr(Drive))), vbErrors.DevUnavailable) + End If + + IO.Directory.SetCurrentDirectory(Drive & Path.VolumeSeparatorChar) + End Sub + + + + Public Sub ChDrive(ByVal Drive As String) + Debug.Assert(Not System.Reflection.Assembly.GetCallingAssembly() Is Utils.VBRuntimeAssembly, _ + "Methods in Microsoft.VisualBasic should not call FileSystem public method. VSWhidbey 476783.") + + If Drive Is Nothing OrElse Drive.Length = 0 Then + Exit Sub + End If + + ChDrive(Drive.Chars(0)) + End Sub + + + + Public Function CurDir() As String + Debug.Assert(Not System.Reflection.Assembly.GetCallingAssembly() Is Utils.VBRuntimeAssembly, _ + "Methods in Microsoft.VisualBasic should not call FileSystem public method. VSWhidbey 476783.") + + Return Directory.GetCurrentDirectory() + End Function + + Public Function CurDir(ByVal Drive As Char) As String + Debug.Assert(Not System.Reflection.Assembly.GetCallingAssembly() Is Utils.VBRuntimeAssembly, _ + "Methods in Microsoft.VisualBasic should not call FileSystem public method. VSWhidbey 476783.") + + Drive = System.Char.ToUpper(Drive, CultureInfo.InvariantCulture) + If (Drive < chLetterA OrElse Drive > chLetterZ) Then + Throw VbMakeException(New ArgumentException(GetResourceString(ResID.Argument_InvalidValue1, "Drive")), vbErrors.DevUnavailable) + End If + + 'GetFullPath("x:.") will return the full directory path + Dim CurrentPath As String = Path.GetFullPath(Drive & Path.VolumeSeparatorChar & ".") + + If Not UnsafeValidDrive(Drive) Then + Throw VbMakeException(New IOException(GetResourceString(ResID.FileSystem_DriveNotFound1, CStr(Drive))), vbErrors.DevUnavailable) + End If + Return CurrentPath + End Function + + + + Public Function Dir() As String + Debug.Assert(Not System.Reflection.Assembly.GetCallingAssembly() Is Utils.VBRuntimeAssembly, _ + "Methods in Microsoft.VisualBasic should not call FileSystem public method. VSWhidbey 476783.") + + Return FindNextFile(System.Reflection.Assembly.GetCallingAssembly()) + End Function + + + + _ + _ + Public Function Dir(ByVal PathName As String, Optional ByVal Attributes As FileAttribute = FileAttribute.Normal) As String + 'VB's FileAttribute is different than the URT's System.IO.FileAttributes: + ' VB URT + 'Normal 0 128 + 'ReadOnly 1 1 + 'Hidden 2 2 + 'System 4 4 + 'Volume 8 -- + 'Directory 16 16 + 'Archive 32 32 + 'Device -- 64 + 'Temporary -- 256 + 'SparseFile -- 512 + 'ReparsePoint -- 1024 + 'Compressed -- 2048 + 'Offline -- 4096 + 'NotContentIndexed -- 8192 + 'Encrypted -- 16384 + + 'Note: Do NOT throw if pathName = "". That's legal for this function - returns the first file found. + + Debug.Assert(Not System.Reflection.Assembly.GetCallingAssembly() Is Utils.VBRuntimeAssembly, _ + "Methods in Microsoft.VisualBasic should not call FileSystem public method. VSWhidbey 476783.") + + If Attributes = FileAttribute.Volume Then + Dim Result As Integer + Dim VolumeName As StringBuilder = New StringBuilder(256) + Dim RootName As String = Nothing + + If (PathName.Length > 0) Then + RootName = Path.GetPathRoot(PathName) + + 'Add a backslash if one isn't there. This is required by GetVolumeInformation + 'Bug 32397 + If RootName.Chars(RootName.Length - 1) <> Path.DirectorySeparatorChar Then + RootName &= Path.DirectorySeparatorChar + End If + End If + + Result = NativeMethods.GetVolumeInformation(RootName, VolumeName, 256, 0, 0, 0, Nothing, 0) + + If Result <> 0 Then + Return VolumeName.ToString + Else + Return "" + End If + Else + 'Dir function always returns files with Normal attribute in addition to others specified. + Dim URTAttributes As System.IO.FileAttributes = CType(Attributes, FileAttributes) Or FileAttributes.Normal + + Return FindFirstFile(System.Reflection.Assembly.GetCallingAssembly(), PathName, URTAttributes) + End If + End Function + + + + Public Sub MkDir(ByVal Path As String) + Debug.Assert(Not System.Reflection.Assembly.GetCallingAssembly() Is Utils.VBRuntimeAssembly, _ + "Methods in Microsoft.VisualBasic should not call FileSystem public method. VSWhidbey 476783.") + + If Path Is Nothing OrElse Path.Length = 0 Then + Throw VbMakeException(New ArgumentException(GetResourceString(ResID.Argument_PathNullOrEmpty)), vbErrors.BadFileNameOrNumber) + End If + + If Directory.Exists(Path) Then + Throw VbMakeException(vbErrors.PathFileAccess) + Else + Directory.CreateDirectory(Path) + End If + End Sub + + + + Public Sub RmDir(ByVal Path As String) + Debug.Assert(Not System.Reflection.Assembly.GetCallingAssembly() Is Utils.VBRuntimeAssembly, _ + "Methods in Microsoft.VisualBasic should not call FileSystem public method. VSWhidbey 476783.") + + 'If null or empty directory, give error + If Path Is Nothing OrElse Path.Length = 0 Then + Throw VbMakeException(New ArgumentException(GetResourceString(ResID.Argument_PathNullOrEmpty)), vbErrors.BadFileNameOrNumber) + End If + + Try + Directory.Delete(Path) + Catch e1 As DirectoryNotFoundException + Throw VbMakeException(e1, vbErrors.PathNotFound) + Catch ex As StackOverflowException + Throw ex + Catch ex As OutOfMemoryException + Throw ex + Catch ex As System.Threading.ThreadAbortException + Throw ex + Catch e2 As Exception + Throw VbMakeException(e2, vbErrors.PathFileAccess) + End Try + End Sub + + + + '============================================================================ + ' File functions. + '============================================================================ + + Private Function PathContainsWildcards(ByVal Path As String) As Boolean + If Path Is Nothing Then + Return False + End If + + If (Path.IndexOf("*"c) <> -1) Then + Return True + End If + + If (Path.IndexOf("?"c) <> -1) Then + Return True + End If + + Return False + End Function + + + + Public Sub FileCopy(ByVal Source As String, ByVal Destination As String) + If (Source Is Nothing) OrElse (Source.Length = 0) Then + Throw VbMakeException(New ArgumentException(GetResourceString(ResID.Argument_PathNullOrEmpty1, "Source")), vbErrors.BadFileNameOrNumber) + End If + + If (Destination Is Nothing) OrElse (Destination.Length = 0) Then + Throw VbMakeException(New ArgumentException(GetResourceString(ResID.Argument_PathNullOrEmpty1, "Destination")), vbErrors.BadFileNameOrNumber) + End If + + ' Error if wildcard characters in name + If PathContainsWildcards(Source) Then + 'CONSIDER: make wildcard exception text + Throw VbMakeException(New ArgumentException(GetResourceString(ResID.Argument_InvalidValue1, "Source")), vbErrors.BadFileNameOrNumber) + End If + + If PathContainsWildcards(Destination) Then + 'CONSIDER: make wildcard exception text + Throw VbMakeException(New ArgumentException(GetResourceString(ResID.Argument_InvalidValue1, "Destination")), vbErrors.BadFileNameOrNumber) + End If + + Dim oAssemblyData As AssemblyData = ProjectData.GetProjectData().GetAssemblyData(System.Reflection.Assembly.GetCallingAssembly()) + + If CheckFileOpen(oAssemblyData, Destination, OpenModeTypes.Output) Then + Throw VbMakeException(New IOException(GetResourceString(ResID.FileSystem_FileAlreadyOpen1, Destination)), vbErrors.FileAlreadyOpen) + End If + + If CheckFileOpen(oAssemblyData, Source, OpenModeTypes.Input) Then + Throw VbMakeException(New IOException(GetResourceString(ResID.FileSystem_FileAlreadyOpen1, Source)), vbErrors.FileAlreadyOpen) + End If + + Try + File.Copy(Source, Destination, True) + + 'VB6 did not copy file attributes, so we must be backwards compatible + File.SetAttributes(Destination, FileAttributes.Archive) + + 'Need to emulate vb6 error codes as much as possible + Catch ex As FileNotFoundException + Throw VbMakeException(ex, vbErrors.FileNotFound) + Catch ex As IOException + Throw VbMakeException(ex, vbErrors.FileAlreadyOpen) + 'REVIEW: Catch ex As AccessException + 'REVIEW: Throw VbMakeException(ex, vbErrors.PathFileAccess) + Catch ex As Exception + Throw ex + End Try + End Sub + + + + Public Function FileDateTime(ByVal PathName As String) As DateTime + Debug.Assert(Not System.Reflection.Assembly.GetCallingAssembly() Is Utils.VBRuntimeAssembly, _ + "Methods in Microsoft.VisualBasic should not call FileSystem public method. VSWhidbey 476783.") + + If PathContainsWildcards(PathName) Then + Throw VbMakeException(New ArgumentException(GetResourceString(ResID.Argument_InvalidValue1, "PathName")), vbErrors.BadFileNameOrNumber) + End If + + If File.Exists(PathName) Then + Return (New FileInfo(PathName)).LastWriteTime + End If + + Throw New FileNotFoundException(GetResourceString(ResID.FileSystem_FileNotFound1, PathName)) + End Function + + + + Public Function FileLen(ByVal PathName As String) As Long + Debug.Assert(Not System.Reflection.Assembly.GetCallingAssembly() Is Utils.VBRuntimeAssembly, _ + "Methods in Microsoft.VisualBasic should not call FileSystem public method. VSWhidbey 476783.") + + If File.Exists(PathName) Then + Return (New FileInfo(PathName)).Length + End If + + Throw New FileNotFoundException(GetResourceString(ResID.FileSystem_FileNotFound1, PathName)) + End Function + + + + Public Function GetAttr(ByVal PathName As String) As FileAttribute + 'VB's FileAttribute is different than the URT's System.IO.FileAttributes: + ' VB URT + 'Normal 0 128 + 'ReadOnly 1 1 + 'Hidden 2 2 + 'System 4 4 + 'Volume 8 -- + 'Directory 16 16 + 'Archive 32 32 + 'Device -- 64 + 'Temporary -- 256 + 'SparseFile -- 512 + 'ReparsePoint -- 1024 + 'Compressed -- 2048 + 'Offline -- 4096 + 'NotContentIndexed -- 8192 + 'Encrypted -- 16384 + + Debug.Assert(Not System.Reflection.Assembly.GetCallingAssembly() Is Utils.VBRuntimeAssembly, _ + "Methods in Microsoft.VisualBasic should not call FileSystem public method. VSWhidbey 476783.") + + Dim WildCards() As Char = {"*"c, "?"c} + + If PathName.IndexOfAny(WildCards) >= 0 Then + Throw VbMakeException(vbErrors.BadFileNameOrNumber) + End If + + Dim f As New FileInfo(PathName) + + If f.Exists Then + 'Mask off any attributes that VB doesn't define. + Return CType(f.Attributes And &H3F, FileAttribute) + Else + Dim d As New DirectoryInfo(PathName) + If d.Exists Then + 'Mask off any attributes that VB doesn't define. + Return CType(d.Attributes And &H3F, FileAttribute) + End If + End If + + If Path.GetFileName(PathName).Length = 0 Then + Throw VbMakeException(vbErrors.BadFileNameOrNumber) + Else + Throw New FileNotFoundException(GetResourceString(ResID.FileSystem_FileNotFound1, PathName)) + End If + + End Function + + + + Public Sub Kill(ByVal PathName As String) + Debug.Assert(Not System.Reflection.Assembly.GetCallingAssembly() Is Utils.VBRuntimeAssembly, _ + "Methods in Microsoft.VisualBasic should not call FileSystem public method. VSWhidbey 476783.") + + Dim dir As DirectoryInfo + Dim DirName As String + Dim FileName As String + Dim files() As FileInfo + Dim file As FileInfo + Dim DeleteCount As Integer + Dim i As Integer + + DirName = Path.GetDirectoryName(PathName) + + If (DirName Is Nothing) OrElse (DirName.Length = 0) Then + DirName = Environment.CurrentDirectory + FileName = PathName + Else + FileName = Path.GetFileName(PathName) + End If + + dir = New DirectoryInfo(DirName) + files = dir.GetFiles(FileName) + DirName = DirName & Path.PathSeparator + + If (Not files Is Nothing) Then + For i = 0 To files.GetUpperBound(0) + file = files(i) + + 'Don't delete hidden or system files + If (file.Attributes And (FileAttribute.Hidden Or FileAttribute.System)) = 0 Then + FileName = file.FullName + + ' error if file is presently open + Dim oAssemblyData As AssemblyData = ProjectData.GetProjectData().GetAssemblyData(System.Reflection.Assembly.GetCallingAssembly()) + If CheckFileOpen(oAssemblyData, FileName, OpenModeTypes.Any) Then + Throw VbMakeException(New IOException(GetResourceString(ResID.FileSystem_FileAlreadyOpen1, FileName)), vbErrors.FileAlreadyOpen) + End If + + Try + + IO.File.Delete(FileName) + DeleteCount += 1 + Catch ex As IOException + 'Need to emulate vb6 error codes as much as possible + Throw VbMakeException(ex, vbErrors.FileAlreadyOpen) + + 'REVIEW: Catch ex As AccessException + 'REVIEW: Throw VbMakeException(ex, vbErrors.PathFileAccess) + + Catch ex As Exception + Throw ex + End Try + + End If + Next i + End If + + If DeleteCount = 0 Then + Throw New IO.FileNotFoundException(GetResourceString(ResID.KILL_NoFilesFound1, PathName)) + End If + End Sub + + + + Public Sub SetAttr(ByVal PathName As String, ByVal Attributes As FileAttribute) + 'VB's FileAttribute is different than the URT's System.IO.FileAttributes: + ' VB URT + 'Normal 0 128 + 'ReadOnly 1 1 + 'Hidden 2 2 + 'System 4 4 + 'Volume 8 -- + 'Directory 16 16 + 'Archive 32 32 + 'Device -- 64 + 'Temporary -- 256 + 'SparseFile -- 512 + 'ReparsePoint -- 1024 + 'Compressed -- 2048 + 'Offline -- 4096 + 'NotContentIndexed -- 8192 + 'Encrypted -- 16384 + + 'Check pathname for errors and if file is open for any mode except sequential input + If (PathName Is Nothing) OrElse (PathName.Length = 0) Then + Throw VbMakeException(New ArgumentException(GetResourceString(ResID.Argument_PathNullOrEmpty)), vbErrors.BadFileNameOrNumber) + End If + + Dim assem As System.Reflection.Assembly = System.Reflection.Assembly.GetCallingAssembly() + Dim oAssemblyData As AssemblyData = ProjectData.GetProjectData().GetAssemblyData(assem) + + VB6CheckPathname(oAssemblyData, PathName, OpenMode.Input) + + 'Only allow _A_RDONLY(1), _A_HIDDEN(2), _A_SYSTEM(4), _A_ARCH(20) + If ((Attributes Or &H27S) <> &H27S) Then + Throw New ArgumentException(GetResourceString(ResID.Argument_InvalidValue1, "Attributes")) + End If + + 'Dir function always returns files with Normal attribute in addition to others specified. + Dim URTAttributes As System.IO.FileAttributes = CType(Attributes, FileAttributes) + System.IO.File.SetAttributes(PathName, URTAttributes) + End Sub + + 'IMPORTANT: This call provides sensitive information whether a device exists and should be used with extreme care + Private Function UnsafeValidDrive(ByVal cDrive As Char) As Boolean 'Return of True means not a valid drive + Dim iDrive As Integer = AscW(cDrive) - AscW(chLetterA) + Return (CLng(UnsafeNativeMethods.GetLogicalDrives()) And CLng(&H2 ^ iDrive)) <> 0 + End Function + + + + '***************************************** + ' FileSystem APIs + '***************************************** + Private Sub ValidateAccess(ByVal Access As OpenAccess) + If Access <> OpenAccess.Default AndAlso _ + Access <> OpenAccess.Read AndAlso _ + Access <> OpenAccess.ReadWrite AndAlso _ + Access <> OpenAccess.Write Then + Throw New ArgumentException(GetResourceString(ResID.Argument_InvalidValue1, "Access")) + End If + End Sub + + + + Private Sub ValidateShare(ByVal Share As OpenShare) + If Share <> OpenShare.Default AndAlso _ + Share <> OpenShare.Shared AndAlso _ + Share <> OpenShare.LockRead AndAlso _ + Share <> OpenShare.LockReadWrite AndAlso _ + Share <> OpenShare.LockWrite Then + Throw New ArgumentException(GetResourceString(ResID.Argument_InvalidValue1, "Share")) + End If + End Sub + + + + Private Sub ValidateMode(ByVal Mode As OpenMode) + If Mode <> OpenMode.Input AndAlso _ + Mode <> OpenMode.Output AndAlso _ + Mode <> OpenMode.Random AndAlso _ + Mode <> OpenMode.Append AndAlso _ + Mode <> OpenMode.Binary Then + Throw New ArgumentException(GetResourceString(ResID.Argument_InvalidValue1, "Mode")) + End If + End Sub + + + + '============================================================================ + ' Initialization functions. + '============================================================================ + '====================================== + ' Public APIs + '====================================== + Public Sub FileOpen( _ + ByVal FileNumber As Integer, _ + ByVal FileName As String, _ + ByVal Mode As OpenMode, _ + Optional ByVal Access As OpenAccess = OpenAccess.Default, _ + Optional ByVal Share As OpenShare = OpenShare.Default, _ + Optional ByVal RecordLength As Integer = -1) + + Try + ValidateMode(Mode) + ValidateAccess(Access) + ValidateShare(Share) + + If (FileNumber < FIRST_LOCAL_CHANNEL OrElse FileNumber > LAST_LOCAL_CHANNEL) Then + Throw VbMakeException(vbErrors.BadFileNameOrNumber) + End If + Dim assem As System.Reflection.Assembly = System.Reflection.Assembly.GetCallingAssembly() + vbIOOpenFile(assem, FileNumber, FileName, Mode, Access, Share, RecordLength) + Catch ex As Exception + Throw ex + End Try + End Sub + + + + Public Sub FileClose(ByVal ParamArray FileNumbers() As Integer) + 'If the paramarray is empty, then all files get closed + + Try + Dim assem As System.Reflection.Assembly = System.Reflection.Assembly.GetCallingAssembly() + Dim oAssemblyData As AssemblyData + + oAssemblyData = ProjectData.GetProjectData().GetAssemblyData(assem) + + If (FileNumbers Is Nothing) OrElse (FileNumbers.Length = 0) Then + CloseAllFiles(oAssemblyData) + Else + Dim Index As Integer + + For Index = 0 To FileNumbers.GetUpperBound(0) + InternalCloseFile(oAssemblyData, FileNumbers(Index)) + Next + End If + Catch ex As Exception + Throw ex + End Try + End Sub + + + + Private Sub ValidateGetPutRecordNumber(ByVal RecordNumber As Long) + If RecordNumber < 1 AndAlso RecordNumber <> -1 Then + Throw VbMakeException(New ArgumentException(GetResourceString(ResID.Argument_InvalidValue1, "RecordNumber")), vbErrors.BadRecordNum) + End If + End Sub + + + + Public Sub FileGetObject(ByVal FileNumber As Integer, ByRef Value As Object, Optional ByVal RecordNumber As Long = -1) + Try + ValidateGetPutRecordNumber(RecordNumber) + Dim assem As System.Reflection.Assembly = System.Reflection.Assembly.GetCallingAssembly() + GetStream(assem, FileNumber).GetObject(Value, RecordNumber) + Catch ex As Exception + Throw ex + End Try + End Sub + + + +#If 0 Then + 'This was intended to catch FileGet uses that should be FileGetObject uses, however, when this code is enabled it + 'causes problems with binding to structures, arrays of structures, and arrays of decimal. + _ + Public Sub FileGet(ByVal FileNumber As Object, ByRef Value As Object, Optional ByVal RecordNumber As Object = -1) + End Sub +#End If + + + + Public Sub FileGet(ByVal FileNumber As Integer, ByRef Value As ValueType, Optional ByVal RecordNumber As Long = -1) + Try + ValidateGetPutRecordNumber(RecordNumber) + Dim assem As System.Reflection.Assembly = System.Reflection.Assembly.GetCallingAssembly() + GetStream(assem, FileNumber).Get(Value, RecordNumber) + Catch ex As Exception + Throw ex + End Try + End Sub + + + + Public Sub FileGet(ByVal FileNumber As Integer, ByRef Value As System.Array, Optional ByVal RecordNumber As Long = -1, _ + Optional ByVal ArrayIsDynamic As Boolean = False, Optional ByVal StringIsFixedLength As Boolean = False) + Try + ValidateGetPutRecordNumber(RecordNumber) + Dim assem As System.Reflection.Assembly = System.Reflection.Assembly.GetCallingAssembly() + GetStream(assem, FileNumber).Get(Value, RecordNumber, ArrayIsDynamic, StringIsFixedLength) + Catch ex As Exception + Throw ex + End Try + End Sub + + + + Public Sub FileGet(ByVal FileNumber As Integer, ByRef Value As Boolean, Optional ByVal RecordNumber As Long = -1) + Try + ValidateGetPutRecordNumber(RecordNumber) + Dim assem As System.Reflection.Assembly = System.Reflection.Assembly.GetCallingAssembly() + GetStream(assem, FileNumber).Get(Value, RecordNumber) + Catch ex As Exception + Throw ex + End Try + End Sub + + + + Public Sub FileGet(ByVal FileNumber As Integer, ByRef Value As Byte, Optional ByVal RecordNumber As Long = -1) + Try + ValidateGetPutRecordNumber(RecordNumber) + Dim assem As System.Reflection.Assembly = System.Reflection.Assembly.GetCallingAssembly() + GetStream(assem, FileNumber).Get(Value, RecordNumber) + Catch ex As Exception + Throw ex + End Try + End Sub + + + + Public Sub FileGet(ByVal FileNumber As Integer, ByRef Value As Short, Optional ByVal RecordNumber As Long = -1) + Try + ValidateGetPutRecordNumber(RecordNumber) + Dim assem As System.Reflection.Assembly = System.Reflection.Assembly.GetCallingAssembly() + GetStream(assem, FileNumber).Get(Value, RecordNumber) + Catch ex As Exception + Throw ex + End Try + End Sub + + + + Public Sub FileGet(ByVal FileNumber As Integer, ByRef Value As Integer, Optional ByVal RecordNumber As Long = -1) + Try + ValidateGetPutRecordNumber(RecordNumber) + Dim assem As System.Reflection.Assembly = System.Reflection.Assembly.GetCallingAssembly() + GetStream(assem, FileNumber).Get(Value, RecordNumber) + Catch ex As Exception + Throw ex + End Try + End Sub + + + + Public Sub FileGet(ByVal FileNumber As Integer, ByRef Value As Long, Optional ByVal RecordNumber As Long = -1) + Try + ValidateGetPutRecordNumber(RecordNumber) + Dim assem As System.Reflection.Assembly = System.Reflection.Assembly.GetCallingAssembly() + GetStream(assem, FileNumber).Get(Value, RecordNumber) + Catch ex As Exception + Throw ex + End Try + End Sub + + + + Public Sub FileGet(ByVal FileNumber As Integer, ByRef Value As Char, Optional ByVal RecordNumber As Long = -1) + Try + ValidateGetPutRecordNumber(RecordNumber) + Dim assem As System.Reflection.Assembly = System.Reflection.Assembly.GetCallingAssembly() + GetStream(assem, FileNumber).Get(Value, RecordNumber) + Catch ex As Exception + Throw ex + End Try + End Sub + + + + Public Sub FileGet(ByVal FileNumber As Integer, ByRef Value As Single, Optional ByVal RecordNumber As Long = -1) + Try + ValidateGetPutRecordNumber(RecordNumber) + Dim assem As System.Reflection.Assembly = System.Reflection.Assembly.GetCallingAssembly() + GetStream(assem, FileNumber).Get(Value, RecordNumber) + Catch ex As Exception + Throw ex + End Try + End Sub + + + + Public Sub FileGet(ByVal FileNumber As Integer, ByRef Value As Double, Optional ByVal RecordNumber As Long = -1) + Try + ValidateGetPutRecordNumber(RecordNumber) + Dim assem As System.Reflection.Assembly = System.Reflection.Assembly.GetCallingAssembly() + GetStream(assem, FileNumber).Get(Value, RecordNumber) + Catch ex As Exception + Throw ex + End Try + End Sub + + + + Public Sub FileGet(ByVal FileNumber As Integer, ByRef Value As Decimal, Optional ByVal RecordNumber As Long = -1) + Try + ValidateGetPutRecordNumber(RecordNumber) + Dim assem As System.Reflection.Assembly = System.Reflection.Assembly.GetCallingAssembly() + GetStream(assem, FileNumber).Get(Value, RecordNumber) + Catch ex As Exception + Throw ex + End Try + End Sub + + + + Public Sub FileGet(ByVal FileNumber As Integer, ByRef Value As String, Optional ByVal RecordNumber As Long = -1, Optional ByVal StringIsFixedLength As Boolean = False) + Try + ValidateGetPutRecordNumber(RecordNumber) + Dim assem As System.Reflection.Assembly = System.Reflection.Assembly.GetCallingAssembly() + GetStream(assem, FileNumber).Get(Value, RecordNumber, StringIsFixedLength) + Catch ex As Exception + Throw ex + End Try + End Sub + + + + Public Sub FileGet(ByVal FileNumber As Integer, ByRef Value As Date, Optional ByVal RecordNumber As Long = -1) + Try + ValidateGetPutRecordNumber(RecordNumber) + Dim assem As System.Reflection.Assembly = System.Reflection.Assembly.GetCallingAssembly() + GetStream(assem, FileNumber).Get(Value, RecordNumber) + Catch ex As Exception + Throw ex + End Try + End Sub + + + + Public Sub FilePutObject(ByVal FileNumber As Integer, ByVal Value As Object, Optional ByVal RecordNumber As Long = -1) + Try + ValidateGetPutRecordNumber(RecordNumber) + Dim assem As System.Reflection.Assembly = System.Reflection.Assembly.GetCallingAssembly() + GetStream(assem, FileNumber, OpenModeTypes.Binary Or OpenModeTypes.Random).PutObject(Value, RecordNumber) + Catch ex As Exception + Throw ex + End Try + End Sub + + + + _ + Public Sub FilePut(ByVal FileNumber As Object, ByVal Value As Object, Optional ByVal RecordNumber As Object = -1) + Throw New ArgumentException(GetResourceString(ResID.UseFilePutObject)) + End Sub + + + + Public Sub FilePut(ByVal FileNumber As Integer, ByVal Value As ValueType, Optional ByVal RecordNumber As Long = -1) + Try + ValidateGetPutRecordNumber(RecordNumber) + Dim assem As System.Reflection.Assembly = System.Reflection.Assembly.GetCallingAssembly() + GetStream(assem, FileNumber, OpenModeTypes.Binary Or OpenModeTypes.Random).Put(Value, RecordNumber) + Catch ex As Exception + Throw ex + End Try + End Sub + + + + Public Sub FilePut(ByVal FileNumber As Integer, ByVal Value As System.Array, Optional ByVal RecordNumber As Long = -1, _ + Optional ByVal ArrayIsDynamic As Boolean = False, Optional ByVal StringIsFixedLength As Boolean = False) + + Try + ValidateGetPutRecordNumber(RecordNumber) + Dim assem As System.Reflection.Assembly = System.Reflection.Assembly.GetCallingAssembly() + GetStream(assem, FileNumber, OpenModeTypes.Binary Or OpenModeTypes.Random).Put(Value, RecordNumber, ArrayIsDynamic, StringIsFixedLength) + Catch ex As Exception + Throw ex + End Try + End Sub + + + + Public Sub FilePut(ByVal FileNumber As Integer, ByVal Value As Boolean, Optional ByVal RecordNumber As Long = -1) + Try + ValidateGetPutRecordNumber(RecordNumber) + Dim assem As System.Reflection.Assembly = System.Reflection.Assembly.GetCallingAssembly() + GetStream(assem, FileNumber, OpenModeTypes.Binary Or OpenModeTypes.Random).Put(Value, RecordNumber) + Catch ex As Exception + Throw ex + End Try + End Sub + + + + Public Sub FilePut(ByVal FileNumber As Integer, ByVal Value As Byte, Optional ByVal RecordNumber As Long = -1) + Try + ValidateGetPutRecordNumber(RecordNumber) + Dim assem As System.Reflection.Assembly = System.Reflection.Assembly.GetCallingAssembly() + GetStream(assem, FileNumber, OpenModeTypes.Binary Or OpenModeTypes.Random).Put(Value, RecordNumber) + Catch ex As Exception + Throw ex + End Try + End Sub + + + + Public Sub FilePut(ByVal FileNumber As Integer, ByVal Value As Short, Optional ByVal RecordNumber As Long = -1) + Try + ValidateGetPutRecordNumber(RecordNumber) + Dim assem As System.Reflection.Assembly = System.Reflection.Assembly.GetCallingAssembly() + GetStream(assem, FileNumber, OpenModeTypes.Binary Or OpenModeTypes.Random).Put(Value, RecordNumber) + Catch ex As Exception + Throw ex + End Try + End Sub + + + + Public Sub FilePut(ByVal FileNumber As Integer, ByVal Value As Integer, Optional ByVal RecordNumber As Long = -1) + Try + ValidateGetPutRecordNumber(RecordNumber) + Dim assem As System.Reflection.Assembly = System.Reflection.Assembly.GetCallingAssembly() + GetStream(assem, FileNumber, OpenModeTypes.Binary Or OpenModeTypes.Random).Put(Value, RecordNumber) + Catch ex As Exception + Throw ex + End Try + End Sub + + + + Public Sub FilePut(ByVal FileNumber As Integer, ByVal Value As Long, Optional ByVal RecordNumber As Long = -1) + Try + ValidateGetPutRecordNumber(RecordNumber) + Dim assem As System.Reflection.Assembly = System.Reflection.Assembly.GetCallingAssembly() + GetStream(assem, FileNumber, OpenModeTypes.Binary Or OpenModeTypes.Random).Put(Value, RecordNumber) + Catch ex As Exception + Throw ex + End Try + End Sub + + + + Public Sub FilePut(ByVal FileNumber As Integer, ByVal Value As Char, Optional ByVal RecordNumber As Long = -1) + Try + ValidateGetPutRecordNumber(RecordNumber) + Dim assem As System.Reflection.Assembly = System.Reflection.Assembly.GetCallingAssembly() + GetStream(assem, FileNumber, OpenModeTypes.Binary Or OpenModeTypes.Random).Put(Value, RecordNumber) + Catch ex As Exception + Throw ex + End Try + End Sub + + + + Public Sub FilePut(ByVal FileNumber As Integer, ByVal Value As Single, Optional ByVal RecordNumber As Long = -1) + Try + ValidateGetPutRecordNumber(RecordNumber) + Dim assem As System.Reflection.Assembly = System.Reflection.Assembly.GetCallingAssembly() + GetStream(assem, FileNumber, OpenModeTypes.Binary Or OpenModeTypes.Random).Put(Value, RecordNumber) + Catch ex As Exception + Throw ex + End Try + End Sub + + + + Public Sub FilePut(ByVal FileNumber As Integer, ByVal Value As Double, Optional ByVal RecordNumber As Long = -1) + Try + ValidateGetPutRecordNumber(RecordNumber) + Dim assem As System.Reflection.Assembly = System.Reflection.Assembly.GetCallingAssembly() + GetStream(assem, FileNumber, OpenModeTypes.Binary Or OpenModeTypes.Random).Put(Value, RecordNumber) + Catch ex As Exception + Throw ex + End Try + End Sub + + + + Public Sub FilePut(ByVal FileNumber As Integer, ByVal Value As Decimal, Optional ByVal RecordNumber As Long = -1) + Try + ValidateGetPutRecordNumber(RecordNumber) + Dim assem As System.Reflection.Assembly = System.Reflection.Assembly.GetCallingAssembly() + GetStream(assem, FileNumber, OpenModeTypes.Binary Or OpenModeTypes.Random).Put(Value, RecordNumber) + Catch ex As Exception + Throw ex + End Try + End Sub + + + + Public Sub FilePut(ByVal FileNumber As Integer, ByVal Value As String, Optional ByVal RecordNumber As Long = -1, Optional ByVal StringIsFixedLength As Boolean = False) + Try + ValidateGetPutRecordNumber(RecordNumber) + Dim assem As System.Reflection.Assembly = System.Reflection.Assembly.GetCallingAssembly() + GetStream(assem, FileNumber, OpenModeTypes.Binary Or OpenModeTypes.Random).Put(Value, RecordNumber, StringIsFixedLength) + Catch ex As Exception + Throw ex + End Try + End Sub + + + + Public Sub FilePut(ByVal FileNumber As Integer, ByVal Value As Date, Optional ByVal RecordNumber As Long = -1) + Try + ValidateGetPutRecordNumber(RecordNumber) + Dim assem As System.Reflection.Assembly = System.Reflection.Assembly.GetCallingAssembly() + GetStream(assem, FileNumber, OpenModeTypes.Binary Or OpenModeTypes.Random).Put(Value, RecordNumber) + Catch ex As Exception + Throw ex + End Try + End Sub + + + + Public Sub Print(ByVal FileNumber As Integer, ByVal ParamArray Output() As Object) + Try + Dim assem As System.Reflection.Assembly = System.Reflection.Assembly.GetCallingAssembly() + GetStream(assem, FileNumber).Print(CType(Output, Object())) + Catch ex As Exception + Throw ex + End Try + End Sub + + + + Public Sub PrintLine(ByVal FileNumber As Integer, ByVal ParamArray Output() As Object) + Try + Dim assem As System.Reflection.Assembly = System.Reflection.Assembly.GetCallingAssembly() + GetStream(assem, FileNumber).PrintLine(CType(Output, Object())) + Catch ex As Exception + Throw ex + End Try + End Sub + + + Public Sub Input(ByVal FileNumber As Integer, ByRef Value As Object) + Try + Dim assem As System.Reflection.Assembly = System.Reflection.Assembly.GetCallingAssembly() + GetStream(assem, FileNumber).Input(Value) + Catch ex As Exception + Throw ex + End Try + End Sub + + + + Public Sub Input(ByVal FileNumber As Integer, ByRef Value As Boolean) + Try + Dim assem As System.Reflection.Assembly = System.Reflection.Assembly.GetCallingAssembly() + GetStream(assem, FileNumber).Input(Value) + Catch ex As Exception + Throw ex + End Try + End Sub + + + + Public Sub Input(ByVal FileNumber As Integer, ByRef Value As Byte) + Try + Dim assem As System.Reflection.Assembly = System.Reflection.Assembly.GetCallingAssembly() + GetStream(assem, FileNumber).Input(Value) + Catch ex As Exception + Throw ex + End Try + End Sub + + + + Public Sub Input(ByVal FileNumber As Integer, ByRef Value As Short) + Try + Dim assem As System.Reflection.Assembly = System.Reflection.Assembly.GetCallingAssembly() + GetStream(assem, FileNumber).Input(Value) + Catch ex As Exception + Throw ex + End Try + End Sub + + + + Public Sub Input(ByVal FileNumber As Integer, ByRef Value As Integer) + Try + Dim assem As System.Reflection.Assembly = System.Reflection.Assembly.GetCallingAssembly() + GetStream(assem, FileNumber).Input(Value) + Catch ex As Exception + Throw ex + End Try + End Sub + + + + Public Sub Input(ByVal FileNumber As Integer, ByRef Value As Long) + Try + Dim assem As System.Reflection.Assembly = System.Reflection.Assembly.GetCallingAssembly() + GetStream(assem, FileNumber).Input(Value) + Catch ex As Exception + Throw ex + End Try + End Sub + + + + Public Sub Input(ByVal FileNumber As Integer, ByRef Value As Char) + Try + Dim assem As System.Reflection.Assembly = System.Reflection.Assembly.GetCallingAssembly() + GetStream(assem, FileNumber).Input(Value) + Catch ex As Exception + Throw ex + End Try + End Sub + + + + Public Sub Input(ByVal FileNumber As Integer, ByRef Value As Single) + Try + Dim assem As System.Reflection.Assembly = System.Reflection.Assembly.GetCallingAssembly() + GetStream(assem, FileNumber).Input(Value) + Catch ex As Exception + Throw ex + End Try + End Sub + + + + Public Sub Input(ByVal FileNumber As Integer, ByRef Value As Double) + Try + Dim assem As System.Reflection.Assembly = System.Reflection.Assembly.GetCallingAssembly() + GetStream(assem, FileNumber).Input(Value) + Catch ex As Exception + Throw ex + End Try + End Sub + + + + Public Sub Input(ByVal FileNumber As Integer, ByRef Value As Decimal) + Try + Dim assem As System.Reflection.Assembly = System.Reflection.Assembly.GetCallingAssembly() + GetStream(assem, FileNumber).Input(Value) + Catch ex As Exception + Throw ex + End Try + End Sub + + + + Public Sub Input(ByVal FileNumber As Integer, ByRef Value As String) + Try + Dim assem As System.Reflection.Assembly = System.Reflection.Assembly.GetCallingAssembly() + GetStream(assem, FileNumber).Input(Value) + Catch ex As Exception + Throw ex + End Try + End Sub + + + + Public Sub Input(ByVal FileNumber As Integer, ByRef Value As Date) + Try + Dim assem As System.Reflection.Assembly = System.Reflection.Assembly.GetCallingAssembly() + GetStream(assem, FileNumber).Input(Value) + Catch ex As Exception + Throw ex + End Try + End Sub + + + + Public Sub [Write](ByVal FileNumber As Integer, ByVal ParamArray Output() As Object) + Try + Dim assem As System.Reflection.Assembly = System.Reflection.Assembly.GetCallingAssembly() + GetStream(assem, FileNumber).WriteHelper(Output) + Catch ex As Exception + Throw ex + End Try + End Sub + + + + Public Sub WriteLine(ByVal FileNumber As Integer, ByVal ParamArray Output() As Object) + Try + Dim assem As System.Reflection.Assembly = System.Reflection.Assembly.GetCallingAssembly() + GetStream(assem, FileNumber).WriteLineHelper(Output) + Catch ex As Exception + Throw ex + End Try + End Sub + + + + Public Function InputString(ByVal FileNumber As Integer, ByVal CharCount As Integer) As String + Try + Dim oFile As VB6File + + If (CharCount < 0 OrElse CharCount > (&H7FFFFFFFI / 2)) Then + Throw New ArgumentException(GetResourceString(ResID.Argument_InvalidValue1, "CharCount")) + End If + + Dim assem As System.Reflection.Assembly = System.Reflection.Assembly.GetCallingAssembly() + + oFile = GetChannelObj(assem, FileNumber) + oFile.Lock() + + Try + InputString = oFile.InputString(CharCount) + Finally + oFile.Unlock() + End Try + Catch ex As Exception + Throw ex + End Try + End Function + + + + Public Function [LineInput](ByVal FileNumber As Integer) As String + Dim oFile As VB6File + + Dim assem As System.Reflection.Assembly = System.Reflection.Assembly.GetCallingAssembly() + oFile = GetStream(assem, FileNumber) + CheckInputCapable(oFile) + + If oFile.EOF() Then + Throw VbMakeException(vbErrors.EndOfFile) + End If + + Return oFile.LineInput() + End Function + + + + Public Sub Lock(ByVal FileNumber As Integer) + Dim assem As System.Reflection.Assembly = System.Reflection.Assembly.GetCallingAssembly() + GetStream(assem, FileNumber).Lock() + End Sub + + + + Public Sub Lock(ByVal FileNumber As Integer, ByVal Record As Long) + Dim assem As System.Reflection.Assembly = System.Reflection.Assembly.GetCallingAssembly() + GetStream(assem, FileNumber).Lock(Record) + End Sub + + + + Public Sub Lock(ByVal FileNumber As Integer, ByVal FromRecord As Long, ByVal ToRecord As Long) + Dim assem As System.Reflection.Assembly = System.Reflection.Assembly.GetCallingAssembly() + GetStream(assem, FileNumber).Lock(FromRecord, ToRecord) + End Sub + + + + Public Sub Unlock(ByVal FileNumber As Integer) + Dim assem As System.Reflection.Assembly = System.Reflection.Assembly.GetCallingAssembly() + GetStream(assem, FileNumber).Unlock() + End Sub + + + + Public Sub Unlock(ByVal FileNumber As Integer, ByVal Record As Long) + Dim assem As System.Reflection.Assembly = System.Reflection.Assembly.GetCallingAssembly() + GetStream(assem, FileNumber).Unlock(Record) + End Sub + + + + Public Sub Unlock(ByVal FileNumber As Integer, ByVal FromRecord As Long, ByVal ToRecord As Long) + Dim assem As System.Reflection.Assembly = System.Reflection.Assembly.GetCallingAssembly() + GetStream(assem, FileNumber).Unlock(FromRecord, ToRecord) + End Sub + + + + Public Sub FileWidth(ByVal FileNumber As Integer, ByVal RecordWidth As Integer) + Dim assem As System.Reflection.Assembly = System.Reflection.Assembly.GetCallingAssembly() + GetStream(assem, FileNumber).SetWidth(RecordWidth) + End Sub + + + + Public Function [FreeFile]() As Integer + Dim indChannel As Integer + Dim oFile As VB6File + + ' get the project object + Dim assem As System.Reflection.Assembly = System.Reflection.Assembly.GetCallingAssembly() + Dim oAssemblyData As AssemblyData + + oAssemblyData = ProjectData.GetProjectData().GetAssemblyData(assem) + + For indChannel = 1 To 255 + oFile = oAssemblyData.GetChannelObj(indChannel) + If oFile Is Nothing Then + Return indChannel + End If + Next + + Throw VbMakeException(vbErrors.TooManyFiles) + End Function + + + + 'Function Seek + ' + 'RANDOM MODE - Sets the number of next record to read/write + 'other modes - Sets the byte position at which the next operation + ' will take place + ' + Public Sub Seek(ByVal FileNumber As Integer, ByVal Position As Long) + Dim assem As System.Reflection.Assembly = System.Reflection.Assembly.GetCallingAssembly() + GetStream(assem, FileNumber).Seek(Position) + End Sub + + + + 'Function Seek + ' + 'RANDOM MODE - Returns number of next record + 'other modes - Returns the byte position at which the next operation + ' will take place + ' + Public Function Seek(ByVal FileNumber As Integer) As Long + Dim assem As System.Reflection.Assembly = System.Reflection.Assembly.GetCallingAssembly() + Return GetStream(assem, FileNumber).Seek() + End Function + + + + Public Function EOF(ByVal FileNumber As Integer) As Boolean + Dim assem As System.Reflection.Assembly = System.Reflection.Assembly.GetCallingAssembly() + Return GetStream(assem, FileNumber).EOF() + End Function + + + + Public Function Loc(ByVal FileNumber As Integer) As Long + Dim assem As System.Reflection.Assembly = System.Reflection.Assembly.GetCallingAssembly() + Return GetStream(assem, FileNumber).LOC() + End Function + + + + Public Function LOF(ByVal FileNumber As Integer) As Long + Dim assem As System.Reflection.Assembly = System.Reflection.Assembly.GetCallingAssembly() + Return GetStream(assem, FileNumber).LOF() + End Function + + + + Public Function TAB() As TabInfo + Dim Result As TabInfo + Result.Column = -1 + Return Result + End Function + + + + Public Function TAB(ByVal Column As Short) As TabInfo + Dim Result As TabInfo + If Column < 1 Then + Column = 1 + End If + + Result.Column = Column + Return Result + End Function + + + + Public Function SPC(ByVal Count As Short) As SpcInfo + Dim Result As SpcInfo + If Count < 1 Then + Count = 0 + End If + + Result.Count = Count + Return Result + End Function + + + + Public Function FileAttr(ByVal FileNumber As Integer) As OpenMode + Dim assem As System.Reflection.Assembly = System.Reflection.Assembly.GetCallingAssembly() + Return GetStream(assem, FileNumber).GetMode() + End Function + + + + Public Sub Reset() + CloseAllFiles(System.Reflection.Assembly.GetCallingAssembly()) + End Sub + + + + _ + _ + Public Sub Rename(ByVal OldPath As String, ByVal NewPath As String) + Dim oAssemblyData As AssemblyData = ProjectData.GetProjectData().GetAssemblyData(System.Reflection.Assembly.GetCallingAssembly()) + OldPath = VB6CheckPathname(oAssemblyData, OldPath, CType(OpenModeTypes.Any, OpenMode)) + NewPath = VB6CheckPathname(oAssemblyData, NewPath, CType(OpenModeTypes.Any, OpenMode)) + + Dim Result As Integer + Dim ErrCode As Integer + + '*** SECURITY CHECK - SECURITY CHECK - SECURITY CHECK - SECURITY CHECK - SECURITY CHECK - SECURITY CHECK *** + 'FILES CAN BE MOVED CROSS VOLUME - SO READ | WRITE (DELETE) permissions are required + Call (New FileIOPermission( _ + FileIOPermissionAccess.Read Or FileIOPermissionAccess.Write, OldPath)).Demand() + + '*** SECURITY CHECK - SECURITY CHECK - SECURITY CHECK - SECURITY CHECK - SECURITY CHECK - SECURITY CHECK *** + Call (New FileIOPermission(FileIOPermissionAccess.Write, NewPath)).Demand() + + Result = UnsafeNativeMethods.MoveFile(OldPath, NewPath) + If Result = 0 Then + ErrCode = System.Runtime.InteropServices.Marshal.GetLastWin32Error() + + Select Case ErrCode + Case ERROR_FILE_NOT_FOUND + Throw VbMakeException(vbErrors.FileNotFound) + + Case ERROR_FILE_EXISTS, _ + ERROR_ALREADY_EXISTS + Throw VbMakeException(vbErrors.FileAlreadyExists) + + Case ERROR_INVALID_ACCESS + Throw VbMakeException(vbErrors.PathFileAccess) + + Case ERROR_NOT_SAME_DEVICE + Throw VbMakeException(vbErrors.DifferentDrive) + + Case Else + Throw VbMakeException(vbErrors.IllegalFuncCall) + End Select + End If + End Sub + + + + '====================================== + 'Private APIs + '====================================== + Private Function GetStream(ByVal assem As System.Reflection.Assembly, ByVal FileNumber As Integer) As VB6File + Return GetStream(assem, FileNumber, CType(OpenModeTypes.Input Or _ + OpenModeTypes.Output Or _ + OpenModeTypes.Random Or _ + OpenModeTypes.Append Or _ + OpenModeTypes.Binary, OpenModeTypes)) + End Function + + + + Private Function GetStream(ByVal assem As System.Reflection.Assembly, ByVal FileNumber As Integer, ByVal mode As OpenModeTypes) As VB6File + Dim Result As VB6File + If (FileNumber < FIRST_LOCAL_CHANNEL) OrElse (FileNumber > LAST_LOCAL_CHANNEL) Then + Throw VbMakeException(vbErrors.BadFileNameOrNumber) + End If + + Result = GetChannelObj(assem, FileNumber) + + If (OpenModeTypesFromOpenMode(Result.GetMode()) Or mode) = 0 Then + Result = Nothing + Throw VbMakeException(vbErrors.BadFileMode) + End If + + Return Result + End Function + + + + Private Function OpenModeTypesFromOpenMode(ByVal om As OpenMode) As OpenModeTypes + If (om = OpenMode.Input) Then + Return OpenModeTypes.Input + ElseIf (om = OpenMode.Output) Then + Return OpenModeTypes.Output + ElseIf (om = OpenMode.Append) Then + Return OpenModeTypes.Append + ElseIf (om = OpenMode.Binary) Then + Return OpenModeTypes.Binary + ElseIf (om = OpenMode.Random) Then + Return OpenModeTypes.Random + ElseIf CInt(om) = CInt(OpenModeTypes.Any) Then + Return OpenModeTypes.Any + End If + + ' Fix FxCop violation. This is security-in-depth mean. This exception should never been hit. + ' We will throw Arguments are not valid. + Throw New ArgumentException(GetResourceString(ResID.Argument_InvalidValue), "om") + End Function + + + Friend Sub CloseAllFiles(ByVal assem As System.Reflection.Assembly) + CloseAllFiles(ProjectData.GetProjectData().GetAssemblyData(assem)) + End Sub + + Friend Sub CloseAllFiles(ByVal oAssemblyData As AssemblyData) + Dim FileNumber As Integer + + For FileNumber = 1 To 255 + InternalCloseFile(oAssemblyData, FileNumber) + Next + End Sub + + + + Private Sub InternalCloseFile(ByVal oAssemblyData As AssemblyData, ByVal FileNumber As Integer) + If FileNumber = 0 Then + CloseAllFiles(oAssemblyData) + Exit Sub + End If + + Dim oFile As VB6File + + oFile = GetChannelOrNull(oAssemblyData, FileNumber) + + If oFile Is Nothing Then + Else + oAssemblyData.SetChannelObj(FileNumber, Nothing) + + If Not oFile Is Nothing Then ' FileNumber not opened + oFile.CloseFile() + End If + End If + End Sub + + + + Friend Function VB6CheckPathname(ByVal oAssemblyData As AssemblyData, ByVal sPath As String, ByVal mode As OpenMode) As String + Dim Result As String + ' Error if wildcard characters in pathname + If (sPath.IndexOf("?"c) <> -1 OrElse sPath.IndexOf("*"c) <> -1) Then + Throw New ArgumentException(GetResourceString(ResID.Argument_InvalidPathChars1, sPath)) + End If + + ' process the name to check for errors + Result = (New FileInfo(sPath)).FullName + + ' Error if file is already open and conflicting mode + If CheckFileOpen(oAssemblyData, Result, OpenModeTypesFromOpenMode(mode)) Then + Throw VbMakeException(vbErrors.FileAlreadyOpen) + End If + + Return Result + End Function + + + + Friend Function CheckFileOpen(ByVal oAssemblyData As AssemblyData, ByVal sPath As String, ByVal NewFileMode As OpenModeTypes) As Boolean + Dim lChannel As Integer + Dim lIndexMax As Integer + Dim mode As OpenMode + Dim oFile As VB6File + + lIndexMax = 255 + + For lChannel = 1 To lIndexMax + oFile = GetChannelOrNull(oAssemblyData, lChannel) + If oFile Is Nothing Then + 'continue looking + Else + mode = oFile.GetMode() + + ' compare the filename with the input string case insensitive + ' exit loop if match occurs and both files are not sequential input + ' and not random/binary. + If System.String.Compare(sPath, oFile.GetAbsolutePath(), StringComparison.OrdinalIgnoreCase) = 0 Then + ' If path is the same, then verify + ' that neither file is open for sequential input + ' and that both are open for the same mode (either Binary or Random) + If CInt(NewFileMode) = -1 Then + 'Special case for any open mode + Return True + Else + If (NewFileMode Or mode) <> OpenMode.Input Then + If (NewFileMode Or mode Or OpenModeTypes.Binary Or OpenModeTypes.Random) <> (OpenModeTypes.Binary Or OpenModeTypes.Random) Then + Return True + End If + End If + End If + End If + End If + Next + + Return False + End Function + + + + Private Sub vbIOOpenFile(ByVal assem As System.Reflection.Assembly, _ + ByVal FileNumber As Integer, _ + ByVal FileName As String, _ + ByVal Mode As OpenMode, _ + ByVal Access As OpenAccess, _ + ByVal Share As OpenShare, _ + ByVal RecordLength As Integer) + Dim oFile As VB6File + Dim oAssemblyData As AssemblyData + + oAssemblyData = ProjectData.GetProjectData().GetAssemblyData(assem) + + If Not GetChannelOrNull(oAssemblyData, FileNumber) Is Nothing Then + Throw VbMakeException(vbErrors.FileAlreadyOpen) + End If + + If (FileName Is Nothing) OrElse (FileName.Length = 0) Then + Throw VbMakeException(vbErrors.PathFileAccess) + End If + + FileName = (New FileInfo(FileName)).FullName + + If CheckFileOpen(oAssemblyData, FileName, OpenModeTypesFromOpenMode(Mode)) Then + Throw VbMakeException(vbErrors.FileAlreadyOpen) + End If + + If (RecordLength <> -1 AndAlso RecordLength <= 0) Then + Throw VbMakeException(vbErrors.IllegalFuncCall) + End If + + If Mode = OpenMode.Binary Then + RecordLength = 1 + ElseIf RecordLength = -1 Then + If Mode = OpenMode.Random Then + RecordLength = 128 + Else + RecordLength = 512 + End If + End If + + '------------------------------------------------------------------ + ' possible combinations of mode and access, and order of access + ' (other combinations are not passed to rtFileOpen.) + ' + ' mode = MODE_SEQ_IN + ' access = ACCESS_NONE read + ' access = ACCESS_READ read + ' + ' mode = MODE_SEQ_OUT + ' access = ACCESS_NONE write + ' access = ACCESS_WRITE write + ' + ' mode = MODE_RANDOM or MODE_BINARY + ' access = ACCESS_NONE read/write, write, read + ' access = ACCESS_READ read + ' access = ACCESS_WRITE write + ' access = ACCESS_READ_WRITE read/write + ' + ' mode = MODE_SEQ_APP + ' access = ACCESS_NONE read/write, write + ' access = ACCESS_WRITE write + '------------------------------------------------------------------ + + If Share = OpenShare.Default Then + Share = OpenShare.LockReadWrite + End If + + Select Case Mode + + Case OpenMode.Input + If (Access <> OpenAccess.Read) AndAlso (Access <> OpenAccess.Default) Then + Throw New ArgumentException(GetResourceString(ResID.FileSystem_IllegalInputAccess)) + End If + oFile = New VB6InputFile(FileName, Share) + Case OpenMode.Output + If (Access <> OpenAccess.Write) AndAlso (Access <> OpenAccess.Default) Then + Throw New ArgumentException(GetResourceString(ResID.FileSystem_IllegalOutputAccess)) + End If + oFile = New VB6OutputFile(FileName, Share, False) + Case OpenMode.Random + If (Access = OpenAccess.Default) Then + Access = OpenAccess.ReadWrite + End If + oFile = New VB6RandomFile(FileName, Access, Share, RecordLength) + Case OpenMode.Append + If (Access <> OpenAccess.Write) AndAlso (Access <> OpenAccess.ReadWrite) AndAlso (Access <> OpenAccess.Default) Then + Throw New ArgumentException(GetResourceString(ResID.FileSystem_IllegalAppendAccess)) + End If + oFile = New VB6OutputFile(FileName, Share, True) + Case OpenMode.Binary + If (Access = OpenAccess.Default) Then + Access = OpenAccess.ReadWrite + End If + oFile = New VB6BinaryFile(FileName, Access, Share) + Case Else + Throw VbMakeException(vbErrors.InternalError) + End Select + + AddFileToList(oAssemblyData, FileNumber, oFile) + End Sub + + + + Private Sub AddFileToList(ByVal oAssemblyData As AssemblyData, ByVal FileNumber As Integer, ByVal oFile As VB6File) + If oFile Is Nothing Then + Throw VbMakeException(vbErrors.InternalError) + Else + oFile.OpenFile() + + oAssemblyData.SetChannelObj(FileNumber, oFile) + End If + End Sub + + + + '====================================== + ' Static methods + '====================================== + ' GetChannelOrNull() which will throw an exception on bad FileNumber number. + ' If the table entry is null (e.g. FileNumber is not open) throw an exception + Friend Function GetChannelObj(ByVal assem As System.Reflection.Assembly, ByVal FileNumber As Integer) As VB6File + Dim oFile As VB6File + + oFile = GetChannelOrNull(ProjectData.GetProjectData().GetAssemblyData(assem), FileNumber) + + If oFile Is Nothing Then + Throw VbMakeException(vbErrors.BadFileNameOrNumber) + End If + + Return oFile + End Function + + + + '====================================== + ' Protected and Private methods + '====================================== + ' Error an exception only on bad file number. + ' If the table entry is null, return it. + Private Function GetChannelOrNull(ByVal oAssemblyData As AssemblyData, ByVal FileNumber As Integer) As VB6File + Return oAssemblyData.GetChannelObj(FileNumber) + End Function + + + + Private Sub CheckInputCapable(ByVal oFile As VB6File) + If Not oFile.CanInput() Then + Throw VbMakeException(vbErrors.BadFileMode) + End If + End Sub + + + + End Module +End Namespace + diff --git a/Microsoft.VisualBasic/runtime/msvbalib/Financial.vb b/Microsoft.VisualBasic/runtime/msvbalib/Financial.vb new file mode 100644 index 000000000..39163121e --- /dev/null +++ b/Microsoft.VisualBasic/runtime/msvbalib/Financial.vb @@ -0,0 +1,962 @@ +' Copyright (c) Microsoft Corporation. All rights reserved. + +Imports System +Imports System.Math + +Imports Microsoft.VisualBasic.CompilerServices +Imports Microsoft.VisualBasic.CompilerServices.Utils + +Namespace Microsoft.VisualBasic + + Public Module Financial + + '============================================================================ + ' Financial functions. + '============================================================================ + + Private Const cnL_IT_STEP As Double = 0.00001 + Private Const cnL_IT_EPSILON As Double = 0.0000001 + + + '------------------------------------------------------------- + ' + ' Name : DDB + + ' Purpose : Calculates depreciation for a period based on the + ' doubly-declining balance method. Returns the result. + ' It raises an error if parameters are invalid. + + ' Derivation : At each period, 2*balance/nper is subtracted + ' from the balance. The balance starts at the purchase + ' value, and the total of the payments may not + ' exceed (value - salvage). The algorithm uses a non- + ' iterative method to calculate the payment. + ' Note that only the integral values of nper and per make any + ' sense. However, Excel allowed non-integral values, and + ' thus these routines also work with non-integral input. + ' + ' PMT = 2 * (value / nper) * ( (nper -2) / nper ) ^ (per - 1) + ' + ' total = value * ( 1 - ( (nper - 2) / nper ) ^ per ) + ' + ' excess = total - (value - salvage) + ' + ' ddb = PMT : if excess <= 0 + ' PMT-excess : if PMT >= excess > 0 + ' 0 : if excess > PMT + ' Returns : Double + ' + '------------------------------------------------------------- + ' + Public Function DDB(ByVal Cost As Double, ByVal Salvage As Double, ByVal Life As Double, ByVal Period As Double, Optional ByVal Factor As Double = 2.0) As Double + + Dim dRet As Double + Dim dTot As Double + Dim dExcess As Double + Dim dTemp As Double + Dim dNTemp As Double + + ' Handle invalid parameters + If Factor <= 0.0# OrElse Salvage < 0.0# OrElse Period <= 0.0# OrElse Period > Life Then + Throw New ArgumentException(GetResourceString(ResID.Argument_InvalidValue1, "Factor")) + End If + + ' Handle special (trivial) cases + If Cost <= 0.0# Then + Return 0.0# + End If + + If Life < 2.0# Then + Return (Cost - Salvage) + End If + + If Life = 2.0# AndAlso Period > 1.0# Then + Return 0.0# + End If + + If Life = 2.0# AndAlso Period <= 1.0# Then + Return (Cost - Salvage) + End If + + If Period <= 1.0# Then + dRet = Cost * Factor / Life + dTemp = Cost - Salvage + If dRet > dTemp Then + Return dTemp + Else + Return dRet + End If + End If + + ' Perform the calculation + dTemp = (Life - Factor) / Life + dNTemp = Period - 1.0# + + ' WARSI Using the exponent operator for pow(..) in C code of DDB. Still got + ' to make sure that they (pow and ^) are same for all conditions + dRet = Factor * Cost / Life * dTemp ^ dNTemp + + ' WARSI Using the exponent operator for pow(..) in C code of DDB. Still got + ' to make sure that they (pow and ^) are same for all conditions + dTot = Cost * (1 - dTemp ^ Period) + dExcess = dTot - Cost + Salvage + + If dExcess > 0.0# Then + dRet = dRet - dExcess + End If + + If dRet >= 0.0# Then + DDB = dRet + Else + DDB = 0.0# + End If + + + End Function + + + + '------------------------------------------------------------- + ' + ' Name : FV + ' Purpose : It is computed as - + + ' (1+rate)^nper - 1 + ' fv = -pv*(1+rate)^nper - PMT*(1+rate*type)* ----------------- + ' rate + ' + ' fv = -pv - PMT * nper : if rate == 0 + ' + ' + ' Returns : Double + ' + '------------------------------------------------------------- + ' + Public Function FV(ByVal Rate As Double, ByVal NPer As Double, ByVal Pmt As Double, Optional ByVal PV As Double = 0, Optional ByVal Due As DueDate = DueDate.EndOfPeriod) As Double + Return FV_Internal(Rate, NPer, Pmt, PV, Due) + End Function + + + + Private Function FV_Internal(ByVal Rate As Double, ByVal NPer As Double, ByVal Pmt As Double, Optional ByVal PV As Double = 0, Optional ByVal Due As DueDate = DueDate.EndOfPeriod) As Double + Dim dTemp As Double + Dim dTemp2 As Double + Dim dTemp3 As Double + + 'Performing calculation + If Rate = 0.0# Then + Return (-PV - Pmt * NPer) + End If + + If Due <> DueDate.EndOfPeriod Then + dTemp = 1.0# + Rate + Else + dTemp = 1.0# + End If + + dTemp3 = 1.0# + Rate + dTemp2 = System.Math.Pow(dTemp3, NPer) + + 'Do divides before multiplies to avoid OverflowExceptions + Return ((-PV) * dTemp2) - ((Pmt / Rate) * dTemp * (dTemp2 - 1.0#)) + End Function + + + + '------------------------------------------------------------- + ' + ' Name : IPmt + ' Purpose : This function calculates the interest part of a + ' payment for a given period. The payment is part of + ' a series of regular payments described by the other + ' arguments. The value is returned. The function + ' Raises an expection if params are invalid. This function + ' calls FV and PMT. It calculates value of annuity (FV) at the + ' begining of period for which IPMT is desired. Since interest + ' rate is constant FV*rate would give IPMT. + ' + ' if type = 1 and per = 1 then IPMT = 0. + ' + ' if (type = 0 ) IPMT = FV(per-1)*rate + ' else IPMT = FV(per-2)*rate + ' Returns : Double + ' + '------------------------------------------------------------- + ' + Public Function IPmt(ByVal Rate As Double, ByVal Per As Double, ByVal NPer As Double, ByVal PV As Double, Optional ByVal FV As Double = 0, Optional ByVal Due As DueDate = DueDate.EndOfPeriod) As Double + + Dim Pmt As Double + Dim dTFv As Double + Dim dTemp As Double + + If Due <> DueDate.EndOfPeriod Then + dTemp = 2.0# + Else + dTemp = 1.0# + End If + + ' Type = 0 or non-zero only. Offset to calculate FV + If (Per <= 0.0#) OrElse (Per >= NPer + 1) Then + Throw New ArgumentException(GetResourceString(ResID.Argument_InvalidValue1, "Per")) + End If + + If (Due <> DueDate.EndOfPeriod) AndAlso (Per = 1.0#) Then + Return 0.0# + End If + + ' Calculate PMT (i.e. annuity) for given parms. Rqrd for FV + Pmt = PMT_Internal(Rate, NPer, PV, FV, Due) + + ' Calculate FV just before the period on which interest would be applied + If Due <> DueDate.EndOfPeriod Then + PV = PV + Pmt + End If + + dTFv = FV_Internal(Rate, (Per - dTemp), Pmt, PV, DueDate.EndOfPeriod) + + Return (dTFv * Rate) + + End Function + + + + '------------------------------------------------------------- + ' + ' Name : IRR + ' Purpose : This function uses an iterative procedure to find + ' the Internal Rate of Return of an investment. The algorithm + ' basically uses the secant method to find a rate for which the + ' NPV of the cash flow is 0. + ' This function raises an exception if the parameters are invalid. + ' + ' This routine uses a slightly different version of the secant + ' routine in Rate. The basic changes are: + ' - uses LDoNPV to get the 'Y-value' + ' - does not allow Rate to go below -1. + ' (if the Rate drops below -1, it is forced above again) + ' - has a double condition for termination: + ' NPV = 0 (within L_IT_EPSILON) + ' Rate1 - Rate0 approaches zero (rate is converging) + ' This last does not parallel Excel, but avoids a class of + ' spurious answers. Otherwise, performance is comparable to + ' Excel's, and accuracy is often better. + ' + ' Returns : Double + ' + '------------------------------------------------------------- + ' + Public Function IRR(ByRef ValueArray() As Double, Optional ByVal Guess As Double = 0.1) As Double + + Dim dTemp As Double + Dim dRate0 As Double + Dim dRate1 As Double + Dim dNPv0 As Double + Dim dNpv1 As Double + Dim dNpvEpsilon As Double + Dim dTemp1 As Double + Dim lIndex As Integer + Dim lCVal As Integer + Dim lUpper As Integer + + 'Compiler assures that rank of ValueArray is always 1, no need to check it. + 'WARSI Check for error codes returned by UBound. Check if they match with C code + Try 'Needed to catch dynamic arrays which have not been constructed yet. + lUpper = ValueArray.GetUpperBound(0) + Catch ex As StackOverflowException + Throw ex + Catch ex As OutOfMemoryException + Throw ex + Catch ex As System.Threading.ThreadAbortException + Throw ex + Catch + Throw New ArgumentException(GetResourceString(ResID.Argument_InvalidValue1, "ValueArray")) + End Try + + lCVal = lUpper + 1 + + 'Function fails for invalid parameters + If Guess <= (-1.0#) Then + Throw New ArgumentException(GetResourceString(ResID.Argument_InvalidValue1, "Guess")) + End If + + If lCVal <= 1 Then + Throw New ArgumentException(GetResourceString(ResID.Argument_InvalidValue1, "ValueArray")) + End If + + 'We scale the epsilon depending on cash flow values. It is necessary + 'because even in max accuracy case where diff is in 16th digit it + 'would get scaled up. + If ValueArray(0) > 0.0# Then + dTemp = ValueArray(0) + Else + dTemp = -ValueArray(0) + End If + + For lIndex = 0 To lUpper + 'Get max of values in cash flow + If ValueArray(lIndex) > dTemp Then + dTemp = ValueArray(lIndex) + ElseIf (-ValueArray(lIndex)) > dTemp Then + dTemp = -ValueArray(lIndex) + End If + Next lIndex + + dNpvEpsilon = dTemp * cnL_IT_EPSILON * 0.01 + + 'Set up the initial values for the secant method + dRate0 = Guess + dNPv0 = OptPV2(ValueArray,dRate0) + + If dNPv0 > 0.0# Then + dRate1 = dRate0 + cnL_IT_STEP + Else + dRate1 = dRate0 - cnL_IT_STEP + End If + + If dRate1 <= (-1.0#) Then + Throw New ArgumentException(GetResourceString(ResID.Argument_InvalidValue1, "Rate")) + End If + + dNpv1 = OptPV2(ValueArray, dRate1) + + For lIndex = 0 To 39 + If dNpv1 = dNPv0 Then + If dRate1 > dRate0 Then + dRate0 = dRate0 - cnL_IT_STEP + Else + dRate0 = dRate0 + cnL_IT_STEP + End If + dNPv0 = OptPV2(ValueArray, dRate0) + If dNpv1 = dNPv0 Then + Throw New ArgumentException(GetResourceString(ResID.Argument_InvalidValue)) + End If + End If + + dRate0 = dRate1 - (dRate1 - dRate0) * dNpv1 / (dNpv1 - dNPv0) + + 'Secant method of generating next approximation + If dRate0 <= (-1.0#) Then + dRate0 = (dRate1 - 1.0#) * 0.5 + End If + + 'Basically give the algorithm a second chance. Helps the + 'algorithm when it starts to diverge to -ve side + dNPv0 = OptPV2(ValueArray, dRate0) + If dRate0 > dRate1 Then + dTemp = dRate0 - dRate1 + Else + dTemp = dRate1 - dRate0 + End If + + If dNPv0 > 0.0# Then + dTemp1 = dNPv0 + Else + dTemp1 = -dNPv0 + End If + + 'Test : npv - > 0 and rate converges + If dTemp1 < dNpvEpsilon AndAlso dTemp < cnL_IT_EPSILON Then + Return dRate0 + End If + + 'Exchange the values - store the new values in the 1's + dTemp = dNPv0 + dNPv0 = dNpv1 + dNpv1 = dTemp + dTemp = dRate0 + dRate0 = dRate1 + dRate1 = dTemp + Next lIndex + + Throw New ArgumentException(GetResourceString(ResID.Argument_InvalidValue)) + End Function + + + + '------------------------------------------------------------- + ' + ' Name : MIRR + ' Returns : Double + ' + '------------------------------------------------------------- + ' + Public Function MIRR(ByRef ValueArray() As Double, ByVal FinanceRate As Double, ByVal ReinvestRate As Double) As Double + + Dim dNpvPos As Double + Dim dNpvNeg As Double + Dim dTemp As Double + Dim dTemp1 As Double + Dim dNTemp2 As Double + Dim lCVal As Integer + Dim lLower As Integer + Dim lUpper As Integer + + If ValueArray.Rank <> 1 Then + Throw New ArgumentException(GetResourceString(ResID.Argument_RankEQOne1, "ValueArray")) + End If + + lLower = 0 + lUpper = ValueArray.GetUpperBound(0) + lCVal = lUpper - lLower + 1 + + If FinanceRate = -1.0# Then + Throw New ArgumentException(GetResourceString(ResID.Argument_InvalidValue1, "FinanceRate")) + End If + + If ReinvestRate = -1.0# Then + Throw New ArgumentException(GetResourceString(ResID.Argument_InvalidValue1, "ReinvestRate")) + End If + + If lCVal <= 1 Then + Throw New ArgumentException(GetResourceString(ResID.Argument_InvalidValue1, "ValueArray")) + End If + + dNpvNeg = LDoNPV(FinanceRate, ValueArray, -1) + If dNpvNeg = 0.0# Then + Throw New DivideByZeroException(GetResourceString(ResID.Financial_CalcDivByZero)) + End If + + dNpvPos = LDoNPV(ReinvestRate, ValueArray, 1) ' npv of +ve values + dTemp1 = ReinvestRate + 1.0# + dNTemp2 = lCVal + + dTemp = -dNpvPos * dTemp1 ^ dNTemp2 / (dNpvNeg * (FinanceRate + 1.0#)) + + If dTemp < 0.0# Then + Throw New ArgumentException(GetResourceString(ResID.Argument_InvalidValue)) + End If + + dTemp1 = 1 / (lCVal - 1.0#) + + MIRR = dTemp ^ dTemp1 - 1.0# + + End Function + + + + '------------------------------------------------------------- + ' + ' Name : NPer + ' Purpose : + ' + ' -fv + PMT*(1+rate*type) / rate + ' (1+rate)^nper = ------------------------------ + ' pv + PMT*(1+rate*type) / rate + ' + ' this yields the log expression used in this function. + ' + ' nper = (-fv - pv) / PMT : if rate == 0 + ' + ' Returns : Double + ' + '------------------------------------------------------------- + ' + Public Function NPer(ByVal Rate As Double, ByVal Pmt As Double, ByVal PV As Double, Optional ByVal FV As Double = 0, Optional ByVal Due As DueDate = DueDate.EndOfPeriod) As Double + + Dim dTemp3 As Double + Dim dTempFv As Double + Dim dTempPv As Double + Dim dTemp4 As Double + + ' Checking Error Conditions + If Rate <= -1.0# Then + Throw New ArgumentException(GetResourceString(ResID.Argument_InvalidValue1, "Rate")) + End If + + If Rate = 0.0# Then + If Pmt = 0.0# Then + Throw New ArgumentException(GetResourceString(ResID.Argument_InvalidValue1, "Pmt")) + End If + Return (-(PV + FV) / Pmt) + Else + If Due <> 0 Then + dTemp3 = Pmt * (1.0# + Rate) / Rate + Else + dTemp3 = Pmt / Rate + End If + dTempFv = -FV + dTemp3 + dTempPv = PV + dTemp3 + + ' Make sure the values fit the domain of log() + If dTempFv < 0.0# AndAlso dTempPv < 0.0# Then + dTempFv = -1 * dTempFv + dTempPv = -1 * dTempPv + ElseIf dTempFv <= 0.0# OrElse dTempPv <= 0.0# Then + Throw New ArgumentException(GetResourceString(ResID.Financial_CannotCalculateNPer)) + End If + + dTemp4 = Rate + 1.0# + Return (Log(dTempFv) - Log(dTempPv)) / Log(dTemp4) + End If + + End Function + + + + '------------------------------------------------------------- + ' + ' Name : NPV + ' Purpose : + ' This function calculates the Net Present Value of a series of + ' payments at a given rate. It uses LDoNPV to get the value. No + ' real work is done here, just some error checking. + ' As with the others, this function puts its status in *lpwStatus, + ' and returns the result as a double. + ' + ' Value1 Value2 Value3 + ' npv = -------- + ---------- + ---------- + ... for the series... + ' (1+rate) (1+rate)^2 (1+rate)^3 + ' + ' + ' Returns : Double + ' + '------------------------------------------------------------- + ' + Public Function NPV(ByVal Rate As Double, ByRef ValueArray() As Double) As Double + + Dim lCVal As Integer + Dim lLower As Integer + Dim lUpper As Integer + + If (ValueArray Is Nothing) Then + Throw New ArgumentException(GetResourceString(ResID.Argument_InvalidNullValue1, "ValueArray")) + End If + + If ValueArray.Rank <> 1 Then + Throw New ArgumentException(GetResourceString(ResID.Argument_RankEQOne1, "ValueArray")) + End If + + lLower = 0 + lUpper = ValueArray.GetUpperBound(0) + lCVal = lUpper - lLower + 1 + + If Rate = (-1.0#) Then + Throw New ArgumentException(GetResourceString(ResID.Argument_InvalidValue1, "Rate")) + End If + If lCVal < 1 Then + Throw New ArgumentException(GetResourceString(ResID.Argument_InvalidValue1, "ValueArray")) + End If + + NPV = LDoNPV(Rate, ValueArray, 0) + + End Function + + + + '------------------------------------------------------------- + ' + ' Name : PMT + ' Purpose : + ' This function, together with the four following + ' it (Pv, Fv, NPer and Rate), can calculate + ' a certain value associated with a regular series of + ' equal-sized payments. This series can be fully described + ' by these values: + ' Pv - present value + ' Fv - future value (at end of series) + ' PMT - the regular payment + ' nPer - the number of 'periods' over which the + ' money is paid + ' Rate - the interest rate per period. + ' (type - payments at beginning (1) or end (0) of + ' the period). + ' Each function can determine one of the values, given the others. + ' + ' General Function for the above values: + ' + ' (1+rate)^nper - 1 + ' pv * (1+rate)^nper + PMT*(1+rate*type)*----------------- + fv = 0 + ' rate + ' rate == 0 -> pv + PMT*nper + fv = 0 + ' + ' Thus: + ' (-fv - pv*(1+rate)^nper) * rate + ' PMT = ------------------------------------- + ' (1+rate*type) * ( (1+rate)^nper - 1 ) + ' + ' PMT = (-fv - pv) / nper : if rate == 0 + ' + ' + ' Returns : Double + ' + '------------------------------------------------------------- + ' + Public Function Pmt(ByVal Rate As Double, ByVal NPer As Double, ByVal PV As Double, Optional ByVal FV As Double = 0, Optional ByVal Due As DueDate = DueDate.EndOfPeriod) As Double + Return PMT_Internal(Rate, NPer, PV, FV, Due) + End Function + + Private Function PMT_Internal(ByVal Rate As Double, ByVal NPer As Double, ByVal PV As Double, Optional ByVal FV As Double = 0, Optional ByVal Due As DueDate = DueDate.EndOfPeriod) As Double + Dim dTemp As Double + Dim dTemp2 As Double + Dim dTemp3 As Double + + ' Checking for error conditions + If NPer = 0.0# Then + Throw New ArgumentException(GetResourceString(ResID.Argument_InvalidValue1, "NPer")) + End If + + If Rate = 0.0# Then + Return ((-FV - PV) / NPer) + Else + If Due <> 0 Then + dTemp = 1.0# + Rate + Else + dTemp = 1.0# + End If + dTemp3 = Rate + 1.0# + ' WARSI Using the exponent operator for pow(..) in C code of PMT. Still got + ' to make sure that they (pow and ^) are same for all conditions + dTemp2 = dTemp3 ^ NPer + Return ((-FV - PV * dTemp2) / (dTemp * (dTemp2 - 1.0#)) * Rate) + End If + + End Function + + + + '------------------------------------------------------------- + ' + ' Name : PPmt + ' Purpose : This function calculates the principal part of a + ' payment for a given period. + ' + ' Since PMT = IPMT +PPMT therefore + ' PPMT = PMT - IPMT + ' + ' Returns : Double + ' + '------------------------------------------------------------- + ' + Public Function PPmt(ByVal Rate As Double, ByVal Per As Double, ByVal NPer As Double, ByVal PV As Double, Optional ByVal FV As Double = 0, Optional ByVal Due As DueDate = DueDate.EndOfPeriod) As Double + + Dim Pmt As Double + Dim dIPMT As Double + + ' Checking for error conditions + If (Per <= 0.0#) OrElse (Per >= (NPer + 1)) Then + Throw New ArgumentException(GetResourceString(ResID.PPMT_PerGT0AndLTNPer, "Per")) + End If + + Pmt = PMT_Internal(Rate, NPer, PV, FV, Due) + dIPMT = IPmt(Rate, Per, NPer, PV, FV, Due) + + Return (Pmt - dIPMT) + + End Function + + + + '------------------------------------------------------------- + ' + ' Name : PV + ' Purpose : + ' -fv - PMT * (1+rate*type) * ( (1+rate)^nper-1) / rate + ' pv = ----------------------------------------------------- + ' (1 + rate) ^ nper + ' + ' pv = -fv - PMT * nper : if rate == 0 + ' Returns : Double + ' + '------------------------------------------------------------- + ' + Public Function PV(ByVal Rate As Double, ByVal NPer As Double, ByVal Pmt As Double, Optional ByVal FV As Double = 0, Optional ByVal Due As DueDate = DueDate.EndOfPeriod) As Double + + Dim dTemp As Double + Dim dTemp2 As Double + Dim dTemp3 As Double + + If Rate = 0.0# Then + Return (-FV - Pmt * NPer) + Else + If Due <> 0 Then + dTemp = 1.0# + Rate + Else + dTemp = 1.0# + End If + dTemp3 = 1.0# + Rate + + ' WARSI Using the exponent operator for pow(..) in C code of PV. Still got + ' to make sure that they (pow and ^) are same for all conditions + dTemp2 = dTemp3 ^ NPer + + 'Do divides before multiplies to avoid OverFlowExceptions + Return (-(FV + Pmt * dTemp * ((dTemp2 - 1.0#) / Rate)) / dTemp2) + End If + + End Function + + + + '------------------------------------------------------------- + ' + ' Name : Rate + ' Purpose : + ' See PMT, above, for general details. This + ' function is not as simple as the others. Due to the + ' nature of the equation that links the 5 values (see + ' Excel manual - PV), it is not practical to solve for + ' rate algebraically. As a result, this function implements + ' the secant method of approximation. LEvalRate provides + ' the 'Y-values', for given rates. + ' + ' Basic secant method: + ' determine Rate0 and Rate1. Use LEvalRate to get Y0, Y1. + ' + ' Y0 + ' Rate2 = Rate1 - (Rate1 - Rate0) * --------- + ' (Y1 - Y0) + ' + ' Get Y2 from Rate2, LEvalRate. move 1->0, 2->1 and repeat. + ' + ' stop when abs( Yn ) < L_IT_EPSILON + ' + ' + ' Returns : Double + ' + '------------------------------------------------------------- + ' + Public Function Rate(ByVal NPer As Double, ByVal Pmt As Double, ByVal PV As Double, Optional ByVal FV As Double = 0, Optional ByVal Due As DueDate = DueDate.EndOfPeriod, Optional ByVal Guess As Double = 0.1) As Double + + Dim dTemp As Double + Dim dRate0 As Double + Dim dRate1 As Double + Dim dY0 As Double + Dim dY1 As Double + Dim I As Integer + + ' Check for error condition + If NPer <= 0.0# Then + Throw New ArgumentException(GetResourceString(ResID.Rate_NPerMustBeGTZero)) + End If + + dRate0 = Guess + dY0 = LEvalRate(dRate0, NPer, Pmt, PV, FV, Due) + If dY0 > 0 Then + dRate1 = (dRate0 / 2) + Else + dRate1 = (dRate0 * 2) + End If + + dY1 = LEvalRate(dRate1, NPer, Pmt, PV, FV, Due) + + For I = 0 To 39 + If dY1 = dY0 Then + If dRate1 > dRate0 Then + dRate0 = dRate0 - cnL_IT_STEP + Else + dRate0 = dRate0 - cnL_IT_STEP * (-1) + End If + dY0 = LEvalRate(dRate0, NPer, Pmt, PV, FV, Due) + If dY1 = dY0 Then + Throw New ArgumentException(GetResourceString(ResID.Financial_CalcDivByZero)) + End If + End If + + dRate0 = dRate1 - (dRate1 - dRate0) * dY1 / (dY1 - dY0) + + ' Secant method of generating next approximation + dY0 = LEvalRate(dRate0, NPer, Pmt, PV, FV, Due) + If Abs(dY0) < cnL_IT_EPSILON Then + Return dRate0 + End If + + dTemp = dY0 + dY0 = dY1 + dY1 = dTemp + dTemp = dRate0 + dRate0 = dRate1 + dRate1 = dTemp + Next I + + Throw New ArgumentException(GetResourceString(ResID.Financial_CannotCalculateRate)) + + End Function + + + + '------------------------------------------------------------- + ' + ' Name : SLN + ' Purpose : It calculates the depreciation by the straight + ' line method and returns the result. It raises + ' an error if parameters are invalid. + ' + ' sln = (value - salvage) / nper + ' + ' Returns : Double + ' + '------------------------------------------------------------- + ' + Public Function SLN(ByVal Cost As Double, ByVal Salvage As Double, ByVal Life As Double) As Double + + If Life = 0.0# Then + Throw New ArgumentException(GetResourceString(ResID.Financial_LifeNEZero)) + End If + + Return (Cost - Salvage) / (Life) + + End Function + + + + '------------------------------------------------------------- + ' + ' Name : SYD + ' Purpose : Calculates depreciation for a period by the + ' sum-of-years-digits method. The result is returned. + ' It raises an error if parameters are invalid. + ' + ' 2 + ' syd = (value - salvage) (nper - per + 1) * ------------ + ' (nper)(nper + 1) + ' + ' Derivation : The value of the asset is divided into even parts. + ' The first period gets N, the second gets N-1, the last + ' gets 1. + ' Returns : Double + ' + '------------------------------------------------------------- + ' + Public Function SYD(ByVal Cost As Double, ByVal Salvage As Double, ByVal Life As Double, ByVal Period As Double) As Double + + Dim Result As Double + + If Salvage < 0.0# Then + Throw New ArgumentException(GetResourceString(ResID.Financial_ArgGEZero1, "Salvage")) + End If + If Period > Life Then + Throw New ArgumentException(GetResourceString(ResID.Financial_PeriodLELife)) + End If + If Period <= 0.0# Then + Throw New ArgumentException(GetResourceString(ResID.Financial_ArgGTZero1, "Period")) + End If + + 'Avoid OverflowExceptions by dividing before multiplying + Result = (Cost - Salvage) / (Life * (Life + 1)) + Return (Result * (Life + 1 - Period) * 2) + + End Function + + + '------------------------------------------------------------- + ' + ' Name : LEvalRate + ' Purpose : A local helper function. Does a useful calculation + ' for Rate. The function is derived from the General + ' formulation noted above (PMT). + ' Returns : Double + ' + '------------------------------------------------------------- + ' + Private Function LEvalRate(ByVal Rate As Double, ByVal NPer As Double, ByVal Pmt As Double, ByVal PV As Double, ByVal dFv As Double, ByVal Due As DueDate) As Double + + Dim dTemp1 As Double + Dim dTemp2 As Double + Dim dTemp3 As Double + + If Rate = 0.0# Then + Return (PV + Pmt * NPer + dFv) + Else + dTemp3 = Rate + 1.0# + ' WARSI Using the exponent operator for pow(..) in C code of LEvalRate. Still got + ' to make sure that they (pow and ^) are same for all conditions + dTemp1 = dTemp3 ^ NPer + + If Due <> 0 Then + dTemp2 = 1 + Rate + Else + dTemp2 = 1.0# + End If + Return (PV * dTemp1 + Pmt * dTemp2 * (dTemp1 - 1) / Rate + dFv) + End If + + End Function + + + '------------------------------------------------------------- + ' + ' Name : LDoNPV + ' Purpose : + ' This function performs NPV calculations for NPV, + ' MIRR, and IRR. The wNType variable is used to set + ' the type of calculation: 0 -> do all values, + ' 1 -> only values > 0, + ' -1 -> only values < 0. + ' Note the array pointer, lpdblVal, is preceded by the count of + ' the entries. + ' Since this is just an internal-use function, no fancy exports + ' are done. It assumes that error checking is done by the caller. + ' + ' Value1 Value2 Value3 + ' npv = -------- + ---------- + ---------- + ... for the series... + ' (1+rate) (1+rate)^2 (1+rate)^3 + ' + ' Returns : Double + ' + '------------------------------------------------------------- + ' + + Private Function LDoNPV(ByVal Rate As Double, ByRef ValueArray() As Double, ByVal iWNType As Integer) As Double + + Dim bSkipPos As Boolean + Dim bSkipNeg As Boolean + + Dim dTemp2 As Double + Dim dTotal As Double + Dim dTVal As Double + Dim I As Integer + Dim lLower As Integer + Dim lUpper As Integer + + bSkipPos = iWNType < 0 + bSkipNeg = iWNType > 0 + + dTemp2 = 1.0# + dTotal = 0.0# + + lLower = 0 + lUpper = ValueArray.GetUpperBound(0) + + For I = lLower To lUpper + dTVal = ValueArray(I) + dTemp2 = dTemp2 + dTemp2 * Rate + + If Not ((bSkipPos AndAlso dTVal > 0.0#) OrElse (bSkipNeg AndAlso dTVal < 0.0#)) Then + dTotal = dTotal + dTVal / dTemp2 + End If + Next I + + LDoNPV = dTotal + + End Function + + '------------------------------------------------------------------------------------------------------ + ' Optimized version of PV2 + '------------------------------------------------------------------------------------------------------ + + Private Function OptPV2(ByRef ValueArray() As Double, Optional ByVal Guess As Double = 0.1) As Double + + Dim lUpper, lLower, lIndex As Integer + + lLower = 0 + lUpper = ValueArray.GetUpperBound(0) + + Dim dTotal As Double = 0.0 + Dim divRate As Double = 1.0 + Guess + + While lLower <= lUpper AndAlso ValueArray(lLower) = 0.0 + lLower = lLower + 1 + End While + + For lIndex = lUpper To lLower Step -1 + dTotal = dTotal / divRate + dTotal = dTotal + ValueArray(lIndex) + Next lIndex + Return dTotal + + End Function + + + End Module + +End Namespace diff --git a/Microsoft.VisualBasic/runtime/msvbalib/Globals.vb b/Microsoft.VisualBasic/runtime/msvbalib/Globals.vb new file mode 100644 index 000000000..ea9b09e22 --- /dev/null +++ b/Microsoft.VisualBasic/runtime/msvbalib/Globals.vb @@ -0,0 +1,551 @@ +' Copyright (c) Microsoft Corporation. All rights reserved. + +Imports System +Imports Microsoft.VisualBasic.CompilerServices +Imports Microsoft.VisualBasic.CompilerServices.Utils + +Namespace Microsoft.VisualBasic +#If Not LATEBINDING Then + Public Enum VariantType + [Empty] = 0 + [Null] = 1 + [Short] = 2 + [Integer] = 3 + [Single] = 4 + [Double] = 5 + [Currency] = 6 + [Date] = 7 + [String] = 8 + [Object] = 9 + [Error] = 10 + [Boolean] = 11 + [Variant] = 12 + [DataObject] = 13 + [Decimal] = 14 + [Byte] = 17 + [Char] = 18 + [Long] = 20 + [UserDefinedType] = 36 + [Array] = 8192 + End Enum +#End If + +#If Not TELESTO Then + Public Enum AppWinStyle As Short + Hide = 0 + NormalFocus = 1 + MinimizedFocus = 2 + MaximizedFocus = 3 + NormalNoFocus = 4 + MinimizedNoFocus = 6 + End Enum +#End If + + + Public Enum CallType + Method = 1 + [Get] = 2 + [Let] = 4 + [Set] = 8 + End Enum + + +#If Not LATEBINDING Then + Public Enum CompareMethod + [Binary] = 0 + [Text] = 1 + End Enum + + + + Public Enum DateFormat + GeneralDate = 0 + LongDate = 1 + ShortDate = 2 + LongTime = 3 + ShortTime = 4 + End Enum + + + + Public Enum FirstDayOfWeek + System = 0 + Sunday = 1 + Monday = 2 + Tuesday = 3 + Wednesday = 4 + Thursday = 5 + Friday = 6 + Saturday = 7 + End Enum + + +#If Not TELESTO Then + Public Enum FileAttribute + [Normal] = 0 + [ReadOnly] = 1 + [Hidden] = 2 + [System] = 4 + [Volume] = 8 + [Directory] = 16 + [Archive] = 32 + End Enum +#End If + + + Public Enum FirstWeekOfYear + System = 0 + Jan1 = 1 + FirstFourDays = 2 + FirstFullWeek = 3 + End Enum + + +#If Not TELESTO Then + Public Enum VbStrConv + [None] = 0 + [Uppercase] = 1 + [Lowercase] = 2 + [ProperCase] = 3 + [Wide] = 4 + [Narrow] = 8 + [Katakana] = 16 + [Hiragana] = 32 + '[Unicode] = 64 'OBSOLETE + '[FromUnicode] = 128 'OBSOLETE + [SimplifiedChinese] = 256 + [TraditionalChinese] = 512 + [LinguisticCasing] = 1024 + End Enum +#End If + + + Public Enum TriState + [False] = 0 + [True] = -1 + [UseDefault] = -2 + End Enum + + + + Public Enum DateInterval + [Year] = 0 + [Quarter] = 1 + [Month] = 2 + [DayOfYear] = 3 + [Day] = 4 + [WeekOfYear] = 5 + [Weekday] = 6 + [Hour] = 7 + [Minute] = 8 + [Second] = 9 + End Enum + + +#If Not TELESTO Then + + Public Enum DueDate + EndOfPeriod = 0 + BegOfPeriod = 1 + End Enum + + + Public Enum OpenMode + [Input] = 1 + [Output] = 2 + [Random] = 4 + [Append] = 8 + [Binary] = 32 + End Enum + + + + Friend Enum OpenModeTypes + [Input] = 1 + [Output] = 2 + [Random] = 4 + [Append] = 8 + [Binary] = 32 + [Any] = -1 + End Enum + + + + Public Enum OpenAccess + [Default] = -1 + [Read] = System.IO.FileAccess.Read + [ReadWrite] = System.IO.FileAccess.ReadWrite + [Write] = System.IO.FileAccess.Write + End Enum + + + + Public Enum OpenShare + [Default] = -1 + [Shared] = System.IO.FileShare.ReadWrite + [LockRead] = System.IO.FileShare.Write + [LockReadWrite] = System.IO.FileShare.None + [LockWrite] = System.IO.FileShare.Read + End Enum + + + + _ + Public Structure TabInfo + Public Column As Short + End Structure + + + + _ + Public Structure SpcInfo + Public Count As Short + End Structure + + Public Enum MsgBoxResult + Ok = 1 + Cancel = 2 + Abort = 3 + Retry = 4 + Ignore = 5 + Yes = 6 + No = 7 + End Enum + + _ + Public Enum MsgBoxStyle + 'You may BitOr one value from each group + 'Button group: Lower 4 bits, &H00F + OkOnly = &H0I + OkCancel = &H1I + AbortRetryIgnore = &H2I + YesNoCancel = &H3I + YesNo = &H4I + RetryCancel = &H5I + + 'Icon Group: Middle 4 bits &H0F0 + Critical = &H10I 'Same as Windows.Forms.MessageBox.IconError + Question = &H20I 'Same As Windows.MessageBox.IconQuestion + Exclamation = &H30I 'Same As Windows.MessageBox.IconExclamation + Information = &H40I 'Same As Windows.MessageBox.IconInformation + + 'Default Group: High 4 bits &HF00 + DefaultButton1 = 0 + DefaultButton2 = &H100I + DefaultButton3 = &H200I + 'UNSUPPORTED IN VB7 + 'DefaultButton4 = &H300I + + ApplicationModal = &H0I + SystemModal = &H1000I + + MsgBoxHelp = &H4000I + MsgBoxRight = &H80000I + MsgBoxRtlReading = &H100000I + MsgBoxSetForeground = &H10000I + End Enum + + + + ' ------------------------------------------------------------------- + ' VBFixedString is used by the runtime to determine + ' if the field should be written/read without the string length descriptor. + ' ------------------------------------------------------------------- + _ + Public NotInheritable Class VBFixedStringAttribute + Inherits System.Attribute + + Private m_Length As Integer + + Public ReadOnly Property Length() As Integer + Get + Return m_Length + End Get + End Property + + + Public Sub New(ByVal Length As Integer) + If (Length < 1 OrElse Length > System.Int16.MaxValue) Then + Throw New ArgumentException(GetResourceString(ResID.Invalid_VBFixedString)) + End If + m_Length = Length + End Sub + End Class + + + + ' ------------------------------------------------------------------- + ' VBFixedArray is used by the runtime to determine + ' if the array should be written/read without the array descriptor. + ' ------------------------------------------------------------------- + _ + Public NotInheritable Class VBFixedArrayAttribute + Inherits System.Attribute + + Friend FirstBound As Integer + Friend SecondBound As Integer + + Public ReadOnly Property Bounds() As Integer() + Get + If Me.SecondBound = -1 Then + Return New Integer() {Me.FirstBound} + Else + Return New Integer() {Me.FirstBound, Me.SecondBound} + End If + End Get + End Property + + Public ReadOnly Property Length() As Integer + Get + If Me.SecondBound = -1 Then + Return (Me.FirstBound + 1) + Else + Return (Me.FirstBound + 1) * (Me.SecondBound + 1) + End If + End Get + End Property + + Public Sub New(ByVal UpperBound1 As Integer) + + 'Validate all the bounds + If UpperBound1 < 0 Then + Throw New ArgumentException(GetResourceString(ResID.Invalid_VBFixedArray)) + End If + + Me.FirstBound = UpperBound1 + Me.SecondBound = -1 + + End Sub + + Public Sub New(ByVal UpperBound1 As Integer, ByVal UpperBound2 As Integer) + + 'Validate all the bounds + If UpperBound1 < 0 OrElse UpperBound2 < 0 Then + Throw New ArgumentException(GetResourceString(ResID.Invalid_VBFixedArray)) + End If + + Me.FirstBound = UpperBound1 + Me.SecondBound = UpperBound2 + + End Sub + + End Class + + + + ' ------------------------------------------------------------------- + ' ComClass is used by the VB compiler to mark a public class + ' that will be exposed via COM interop. + ' ------------------------------------------------------------------- + _ + Public NotInheritable Class ComClassAttribute + Inherits System.Attribute + + Private m_ClassID As String + Private m_InterfaceID As String + Private m_EventID As String + Private m_InterfaceShadows As Boolean = False + + + + Public Sub New() + End Sub + + + + Public Sub New(ByVal _ClassID As String) + m_ClassID = _ClassID + End Sub + + + + Public Sub New(ByVal _ClassID As String, ByVal _InterfaceID As String) + m_ClassID = _ClassID + m_InterfaceID = _InterfaceID + End Sub + + + + Public Sub New(ByVal _ClassID As String, ByVal _InterfaceID As String, ByVal _EventId As String) + m_ClassID = _ClassID + m_InterfaceID = _InterfaceID + m_EventID = _EventId + End Sub + + + + Public ReadOnly Property ClassID() As String + Get + Return m_ClassID + End Get + End Property + + + + Public ReadOnly Property InterfaceID() As String + Get + Return m_InterfaceID + End Get + End Property + + + + Public ReadOnly Property EventID() As String + Get + Return m_EventID + End Get + End Property + + + + Public Property InterfaceShadows() As Boolean + Get + Return m_InterfaceShadows + End Get + Set(ByVal Value As Boolean) + m_InterfaceShadows = Value + End Set + End Property + End Class + +#End If 'NOT TELESTO + + '''************************************************************************** + ''' ;MyGroupCollectionAttribute + ''' + ''' This attribute is put on an empty 'container class' that the compiler then fills with + ''' properties that return instances of all the types found in the project which derive + ''' from the TypeToCollect argument. + ''' + ''' This is how My.Forms is built, for instance. + ''' + ''' + ''' WARNING: Do not rename this attribute or move it out of this module. Otherwise there + ''' are compiler changes that will need to be made + ''' +#If TELESTO Then + 'FIXME _ + _ + Public NotInheritable Class MyGroupCollectionAttribute : Inherits Attribute +#Else + _ + _ + Public NotInheritable Class MyGroupCollectionAttribute : Inherits Attribute +#End If + + '''************************************************************************** + ''' ;New + ''' + ''' + ''' Compiler will generate accessors for classes that derived from this type + ''' Name of the factory method to create the instances + ''' Name of the method that will dispose of the instances + ''' "Name of the My.* method to call to get the default instance for the types in the container + Public Sub New(ByVal typeToCollect As String, ByVal createInstanceMethodName As String, _ + ByVal disposeInstanceMethodName As String, ByVal defaultInstanceAlias As String) + + m_NameOfBaseTypeToCollect = typeToCollect + m_NameOfCreateMethod = createInstanceMethodName + m_NameOfDisposeMethod = disposeInstanceMethodName + m_DefaultInstanceAlias = defaultInstanceAlias + + End Sub + + '''************************************************************************** + ''' ;MyGroupName + ''' + ''' The name of the base type we are trying to collect + ''' + Public ReadOnly Property MyGroupName() As String + Get + Return m_NameOfBaseTypeToCollect + End Get + End Property + + + '''************************************************************************** + ''' ;CreateMethod + ''' + ''' Name of the factory method to create the instances + ''' + Public ReadOnly Property CreateMethod() As String + Get + Return m_NameOfCreateMethod + End Get + End Property + + + '''************************************************************************** + ''' ;DisposeMethod + ''' + ''' Name of the method that will dispose of the instances + ''' + Public ReadOnly Property DisposeMethod() As String + Get + Return m_NameOfDisposeMethod + End Get + End Property + + '''************************************************************************** + ''' ;DefaultInstanceAlias + ''' + ''' Provides the name of the My.* methods to call to get the 'default instance' + ''' + Public ReadOnly Property DefaultInstanceAlias() As String + Get + Return m_DefaultInstanceAlias + End Get + End Property + + Private m_NameOfBaseTypeToCollect, m_NameOfCreateMethod, m_NameOfDisposeMethod, m_DefaultInstanceAlias As String + End Class 'MyGroupCollectionAttribute + + '''************************************************************************** + ''' ;HideModuleNameAttribute + ''' + ''' When applied to a module, Intellisense will hide the module from + ''' the statement completion list, but not the contents of the module. + ''' + ''' + ''' WARNING: Do not rename this attribute or move it out of this module. Otherwise there + ''' are compiler changes that will need to be made + ''' + _ + Public NotInheritable Class HideModuleNameAttribute + Inherits System.Attribute + + End Class + + Public Module Globals + Public ReadOnly Property ScriptEngine() As String + Get + Return "VB" + End Get + End Property + + + Public ReadOnly Property ScriptEngineMajorVersion() As Integer + Get + Return CompilerServices._Version.Major + End Get + End Property + + + + Public ReadOnly Property ScriptEngineMinorVersion() As Integer + Get + Return CompilerServices._Version.Minor + End Get + End Property + + + Public ReadOnly Property ScriptEngineBuildVersion() As Integer + Get + Return CompilerServices._Version.Build + End Get + End Property + End Module +#End If +End Namespace + + diff --git a/Microsoft.VisualBasic/runtime/msvbalib/Helpers/Attributes.vb b/Microsoft.VisualBasic/runtime/msvbalib/Helpers/Attributes.vb new file mode 100644 index 000000000..ce469a8bb --- /dev/null +++ b/Microsoft.VisualBasic/runtime/msvbalib/Helpers/Attributes.vb @@ -0,0 +1,108 @@ +' Copyright (c) Microsoft Corporation. All rights reserved. + +Imports System + +Namespace Microsoft.VisualBasic.CompilerServices + + + ' ------------------------------------------------------------------- + ' StandardModuleAttribute is used by the compiler to mark all Module + ' declarations. This is needed so we can promote the module's + ' contents into the default namespace. + ' + ' WARNING: Do not rename this attribute or move it out of this + ' module. Otherwise there are compiler changes that will + ' need to be made! + ' ------------------------------------------------------------------- +#If TELESTO And Not NETCORE Then + _ + Friend NotInheritable Class StandardModuleAttribute 'FIXME: System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)> +#Else + _ + Public NotInheritable Class StandardModuleAttribute +#End If + Inherits System.Attribute + + Public Sub New() + MyBase.New() + End Sub + End Class + + ' ------------------------------------------------------------------- + ' OptionTextAttribute is used by the compiler to mark all Classes/Modules + ' as to whether we Option Compare Text is defined or not + ' + ' WARNING: Do not rename this attribute or move it out of this + ' module. Otherwise there are compiler changes that will + ' need to be made! + ' ------------------------------------------------------------------- +#If TELESTO And Not NETCORE Then + _ + Friend NotInheritable Class OptionTextAttribute 'FIXME: +#Else + _ + Public NotInheritable Class OptionTextAttribute +#End If + Inherits System.Attribute + + Public Sub New() + MyBase.New() + End Sub + End Class + +#If Not LATEBINDING Then + ' ------------------------------------------------------------------- + ' OptionCompareAttribute is used by the compiler to determine + ' when the Option Compare setting should be passed as the default + ' value for the attributed argument. + ' + ' WARNING: Do not rename this attribute or move it out of this + ' module. Otherwise there are compiler changes that will + ' need to be made! + ' ------------------------------------------------------------------- +#If TELESTO Then + _ + Public NotInheritable Class OptionCompareAttribute 'FIXME: System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)> +#Else + _ + Public NotInheritable Class OptionCompareAttribute +#End If + + Inherits System.Attribute + + Public Sub New() + MyBase.New() + End Sub + End Class + + '''************************************************************************** + ''' ;DesignerGeneratedAttribute + ''' + ''' When applied to a class, the compiler will generate an implicit call to + ''' to a private InitializeComponent method from the default synthetic + ''' constructor. The compiler will also verify that this method is called + ''' from user defined constructors and report a warning or error it it is not. + ''' The IDE will honor this attribute when generating code on behalf of the + ''' user. + ''' + ''' + ''' WARNING: Do not rename this attribute or move it out of this module. Otherwise there + ''' are compiler changes that will need to be made + ''' +#If TELESTO Then + _ + Public NotInheritable Class DesignerGeneratedAttribute 'FIXME: +#Else + _ + _ + Public NotInheritable Class DesignerGeneratedAttribute +#End If + + Inherits System.Attribute + + End Class +#End If +End Namespace diff --git a/Microsoft.VisualBasic/runtime/msvbalib/Helpers/BooleanType.vb b/Microsoft.VisualBasic/runtime/msvbalib/Helpers/BooleanType.vb new file mode 100644 index 000000000..e63955483 --- /dev/null +++ b/Microsoft.VisualBasic/runtime/msvbalib/Helpers/BooleanType.vb @@ -0,0 +1,155 @@ +' Copyright (c) Microsoft Corporation. All rights reserved. + +Imports System +Imports System.Globalization + +Imports Microsoft.VisualBasic.CompilerServices.Utils + +Namespace Microsoft.VisualBasic.CompilerServices + +#Region " BACKWARDS COMPATIBILITY " + + 'WARNING WARNING WARNING WARNING WARNING + 'This code exists to support Everett compiled applications. Make sure you understand + 'the backwards compatibility ramifications of any edit you make in this region. + 'WARNING WARNING WARNING WARNING WARNING + + _ + Public NotInheritable Class BooleanType + ' Prevent creation. + Private Sub New() + End Sub + + Public Shared Function FromString(ByVal Value As String) As Boolean + + If Value Is Nothing Then + 'For VB6 compatibility, treat Nothing as empty string. + Value = "" + End If + + Try + Dim loc As CultureInfo = GetCultureInfo() + + 'Use untrimmed Value to test for 'True'/'False' + If System.String.Compare(Value, Boolean.FalseString, True, loc) = 0 Then + Return False + ElseIf System.String.Compare(Value, Boolean.TrueString, True, loc) = 0 Then + Return True + End If + + Dim i64Value As Int64 + + If IsHexOrOctValue(Value, i64Value) Then + Return CBool(i64Value) + End If + + Return CBool(DoubleType.Parse(Value)) + + Catch e As FormatException + Throw New InvalidCastException(GetResourceString(ResID.InvalidCast_FromStringTo, Left(Value, 32), "Boolean"), e) + End Try + + End Function + + Public Shared Function FromObject(ByVal Value As Object) As Boolean + + If Value Is Nothing Then + Return False + End If + + Dim ValueInterface As IConvertible + Dim ValueTypeCode As TypeCode + + ValueInterface = TryCast(Value, IConvertible) + + If Not ValueInterface Is Nothing Then + + ValueTypeCode = ValueInterface.GetTypeCode() + + Select Case ValueTypeCode + + Case TypeCode.Boolean + If TypeOf Value Is Boolean Then + Return CBool(DirectCast(Value, Boolean)) + Else + Return CBool(ValueInterface.ToBoolean(Nothing)) + End If + + Case TypeCode.Byte + 'Using ToByte also handles enums + If TypeOf Value Is Byte Then + Return CBool(DirectCast(Value, Byte)) + Else + Return CBool(ValueInterface.ToByte(Nothing)) + End If + + Case TypeCode.Int16 + If TypeOf Value Is Int16 Then + Return CBool(DirectCast(Value, Int16)) + Else + 'Using ToInt16 also handles enums + Return CBool(ValueInterface.ToInt16(Nothing)) + End If + + Case TypeCode.Int32 + If TypeOf Value Is Int32 Then + Return CBool(DirectCast(Value, Int32)) + Else + 'Using ToInt32 also handles enums + Return CBool(ValueInterface.ToInt32(Nothing)) + End If + + Case TypeCode.Int64 + If TypeOf Value Is Int64 Then + Return CBool(DirectCast(Value, Int64)) + Else + 'Using ToInt64 also handles enums + Return CBool(ValueInterface.ToInt64(Nothing)) + End If + + Case TypeCode.Single + If TypeOf Value Is Single Then + Return CBool(DirectCast(Value, Single)) + Else + Return CBool(ValueInterface.ToSingle(Nothing)) + End If + + Case TypeCode.Double + If TypeOf Value Is Double Then + Return CBool(DirectCast(Value, Double)) + Else + Return CBool(ValueInterface.ToDouble(Nothing)) + End If + + Case TypeCode.Decimal + Return DecimalToBoolean(ValueInterface) + + Case TypeCode.String + Dim ValueString As String = TryCast(Value, String) + + If ValueString IsNot Nothing Then + Return CBool(BooleanType.FromString(ValueString)) + Else + Return CBool(BooleanType.FromString(ValueInterface.ToString(Nothing))) + End If + Case TypeCode.Char, _ + TypeCode.DateTime + ' Fall through to error + + Case Else + ' Fall through to error + End Select + End If + + Throw New InvalidCastException(GetResourceString(ResID.InvalidCast_FromTo, VBFriendlyName(Value), "Boolean")) + End Function + + Private Shared Function DecimalToBoolean(ByVal ValueInterface As IConvertible) As Boolean + Return CBool(ValueInterface.ToDecimal(Nothing)) + End Function + + End Class + +#End Region + +End Namespace diff --git a/Microsoft.VisualBasic/runtime/msvbalib/Helpers/ByteType.vb b/Microsoft.VisualBasic/runtime/msvbalib/Helpers/ByteType.vb new file mode 100644 index 000000000..fc20f84d5 --- /dev/null +++ b/Microsoft.VisualBasic/runtime/msvbalib/Helpers/ByteType.vb @@ -0,0 +1,137 @@ +' Copyright (c) Microsoft Corporation. All rights reserved. + +Imports System +Imports System.Globalization + +Imports Microsoft.VisualBasic.CompilerServices.Utils + +Namespace Microsoft.VisualBasic.CompilerServices + +#Region " BACKWARDS COMPATIBILITY " + + 'WARNING WARNING WARNING WARNING WARNING + 'This code exists to support Everett compiled applications. Make sure you understand + 'the backwards compatibility ramifications of any edit you make in this region. + 'WARNING WARNING WARNING WARNING WARNING + + _ + Public NotInheritable Class ByteType + ' Prevent creation. + Private Sub New() + End Sub + + Public Shared Function FromString(ByVal Value As String) As Byte + + If Value Is Nothing Then + Return 0 + End If + + Try + Dim i64Value As Int64 + + If IsHexOrOctValue(Value, i64Value) Then + Return CByte(i64Value) + End If + + Return CByte(DoubleType.Parse(Value)) + + Catch e As FormatException + Throw New InvalidCastException(GetResourceString(ResID.InvalidCast_FromStringTo, Left(Value, 32), "Byte"), e) 'UNSIGNED: make these strings constants + End Try + + End Function + + Public Shared Function FromObject(ByVal Value As Object) As Byte + + If Value Is Nothing Then + Return 0 + End If + + Dim ValueInterface As IConvertible + Dim ValueTypeCode As TypeCode + + ValueInterface = TryCast(Value, IConvertible) + + If ValueInterface Is Nothing Then + GoTo ThrowInvalidCast + End If + + ValueTypeCode = ValueInterface.GetTypeCode() + + Select Case ValueTypeCode + + Case TypeCode.Boolean + Return CByte(ValueInterface.ToBoolean(Nothing)) + + Case TypeCode.Byte + If TypeOf Value Is System.Byte Then + Return CByte(DirectCast(Value, Byte)) + Else + Return CByte(ValueInterface.ToByte(Nothing)) + End If + + Case TypeCode.Int16 + If TypeOf Value Is System.Int16 Then + Return CByte(DirectCast(Value, Int16)) + Else + Return CByte(ValueInterface.ToInt16(Nothing)) + End If + + Case TypeCode.Int32 + If TypeOf Value Is System.Int32 Then + Return CByte(DirectCast(Value, Int32)) + Else + Return CByte(ValueInterface.ToInt32(Nothing)) + End If + + Case TypeCode.Int64 + If TypeOf Value Is System.Int64 Then + Return CByte(DirectCast(Value, Int64)) + Else + Return CByte(ValueInterface.ToInt64(Nothing)) + End If + + Case TypeCode.Single + If TypeOf Value Is System.Single Then + Return CByte(DirectCast(Value, Single)) + Else + Return CByte(ValueInterface.ToSingle(Nothing)) + End If + + Case TypeCode.Double + If TypeOf Value Is System.Double Then + Return CByte(DirectCast(Value, Double)) + Else + Return CByte(ValueInterface.ToDouble(Nothing)) + End If + + Case TypeCode.Decimal + 'Do not use .ToDecimal because of jit temp issue effects all perf + Return DecimalToByte(ValueInterface) + + Case TypeCode.String + Return ByteType.FromString(ValueInterface.ToString(Nothing)) + Case TypeCode.Char, _ + TypeCode.DateTime + ' Fall through to error + + Case Else + ' Fall through to error + End Select + +ThrowInvalidCast: + Throw New InvalidCastException(GetResourceString(ResID.InvalidCast_FromTo, VBFriendlyName(Value), "Byte")) + + End Function + + Private Shared Function DecimalToByte(ByVal ValueInterface As IConvertible) As Byte + Return CByte(ValueInterface.ToDecimal(Nothing)) + End Function + + End Class + +#End Region + +End Namespace + + diff --git a/Microsoft.VisualBasic/runtime/msvbalib/Helpers/CharArrayType.vb b/Microsoft.VisualBasic/runtime/msvbalib/Helpers/CharArrayType.vb new file mode 100644 index 000000000..c41097d66 --- /dev/null +++ b/Microsoft.VisualBasic/runtime/msvbalib/Helpers/CharArrayType.vb @@ -0,0 +1,72 @@ +' Copyright (c) Microsoft Corporation. All rights reserved. + +Imports System +Imports Microsoft.VisualBasic.CompilerServices.Utils + +Namespace Microsoft.VisualBasic.CompilerServices + +#Region " BACKWARDS COMPATIBILITY " + + 'WARNING WARNING WARNING WARNING WARNING + 'This code exists to support Everett compiled applications. Make sure you understand + 'the backwards compatibility ramifications of any edit you make in this region. + 'WARNING WARNING WARNING WARNING WARNING + + _ + Public NotInheritable Class CharArrayType + ' Prevent creation. + Private Sub New() + End Sub + + Public Shared Function FromString(ByVal Value As String) As Char() + + If Value Is Nothing Then + + Value = "" + + End If + + Return Value.ToCharArray() + + End Function + + Public Shared Function FromObject(ByVal Value As Object) As Char() + + If Value Is Nothing Then + + Return "".ToCharArray() + + End If + + Dim CharArray As Char() = TryCast(Value, Char()) + + If CharArray IsNot Nothing AndAlso CharArray.Rank = 1 Then + + Return CharArray + + Else + Dim ValueInterface As IConvertible + + ValueInterface = TryCast(Value, IConvertible) + + If Not ValueInterface Is Nothing Then + + If (ValueInterface.GetTypeCode() = TypeCode.String) Then + Return ValueInterface.ToString(Nothing).ToCharArray() + End If + + End If + + End If + + Throw New InvalidCastException(GetResourceString(ResID.InvalidCast_FromTo, VBFriendlyName(Value), "Char()")) + + End Function + + End Class + +#End Region + +End Namespace + + diff --git a/Microsoft.VisualBasic/runtime/msvbalib/Helpers/CharType.vb b/Microsoft.VisualBasic/runtime/msvbalib/Helpers/CharType.vb new file mode 100644 index 000000000..e2d81106a --- /dev/null +++ b/Microsoft.VisualBasic/runtime/msvbalib/Helpers/CharType.vb @@ -0,0 +1,77 @@ +' Copyright (c) Microsoft Corporation. All rights reserved. + +Imports System + +Imports Microsoft.VisualBasic.CompilerServices.Utils + +Namespace Microsoft.VisualBasic.CompilerServices + +#Region " BACKWARDS COMPATIBILITY " + + 'WARNING WARNING WARNING WARNING WARNING + 'This code exists to support Everett compiled applications. Make sure you understand + 'the backwards compatibility ramifications of any edit you make in this region. + 'WARNING WARNING WARNING WARNING WARNING + + _ + Public NotInheritable Class CharType + ' Prevent creation. + Private Sub New() + End Sub + + Public Shared Function FromString(ByVal Value As String) As Char + If (Value Is Nothing) OrElse (Value.Length = 0) Then + Return ControlChars.NullChar + End If + + Return Value.Chars(0) + End Function + + Public Shared Function FromObject(ByVal Value As Object) As Char + + If Value Is Nothing Then + Return ChrW(0) + End If + + Dim ValueInterface As IConvertible + Dim ValueTypeCode As TypeCode + + ValueInterface = TryCast(Value, IConvertible) + + If Not ValueInterface Is Nothing Then + + ValueTypeCode = ValueInterface.GetTypeCode() + + Select Case ValueTypeCode + Case TypeCode.Char + Return ValueInterface.ToChar(Nothing) + + Case TypeCode.String + Return CharType.FromString(ValueInterface.ToString(Nothing)) + + Case TypeCode.Boolean, _ + TypeCode.Byte, _ + TypeCode.Int16, _ + TypeCode.Int32, _ + TypeCode.Int64, _ + TypeCode.Single, _ + TypeCode.Double, _ + TypeCode.Decimal, _ + TypeCode.DateTime + ' Fall through to error + + Case Else + ' Fall through to error + End Select + End If + + Throw New InvalidCastException(GetResourceString(ResID.InvalidCast_FromTo, VBFriendlyName(Value), "Char")) + End Function + + End Class + +#End Region + +End Namespace + + diff --git a/Microsoft.VisualBasic/runtime/msvbalib/Helpers/ConversionResolution.vb b/Microsoft.VisualBasic/runtime/msvbalib/Helpers/ConversionResolution.vb new file mode 100644 index 000000000..a21b0df24 --- /dev/null +++ b/Microsoft.VisualBasic/runtime/msvbalib/Helpers/ConversionResolution.vb @@ -0,0 +1,1224 @@ +' Copyright (c) Microsoft Corporation. All rights reserved. + +Imports System +Imports System.Reflection +Imports System.Diagnostics +Imports System.Collections.Generic + +Imports Microsoft.VisualBasic.CompilerServices.Symbols +Imports Microsoft.VisualBasic.CompilerServices.ConversionResolution +Imports Microsoft.VisualBasic.CompilerServices.OperatorCaches + +Namespace Microsoft.VisualBasic.CompilerServices + + Friend Class ConversionResolution + ' Prevent creation. + Private Sub New() + End Sub + + Friend Enum ConversionClass As SByte + Bad + Identity + [Widening] + [Narrowing] + None + Ambiguous + End Enum + + Private Shared ReadOnly ConversionTable As ConversionClass()() + Friend Shared ReadOnly NumericSpecificityRank As Integer() + Friend Shared ReadOnly ForLoopWidestTypeCode As TypeCode()() + + Shared Sub New() + Const Max As Integer = TypeCode.String + + Const Bad_ As ConversionClass = ConversionClass.Bad + Const Iden As ConversionClass = ConversionClass.Identity + Const Wide As ConversionClass = ConversionClass.Widening + Const Narr As ConversionClass = ConversionClass.Narrowing + Const None As ConversionClass = ConversionClass.None + + 'Columns represent Source type, Rows represent Target type. + ' empty obj dbnul bool char sbyte byte short ushrt int uint lng ulng sng dbl dec date str + ConversionTable = New ConversionClass(Max)() _ + { _ + New ConversionClass(Max) {Bad_, Bad_, Bad_, Bad_, Bad_, Bad_, Bad_, Bad_, Bad_, Bad_, Bad_, Bad_, Bad_, Bad_, Bad_, Bad_, Bad_, Bad_, Bad_}, _ + New ConversionClass(Max) {Bad_, Iden, Bad_, Wide, Wide, Wide, Wide, Wide, Wide, Wide, Wide, Wide, Wide, Wide, Wide, Wide, Wide, Bad_, Wide}, _ + New ConversionClass(Max) {Bad_, Bad_, Bad_, Bad_, Bad_, Bad_, Bad_, Bad_, Bad_, Bad_, Bad_, Bad_, Bad_, Bad_, Bad_, Bad_, Bad_, Bad_, Bad_}, _ + New ConversionClass(Max) {Bad_, Narr, Bad_, Iden, None, Narr, Narr, Narr, Narr, Narr, Narr, Narr, Narr, Narr, Narr, Narr, None, Bad_, Narr}, _ + New ConversionClass(Max) {Bad_, Narr, Bad_, None, Iden, None, None, None, None, None, None, None, None, None, None, None, None, Bad_, Narr}, _ + New ConversionClass(Max) {Bad_, Narr, Bad_, Narr, None, Iden, Narr, Narr, Narr, Narr, Narr, Narr, Narr, Narr, Narr, Narr, None, Bad_, Narr}, _ + New ConversionClass(Max) {Bad_, Narr, Bad_, Narr, None, Narr, Iden, Narr, Narr, Narr, Narr, Narr, Narr, Narr, Narr, Narr, None, Bad_, Narr}, _ + New ConversionClass(Max) {Bad_, Narr, Bad_, Narr, None, Wide, Wide, Iden, Narr, Narr, Narr, Narr, Narr, Narr, Narr, Narr, None, Bad_, Narr}, _ + New ConversionClass(Max) {Bad_, Narr, Bad_, Narr, None, Narr, Wide, Narr, Iden, Narr, Narr, Narr, Narr, Narr, Narr, Narr, None, Bad_, Narr}, _ + New ConversionClass(Max) {Bad_, Narr, Bad_, Narr, None, Wide, Wide, Wide, Wide, Iden, Narr, Narr, Narr, Narr, Narr, Narr, None, Bad_, Narr}, _ + New ConversionClass(Max) {Bad_, Narr, Bad_, Narr, None, Narr, Wide, Narr, Wide, Narr, Iden, Narr, Narr, Narr, Narr, Narr, None, Bad_, Narr}, _ + New ConversionClass(Max) {Bad_, Narr, Bad_, Narr, None, Wide, Wide, Wide, Wide, Wide, Wide, Iden, Narr, Narr, Narr, Narr, None, Bad_, Narr}, _ + New ConversionClass(Max) {Bad_, Narr, Bad_, Narr, None, Narr, Wide, Narr, Wide, Narr, Wide, Narr, Iden, Narr, Narr, Narr, None, Bad_, Narr}, _ + New ConversionClass(Max) {Bad_, Narr, Bad_, Narr, None, Wide, Wide, Wide, Wide, Wide, Wide, Wide, Wide, Iden, Narr, Wide, None, Bad_, Narr}, _ + New ConversionClass(Max) {Bad_, Narr, Bad_, Narr, None, Wide, Wide, Wide, Wide, Wide, Wide, Wide, Wide, Wide, Iden, Wide, None, Bad_, Narr}, _ + New ConversionClass(Max) {Bad_, Narr, Bad_, Narr, None, Wide, Wide, Wide, Wide, Wide, Wide, Wide, Wide, Narr, Narr, Iden, None, Bad_, Narr}, _ + New ConversionClass(Max) {Bad_, Narr, Bad_, None, None, None, None, None, None, None, None, None, None, None, None, None, Iden, Bad_, Narr}, _ + New ConversionClass(Max) {Bad_, Bad_, Bad_, Bad_, Bad_, Bad_, Bad_, Bad_, Bad_, Bad_, Bad_, Bad_, Bad_, Bad_, Bad_, Bad_, Bad_, Bad_, Bad_}, _ + New ConversionClass(Max) {Bad_, Narr, Bad_, Narr, Wide, Narr, Narr, Narr, Narr, Narr, Narr, Narr, Narr, Narr, Narr, Narr, Narr, Bad_, Iden} _ + } + + 'This table is the relative ordering of the specificity of types. It is used during + 'overload resolution to answer the question: 'Of numeric types a and b, which is more specific?'. + ' + 'The general rules encoded in this table are: + ' Smaller types are more specific than larger types. + ' Signed types are more specific than unsigned types of equal or greater widths, + ' with the exception of Byte which is more specific than SByte (for backwards compatibility). + + NumericSpecificityRank = New Integer(Max) {} + NumericSpecificityRank(TypeCode.Byte) = 1 + NumericSpecificityRank(TypeCode.SByte) = 2 + NumericSpecificityRank(TypeCode.Int16) = 3 + NumericSpecificityRank(TypeCode.UInt16) = 4 + NumericSpecificityRank(TypeCode.Int32) = 5 + NumericSpecificityRank(TypeCode.UInt32) = 6 + NumericSpecificityRank(TypeCode.Int64) = 7 + NumericSpecificityRank(TypeCode.UInt64) = 8 + NumericSpecificityRank(TypeCode.Decimal) = 9 + NumericSpecificityRank(TypeCode.Single) = 10 + NumericSpecificityRank(TypeCode.Double) = 11 + + ' This table specifies the "widest" type to be used in For Loops + ' It should match the results of the Add Operator. + + ForLoopWidestTypeCode = New TypeCode(Max)() _ + { _ + New TypeCode(Max) {TypeCode.Empty, TypeCode.Empty, TypeCode.Empty, TypeCode.Empty, TypeCode.Empty, TypeCode.Empty, TypeCode.Empty, TypeCode.Empty, TypeCode.Empty, TypeCode.Empty, TypeCode.Empty, TypeCode.Empty, TypeCode.Empty, TypeCode.Empty, TypeCode.Empty, TypeCode.Empty, TypeCode.Empty, TypeCode.Empty, TypeCode.Empty}, _ + New TypeCode(Max) {TypeCode.Empty, TypeCode.Empty, TypeCode.Empty, TypeCode.Empty, TypeCode.Empty, TypeCode.Empty, TypeCode.Empty, TypeCode.Empty, TypeCode.Empty, TypeCode.Empty, TypeCode.Empty, TypeCode.Empty, TypeCode.Empty, TypeCode.Empty, TypeCode.Empty, TypeCode.Empty, TypeCode.Empty, TypeCode.Empty, TypeCode.Empty}, _ + New TypeCode(Max) {TypeCode.Empty, TypeCode.Empty, TypeCode.Empty, TypeCode.Empty, TypeCode.Empty, TypeCode.Empty, TypeCode.Empty, TypeCode.Empty, TypeCode.Empty, TypeCode.Empty, TypeCode.Empty, TypeCode.Empty, TypeCode.Empty, TypeCode.Empty, TypeCode.Empty, TypeCode.Empty, TypeCode.Empty, TypeCode.Empty, TypeCode.Empty}, _ + New TypeCode(Max) {TypeCode.Empty, TypeCode.Empty, TypeCode.Empty, TypeCode.Int16, TypeCode.Empty, TypeCode.SByte, TypeCode.Int16, TypeCode.Int16, TypeCode.Int32, TypeCode.Int32, TypeCode.Int64, TypeCode.Int64, TypeCode.Decimal, TypeCode.Single, TypeCode.Double, TypeCode.Decimal, TypeCode.Empty, TypeCode.Empty, TypeCode.Empty}, _ + New TypeCode(Max) {TypeCode.Empty, TypeCode.Empty, TypeCode.Empty, TypeCode.Empty, TypeCode.Empty, TypeCode.Empty, TypeCode.Empty, TypeCode.Empty, TypeCode.Empty, TypeCode.Empty, TypeCode.Empty, TypeCode.Empty, TypeCode.Empty, TypeCode.Empty, TypeCode.Empty, TypeCode.Empty, TypeCode.Empty, TypeCode.Empty, TypeCode.Empty}, _ + New TypeCode(Max) {TypeCode.Empty, TypeCode.Empty, TypeCode.Empty, TypeCode.SByte, TypeCode.Empty, TypeCode.SByte, TypeCode.Int16, TypeCode.Int16, TypeCode.Int32, TypeCode.Int32, TypeCode.Int64, TypeCode.Int64, TypeCode.Decimal, TypeCode.Single, TypeCode.Double, TypeCode.Decimal, TypeCode.Empty, TypeCode.Empty, TypeCode.Empty}, _ + New TypeCode(Max) {TypeCode.Empty, TypeCode.Empty, TypeCode.Empty, TypeCode.Int16, TypeCode.Empty, TypeCode.Int16, TypeCode.Byte, TypeCode.Int16, TypeCode.UInt16, TypeCode.Int32, TypeCode.UInt32, TypeCode.Int64, TypeCode.UInt64, TypeCode.Single, TypeCode.Double, TypeCode.Decimal, TypeCode.Empty, TypeCode.Empty, TypeCode.Empty}, _ + New TypeCode(Max) {TypeCode.Empty, TypeCode.Empty, TypeCode.Empty, TypeCode.Int16, TypeCode.Empty, TypeCode.Int16, TypeCode.Int16, TypeCode.Int16, TypeCode.Int32, TypeCode.Int32, TypeCode.Int64, TypeCode.Int64, TypeCode.Decimal, TypeCode.Single, TypeCode.Double, TypeCode.Decimal, TypeCode.Empty, TypeCode.Empty, TypeCode.Empty}, _ + New TypeCode(Max) {TypeCode.Empty, TypeCode.Empty, TypeCode.Empty, TypeCode.Int32, TypeCode.Empty, TypeCode.Int32, TypeCode.UInt16, TypeCode.Int32, TypeCode.UInt16, TypeCode.Int32, TypeCode.UInt32, TypeCode.Int64, TypeCode.UInt64, TypeCode.Single, TypeCode.Double, TypeCode.Decimal, TypeCode.Empty, TypeCode.Empty, TypeCode.Empty}, _ + New TypeCode(Max) {TypeCode.Empty, TypeCode.Empty, TypeCode.Empty, TypeCode.Int32, TypeCode.Empty, TypeCode.Int32, TypeCode.Int32, TypeCode.Int32, TypeCode.Int32, TypeCode.Int32, TypeCode.Int64, TypeCode.Int64, TypeCode.Decimal, TypeCode.Single, TypeCode.Double, TypeCode.Decimal, TypeCode.Empty, TypeCode.Empty, TypeCode.Empty}, _ + New TypeCode(Max) {TypeCode.Empty, TypeCode.Empty, TypeCode.Empty, TypeCode.Int64, TypeCode.Empty, TypeCode.Int64, TypeCode.UInt32, TypeCode.Int64, TypeCode.UInt32, TypeCode.Int64, TypeCode.UInt32, TypeCode.Int64, TypeCode.UInt64, TypeCode.Single, TypeCode.Double, TypeCode.Decimal, TypeCode.Empty, TypeCode.Empty, TypeCode.Empty}, _ + New TypeCode(Max) {TypeCode.Empty, TypeCode.Empty, TypeCode.Empty, TypeCode.Int64, TypeCode.Empty, TypeCode.Int64, TypeCode.Int64, TypeCode.Int64, TypeCode.Int64, TypeCode.Int64, TypeCode.Int64, TypeCode.Int64, TypeCode.Decimal, TypeCode.Single, TypeCode.Double, TypeCode.Decimal, TypeCode.Empty, TypeCode.Empty, TypeCode.Empty}, _ + New TypeCode(Max) {TypeCode.Empty, TypeCode.Empty, TypeCode.Empty, TypeCode.Decimal, TypeCode.Empty, TypeCode.Decimal, TypeCode.UInt64, TypeCode.Decimal, TypeCode.UInt64, TypeCode.Decimal, TypeCode.UInt64, TypeCode.Decimal, TypeCode.UInt64, TypeCode.Single, TypeCode.Double, TypeCode.Decimal, TypeCode.Empty, TypeCode.Empty, TypeCode.Empty}, _ + New TypeCode(Max) {TypeCode.Empty, TypeCode.Empty, TypeCode.Empty, TypeCode.Single, TypeCode.Empty, TypeCode.Single, TypeCode.Single, TypeCode.Single, TypeCode.Single, TypeCode.Single, TypeCode.Single, TypeCode.Single, TypeCode.Single, TypeCode.Single, TypeCode.Double, TypeCode.Single, TypeCode.Empty, TypeCode.Empty, TypeCode.Empty}, _ + New TypeCode(Max) {TypeCode.Empty, TypeCode.Empty, TypeCode.Empty, TypeCode.Double, TypeCode.Empty, TypeCode.Double, TypeCode.Double, TypeCode.Double, TypeCode.Double, TypeCode.Double, TypeCode.Double, TypeCode.Double, TypeCode.Double, TypeCode.Double, TypeCode.Double, TypeCode.Double, TypeCode.Empty, TypeCode.Empty, TypeCode.Empty}, _ + New TypeCode(Max) {TypeCode.Empty, TypeCode.Empty, TypeCode.Empty, TypeCode.Decimal, TypeCode.Empty, TypeCode.Decimal, TypeCode.Decimal, TypeCode.Decimal, TypeCode.Decimal, TypeCode.Decimal, TypeCode.Decimal, TypeCode.Decimal, TypeCode.Decimal, TypeCode.Single, TypeCode.Double, TypeCode.Decimal, TypeCode.Empty, TypeCode.Empty, TypeCode.Empty}, _ + New TypeCode(Max) {TypeCode.Empty, TypeCode.Empty, TypeCode.Empty, TypeCode.Empty, TypeCode.Empty, TypeCode.Empty, TypeCode.Empty, TypeCode.Empty, TypeCode.Empty, TypeCode.Empty, TypeCode.Empty, TypeCode.Empty, TypeCode.Empty, TypeCode.Empty, TypeCode.Empty, TypeCode.Empty, TypeCode.Empty, TypeCode.Empty, TypeCode.Empty}, _ + New TypeCode(Max) {TypeCode.Empty, TypeCode.Empty, TypeCode.Empty, TypeCode.Empty, TypeCode.Empty, TypeCode.Empty, TypeCode.Empty, TypeCode.Empty, TypeCode.Empty, TypeCode.Empty, TypeCode.Empty, TypeCode.Empty, TypeCode.Empty, TypeCode.Empty, TypeCode.Empty, TypeCode.Empty, TypeCode.Empty, TypeCode.Empty, TypeCode.Empty}, _ + New TypeCode(Max) {TypeCode.Empty, TypeCode.Empty, TypeCode.Empty, TypeCode.Empty, TypeCode.Empty, TypeCode.Empty, TypeCode.Empty, TypeCode.Empty, TypeCode.Empty, TypeCode.Empty, TypeCode.Empty, TypeCode.Empty, TypeCode.Empty, TypeCode.Empty, TypeCode.Empty, TypeCode.Empty, TypeCode.Empty, TypeCode.Empty, TypeCode.Empty} _ + } + + VerifyTypeCodeEnum() + +#If DEBUG Then + VerifyForLoopWidestType() +#End If + End Sub + +#If DEBUG Then + _ + Private Shared Sub VerifyForLoopWidestType() + Const Max As Integer = TypeCode.String + + For Index1 As Integer = 0 To Max + Dim tc1 As TypeCode = CType(Index1, TypeCode) + + If IsNumericType(tc1) Then + For Index2 As Integer = 0 To Max + Dim tc2 As TypeCode = CType(Index2, TypeCode) + + If IsNumericType(tc2) Then + Dim tc as TypeCode = ForLoopWidestTypeCode(tc1)(tc2) + + Dim Type1 As Type = MapTypeCodeToType(tc1) + Dim Type2 As Type = MapTypeCodeToType(tc2) + + Dim o1 As Object = 0 + Dim o2 As Object = 0 + + o1 = CType(o1, IConvertible).ToType(Type1, Nothing) + o2 = CType(o2, IConvertible).ToType(Type2, Nothing) + + Dim Result As Object = Operators.AddObject(o1, o2) + + Debug.Assert(GetTypeCode(Result.GetType()) = tc, "Widest type is invalid") + End If + Next + End If + Next + End Sub +#End If + + _ + Private Shared Sub VerifyTypeCodeEnum() + Debug.Assert(TypeCode.Empty = 0, "wrong value!") + Debug.Assert(TypeCode.Object = 1, "wrong value!") + Debug.Assert(TypeCode.Boolean = 3, "yte is wrong value!") + Debug.Assert(TypeCode.Char = 4, "wrong value!") + Debug.Assert(TypeCode.SByte = 5, "wrong value!") + Debug.Assert(TypeCode.Byte = 6, "wrong value!") + Debug.Assert(TypeCode.Int16 = 7, "wrong value!") + Debug.Assert(TypeCode.UInt16 = 8, "wrong value!") + Debug.Assert(TypeCode.Int32 = 9, "wrong value!") + Debug.Assert(TypeCode.UInt32 = 10, "wrong value!") + Debug.Assert(TypeCode.Int64 = 11, "wrong value!") + Debug.Assert(TypeCode.UInt64 = 12, "wrong value!") + Debug.Assert(TypeCode.Single = 13, "wrong value!") + Debug.Assert(TypeCode.Double = 14, "wrong value!") + Debug.Assert(TypeCode.Decimal = 15, "wrong value!") + Debug.Assert(TypeCode.DateTime = 16, "wrong value!") + Debug.Assert(TypeCode.String = 18, "wrong value!") + End Sub + + Friend Shared Function ClassifyConversion(ByVal TargetType As System.Type, ByVal SourceType As System.Type, ByRef OperatorMethod As Method) As ConversionClass + 'This function classifies the nature of the conversion from the source type to the target + 'type. If such a conversion requires a user-defined conversion, it will be supplied as an + 'out parameter. + + Debug.Assert(Not TargetType.IsByRef AndAlso Not SourceType.IsByRef, "ByRef types unexpected.") + + Dim Result As ConversionClass = ClassifyPredefinedConversion(TargetType, SourceType) + + If Result = ConversionClass.None AndAlso _ + Not IsInterface(SourceType) AndAlso _ + Not IsInterface(TargetType) AndAlso _ + (IsClassOrValueType(SourceType) OrElse IsClassOrValueType(TargetType)) AndAlso _ + Not (IsIntrinsicType(SourceType) AndAlso IsIntrinsicType(TargetType)) Then + + Result = ClassifyUserDefinedConversion(TargetType, SourceType, OperatorMethod) + End If + + Return Result + + End Function + + Friend Shared Function ClassifyIntrinsicConversion(ByVal TargetTypeCode As System.TypeCode, ByVal SourceTypeCode As System.TypeCode) As ConversionClass + + Debug.Assert(IsIntrinsicType(TargetTypeCode) AndAlso IsIntrinsicType(SourceTypeCode), "expected intrinsics here") + Return ConversionTable(TargetTypeCode)(SourceTypeCode) + End Function + + Friend Shared Function ClassifyPredefinedCLRConversion(ByVal TargetType As System.Type, ByVal SourceType As System.Type) As ConversionClass + ' This function classifies all intrinsic CLR conversions, such as inheritance, + ' implementation, and array covariance. + + Debug.Assert(Not TargetType.IsByRef AndAlso Not SourceType.IsByRef, "ByRef types unexpected.") + + 'CONSIDER: we can we use IsAssignableFrom to cut out a number of these checks (probably the widening ones)? + + ' *IDENTITY* + If TargetType Is SourceType Then Return ConversionClass.Identity + + ' *INHERITANCE* + If IsRootObjectType(TargetType) OrElse IsOrInheritsFrom(SourceType, TargetType) Then + Return ConversionClass.Widening + End If + + If IsRootObjectType(SourceType) OrElse IsOrInheritsFrom(TargetType, SourceType) Then + Return ConversionClass.Narrowing + End If + + ' *INTERFACE IMPLEMENTATION* + If IsInterface(SourceType) Then + + If IsClass(TargetType) OrElse IsArrayType(TargetType) OrElse IsGenericParameter(TargetType) Then + ' Even if a class is marked NotInheritable, it can still be a COM class and implement + ' any interface dynamically at runtime, so we must allow a narrowing conversion. + + Return ConversionClass.Narrowing + End If + + If IsInterface(TargetType) Then + Return ConversionClass.Narrowing + End If + + If IsValueType(TargetType) Then + If [Implements](TargetType, SourceType) Then + Return ConversionClass.Narrowing + Else + Return ConversionClass.None + End If + End If +#If TELESTO Then + Debug.Assert(False,"all conversions from interface should have been handled by now") +#Else + Debug.Fail("all conversions from interface should have been handled by now") +#End If + Return ConversionClass.Narrowing + End If + + If IsInterface(TargetType) Then + + If (IsArrayType(SourceType)) Then + Return _ + ClassifyCLRArrayToInterfaceConversion(TargetType, Sourcetype) + End If + + If IsValueType(SourceType) Then + If [Implements](SourceType, TargetType) Then + Return ConversionClass.Widening + Else + Return ConversionClass.None + End If + End If + + If IsClass(SourceType) Then + If [Implements](SourceType, TargetType) Then + Return ConversionClass.Widening + Else + Return ConversionClass.Narrowing + End If + End If + + 'generic params are handled later + End If + + ' *ENUMERATION* + If IsEnum(SourceType) OrElse IsEnum(TargetType) Then + + If GetTypeCode(SourceType) = GetTypeCode(TargetType) Then + If IsEnum(TargetType) Then + Return ConversionClass.Narrowing + Else + Return ConversionClass.Widening + End If + End If + + Return ConversionClass.None + End If + + ' *GENERIC PARAMETERS* + If IsGenericParameter(SourceType) Then + If Not IsClassOrInterface(TargetType) Then + Return ConversionClass.None + End If + + 'Return the best conversion from any constraint type to the target type. + For Each InterfaceConstraint As Type In GetInterfaceConstraints(SourceType) + Dim Classification As ConversionClass = _ + ClassifyPredefinedConversion(TargetType, InterfaceConstraint) + + If Classification = ConversionClass.Widening OrElse _ + Classification = ConversionClass.Identity Then + 'A conversion from a constraint type cannot be an identity conversion + '(because a conversion operation is necessary in the generated code), + 'so don't allow it to look any better than Widening. + Return ConversionClass.Widening + End If + Next + + Dim ClassConstraint As Type = GetClassConstraint(SourceType) + If ClassConstraint IsNot Nothing Then + Dim Classification As ConversionClass = _ + ClassifyPredefinedConversion(TargetType, ClassConstraint) + + If Classification = ConversionClass.Widening OrElse _ + Classification = ConversionClass.Identity Then + 'A conversion from a constraint type cannot be an identity conversion + '(because a conversion operation is necessary in the generated code), + 'so don't allow it to look any better than Widening. + Return ConversionClass.Widening + End If + End If + + Return IIf(IsInterface(TargetType), ConversionClass.Narrowing, ConversionClass.None) + End If + + If IsGenericParameter(TargetType) Then + Debug.Assert(Not IsInterface(SourceType), _ + "conversions from interfaces should have been handled by now") + + 'If one of the constraint types is a class type, a narrowing conversion exists from that class type. + Dim ClassConstraint As Type = GetClassConstraint(TargetType) + If ClassConstraint IsNot Nothing AndAlso IsOrInheritsFrom(ClassConstraint, SourceType) Then + Return ConversionClass.Narrowing + End If + + Return ConversionClass.None + End If + + ' *ARRAY COVARIANCE* + If IsArrayType(SourceType) AndAlso IsArrayType(TargetType) Then + + If SourceType.GetArrayRank = TargetType.GetArrayRank Then + + ' The element types must either be the same or + ' the source element type must extend or implement the + ' target element type. (VB implements array covariance.) + + Return _ + ClassifyCLRConversionForArrayElementTypes( _ + TargetType.GetElementType, _ + SourceType.GetElementType) + + End If + + Return ConversionClass.None + End If + + Return ConversionClass.None + + End Function + + Private Shared Function ClassifyCLRArrayToInterfaceConversion(ByVal TargetInterface As System.Type, ByVal SourceArrayType As System.Type) As ConversionClass + + Debug.Assert(IsInterface(TargetInterface), "Non-Interface type unexpected!!!") + Debug.Assert(IsArrayType(SourceArrayType), "Non-Array type unexpected!!!") + + ' No need to get to System.Array, [Implements] works for arrays with respect to the interfaces on System.Array + ' + If ([Implements](SourceArrayType, TargetInterface)) + Return ConversionClass.Widening + End If + + ' Multi-dimensional arrays do not support IList + ' + If (SourceArrayType.GetArrayRank > 1) + Return ConversionClass.Narrowing + End If + + + ' Check for the conversion from the Array of element type T to + ' 1. IList(Of T) - Widening + ' 2. Some interface that IList(Of T) inherits from - Widening + ' 3. IList(Of SomeType that T inherits from) - Widening + ' yes, generics covariance is allowed in the array case + ' 4. Some interface that IList(Of SomeType that T inherits from) + ' inherits from - Widening + ' 5. Some interface that inherits from IList(Of T) - Narrowing + ' 6. Some interface that inherits from IList(Of SomeType that T inherits from) + ' - Narrowing + ' + ' 5 and 6 are not checked for explicitly since from array to interface that + ' the array does not widen to, we anyway return narrowing. + ' + + Dim SourceElementType As Type = SourceArrayType.GetElementType + Dim Conversion As ConversionClass = ConversionClass.None + + If (TargetInterface.IsGenericType AndAlso Not TargetInterface.IsGenericTypeDefinition) Then + + Dim RawTargetInterface As Type = TargetInterface.GetGenericTypeDefinition() + + If (RawTargetInterface Is GetType(System.Collections.Generic.IList(Of )) OrElse _ + RawTargetInterface Is GetType(System.Collections.Generic.ICollection(Of )) OrElse _ + RawTargetInterface Is GetType(System.Collections.Generic.IEnumerable(Of ))) Then + + Conversion = _ + ClassifyCLRConversionForArrayElementTypes( _ + TargetInterface.GetGenericArguments()(0), _ + SourceElementType) + End If + + Else + Conversion = _ + ClassifyPredefinedCLRConversion( _ + TargetInterface, _ + GetType(System.Collections.Generic.IList(Of )).MakeGenericType(New Type() {SourceElementType})) + End If + + + If (Conversion = ConversionClass.Identity OrElse _ + Conversion = ConversionClass.Widening) + + Return ConversionClass.Widening + End If + + Return ConversionClass.Narrowing + + End Function + + + Private Shared Function ClassifyCLRConversionForArrayElementTypes(ByVal TargetElementType As System.Type, ByVal SourceElementType As System.Type) As ConversionClass + + ' The element types must either be the same or + ' the source element type must extend or implement the + ' target element type. (VB implements array covariance.) + + ' Generic params are handled correctly here. + + If IsReferenceType(SourceElementType) AndAlso _ + IsReferenceType(TargetElementType) Then + Return ClassifyPredefinedCLRConversion(TargetElementType, SourceElementType) + End If + + If IsValueType(SourceElementType) AndAlso _ + IsValueType(TargetElementType) Then + Return ClassifyPredefinedCLRConversion(TargetElementType, SourceElementType) + End If + + ' Bug VSWhidbey 369131. + ' Array co-variance and back-casting special case for generic parameters. + ' + If IsGenericParameter(SourceElementType) AndAlso _ + IsGenericParameter(TargetElementType) Then + + If SourceElementType Is TargetElementType Then + Return ConversionClass.Identity + End If + + If IsReferenceType(SourceElementType) AndAlso _ + IsOrInheritsFrom(SourceElementType, TargetElementType) Then + Return ConversionClass.Widening + End If + + If IsReferenceType(TargetElementType) AndAlso _ + IsOrInheritsFrom(TargetElementType, SourceElementType) Then + Return ConversionClass.Narrowing + End If + End If + + Return ConversionClass.None + End Function + + + Friend Shared Function ClassifyPredefinedConversion(ByVal TargetType As System.Type, ByVal SourceType As System.Type) As ConversionClass + ' This function classifies all intrinsic language conversions, such as inheritance, + ' implementation, array covariance, and conversions between intrinsic types. + + Debug.Assert(Not TargetType.IsByRef AndAlso Not SourceType.IsByRef, "ByRef types unexpected.") + + ' Make an easy reference comparison for a common case. More complicated type comparisons will happen later. + If TargetType Is SourceType Then Return ConversionClass.Identity + + Dim SourceTypeCode As TypeCode = GetTypeCode(SourceType) + Dim TargetTypeCode As TypeCode = GetTypeCode(TargetType) + + If (IsIntrinsicType(SourceTypeCode) AndAlso IsIntrinsicType(TargetTypeCode)) Then + + If IsEnum(TargetType) Then + If IsIntegralType(SourceTypeCode) AndAlso IsIntegralType(TargetTypeCode) Then + ' Conversion from an integral type (including an Enum type) + ' to an Enum type (that has an integral underlying type) + ' is narrowing. Enums do not necessarily have integral underlying types. + Return ConversionClass.Narrowing + End If + End If + + If SourceTypeCode = TargetTypeCode AndAlso IsEnum(SourceType) Then + ' Conversion from an Enum to it's underlying type is widening. + ' If we used ClassifyIntrinsicConversion, this kind of conversion + ' would be classified as identity, and that would not be good. + ' Catch this case here. + Return ConversionClass.Widening + End If + + Return ClassifyIntrinsicConversion(TargetTypeCode, SourceTypeCode) + + End If + + ' Try VB specific conversions from String-->Char() or Char()-->String. + + If IsCharArrayRankOne(SourceType) AndAlso IsStringType(TargetType) Then + ' Array of Char widens to String. + Return ConversionClass.Widening + End If + + If IsCharArrayRankOne(TargetType) AndAlso IsStringType(SourceType) Then + ' String narrows to array of Char. + Return ConversionClass.Narrowing + End If + + Return ClassifyPredefinedCLRConversion(TargetType, SourceType) + + End Function + + Private Shared Function CollectConversionOperators( _ + ByVal TargetType As System.Type, _ + ByVal SourceType As System.Type, _ + ByRef FoundTargetTypeOperators As Boolean, _ + ByRef FoundSourceTypeOperators As Boolean) As List(Of Method) + + 'Find all Widening and Narrowing conversion operators. Combine the lists + 'with the Widening operators grouped at the front. + + 'From the perspective of VB, intrinsic types have no conversion operators. + 'Substitute in Object for these types. + If IsIntrinsicType(TargetType) Then TargetType = GetType(Object) + If IsIntrinsicType(SourceType) Then SourceType = GetType(Object) + + Dim Result As List(Of Method) = _ + Operators.CollectOperators( _ + UserDefinedOperator.Widen, _ + TargetType, _ + SourceType, _ + FoundTargetTypeOperators, _ + FoundSourceTypeOperators) + + Dim NarrowingOperators As List(Of Method) = _ + Operators.CollectOperators( _ + UserDefinedOperator.Narrow, _ + TargetType, _ + SourceType, _ + FoundTargetTypeOperators, _ + FoundSourceTypeOperators) + + Result.AddRange(NarrowingOperators) + Return Result + End Function + + Private Shared Function Encompasses(ByVal Larger As System.Type, ByVal Smaller As System.Type) As Boolean + 'Definition: LARGER is said to encompass SMALLER if SMALLER widens to or is LARGER. + + 'CONSIDER: since determining encompasses is quite commonly used, + 'and only depends on widening or identity, a special function for classifying + 'just predefined widening conversions could be a performance gain. + Dim Result As ConversionClass = _ + ClassifyPredefinedConversion(Larger, Smaller) + + Return Result = ConversionClass.Widening OrElse Result = ConversionClass.Identity + End Function + + Private Shared Function NotEncompasses(ByVal Larger As System.Type, ByVal Smaller As System.Type) As Boolean + 'Definition: LARGER is said to not encompass SMALLER if SMALLER narrows to or is LARGER. + + 'CONSIDER: since determining encompasses is quite commonly used, + 'and only depends on widening or identity, a special function for classifying + 'just predefined narrowing conversions could be a performance gain. + Dim Result As ConversionClass = _ + ClassifyPredefinedConversion(Larger, Smaller) + + Return Result = ConversionClass.Narrowing OrElse Result = ConversionClass.Identity + End Function + + + Private Shared Function MostEncompassing(ByVal Types As List(Of System.Type)) As System.Type + 'Given a set TYPES, determine the most encompassing type. An element + 'CANDIDATE of TYPES is said to be most encompassing if no other element of + 'TYPES encompasses CANDIDATE. + + Debug.Assert(Types.Count > 0, "unexpected empty set") + Dim MaxEncompassing As System.Type = Types.Item(0) + + For Index As Integer = 1 To Types.Count - 1 + Dim Candidate As Type = Types.Item(Index) + + If Encompasses(Candidate, MaxEncompassing) Then + Debug.Assert(Candidate Is MaxEncompassing OrElse Not Encompasses(MaxEncompassing, Candidate), _ + "surprisingly, two types encompass each other") + MaxEncompassing = Candidate + ElseIf Not Encompasses(MaxEncompassing, Candidate) Then + 'We have detected more than one most encompassing type in the set. + 'Return Nothing to indicate this error condition. + Return Nothing + End If + Next + + Return MaxEncompassing + End Function + + + Private Shared Function MostEncompassed(ByVal Types As List(Of System.Type)) As System.Type + 'Given a set TYPES, determine the most encompassed type. An element + 'CANDIDATE of TYPES is said to be most encompassed if CANDIDATE encompasses + 'no other element of TYPES. + + Debug.Assert(Types.Count > 0, "unexpected empty set") + + Dim MaxEncompassed As System.Type = Types.Item(0) + + For Index As Integer = 1 To Types.Count - 1 + Dim Candidate As Type = Types.Item(Index) + + If Encompasses(MaxEncompassed, Candidate) Then + Debug.Assert(Candidate Is MaxEncompassed OrElse Not Encompasses(Candidate, MaxEncompassed), _ + "surprisingly, two types encompass each other") + MaxEncompassed = Candidate + ElseIf Not Encompasses(Candidate, MaxEncompassed) Then + 'We have detected more than one most encompassed type in the set. + 'Return Nothing to indicate this error condition. + Return Nothing + End If + Next + + Return MaxEncompassed + End Function + + Private Shared Sub FindBestMatch( _ + ByVal TargetType As Type, _ + ByVal SourceType As Type, _ + ByVal SearchList As List(Of Method), _ + ByVal ResultList As List(Of Method), _ + ByRef GenericMembersExistInList As Boolean) + + 'Given a set of conversion operators which convert from INPUT to RESULT, return the set + 'of operators for which INPUT is SOURCE and RESULT is TARGET. + + For Each Item As Method In SearchList + Dim Current As MethodBase = Item.AsMethod + Dim InputType As System.Type = Current.GetParameters(0).ParameterType + Dim ResultType As System.Type = DirectCast(Current, MethodInfo).ReturnType + + If InputType Is SourceType AndAlso ResultType Is TargetType Then + InsertInOperatorListIfLessGenericThanExisting(Item, ResultList, GenericMembersExistInList) + End If + Next + Return + + End Sub + + Private Shared Sub InsertInOperatorListIfLessGenericThanExisting( _ + Byval OperatorToInsert As Method, _ + ByVal OperatorList As List(Of Method), _ + Byref GenericMembersExistInList As Boolean) + + If IsGeneric(OperatorToInsert.DeclaringType) Then + GenericMembersExistInList = True + End If + + If GenericMembersExistInList Then + + For i as Integer = OperatorList.Count -1 to 0 Step -1 + + Dim Existing As Method = OperatorList.Item(i) + Dim LeastGeneric As Method = OverloadResolution.LeastGenericProcedure(Existing, OperatorToInsert) + + If LeastGeneric Is Existing Then + ' An existing one is less generic than the current operator being + ' considered, so skip adding the current operator to the operator + ' list. + Return + + Else If LeastGeneric IsNot Nothing Then + ' The current operator is less generic than an existing operator, + ' so remove the existing operator from the list and continue to + ' check if any other exisiting operator can be removed from the + ' result set. + ' + OperatorList.Remove(Existing) + End If + Next + End If + + OperatorList.Add(OperatorToInsert) + End Sub + + Private Shared Function ResolveConversion( _ + ByVal TargetType As System.Type, _ + ByVal SourceType As System.Type, _ + ByVal OperatorSet As List(Of Method), _ + ByVal WideningOnly As Boolean, _ + ByRef ResolutionIsAmbiguous As Boolean) As List(Of Method) + + + 'This function resolves which user-defined conversion operator contained in the input set + 'can be used to perform the conversion from source type S to target type T. + ' + 'The algorithm defies succinct explaination, but roughly: + ' + 'Conversions of the form S-->T use only one user-defined conversion at a time, i.e., + 'user-defined conversions are not chained together. It may be necessary to convert to and + 'from intermediate types using predefined conversions to match the signature of the + 'user-defined conversion exactly, so the conversion "path" is comprised of at most three + 'parts: + ' + ' 1) [ predefined conversion S-->Sx ] + ' 2) User-defined conversion Sx-->Tx + ' 3) [ predefined conversion Tx-->T ] + ' + ' Where Sx is the intermediate source type + ' and Tx is the intermediate target type + ' + ' Steps 1 and 3 are optional given S == Sx or Tx == T. + ' + 'Much of the algorithm below concerns itself with finding Sx and Tx. The rules are: + ' + ' - If a conversion operator in the set converts from S, then Sx is S. + ' - If a conversion operator in the set converts to T, then Tx is T. + ' - Otherwise Sx and Tx are the "closest" types to S and T. If multiple types are + ' equally close, the conversion is ambiguous. + ' + 'Each operator presents a possibility for Sx (the parameter type of the operator). Given + 'these choices, the "closest" type to S is the smallest (most encompassed) type that S + 'widens to. If S widens to none of the possible types, then the "closest" type to S is + 'the largest (most encompassing) type that widens to S. In this way, the algorithm + 'always prefers widening from S over narrowing from S. + ' + 'Similarily, each operator presents a possibility for Tx (the return type of the operator). + 'Given these choices, the "closest" type to T is the largest (most encompassing) type that + 'widens to T. If none of the possible types widen to T, then the "closest" type to T is + 'the smallest (most encompassed) type that T widens to. In this way, the algorithm + 'always prefers widening to T over narrowing to T. + ' + 'Upon deciding Sx and Tx, if one operator's operands exactly match Sx and Tx, then that + 'operator is chosen. If no operators match, or if multiple operators match, the conversion + 'is impossible. + ' + 'Refer to the language specification as it covers all details of the algorithm. + + ResolutionIsAmbiguous = False + + Dim MostSpecificSourceType As System.Type = Nothing + Dim MostSpecificTargetType As System.Type = Nothing + + Dim GenericOperatorChoicesFound As Boolean = False + Dim OperatorChoices As List(Of Method) = New List(Of Method)(OperatorSet.Count) + Dim Candidates As List(Of Method) = New List(Of Method)(OperatorSet.Count) + + Dim SourceBases As List(Of Type) = New List(Of Type)(OperatorSet.Count) + Dim TargetDeriveds As List(Of Type) = New List(Of Type)(OperatorSet.Count) + Dim SourceDeriveds As List(Of Type) = Nothing + Dim TargetBases As List(Of Type) = Nothing + + If Not WideningOnly Then + SourceDeriveds = New List(Of Type)(OperatorSet.Count) + TargetBases = New List(Of Type)(OperatorSet.Count) + End If + + 'To minimize the number of calls to Encompasses, we categorize conversions + 'into three flavors: + ' + ' 1) Base of Source to Derived of Target (only flavor that can be completely widening) + ' 2) Base of Source to Base of Target + ' 3) Derived of Source to Base of Target + ' + 'For each flavor, we place the input and result type into the corresponding + 'type set. Then we calculate most encompassing/encompassed using the type sets. + + For Each CurrentMethod As Method In OperatorSet + + Dim Current As MethodBase = CurrentMethod.AsMethod + + 'Performance trick: the operators are grouped by widening and then narrowing + 'conversions. If we are iterating over just widening conversions, we are done + 'once we find a narrowing conversion. + If WideningOnly AndAlso IsNarrowingConversionOperator(Current) Then Exit For + + Dim InputType As System.Type = Current.GetParameters(0).ParameterType + Dim ResultType As System.Type = DirectCast(Current, MethodInfo).ReturnType + + If (IsGeneric(Current) OrElse _ + IsGeneric(Current.DeclaringType)) AndAlso _ + ClassifyPredefinedConversion(ResultType, InputType) <> ConversionClass.None Then + Continue For + End If + + If InputType Is SourceType AndAlso ResultType Is TargetType Then + InsertInOperatorListIfLessGenericThanExisting(CurrentMethod, OperatorChoices, GenericOperatorChoicesFound) + + ElseIf OperatorChoices.Count = 0 Then + + If Encompasses(InputType, SourceType) AndAlso Encompasses(TargetType, ResultType) Then + 'Check SourceBase->TargetDerived flavor. + + Candidates.Add(CurrentMethod) + If InputType Is SourceType Then MostSpecificSourceType = InputType Else SourceBases.Add(InputType) + If ResultType Is TargetType Then MostSpecificTargetType = ResultType Else TargetDeriveds.Add(ResultType) + + ElseIf Not WideningOnly AndAlso _ + Encompasses(InputType, SourceType) AndAlso NotEncompasses(TargetType, ResultType) Then + 'Check SourceBase->TargetBase flavor. + + Candidates.Add(CurrentMethod) + If InputType Is SourceType Then MostSpecificSourceType = InputType Else SourceBases.Add(InputType) + If ResultType Is TargetType Then MostSpecificTargetType = ResultType Else TargetBases.Add(ResultType) + + ElseIf Not WideningOnly AndAlso _ + NotEncompasses(InputType, SourceType) AndAlso NotEncompasses(TargetType, ResultType) Then + 'Check SourceDerived->TargetBase flavor. + + Candidates.Add(CurrentMethod) + If InputType Is SourceType Then MostSpecificSourceType = InputType Else SourceDeriveds.Add(InputType) + If ResultType Is TargetType Then MostSpecificTargetType = ResultType Else TargetBases.Add(ResultType) + + End If + + End If + Next + + 'Now attempt to find the most specific types Sx and Tx by analyzing the type sets + 'we built up in the code above. + + If OperatorChoices.Count = 0 AndAlso Candidates.Count > 0 Then + + If MostSpecificSourceType Is Nothing Then + If SourceBases.Count > 0 Then + MostSpecificSourceType = MostEncompassed(SourceBases) + Else + Debug.Assert(Not WideningOnly AndAlso SourceDeriveds.Count > 0, "unexpected state") + MostSpecificSourceType = MostEncompassing(SourceDeriveds) + End If + End If + + If MostSpecificTargetType Is Nothing Then + If TargetDeriveds.Count > 0 Then + MostSpecificTargetType = MostEncompassing(TargetDeriveds) + Else + Debug.Assert(Not WideningOnly AndAlso TargetBases.Count > 0, "unexpected state") + MostSpecificTargetType = MostEncompassed(TargetBases) + End If + End If + + If MostSpecificSourceType Is Nothing OrElse MostSpecificTargetType Is Nothing Then + ResolutionIsAmbiguous = True + Return New List(Of Method) + End If + + FindBestMatch(MostSpecificTargetType, MostSpecificSourceType, Candidates, OperatorChoices, GenericOperatorChoicesFound) + + End If + + If OperatorChoices.Count > 1 Then + ResolutionIsAmbiguous = True + End If + + Return OperatorChoices + + End Function + + Friend Shared Function ClassifyUserDefinedConversion( _ + ByVal TargetType As System.Type, _ + ByVal SourceType As System.Type, _ + ByRef OperatorMethod As Method) As ConversionClass + + Dim Result As ConversionClass + + 'Check if we have done this classification before. + SyncLock (ConversionCache) + 'First check if both types have no user-defined conversion operators. If so, they cannot + 'convert to each other with user-defined operators. + If UnconvertibleTypeCache.Lookup(TargetType) AndAlso UnconvertibleTypeCache.Lookup(SourceType) Then + Return ConversionClass.None + End If + + 'Now check if we have recently resolved this conversion. + If ConversionCache.Lookup(TargetType, SourceType, Result, OperatorMethod) Then + Return Result + End If + End SyncLock + + 'Perform the expensive work to resolve the user-defined conversion. + Dim FoundTargetTypeOperators As Boolean = False + Dim FoundSourceTypeOperators As Boolean = False + Result = _ + DoClassifyUserDefinedConversion( _ + TargetType, _ + SourceType, _ + OperatorMethod, _ + FoundTargetTypeOperators, _ + FoundSourceTypeOperators) + + 'Save away the results. + SyncLock (ConversionCache) + 'Remember which types have no operators so we can avoid re-doing the work next time. + If Not FoundTargetTypeOperators Then + UnconvertibleTypeCache.Insert(TargetType) + End If + + If Not FoundSourceTypeOperators Then + UnconvertibleTypeCache.Insert(SourceType) + End If + + If FoundTargetTypeOperators OrElse FoundSourceTypeOperators Then + 'Cache the result of the resolution so we can avoid re-doing the work next time, but + 'only when conversion operators were found (otherwise, the type caches will catch this + 'the next time). + ConversionCache.Insert(TargetType, SourceType, Result, OperatorMethod) + End If + End SyncLock + + Return Result + End Function + + Private Shared Function DoClassifyUserDefinedConversion( _ + ByVal TargetType As System.Type, _ + ByVal SourceType As System.Type, _ + ByRef OperatorMethod As Method, _ + ByRef FoundTargetTypeOperators As Boolean, _ + ByRef FoundSourceTypeOperators As Boolean) As ConversionClass + + 'Classifies the conversion from Source to Target using user-defined conversion operators. + 'If such a conversion exists, it will be supplied as an out parameter. + ' + 'The result is a widening conversion from Source to Target if such a conversion exists. + 'Otherwise the result is a narrowing conversion if such a conversion exists. Otherwise + 'no conversion is possible. We perform this two pass process because the conversion + '"path" is not affected by the user implicitly or explicitly specifying the conversion. + ' + 'In other words, a safe (widening) conversion is always taken regardless of whether + 'Option Strict is on or off. + + Debug.Assert(ClassifyPredefinedConversion(TargetType, SourceType) = ConversionClass.None, _ + "predefined conversion is possible, so why try user-defined?") + + OperatorMethod = Nothing + + Dim OperatorSet As List(Of Method) = _ + CollectConversionOperators( _ + TargetType, _ + SourceType, _ + FoundTargetTypeOperators, _ + FoundSourceTypeOperators) + + If OperatorSet.Count = 0 Then + 'No conversion operators, so no conversion is possible. + Return ConversionClass.None + End If + + Dim ResolutionIsAmbiguous As Boolean = False + + Dim OperatorChoices As List(Of Method) = _ + ResolveConversion( _ + TargetType, _ + SourceType, _ + OperatorSet, _ + True, _ + ResolutionIsAmbiguous) + + If OperatorChoices.Count = 1 Then + OperatorMethod = OperatorChoices.Item(0) + OperatorMethod.ArgumentsValidated = True + 'The result from the first pass is necessarily widening. + Return ConversionClass.Widening + + ElseIf OperatorChoices.Count = 0 AndAlso Not ResolutionIsAmbiguous Then + + Debug.Assert(OperatorSet.Count > 0, "expected operators") + + 'Second pass: if the first pass failed, attempt to find a conversion + 'considering BOTH widening and narrowing. + + OperatorChoices = _ + ResolveConversion( _ + TargetType, _ + SourceType, _ + OperatorSet, _ + False, _ + ResolutionIsAmbiguous) + + If OperatorChoices.Count = 1 Then + OperatorMethod = OperatorChoices.Item(0) + OperatorMethod.ArgumentsValidated = True + 'The result from the second pass is necessarily narrowing. + Return ConversionClass.Narrowing + + ElseIf OperatorChoices.Count = 0 Then + 'No conversion possible. + Return ConversionClass.None + + End If + + End If + + 'CONSIDER: If error reporting is improved for conversion resolution, + ' create a useful error message here. + 'Conversion is ambiguous. + Return ConversionClass.Ambiguous + + End Function + + + End Class + + Friend Class OperatorCaches + ' Prevent creation. + Private Sub New() + End Sub + + Friend NotInheritable Class FixedList + + Private Structure Entry + Friend TargetType As Type + Friend SourceType As Type + Friend Classification As ConversionClass + Friend OperatorMethod As Method + Friend [Next] As Integer + Friend Previous As Integer + End Structure + + Private ReadOnly m_List As Entry() + Private ReadOnly m_Size As Integer + Private m_First As Integer + Private m_Last As Integer + Private m_Count As Integer + + Private Const DefaultSize As Integer = 50 + + Friend Sub New() + MyClass.New(DefaultSize) + End Sub + + Friend Sub New(ByVal Size As Integer) + 'Populate the cache list with the maximum number of entires. + 'This simplifies the insertion code for a small upfront cost. + m_Size = Size + + m_List = New Entry(m_Size - 1) {} + For Index As Integer = 0 To m_Size - 2 + m_List(Index).Next = Index + 1 + Next + For Index As Integer = m_Size - 1 To 1 Step -1 + m_List(Index).Previous = Index - 1 + Next + m_List(0).Previous = m_Size - 1 + m_Last = m_Size - 1 + End Sub + + Private Sub MoveToFront(ByVal Item As Integer) + 'Remove Item from its position in the list and move it to the front. + If Item = m_First Then Return + + Dim [Next] As Integer = m_List(Item).Next + Dim Previous As Integer = m_List(Item).Previous + + m_List(Previous).Next = [Next] + m_List([Next]).Previous = Previous + + m_List(m_First).Previous = Item + m_List(m_Last).Next = Item + + m_List(Item).Next = m_First + m_List(Item).Previous = m_Last + + m_First = Item + End Sub + + Friend Sub Insert( _ + ByVal TargetType As Type, _ + ByVal SourceType As Type, _ + ByVal Classification As ConversionClass, _ + ByVal OperatorMethod As Method) + + If m_Count < m_Size Then m_Count += 1 + + 'Replace the least used conversion in the list with a new conversion, and move + 'that entry to the front. + + Dim Item As Integer = m_Last + m_First = Item + m_Last = m_List(m_Last).Previous + + m_List(Item).TargetType = TargetType + m_List(Item).SourceType = SourceType + m_List(Item).Classification = Classification + m_List(Item).OperatorMethod = OperatorMethod + End Sub + + Friend Function Lookup( _ + ByVal TargetType As Type, _ + ByVal SourceType As Type, _ + ByRef Classification As ConversionClass, _ + ByRef OperatorMethod As Method) As Boolean + + Dim Item As Integer = m_First + Dim Iteration As Integer = 0 + + Do While Iteration < m_Count + If TargetType Is m_List(Item).TargetType AndAlso SourceType Is m_List(Item).SourceType Then + Classification = m_List(Item).Classification + OperatorMethod = m_List(Item).OperatorMethod + MoveToFront(Item) + Return True + End If + Item = m_List(Item).Next + Iteration += 1 + Loop + + Classification = ConversionClass.Bad + OperatorMethod = Nothing + Return False + End Function + + End Class + + Friend NotInheritable Class FixedExistanceList + + Private Structure Entry + Friend Type As Type + Friend [Next] As Integer + Friend Previous As Integer + End Structure + + Private ReadOnly m_List As Entry() + Private ReadOnly m_Size As Integer + Private m_First As Integer + Private m_Last As Integer + Private m_Count As Integer + + Private Const DefaultSize As Integer = 50 + + Friend Sub New() + MyClass.New(DefaultSize) + End Sub + + Friend Sub New(ByVal Size As Integer) + 'Populate the list with the maximum number of entires. + 'This simplifies the insertion code for a small upfront cost. + m_Size = Size + + m_List = New Entry(m_Size - 1) {} + For Index As Integer = 0 To m_Size - 2 + m_List(Index).Next = Index + 1 + Next + For Index As Integer = m_Size - 1 To 1 Step -1 + m_List(Index).Previous = Index - 1 + Next + m_List(0).Previous = m_Size - 1 + m_Last = m_Size - 1 + End Sub + + Private Sub MoveToFront(ByVal Item As Integer) + 'Remove Item from its position in the list and move it to the front. + If Item = m_First Then Return + + Dim [Next] As Integer = m_List(Item).Next + Dim Previous As Integer = m_List(Item).Previous + + m_List(Previous).Next = [Next] + m_List([Next]).Previous = Previous + + m_List(m_First).Previous = Item + m_List(m_Last).Next = Item + + m_List(Item).Next = m_First + m_List(Item).Previous = m_Last + + m_First = Item + End Sub + + Friend Sub Insert(ByVal Type As Type) + + If m_Count < m_Size Then m_Count += 1 + + 'Replace the least used conversion in the cache with a new conversion, and move + 'that entry to the front. + + Dim Item As Integer = m_Last + m_First = Item + m_Last = m_List(m_Last).Previous + + m_List(Item).Type = Type + End Sub + + Friend Function Lookup(ByVal Type As Type) As Boolean + + Dim Item As Integer = m_First + Dim Iteration As Integer = 0 + + Do While Iteration < m_Count + If Type Is m_List(Item).Type Then + MoveToFront(Item) + Return True + End If + Item = m_List(Item).Next + Iteration += 1 + Loop + + Return False + End Function + + End Class + + Friend Shared ReadOnly ConversionCache As FixedList + Friend Shared ReadOnly UnconvertibleTypeCache As FixedExistanceList + + Shared Sub New() + ConversionCache = New FixedList + UnconvertibleTypeCache = New FixedExistanceList + End Sub + + End Class + +End Namespace diff --git a/Microsoft.VisualBasic/runtime/msvbalib/Helpers/Conversions.vb b/Microsoft.VisualBasic/runtime/msvbalib/Helpers/Conversions.vb new file mode 100644 index 000000000..67ea26605 --- /dev/null +++ b/Microsoft.VisualBasic/runtime/msvbalib/Helpers/Conversions.vb @@ -0,0 +1,2756 @@ +' Copyright (c) Microsoft Corporation. All rights reserved. + +Imports System +Imports System.Globalization +Imports System.Diagnostics +Imports System.Dynamic +Imports System.Reflection +Imports System.Security + +Imports Microsoft.VisualBasic.CompilerServices.Symbols +Imports Microsoft.VisualBasic.CompilerServices.ConversionResolution +Imports Microsoft.VisualBasic.CompilerServices.ExceptionUtils +Imports Microsoft.VisualBasic.CompilerServices.Utils + +Namespace Microsoft.VisualBasic.CompilerServices + +#If TELESTO Then + Public NotInheritable Class Conversions 'FIXME: +#Else + _ + Public NotInheritable Class Conversions +#End If + + Private Sub New() + End Sub + + Public Shared Function ToBoolean(ByVal Value As String) As Boolean + + If Value Is Nothing Then + 'For VB6 compatibility, treat Nothing as empty string. + Value = "" + End If + + Try + Dim loc As CultureInfo = GetCultureInfo() + + 'Use untrimmed Value to test for 'True'/'False' +#If TELESTO Then + If System.String.Compare(Value, Boolean.FalseString, loc, CompareOptions.IgnoreCase) = 0 Then +#Else + If System.String.Compare(Value, Boolean.FalseString, True, loc) = 0 Then +#End If + Return False +#If TELESTO Then + ElseIf System.String.Compare(Value, Boolean.TrueString, loc, CompareOptions.IgnoreCase) = 0 Then +#Else + ElseIf System.String.Compare(Value, Boolean.TrueString, True, loc) = 0 Then +#End If + Return True + End If + + Dim i64Value As Int64 + + If IsHexOrOctValue(Value, i64Value) Then + Return CBool(i64Value) + End If + + Return CBool(ParseDouble(Value)) + + Catch e As FormatException + Throw New InvalidCastException(GetResourceString(ResID.InvalidCast_FromStringTo, Left(Value, 32), "Boolean"), e) + End Try + + End Function + + Public Shared Function ToBoolean(ByVal Value As Object) As Boolean + + If Value Is Nothing Then + Return False + End If + + Dim ValueInterface As IConvertible + Dim ValueTypeCode As TypeCode + + ValueInterface = TryCast(Value, IConvertible) + + If ValueInterface IsNot Nothing Then + + ValueTypeCode = ValueInterface.GetTypeCode() + + Select Case ValueTypeCode + + Case TypeCode.Boolean + If TypeOf Value Is Boolean Then + Return CBool(DirectCast(Value, Boolean)) + Else + Return CBool(ValueInterface.ToBoolean(Nothing)) + End If + + Case TypeCode.SByte + If TypeOf Value Is SByte Then + Return CBool(DirectCast(Value, SByte)) + Else + Return CBool(ValueInterface.ToSByte(Nothing)) + End If + + Case TypeCode.Byte + If TypeOf Value Is Byte Then + Return CBool(DirectCast(Value, Byte)) + Else + Return CBool(ValueInterface.ToByte(Nothing)) + End If + + Case TypeCode.Int16 + If TypeOf Value Is Int16 Then + Return CBool(DirectCast(Value, Int16)) + Else + Return CBool(ValueInterface.ToInt16(Nothing)) + End If + + Case TypeCode.UInt16 + If TypeOf Value Is UInt16 Then + Return CBool(DirectCast(Value, UInt16)) + Else + Return CBool(ValueInterface.ToUInt16(Nothing)) + End If + + Case TypeCode.Int32 + If TypeOf Value Is Int32 Then + Return CBool(DirectCast(Value, Int32)) + Else + Return CBool(ValueInterface.ToInt32(Nothing)) + End If + + Case TypeCode.UInt32 + If TypeOf Value Is UInt32 Then + Return CBool(DirectCast(Value, UInt32)) + Else + Return CBool(ValueInterface.ToUInt32(Nothing)) + End If + + Case TypeCode.Int64 + If TypeOf Value Is Int64 Then + Return CBool(DirectCast(Value, Int64)) + Else + 'Using ToInt64 also handles enums + Return CBool(ValueInterface.ToInt64(Nothing)) + End If + + Case TypeCode.UInt64 + If TypeOf Value Is UInt64 Then + Return CBool(DirectCast(Value, UInt64)) + Else + Return CBool(ValueInterface.ToUInt64(Nothing)) + End If + + ' This case has been optimized for performance. Test any changes you make here + Case TypeCode.Decimal + If TypeOf Value Is Decimal Then + Return ValueInterface.ToBoolean(Nothing) + Else + Return CBool(ValueInterface.ToDecimal(Nothing)) + End If + + Case TypeCode.Single + If TypeOf Value Is Single Then + Return CBool(DirectCast(Value, Single)) + Else + Return CBool(ValueInterface.ToSingle(Nothing)) + End If + + Case TypeCode.Double + If TypeOf Value Is Double Then + Return CBool(DirectCast(Value, Double)) + Else + Return CBool(ValueInterface.ToDouble(Nothing)) + End If + + Case TypeCode.DateTime, _ + TypeCode.Char + ' Fall through to error + + Case TypeCode.String + Dim StringValue As String = TryCast(Value, String) + + If StringValue IsNot Nothing Then + Return CBool(StringValue) + Else + Return CBool(ValueInterface.ToString(Nothing)) + End If + + Case Else + ' Fall through to error + End Select + End If + + Throw New InvalidCastException(GetResourceString(ResID.InvalidCast_FromTo, VBFriendlyName(Value), "Boolean")) + End Function + + Public Shared Function ToByte(ByVal Value As String) As Byte + + If Value Is Nothing Then + Return 0 + End If + + Try + Dim i64Value As Int64 + + If IsHexOrOctValue(Value, i64Value) Then + Return CByte(i64Value) + End If + + Return CByte(ParseDouble(Value)) + + Catch e As FormatException + Throw New InvalidCastException(GetResourceString(ResID.InvalidCast_FromStringTo, Left(Value, 32), "Byte"), e) 'UNSIGNED: make these strings constants + End Try + + End Function + + Public Shared Function ToByte(ByVal Value As Object) As Byte + + If Value Is Nothing Then + Return 0 + End If + + Dim ValueInterface As IConvertible + Dim ValueTypeCode As TypeCode + + ValueInterface = TryCast(Value, IConvertible) + + If ValueInterface IsNot Nothing Then + + ValueTypeCode = ValueInterface.GetTypeCode() + + Select Case ValueTypeCode + + Case TypeCode.Boolean + 'REVIEW (VSW 395741): Would a try/catch be better here than doing the TypeOf check? When is the TypeOf check going to be False? + If TypeOf Value Is Boolean Then + Return CByte(DirectCast(Value, Boolean)) + Else + Return CByte(ValueInterface.ToBoolean(Nothing)) + End If + + Case TypeCode.SByte + If TypeOf Value Is SByte Then + Return CByte(DirectCast(Value, SByte)) + Else + Return CByte(ValueInterface.ToSByte(Nothing)) + End If + + Case TypeCode.Byte + If TypeOf Value Is Byte Then + Return CByte(DirectCast(Value, Byte)) + Else + Return CByte(ValueInterface.ToByte(Nothing)) + End If + + Case TypeCode.Int16 + If TypeOf Value Is Int16 Then + Return CByte(DirectCast(Value, Int16)) + Else + Return CByte(ValueInterface.ToInt16(Nothing)) + End If + + Case TypeCode.UInt16 + If TypeOf Value Is UInt16 Then + Return CByte(DirectCast(Value, UInt16)) + Else + Return CByte(ValueInterface.ToUInt16(Nothing)) + End If + + Case TypeCode.Int32 + If TypeOf Value Is Int32 Then + Return CByte(DirectCast(Value, Int32)) + Else + Return CByte(ValueInterface.ToInt32(Nothing)) + End If + + Case TypeCode.UInt32 + If TypeOf Value Is UInt32 Then + Return CByte(DirectCast(Value, UInt32)) + Else + Return CByte(ValueInterface.ToUInt32(Nothing)) + End If + + Case TypeCode.Int64 + If TypeOf Value Is Int64 Then + Return CByte(DirectCast(Value, Int64)) + Else + Return CByte(ValueInterface.ToInt64(Nothing)) + End If + + Case TypeCode.UInt64 + If TypeOf Value Is UInt64 Then + Return CByte(DirectCast(Value, UInt64)) + Else + Return CByte(ValueInterface.ToUInt64(Nothing)) + End If + + ' This case has been optimized for performance. Test any changes you make here + Case TypeCode.Decimal + If TypeOf Value Is Decimal Then + Return ValueInterface.ToByte(Nothing) + Else + Return CByte(ValueInterface.ToDecimal(Nothing)) + End If + + Case TypeCode.Single + If TypeOf Value Is Single Then + Return CByte(DirectCast(Value, Single)) + Else + Return CByte(ValueInterface.ToSingle(Nothing)) + End If + + Case TypeCode.Double + If TypeOf Value Is Double Then + Return CByte(DirectCast(Value, Double)) + Else + Return CByte(ValueInterface.ToDouble(Nothing)) + End If + + Case TypeCode.DateTime, _ + TypeCode.Char + ' Fall through to error + + Case TypeCode.String + Dim StringValue As String = TryCast(Value, String) + + + If StringValue IsNot Nothing Then + Return CByte(StringValue) + Else + Return CByte(ValueInterface.ToString(Nothing)) + End If + + Case Else + ' Fall through to error + End Select + End If + + Throw New InvalidCastException(GetResourceString(ResID.InvalidCast_FromTo, VBFriendlyName(Value), "Byte")) + + End Function + + _ + Public Shared Function ToSByte(ByVal Value As String) As SByte + + If Value Is Nothing Then + Return 0 + End If + + Try + Dim i64Value As Int64 + + If IsHexOrOctValue(Value, i64Value) Then + Return CSByte(i64Value) + End If + + Return CSByte(ParseDouble(Value)) + + Catch e As FormatException + Throw New InvalidCastException(GetResourceString(ResID.InvalidCast_FromStringTo, Left(Value, 32), "SByte"), e) + End Try + + End Function + + _ + Public Shared Function ToSByte(ByVal Value As Object) As SByte + + If Value Is Nothing Then + Return 0 + End If + + Dim ValueInterface As IConvertible + Dim ValueTypeCode As TypeCode + + ValueInterface = TryCast(Value, IConvertible) + + If ValueInterface Is Nothing Then + GoTo ThrowInvalidCast + End If + + ValueTypeCode = ValueInterface.GetTypeCode() + + Select Case ValueTypeCode + + Case TypeCode.Boolean + If TypeOf Value Is Boolean Then + Return CSByte(DirectCast(Value, Boolean)) + Else + Return CSByte(ValueInterface.ToBoolean(Nothing)) + End If + + Case TypeCode.SByte + If TypeOf Value Is SByte Then + Return CSByte(DirectCast(Value, SByte)) + Else + Return CSByte(ValueInterface.ToSByte(Nothing)) + End If + + Case TypeCode.Byte + If TypeOf Value Is Byte Then + Return CSByte(DirectCast(Value, Byte)) + Else + Return CSByte(ValueInterface.ToByte(Nothing)) + End If + + Case TypeCode.Int16 + If TypeOf Value Is Int16 Then + Return CSByte(DirectCast(Value, Int16)) + Else + Return CSByte(ValueInterface.ToInt16(Nothing)) + End If + + Case TypeCode.UInt16 + If TypeOf Value Is UInt16 Then + Return CSByte(DirectCast(Value, UInt16)) + Else + Return CSByte(ValueInterface.ToUInt16(Nothing)) + End If + + Case TypeCode.Int32 + If TypeOf Value Is Int32 Then + Return CSByte(DirectCast(Value, Int32)) + Else + Return CSByte(ValueInterface.ToInt32(Nothing)) + End If + + Case TypeCode.UInt32 + If TypeOf Value Is UInt32 Then + Return CSByte(DirectCast(Value, UInt32)) + Else + Return CSByte(ValueInterface.ToUInt32(Nothing)) + End If + + Case TypeCode.Int64 + If TypeOf Value Is Int64 Then + Return CSByte(DirectCast(Value, Int64)) + Else + Return CSByte(ValueInterface.ToInt64(Nothing)) + End If + + Case TypeCode.UInt64 + If TypeOf Value Is UInt64 Then + Return CSByte(DirectCast(Value, UInt64)) + Else + Return CSByte(ValueInterface.ToUInt64(Nothing)) + End If + + ' This case has been optimized for performance. Test any changes you make here + Case TypeCode.Decimal + If TypeOf Value Is Decimal Then + Return ValueInterface.ToSByte(Nothing) + Else + Return CSByte(ValueInterface.ToDecimal(Nothing)) + End If + + Case TypeCode.Single + If TypeOf Value Is Single Then + Return CSByte(DirectCast(Value, Single)) + Else + Return CSByte(ValueInterface.ToSingle(Nothing)) + End If + + Case TypeCode.Double + If TypeOf Value Is Double Then + Return CSByte(DirectCast(Value, Double)) + Else + Return CSByte(ValueInterface.ToDouble(Nothing)) + End If + + Case TypeCode.DateTime, _ + TypeCode.Char + ' Fall through to error + + Case TypeCode.String + Dim StringValue As String = TryCast(Value, String) + + If StringValue IsNot Nothing Then + Return CSByte(StringValue) + Else + Return CSByte(ValueInterface.ToString(Nothing)) + End If + + Case Else + ' Fall through to error + End Select + +ThrowInvalidCast: + Throw New InvalidCastException(GetResourceString(ResID.InvalidCast_FromTo, VBFriendlyName(Value), "SByte")) + + End Function + + Public Shared Function ToShort(ByVal Value As String) As Short + + If Value Is Nothing Then + Return 0 + End If + + Try + Dim i64Value As Int64 + + If IsHexOrOctValue(Value, i64Value) Then + Return CShort(i64Value) + End If + + Return CShort(ParseDouble(Value)) + + Catch e As FormatException + Throw New InvalidCastException(GetResourceString(ResID.InvalidCast_FromStringTo, Left(Value, 32), "Short"), e) + End Try + + End Function + + Public Shared Function ToShort(ByVal Value As Object) As Short + + If Value Is Nothing Then + Return 0 + End If + + Dim ValueInterface As IConvertible + Dim ValueTypeCode As TypeCode + + + ValueInterface = TryCast(Value, IConvertible) + + If ValueInterface IsNot Nothing Then + + ValueTypeCode = ValueInterface.GetTypeCode() + + Select Case ValueTypeCode + + Case TypeCode.Boolean + If TypeOf Value Is Boolean Then + Return CShort(DirectCast(Value, Boolean)) + Else + Return CShort(ValueInterface.ToBoolean(Nothing)) + End If + + Case TypeCode.SByte + If TypeOf Value Is SByte Then + Return CShort(DirectCast(Value, SByte)) + Else + Return CShort(ValueInterface.ToSByte(Nothing)) + End If + + Case TypeCode.Byte + If TypeOf Value Is Byte Then + Return CShort(DirectCast(Value, Byte)) + Else + Return CShort(ValueInterface.ToByte(Nothing)) + End If + + Case TypeCode.Int16 + If TypeOf Value Is Int16 Then + Return CShort(DirectCast(Value, Int16)) + Else + Return CShort(ValueInterface.ToInt16(Nothing)) + End If + + Case TypeCode.UInt16 + If TypeOf Value Is UInt16 Then + Return CShort(DirectCast(Value, UInt16)) + Else + Return CShort(ValueInterface.ToUInt16(Nothing)) + End If + + Case TypeCode.Int32 + If TypeOf Value Is Int32 Then + Return CShort(DirectCast(Value, Int32)) + Else + Return CShort(ValueInterface.ToInt32(Nothing)) + End If + + Case TypeCode.UInt32 + If TypeOf Value Is UInt32 Then + Return CShort(DirectCast(Value, UInt32)) + Else + Return CShort(ValueInterface.ToUInt32(Nothing)) + End If + + Case TypeCode.Int64 + If TypeOf Value Is Int64 Then + Return CShort(DirectCast(Value, Int64)) + Else + Return CShort(ValueInterface.ToInt64(Nothing)) + End If + + Case TypeCode.UInt64 + If TypeOf Value Is UInt64 Then + Return CShort(DirectCast(Value, UInt64)) + Else + Return CShort(ValueInterface.ToUInt64(Nothing)) + End If + + ' This case has been optimized for performance. Test any changes you make here + Case TypeCode.Decimal + If TypeOf Value Is Decimal Then + Return ValueInterface.ToInt16(Nothing) + Else + Return CShort(ValueInterface.ToDecimal(Nothing)) + End If + + Case TypeCode.Single + If TypeOf Value Is Single Then + Return CShort(DirectCast(Value, Single)) + Else + Return CShort(ValueInterface.ToSingle(Nothing)) + End If + + Case TypeCode.Double + If TypeOf Value Is Double Then + Return CShort(DirectCast(Value, Double)) + Else + Return CShort(ValueInterface.ToDouble(Nothing)) + End If + + Case TypeCode.DateTime, _ + TypeCode.Char + ' Fall through to error + + Case TypeCode.String + Dim StringValue As String = TryCast(Value, String) + + If StringValue IsNot Nothing Then + Return CShort(StringValue) + Else + Return CShort(ValueInterface.ToString(Nothing)) + End If + + Case Else + ' Fall through to error + End Select + End If + + Throw New InvalidCastException(GetResourceString(ResID.InvalidCast_FromTo, VBFriendlyName(Value), "Short")) + + End Function + + _ + Public Shared Function ToUShort(ByVal Value As String) As UShort + + If Value Is Nothing Then + Return 0 + End If + + Try + Dim i64Value As Int64 + + If IsHexOrOctValue(Value, i64Value) Then + Return CUShort(i64Value) + End If + + Return CUShort(ParseDouble(Value)) + + Catch e As FormatException + Throw New InvalidCastException(GetResourceString(ResID.InvalidCast_FromStringTo, Left(Value, 32), "UShort"), e) + End Try + + End Function + + _ + Public Shared Function ToUShort(ByVal Value As Object) As UShort + + If Value Is Nothing Then + Return 0 + End If + + Dim ValueInterface As IConvertible + Dim ValueTypeCode As TypeCode + + ValueInterface = TryCast(Value, IConvertible) + + If ValueInterface IsNot Nothing Then + + ValueTypeCode = ValueInterface.GetTypeCode() + + Select Case ValueTypeCode + + Case TypeCode.Boolean + If TypeOf Value Is Boolean Then + Return CUShort(DirectCast(Value, Boolean)) + Else + Return CUShort(ValueInterface.ToBoolean(Nothing)) + End If + + Case TypeCode.SByte + If TypeOf Value Is SByte Then + Return CUShort(DirectCast(Value, SByte)) + Else + Return CUShort(ValueInterface.ToSByte(Nothing)) + End If + + Case TypeCode.Byte + If TypeOf Value Is Byte Then + Return CUShort(DirectCast(Value, Byte)) + Else + Return CUShort(ValueInterface.ToByte(Nothing)) + End If + + Case TypeCode.Int16 + If TypeOf Value Is Int16 Then + Return CUShort(DirectCast(Value, Int16)) + Else + Return CUShort(ValueInterface.ToInt16(Nothing)) + End If + + Case TypeCode.UInt16 + If TypeOf Value Is UInt16 Then + Return CUShort(DirectCast(Value, UInt16)) + Else + Return CUShort(ValueInterface.ToUInt16(Nothing)) + End If + + Case TypeCode.Int32 + If TypeOf Value Is Int32 Then + Return CUShort(DirectCast(Value, Int32)) + Else + Return CUShort(ValueInterface.ToInt32(Nothing)) + End If + + Case TypeCode.UInt32 + If TypeOf Value Is UInt32 Then + Return CUShort(DirectCast(Value, UInt32)) + Else + Return CUShort(ValueInterface.ToUInt32(Nothing)) + End If + + Case TypeCode.Int64 + If TypeOf Value Is Int64 Then + Return CUShort(DirectCast(Value, Int64)) + Else + Return CUShort(ValueInterface.ToInt64(Nothing)) + End If + + Case TypeCode.UInt64 + If TypeOf Value Is UInt64 Then + Return CUShort(DirectCast(Value, UInt64)) + Else + Return CUShort(ValueInterface.ToUInt64(Nothing)) + End If + + ' This case has been optimized for performance. Test any changes you make here + Case TypeCode.Decimal + If TypeOf Value Is Decimal Then + Return ValueInterface.ToUInt16(Nothing) + Else + Return CUShort(ValueInterface.ToDecimal(Nothing)) + End If + + Case TypeCode.Single + If TypeOf Value Is Single Then + Return CUShort(DirectCast(Value, Single)) + Else + Return CUShort(ValueInterface.ToSingle(Nothing)) + End If + + Case TypeCode.Double + If TypeOf Value Is Double Then + Return CUShort(DirectCast(Value, Double)) + Else + Return CUShort(ValueInterface.ToDouble(Nothing)) + End If + + Case TypeCode.DateTime, _ + TypeCode.Char + ' Fall through to error + + Case TypeCode.String + Dim StringValue As String = TryCast(Value, String) + + If StringValue IsNot Nothing Then + Return CUShort(StringValue) + Else + Return CUShort(ValueInterface.ToString(Nothing)) + End If + + Case Else + ' Fall through to error + End Select + End If + + Throw New InvalidCastException(GetResourceString(ResID.InvalidCast_FromTo, VBFriendlyName(Value), "UShort")) + End Function + + Public Shared Function ToInteger(ByVal Value As String) As Integer + + If Value Is Nothing Then + Return 0 + End If + + Try + Dim i64Value As Int64 + + If IsHexOrOctValue(Value, i64Value) Then + Return CInt(i64Value) + End If + + Return CInt(ParseDouble(Value)) + + Catch e As FormatException + Throw New InvalidCastException(GetResourceString(ResID.InvalidCast_FromStringTo, Left(Value, 32), "Integer"), e) + End Try + + End Function + + Public Shared Function ToInteger(ByVal Value As Object) As Integer + + If Value Is Nothing Then + Return 0 + End If + + Dim ValueInterface As IConvertible + Dim ValueTypeCode As TypeCode + + ValueInterface = TryCast(Value, IConvertible) + + If ValueInterface IsNot Nothing Then + + ValueTypeCode = ValueInterface.GetTypeCode() + + Select Case ValueTypeCode + + Case TypeCode.Boolean + If TypeOf Value Is Boolean Then + Return CInt(DirectCast(Value, Boolean)) + Else + Return CInt(ValueInterface.ToBoolean(Nothing)) + End If + + Case TypeCode.SByte + If TypeOf Value Is SByte Then + Return CInt(DirectCast(Value, SByte)) + Else + Return CInt(ValueInterface.ToSByte(Nothing)) + End If + + Case TypeCode.Byte + If TypeOf Value Is Byte Then + Return CInt(DirectCast(Value, Byte)) + Else + Return CInt(ValueInterface.ToByte(Nothing)) + End If + + Case TypeCode.Int16 + If TypeOf Value Is Int16 Then + Return CInt(DirectCast(Value, Int16)) + Else + Return CInt(ValueInterface.ToInt16(Nothing)) + End If + + Case TypeCode.UInt16 + If TypeOf Value Is UInt16 Then + Return CInt(DirectCast(Value, UInt16)) + Else + Return CInt(ValueInterface.ToUInt16(Nothing)) + End If + + Case TypeCode.Int32 + If TypeOf Value Is Int32 Then + Return CInt(DirectCast(Value, Int32)) + Else + Return CInt(ValueInterface.ToInt32(Nothing)) + End If + + Case TypeCode.UInt32 + If TypeOf Value Is UInt32 Then + Return CInt(DirectCast(Value, UInt32)) + Else + Return CInt(ValueInterface.ToUInt32(Nothing)) + End If + + Case TypeCode.Int64 + If TypeOf Value Is Int64 Then + Return CInt(DirectCast(Value, Int64)) + Else + Return CInt(ValueInterface.ToInt64(Nothing)) + End If + + Case TypeCode.UInt64 + If TypeOf Value Is UInt64 Then + Return CInt(DirectCast(Value, UInt64)) + Else + Return CInt(ValueInterface.ToUInt64(Nothing)) + End If + + ' This case has been optimized for performance. Test any changes you make here + Case TypeCode.Decimal + If TypeOf Value Is Decimal Then + Return ValueInterface.ToInt32(Nothing) + Else + Return CInt(ValueInterface.ToDecimal(Nothing)) + End If + + Case TypeCode.Single + If TypeOf Value Is Single Then + Return CInt(DirectCast(Value, Single)) + Else + Return CInt(ValueInterface.ToSingle(Nothing)) + End If + + Case TypeCode.Double + If TypeOf Value Is Double Then + Return CInt(DirectCast(Value, Double)) + Else + Return CInt(ValueInterface.ToDouble(Nothing)) + End If + + Case TypeCode.DateTime, _ + TypeCode.Char + ' Fall through to error + + Case TypeCode.String + Dim StringValue As String = TryCast(Value, String) + + If StringValue IsNot Nothing Then + Return CInt(StringValue) + Else + Return CInt(ValueInterface.ToString(Nothing)) + End If + + Case Else + ' Fall through to error + End Select + End If + + Throw New InvalidCastException(GetResourceString(ResID.InvalidCast_FromTo, VBFriendlyName(Value), "Integer")) + End Function + + _ + Public Shared Function ToUInteger(ByVal Value As String) As UInteger + + If Value Is Nothing Then + Return 0 + End If + + Try + Dim i64Value As Int64 + + If IsHexOrOctValue(Value, i64Value) Then + Return CUInt(i64Value) + End If + + Return CUInt(ParseDouble(Value)) + + Catch e As FormatException + Throw New InvalidCastException(GetResourceString(ResID.InvalidCast_FromStringTo, Left(Value, 32), "UInteger"), e) + End Try + + End Function + + _ + Public Shared Function ToUInteger(ByVal Value As Object) As UInteger + + If Value Is Nothing Then + Return 0 + End If + + Dim ValueInterface As IConvertible + Dim ValueTypeCode As TypeCode + + ValueInterface = TryCast(Value, IConvertible) + + If ValueInterface IsNot Nothing Then + + ValueTypeCode = ValueInterface.GetTypeCode() + + Select Case ValueTypeCode + + Case TypeCode.Boolean + If TypeOf Value Is Boolean Then + Return CUInt(DirectCast(Value, Boolean)) + Else + Return CUInt(ValueInterface.ToBoolean(Nothing)) + End If + + Case TypeCode.SByte + If TypeOf Value Is SByte Then + Return CUInt(DirectCast(Value, SByte)) + Else + Return CUInt(ValueInterface.ToSByte(Nothing)) + End If + + Case TypeCode.Byte + If TypeOf Value Is Byte Then + Return CUInt(DirectCast(Value, Byte)) + Else + Return CUInt(ValueInterface.ToByte(Nothing)) + End If + + Case TypeCode.Int16 + If TypeOf Value Is Int16 Then + Return CUInt(DirectCast(Value, Int16)) + Else + Return CUInt(ValueInterface.ToInt16(Nothing)) + End If + + Case TypeCode.UInt16 + If TypeOf Value Is UInt16 Then + Return CUInt(DirectCast(Value, UInt16)) + Else + Return CUInt(ValueInterface.ToUInt16(Nothing)) + End If + + Case TypeCode.Int32 + If TypeOf Value Is Int32 Then + Return CUInt(DirectCast(Value, Int32)) + Else + Return CUInt(ValueInterface.ToInt32(Nothing)) + End If + + Case TypeCode.UInt32 + If TypeOf Value Is UInt32 Then + Return CUInt(DirectCast(Value, UInt32)) + Else + Return CUInt(ValueInterface.ToUInt32(Nothing)) + End If + + Case TypeCode.Int64 + If TypeOf Value Is Int64 Then + Return CUInt(DirectCast(Value, Int64)) + Else + Return CUInt(ValueInterface.ToInt64(Nothing)) + End If + + Case TypeCode.UInt64 + If TypeOf Value Is UInt64 Then + Return CUInt(DirectCast(Value, UInt64)) + Else + Return CUInt(ValueInterface.ToUInt64(Nothing)) + End If + + ' This case has been optimized for performance. Test any changes you make here + Case TypeCode.Decimal + If TypeOf Value Is Decimal Then + Return ValueInterface.ToUInt32(Nothing) + Else + Return CUInt(ValueInterface.ToDecimal(Nothing)) + End If + + Case TypeCode.Single + If TypeOf Value Is Single Then + Return CUInt(DirectCast(Value, Single)) + Else + Return CUInt(ValueInterface.ToSingle(Nothing)) + End If + + Case TypeCode.Double + If TypeOf Value Is Double Then + Return CUInt(DirectCast(Value, Double)) + Else + Return CUInt(ValueInterface.ToDouble(Nothing)) + End If + + Case TypeCode.DateTime, _ + TypeCode.Char + ' Fall through to error + + Case TypeCode.String + Dim StringValue As String = TryCast(Value, String) + + If StringValue IsNot Nothing Then + Return CUInt(StringValue) + Else + Return CUInt(ValueInterface.ToString(Nothing)) + End If + + Case Else + ' Fall through to error + End Select + End If + + Throw New InvalidCastException(GetResourceString(ResID.InvalidCast_FromTo, VBFriendlyName(Value), "UInteger")) + + End Function + + Public Shared Function ToLong(ByVal Value As String) As Long + + If (Value Is Nothing) Then + Return 0 + End If + + Try + Dim i64Value As Int64 + + If IsHexOrOctValue(Value, i64Value) Then + Return CLng(i64Value) + End If + + 'Using Decimal parse so that we full range of Int64 + ' and still get currency and thousands parsing + Return CLng(ParseDecimal(Value, Nothing)) + + + Catch e As FormatException + Throw New InvalidCastException(GetResourceString(ResID.InvalidCast_FromStringTo, Left(Value, 32), "Long"), e) + End Try + + End Function + + Public Shared Function ToLong(ByVal Value As Object) As Long + + If Value Is Nothing Then + Return 0 + End If + + Dim ValueInterface As IConvertible + Dim ValueTypeCode As TypeCode + + ValueInterface = TryCast(Value, IConvertible) + + If ValueInterface IsNot Nothing Then + + ValueTypeCode = ValueInterface.GetTypeCode() + + Select Case ValueTypeCode + + Case TypeCode.Boolean + If TypeOf Value Is Boolean Then + Return CLng(DirectCast(Value, Boolean)) + Else + Return CLng(ValueInterface.ToBoolean(Nothing)) + End If + + Case TypeCode.SByte + If TypeOf Value Is SByte Then + Return CLng(DirectCast(Value, SByte)) + Else + Return CLng(ValueInterface.ToSByte(Nothing)) + End If + + Case TypeCode.Byte + If TypeOf Value Is Byte Then + Return CLng(DirectCast(Value, Byte)) + Else + Return CLng(ValueInterface.ToByte(Nothing)) + End If + + Case TypeCode.Int16 + If TypeOf Value Is Int16 Then + Return CLng(DirectCast(Value, Int16)) + Else + Return CLng(ValueInterface.ToInt16(Nothing)) + End If + + Case TypeCode.UInt16 + If TypeOf Value Is UInt16 Then + Return CLng(DirectCast(Value, UInt16)) + Else + Return CLng(ValueInterface.ToUInt16(Nothing)) + End If + + Case TypeCode.Int32 + If TypeOf Value Is Int32 Then + Return CLng(DirectCast(Value, Int32)) + Else + Return CLng(ValueInterface.ToInt32(Nothing)) + End If + + Case TypeCode.UInt32 + If TypeOf Value Is UInt32 Then + Return CLng(DirectCast(Value, UInt32)) + Else + Return CLng(ValueInterface.ToUInt32(Nothing)) + End If + + Case TypeCode.Int64 + If TypeOf Value Is Int64 Then + Return CLng(DirectCast(Value, Int64)) + Else + Return CLng(ValueInterface.ToInt64(Nothing)) + End If + + Case TypeCode.UInt64 + If TypeOf Value Is UInt64 Then + Return CLng(DirectCast(Value, UInt64)) + Else + Return CLng(ValueInterface.ToUInt64(Nothing)) + End If + + ' This case has been optimized for performance. Test any changes you make here + Case TypeCode.Decimal + If TypeOf Value Is Decimal Then + Return ValueInterface.ToInt64(Nothing) + Else + Return CLng(ValueInterface.ToDecimal(Nothing)) + End If + + Case TypeCode.Single + If TypeOf Value Is Single Then + Return CLng(DirectCast(Value, Single)) + Else + Return CLng(ValueInterface.ToSingle(Nothing)) + End If + + Case TypeCode.Double + If TypeOf Value Is Double Then + Return CLng(DirectCast(Value, Double)) + Else + Return CLng(ValueInterface.ToDouble(Nothing)) + End If + + Case TypeCode.DateTime, _ + TypeCode.Char + ' Fall through to error + + Case TypeCode.String + Dim StringValue As String = TryCast(Value, String) + + If StringValue IsNot Nothing Then + Return CLng(StringValue) + Else + Return CLng(ValueInterface.ToString(Nothing)) + End If + + Case Else + ' Fall through to error + End Select + End If + + Throw New InvalidCastException(GetResourceString(ResID.InvalidCast_FromTo, VBFriendlyName(Value), "Long")) + End Function + + _ + Public Shared Function ToULong(ByVal Value As String) As ULong + + If (Value Is Nothing) Then + Return 0 + End If + + Try + Dim ui64Value As UInt64 + + If IsHexOrOctValue(Value, ui64Value) Then + Return CULng(ui64Value) + End If + + 'Using Decimal parse so that we full range of Int64 + ' and still get currency and thousands parsing + Return CULng(ParseDecimal(Value, Nothing)) + + Catch e As FormatException + Throw New InvalidCastException(GetResourceString(ResID.InvalidCast_FromStringTo, Left(Value, 32), "ULong"), e) + End Try + + End Function + + _ + Public Shared Function ToULong(ByVal Value As Object) As ULong + + If Value Is Nothing Then + Return 0 + End If + + Dim ValueInterface As IConvertible + Dim ValueTypeCode As TypeCode + + ValueInterface = TryCast(Value, IConvertible) + + If ValueInterface IsNot Nothing Then + + ValueTypeCode = ValueInterface.GetTypeCode() + + Select Case ValueTypeCode + + Case TypeCode.Boolean + If TypeOf Value Is Boolean Then + Return CULng(DirectCast(Value, Boolean)) + Else + Return CULng(ValueInterface.ToBoolean(Nothing)) + End If + + Case TypeCode.SByte + If TypeOf Value Is SByte Then + Return CULng(DirectCast(Value, SByte)) + Else + Return CULng(ValueInterface.ToSByte(Nothing)) + End If + + Case TypeCode.Byte + If TypeOf Value Is Byte Then + Return CULng(DirectCast(Value, Byte)) + Else + Return CULng(ValueInterface.ToByte(Nothing)) + End If + + Case TypeCode.Int16 + If TypeOf Value Is Int16 Then + Return CULng(DirectCast(Value, Int16)) + Else + Return CULng(ValueInterface.ToInt16(Nothing)) + End If + + Case TypeCode.UInt16 + If TypeOf Value Is UInt16 Then + Return CULng(DirectCast(Value, UInt16)) + Else + Return CULng(ValueInterface.ToUInt16(Nothing)) + End If + + Case TypeCode.Int32 + If TypeOf Value Is Int32 Then + Return CULng(DirectCast(Value, Int32)) + Else + Return CULng(ValueInterface.ToInt32(Nothing)) + End If + + Case TypeCode.UInt32 + If TypeOf Value Is UInt32 Then + Return CULng(DirectCast(Value, UInt32)) + Else + Return CULng(ValueInterface.ToUInt32(Nothing)) + End If + + Case TypeCode.Int64 + If TypeOf Value Is Int64 Then + Return CULng(DirectCast(Value, Int64)) + Else + Return CULng(ValueInterface.ToInt64(Nothing)) + End If + + Case TypeCode.UInt64 + If TypeOf Value Is UInt64 Then + Return CULng(DirectCast(Value, UInt64)) + Else + Return CULng(ValueInterface.ToUInt64(Nothing)) + End If + + ' This case has been optimized for performance. Test any changes you make here + Case TypeCode.Decimal + If TypeOf Value Is Decimal Then + Return ValueInterface.ToUInt64(Nothing) + Else + Return CULng(ValueInterface.ToDecimal(Nothing)) + End If + + Case TypeCode.Single + If TypeOf Value Is Single Then + Return CULng(DirectCast(Value, Single)) + Else + Return CULng(ValueInterface.ToSingle(Nothing)) + End If + + Case TypeCode.Double + If TypeOf Value Is Double Then + Return CULng(DirectCast(Value, Double)) + Else + Return CULng(ValueInterface.ToDouble(Nothing)) + End If + + Case TypeCode.DateTime, _ + TypeCode.Char + ' Fall through to error + + Case TypeCode.String + Dim StringValue As String = TryCast(Value, String) + + If StringValue IsNot Nothing Then + Return CULng(StringValue) + Else + Return CULng(ValueInterface.ToString(Nothing)) + End If + + Case Else + ' Fall through to error + End Select + End If + + Throw New InvalidCastException(GetResourceString(ResID.InvalidCast_FromTo, VBFriendlyName(Value), "ULong")) + End Function + + Public Shared Function ToDecimal(ByVal Value As Boolean) As Decimal + If Value Then + Return -1D + Else + Return 0D + End If + End Function + + Public Shared Function ToDecimal(ByVal Value As String) As Decimal + Return ToDecimal(Value, Nothing) + End Function + + Friend Shared Function ToDecimal(ByVal Value As String, ByVal NumberFormat As NumberFormatInfo) As Decimal + If Value Is Nothing Then + Return 0D + End If + + Try + Dim i64Value As Int64 + + If IsHexOrOctValue(Value, i64Value) Then + Return CDec(i64Value) + End If + + Return ParseDecimal(Value, NumberFormat) + + Catch e1 As OverflowException + Throw VbMakeException(vbErrors.Overflow) + Catch e2 As FormatException + Throw New InvalidCastException(GetResourceString(ResID.InvalidCast_FromStringTo, Left(Value, 32), "Decimal")) + End Try + End Function + + Public Shared Function ToDecimal(ByVal Value As Object) As Decimal + Return ToDecimal(Value, Nothing) + End Function + + Friend Shared Function ToDecimal(ByVal Value As Object, ByVal NumberFormat As NumberFormatInfo) As Decimal + + If Value Is Nothing Then + Return 0D + End If + + Dim ValueInterface As IConvertible + Dim ValueTypeCode As TypeCode + + ValueInterface = TryCast(Value, IConvertible) + + If ValueInterface IsNot Nothing Then + + ValueTypeCode = ValueInterface.GetTypeCode() + + Select Case ValueTypeCode + + Case TypeCode.Boolean + If TypeOf Value Is Boolean Then + Return CDec(DirectCast(Value, Boolean)) + Else + Return CDec(ValueInterface.ToBoolean(Nothing)) + End If + + Case TypeCode.SByte + If TypeOf Value Is SByte Then + Return CDec(DirectCast(Value, SByte)) + Else + Return CDec(ValueInterface.ToSByte(Nothing)) + End If + + Case TypeCode.Byte + If TypeOf Value Is Byte Then + Return CDec(DirectCast(Value, Byte)) + Else + Return CDec(ValueInterface.ToByte(Nothing)) + End If + + Case TypeCode.Int16 + If TypeOf Value Is Int16 Then + Return CDec(DirectCast(Value, Int16)) + Else + Return CDec(ValueInterface.ToInt16(Nothing)) + End If + + Case TypeCode.UInt16 + If TypeOf Value Is UInt16 Then + Return CDec(DirectCast(Value, UInt16)) + Else + Return CDec(ValueInterface.ToUInt16(Nothing)) + End If + + Case TypeCode.Int32 + If TypeOf Value Is Int32 Then + Return CDec(DirectCast(Value, Int32)) + Else + Return CDec(ValueInterface.ToInt32(Nothing)) + End If + + Case TypeCode.UInt32 + If TypeOf Value Is UInt32 Then + Return CDec(DirectCast(Value, UInt32)) + Else + Return CDec(ValueInterface.ToUInt32(Nothing)) + End If + + Case TypeCode.Int64 + If TypeOf Value Is Int64 Then + Return CDec(DirectCast(Value, Int64)) + Else + Return CDec(ValueInterface.ToInt64(Nothing)) + End If + + Case TypeCode.UInt64 + If TypeOf Value Is UInt64 Then + Return CDec(DirectCast(Value, UInt64)) + Else + Return CDec(ValueInterface.ToUInt64(Nothing)) + End If + + ' This case has been optimized for performance. Test any changes you make here + Case TypeCode.Decimal + Return ValueInterface.ToDecimal(Nothing) + + Case TypeCode.Single + If TypeOf Value Is Single Then + Return CDec(DirectCast(Value, Single)) + Else + Return CDec(ValueInterface.ToSingle(Nothing)) + End If + + Case TypeCode.Double + If TypeOf Value Is Double Then + Return CDec(DirectCast(Value, Double)) + Else + Return CDec(ValueInterface.ToDouble(Nothing)) + End If + + Case TypeCode.DateTime, _ + TypeCode.Char + ' Fall through to error + + Case TypeCode.String + Return ToDecimal(ValueInterface.ToString(Nothing), NumberFormat) + + Case Else + ' Fall through to error + End Select + End If + + Throw New InvalidCastException(GetResourceString(ResID.InvalidCast_FromTo, VBFriendlyName(Value), "Decimal")) + End Function + + Private Shared Function ParseDecimal(ByVal Value As String, ByVal NumberFormat As NumberFormatInfo) As Decimal + Dim NormalizedNumberFormat As NumberFormatInfo + Dim culture As CultureInfo = GetCultureInfo() + + If NumberFormat Is Nothing Then + NumberFormat = culture.NumberFormat + End If + + ' Normalize number format settings to enable us to first use the numeric settings for both currency and number parsing + ' compatible with VB6 + NormalizedNumberFormat = GetNormalizedNumberFormat(NumberFormat) + + Const flags As NumberStyles = _ + NumberStyles.AllowDecimalPoint Or _ + NumberStyles.AllowExponent Or _ + NumberStyles.AllowLeadingSign Or _ + NumberStyles.AllowLeadingWhite Or _ + NumberStyles.AllowThousands Or _ + NumberStyles.AllowTrailingSign Or _ + NumberStyles.AllowParentheses Or _ + NumberStyles.AllowTrailingWhite Or _ + NumberStyles.AllowCurrencySymbol + + Value = ToHalfwidthNumbers(Value, culture) + + Try + ' Use numeric settings to parse + Return System.Decimal.Parse(Value, flags, NormalizedNumberFormat) + Catch FormatEx As FormatException When Not (NumberFormat Is NormalizedNumberFormat) + ' Use currency settings to parse + Return System.Decimal.Parse(Value, flags, NumberFormat) + Catch Ex As Exception + Throw Ex + End Try + + End Function + + Private Shared Function GetNormalizedNumberFormat(ByVal InNumberFormat As NumberFormatInfo) As NumberFormatInfo + ' This method returns a NumberFormat with the relevant Currency Settings to be the same as the Number Settings + ' In - NumberFormat to be normalized - this is not changed by this Method + ' Return - Normalized NumberFormat + + Dim OutNumberFormat As NumberFormatInfo + + With InNumberFormat + If (Not .CurrencyDecimalSeparator Is Nothing) AndAlso _ + (Not .NumberDecimalSeparator Is Nothing) AndAlso _ + (Not .CurrencyGroupSeparator Is Nothing) AndAlso _ + (Not .NumberGroupSeparator Is Nothing) AndAlso _ + (.CurrencyDecimalSeparator.Length = 1) AndAlso _ + (.NumberDecimalSeparator.Length = 1) AndAlso _ + (.CurrencyGroupSeparator.Length = 1) AndAlso _ + (.NumberGroupSeparator.Length = 1) AndAlso _ + (.CurrencyDecimalSeparator.Chars(0) = .NumberDecimalSeparator.Chars(0)) AndAlso _ + (.CurrencyGroupSeparator.Chars(0) = .NumberGroupSeparator.Chars(0)) AndAlso _ + (.CurrencyDecimalDigits = .NumberDecimalDigits) Then + Return InNumberFormat + End If + End With + + + With InNumberFormat + If (Not .CurrencyDecimalSeparator Is Nothing) AndAlso _ + (Not .NumberDecimalSeparator Is Nothing) AndAlso _ + (.CurrencyDecimalSeparator.Length = .NumberDecimalSeparator.Length) AndAlso _ + (Not .CurrencyGroupSeparator Is Nothing) AndAlso _ + (Not .NumberGroupSeparator Is Nothing) AndAlso _ + (.CurrencyGroupSeparator.Length = .NumberGroupSeparator.Length) Then + + Dim i As Integer + For i = 0 To .CurrencyDecimalSeparator.Length - 1 + If (.CurrencyDecimalSeparator.Chars(i) <> .NumberDecimalSeparator.Chars(i)) Then GoTo MisMatch + Next + + For i = 0 To .CurrencyGroupSeparator.Length - 1 + If (.CurrencyGroupSeparator.Chars(i) <> .NumberGroupSeparator.Chars(i)) Then GoTo MisMatch + Next + + Return InNumberFormat + End If + End With + +MisMatch: + + OutNumberFormat = DirectCast(InNumberFormat.Clone, NumberFormatInfo) + + ' Set the Currency Settings to be the Same as the Numeric Settings + With OutNumberFormat + .CurrencyDecimalSeparator = .NumberDecimalSeparator + .CurrencyGroupSeparator = .NumberGroupSeparator + .CurrencyDecimalDigits = .NumberDecimalDigits + End With + + Return OutNumberFormat + End Function + + Public Shared Function ToSingle(ByVal Value As String) As Single + Return ToSingle(Value, Nothing) + End Function + + Friend Shared Function ToSingle(ByVal Value As String, ByVal NumberFormat As NumberFormatInfo) As Single + + If Value Is Nothing Then + Return 0 + End If + + Try + Dim i64Value As Int64 + + If IsHexOrOctValue(Value, i64Value) Then + Return CSng(i64Value) + End If + + Dim Result As Double = ParseDouble(Value, NumberFormat) + If (Result < System.Single.MinValue OrElse Result > System.Single.MaxValue) AndAlso _ + Not System.Double.IsInfinity(Result) Then + Throw New OverflowException + End If + Return CSng(Result) + + Catch e As FormatException + Throw New InvalidCastException(GetResourceString(ResID.InvalidCast_FromStringTo, Left(Value, 32), "Single"), e) + End Try + + End Function + + Public Shared Function ToSingle(ByVal Value As Object) As Single + Return ToSingle(Value, Nothing) + End Function + + Friend Shared Function ToSingle(ByVal Value As Object, ByVal NumberFormat As NumberFormatInfo) As Single + + If Value Is Nothing Then + Return 0 + End If + + Dim ValueInterface As IConvertible + Dim ValueTypeCode As TypeCode + + + ValueInterface = TryCast(Value, IConvertible) + + If ValueInterface IsNot Nothing Then + + ValueTypeCode = ValueInterface.GetTypeCode() + + Select Case ValueTypeCode + + Case TypeCode.Boolean + If TypeOf Value Is Boolean Then + Return CSng(DirectCast(Value, Boolean)) + Else + Return CSng(ValueInterface.ToBoolean(Nothing)) + End If + + Case TypeCode.SByte + If TypeOf Value Is SByte Then + Return CSng(DirectCast(Value, SByte)) + Else + Return CSng(ValueInterface.ToSByte(Nothing)) + End If + + Case TypeCode.Byte + If TypeOf Value Is Byte Then + Return CSng(DirectCast(Value, Byte)) + Else + Return CSng(ValueInterface.ToByte(Nothing)) + End If + + Case TypeCode.Int16 + If TypeOf Value Is Int16 Then + Return CSng(DirectCast(Value, Int16)) + Else + Return CSng(ValueInterface.ToInt16(Nothing)) + End If + + Case TypeCode.UInt16 + If TypeOf Value Is UInt16 Then + Return CSng(DirectCast(Value, UInt16)) + Else + Return CSng(ValueInterface.ToUInt16(Nothing)) + End If + + Case TypeCode.Int32 + If TypeOf Value Is Int32 Then + Return CSng(DirectCast(Value, Int32)) + Else + Return CSng(ValueInterface.ToInt32(Nothing)) + End If + + Case TypeCode.UInt32 + If TypeOf Value Is UInt32 Then + Return CSng(DirectCast(Value, UInt32)) + Else + Return CSng(ValueInterface.ToUInt32(Nothing)) + End If + + Case TypeCode.Int64 + If TypeOf Value Is Int64 Then + Return CSng(DirectCast(Value, Int64)) + Else + Return CSng(ValueInterface.ToInt64(Nothing)) + End If + + Case TypeCode.UInt64 + If TypeOf Value Is UInt64 Then + Return CSng(DirectCast(Value, UInt64)) + Else + Return CSng(ValueInterface.ToUInt64(Nothing)) + End If + + ' This case has been optimized for performance. Test any changes you make here + Case TypeCode.Decimal + If TypeOf Value Is Decimal Then + Return ValueInterface.ToSingle(Nothing) + Else + Return CSng(ValueInterface.ToDecimal(Nothing)) + End If + + Case TypeCode.Single + If TypeOf Value Is Single Then + Return DirectCast(Value, Single) + Else + Return ValueInterface.ToSingle(Nothing) + End If + + Case TypeCode.Double + If TypeOf Value Is Double Then + Return CSng(DirectCast(Value, Double)) + Else + Return CSng(ValueInterface.ToDouble(Nothing)) + End If + + Case TypeCode.DateTime, _ + TypeCode.Char + ' Fall through to error + + Case TypeCode.String + Return ToSingle(ValueInterface.ToString(Nothing), NumberFormat) + + Case Else + ' Fall through to error + End Select + + End If + + Throw New InvalidCastException(GetResourceString(ResID.InvalidCast_FromTo, VBFriendlyName(Value), "Single")) + End Function + + Public Shared Function ToDouble(ByVal Value As String) As Double + Return ToDouble(Value, Nothing) + End Function + + Friend Shared Function ToDouble(ByVal Value As String, ByVal NumberFormat As NumberFormatInfo) As Double + + If Value Is Nothing Then + Return 0 + End If + + Try + Dim i64Value As Int64 + + If IsHexOrOctValue(Value, i64Value) Then + Return CDbl(i64Value) + End If + Return ParseDouble(Value, NumberFormat) + + Catch e As FormatException + Throw New InvalidCastException(GetResourceString(ResID.InvalidCast_FromStringTo, Left(Value, 32), "Double"), e) + End Try + + End Function + + Public Shared Function ToDouble(ByVal Value As Object) As Double + Return ToDouble(Value, Nothing) + End Function + + Friend Shared Function ToDouble(ByVal Value As Object, ByVal NumberFormat As NumberFormatInfo) As Double + + If Value Is Nothing Then + Return 0 + End If + + Dim ValueInterface As IConvertible + Dim ValueTypeCode As TypeCode + + ValueInterface = TryCast(Value, IConvertible) + + If ValueInterface IsNot Nothing Then + + ValueTypeCode = ValueInterface.GetTypeCode() + + Select Case ValueTypeCode + + Case TypeCode.Boolean + If TypeOf Value Is Boolean Then + Return CDbl(DirectCast(Value, Boolean)) + Else + Return CDbl(ValueInterface.ToBoolean(Nothing)) + End If + + Case TypeCode.SByte + If TypeOf Value Is SByte Then + Return CDbl(DirectCast(Value, SByte)) + Else + Return CDbl(ValueInterface.ToSByte(Nothing)) + End If + + Case TypeCode.Byte + If TypeOf Value Is Byte Then + Return CDbl(DirectCast(Value, Byte)) + Else + Return CDbl(ValueInterface.ToByte(Nothing)) + End If + + Case TypeCode.Int16 + If TypeOf Value Is Int16 Then + Return CDbl(DirectCast(Value, Int16)) + Else + Return CDbl(ValueInterface.ToInt16(Nothing)) + End If + + Case TypeCode.UInt16 + If TypeOf Value Is UInt16 Then + Return CDbl(DirectCast(Value, UInt16)) + Else + Return CDbl(ValueInterface.ToUInt16(Nothing)) + End If + + Case TypeCode.Int32 + If TypeOf Value Is Int32 Then + Return CDbl(DirectCast(Value, Int32)) + Else + Return CDbl(ValueInterface.ToInt32(Nothing)) + End If + + Case TypeCode.UInt32 + If TypeOf Value Is UInt32 Then + Return CDbl(DirectCast(Value, UInt32)) + Else + Return CDbl(ValueInterface.ToUInt32(Nothing)) + End If + + Case TypeCode.Int64 + If TypeOf Value Is Int64 Then + Return CDbl(DirectCast(Value, Int64)) + Else + Return CDbl(ValueInterface.ToInt64(Nothing)) + End If + + Case TypeCode.UInt64 + If TypeOf Value Is UInt64 Then + Return CDbl(DirectCast(Value, UInt64)) + Else + Return CDbl(ValueInterface.ToUInt64(Nothing)) + End If + + ' This case has been optimized for performance. Test any changes you make here + Case TypeCode.Decimal + If TypeOf Value Is Decimal Then + Return ValueInterface.ToDouble(Nothing) + Else + Return CDbl(ValueInterface.ToDecimal(Nothing)) + End If + + Case TypeCode.Single + If TypeOf Value Is Single Then + Return CDbl(DirectCast(Value, Single)) + Else + Return CDbl(ValueInterface.ToSingle(Nothing)) + End If + + Case TypeCode.Double + If TypeOf Value Is Double Then + Return CDbl(DirectCast(Value, Double)) + Else + Return CDbl(ValueInterface.ToDouble(Nothing)) + End If + + Case TypeCode.DateTime, _ + TypeCode.Char + ' Fall through to error + + Case TypeCode.String + Return ToDouble(ValueInterface.ToString(Nothing), NumberFormat) + + Case Else + ' Fall through to error + End Select + End If + + Throw New InvalidCastException(GetResourceString(ResID.InvalidCast_FromTo, VBFriendlyName(Value), "Double")) + + End Function + + Private Shared Function ParseDouble(ByVal Value As String) As Double + Return ParseDouble(Value, Nothing) + End Function + + Friend Shared Function TryParseDouble(ByVal Value As String, ByRef Result As Double) As Boolean + Dim NumberFormat As NumberFormatInfo + Dim NormalizedNumberFormat As NumberFormatInfo + Dim culture As CultureInfo = GetCultureInfo() + + NumberFormat = culture.NumberFormat + NormalizedNumberFormat = GetNormalizedNumberFormat(NumberFormat) + + Const flags As NumberStyles = _ + NumberStyles.AllowDecimalPoint Or _ + NumberStyles.AllowExponent Or _ + NumberStyles.AllowLeadingSign Or _ + NumberStyles.AllowLeadingWhite Or _ + NumberStyles.AllowThousands Or _ + NumberStyles.AllowTrailingSign Or _ + NumberStyles.AllowParentheses Or _ + NumberStyles.AllowTrailingWhite Or _ + NumberStyles.AllowCurrencySymbol + + Value = ToHalfwidthNumbers(Value, culture) + + ' The below code handles the 80% case efficiently and is inefficient only when the numeric and currency settings + ' are different + + If NumberFormat Is NormalizedNumberFormat Then + Return System.Double.TryParse(Value, flags, NormalizedNumberFormat, Result) + Else + Try + ' Use numeric settings to parse + ' Note that we use Parse instead of TryParse in order to distinguish whether the conversion failed + ' due to FormatException or other exception like OverFlowException, etc. + Result = System.Double.Parse(Value, flags, NormalizedNumberFormat) + Return True + Catch FormatEx As FormatException + ' Use currency settings to parse + Try + Return System.Double.TryParse(Value, flags, NumberFormat, Result) + Catch ex As ArgumentException + Return False + End Try + Catch ex As StackOverflowException + Throw ex + Catch ex As OutOfMemoryException + Throw ex + Catch ex As System.Threading.ThreadAbortException + Throw ex + Catch Ex As Exception + Return False + End Try + End If + + End Function + + Private Shared Function ParseDouble(ByVal Value As String, ByVal NumberFormat As NumberFormatInfo) As Double + Dim NormalizedNumberFormat As NumberFormatInfo + Dim culture As CultureInfo = GetCultureInfo() + + If NumberFormat Is Nothing Then + NumberFormat = culture.NumberFormat + End If + + ' Normalize number format settings to enable us to first use the numeric settings for both currency and number parsing + ' compatible with VB6 + NormalizedNumberFormat = GetNormalizedNumberFormat(NumberFormat) + + + Const flags As NumberStyles = _ + NumberStyles.AllowDecimalPoint Or _ + NumberStyles.AllowExponent Or _ + NumberStyles.AllowLeadingSign Or _ + NumberStyles.AllowLeadingWhite Or _ + NumberStyles.AllowThousands Or _ + NumberStyles.AllowTrailingSign Or _ + NumberStyles.AllowParentheses Or _ + NumberStyles.AllowTrailingWhite Or _ + NumberStyles.AllowCurrencySymbol + + + Value = ToHalfwidthNumbers(Value, culture) + + + Try + ' Use numeric settings to parse + Return System.Double.Parse(Value, flags, NormalizedNumberFormat) + Catch FormatEx As FormatException When Not (NumberFormat Is NormalizedNumberFormat) + ' Use currency settings to parse + Return System.Double.Parse(Value, flags, NumberFormat) + Catch Ex As Exception + Throw Ex + End Try + + End Function + + Public Shared Function ToDate(ByVal Value As String) As Date + Dim ParsedDate As System.DateTime + + If TryParseDate(Value, ParsedDate) Then + Return ParsedDate + Else + 'Truncate the string to 32 characters for the message + Throw New InvalidCastException(GetResourceString(ResID.InvalidCast_FromStringTo, Left(Value, 32), "Date")) + End If + End Function + + Public Shared Function ToDate(ByVal Value As Object) As Date + + If Value Is Nothing Then + Return Nothing + End If + + Dim ValueInterface As IConvertible + Dim ValueTypeCode As TypeCode + + ValueInterface = TryCast(Value, IConvertible) + + If ValueInterface IsNot Nothing Then + + ValueTypeCode = ValueInterface.GetTypeCode() + + Select Case ValueTypeCode + Case TypeCode.Boolean, _ + TypeCode.SByte, _ + TypeCode.Byte, _ + TypeCode.Int16, _ + TypeCode.UInt16, _ + TypeCode.Int32, _ + TypeCode.UInt32, _ + TypeCode.Int64, _ + TypeCode.UInt64, _ + TypeCode.Decimal, _ + TypeCode.Single, _ + TypeCode.Double, _ + TypeCode.Char + ' Fall through to error + + Case TypeCode.DateTime + If TypeOf Value Is DateTime Then + Return CDate(DirectCast(Value, DateTime)) + Else + Return CDate(ValueInterface.ToDateTime(Nothing)) + End If + + Case TypeCode.String + Dim StringValue As String = TryCast(Value, String) + + If StringValue IsNot Nothing Then + Return CDate(StringValue) + Else + Return CDate(ValueInterface.ToString(Nothing)) + End If + + Case Else + ' Fall through to error + End Select + + End If + + Throw New InvalidCastException(GetResourceString(ResID.InvalidCast_FromTo, VBFriendlyName(Value), "Date")) + End Function + + Friend Shared Function TryParseDate(ByVal Value As String, ByRef Result As System.DateTime) As Boolean + Const ParseStyle As DateTimeStyles = _ + DateTimeStyles.AllowWhiteSpaces Or _ + DateTimeStyles.NoCurrentDateDefault + Dim Culture As CultureInfo = GetCultureInfo() + Return System.DateTime.TryParse(ToHalfwidthNumbers(Value, Culture), Culture, ParseStyle, Result) + End Function + + Public Shared Function ToChar(ByVal Value As String) As Char + If (Value Is Nothing) OrElse (Value.Length = 0) Then + Return ControlChars.NullChar + End If + + Return Value.Chars(0) + End Function + + Public Shared Function ToChar(ByVal Value As Object) As Char + + If Value Is Nothing Then + Return ControlChars.NullChar + End If + + Dim ValueInterface As IConvertible + Dim ValueTypeCode As TypeCode + + ValueInterface = TryCast(Value, IConvertible) + + If ValueInterface IsNot Nothing Then + + ValueTypeCode = ValueInterface.GetTypeCode() + + Select Case ValueTypeCode + Case TypeCode.Boolean, _ + TypeCode.SByte, _ + TypeCode.Byte, _ + TypeCode.Int16, _ + TypeCode.UInt16, _ + TypeCode.Int32, _ + TypeCode.UInt32, _ + TypeCode.Int64, _ + TypeCode.UInt64, _ + TypeCode.Decimal, _ + TypeCode.Single, _ + TypeCode.Double, _ + TypeCode.DateTime + ' Fall through to error + + Case TypeCode.Char + If TypeOf Value Is Char Then + Return CChar(DirectCast(Value, Char)) + Else + Return ValueInterface.ToChar(Nothing) + End If + + Case TypeCode.String + Dim StringValue As String = TryCast(Value, String) + + If StringValue IsNot Nothing Then + Return CChar(StringValue) + Else + Return CChar(ValueInterface.ToString(Nothing)) + End If + + Case Else + ' Fall through to error + End Select + End If + + Throw New InvalidCastException(GetResourceString(ResID.InvalidCast_FromTo, VBFriendlyName(Value), "Char")) + End Function + + Public Shared Function ToCharArrayRankOne(ByVal Value As String) As Char() + + If Value Is Nothing Then + + Value = "" + + End If + + Return Value.ToCharArray() + + End Function + + Public Shared Function ToCharArrayRankOne(ByVal Value As Object) As Char() + + If Value Is Nothing Then + + Return "".ToCharArray() + + End If + + Dim ArrayValue As Char() = TryCast(Value, Char()) + + If ArrayValue IsNot Nothing AndAlso ArrayValue.Rank = 1 Then + + Return ArrayValue + + Else + Dim ValueInterface As IConvertible + + ValueInterface = TryCast(Value, IConvertible) + + If Not ValueInterface Is Nothing Then + + If (ValueInterface.GetTypeCode() = TypeCode.String) Then + Return ValueInterface.ToString(Nothing).ToCharArray() + End If + + End If + + End If + + Throw New InvalidCastException(GetResourceString(ResID.InvalidCast_FromTo, VBFriendlyName(Value), "Char()")) + + End Function + + Public Shared Shadows Function ToString(ByVal Value As Boolean) As String + If Value Then + Return System.Boolean.TrueString + Else + Return System.Boolean.FalseString + End If + End Function + + Public Shared Shadows Function ToString(ByVal Value As Byte) As String + Return Value.ToString(Nothing, Nothing) + End Function + + Public Shared Shadows Function ToString(ByVal Value As Char) As String + Return Value.ToString() + End Function + + Public Shared Function FromCharArray(ByVal Value() As Char) As String + ' This is a private function used from the debug windows (VS7 264234) + ' NOTE: This is now Public because we no longer import private members + ' from FX assemblies for performance reasons. + Return New String(Value) + End Function + + Public Shared Function FromCharAndCount(ByVal Value As Char, ByVal Count As Integer) As String + ' This is a private function used from the debug windows (VS7 264234) + ' NOTE: This is now Public because we no longer import private members + ' from FX assemblies for performance reasons. + Return New String(Value, Count) + End Function + + Public Shared Function FromCharArraySubset(ByVal Value() As Char, ByVal StartIndex As Integer, ByVal Length As Integer) As String + ' This is a private function used from the debug windows (VS7 264234) + ' NOTE: This is now Public because we no longer import private members + ' from FX assemblies for performance reasons. + Return New String(Value, StartIndex, Length) + End Function + + Public Shared Shadows Function ToString(ByVal Value As Short) As String + Return Value.ToString(Nothing, Nothing) + End Function + + Public Shared Shadows Function ToString(ByVal Value As Integer) As String + Return Value.ToString(Nothing, Nothing) + End Function + + _ + Public Shared Shadows Function ToString(ByVal Value As UInteger) As String 'REVIEW VSW#395745: are these needed here? Why not call ToString directly? Then we can keep from adding more useless helpers to the runtime + Return Value.ToString(Nothing, Nothing) + End Function + + Public Shared Shadows Function ToString(ByVal Value As Long) As String + Return Value.ToString(Nothing, Nothing) + End Function + + _ + Public Shared Shadows Function ToString(ByVal Value As ULong) As String + Return Value.ToString(Nothing, Nothing) + End Function + + Public Shared Shadows Function ToString(ByVal Value As Single) As String + Return ToString(Value, Nothing) + End Function + + Public Shared Shadows Function ToString(ByVal Value As Double) As String + Return ToString(Value, Nothing) + End Function + + + Public Shared Shadows Function ToString(ByVal Value As Single, ByVal NumberFormat As NumberFormatInfo) As String + Return Value.ToString(Nothing, NumberFormat) + End Function + + Public Shared Shadows Function ToString(ByVal Value As Double, ByVal NumberFormat As NumberFormatInfo) As String + Return Value.ToString("G", NumberFormat) + End Function + + Public Shared Shadows Function ToString(ByVal Value As Date) As String + Dim TimeTicks As Long = Value.TimeOfDay.Ticks + + If (TimeTicks = Value.Ticks) OrElse _ + (Value.Year = 1899 AndAlso Value.Month = 12 AndAlso Value.Day = 30) Then 'OA Date with no date is 1899-12-30 + 'No date (1/1/1) + 'VSW 395746: Can't change OA DATE now because of backwards compatibility. + Return Value.ToString("T", Nothing) + ElseIf TimeTicks = 0 Then + 'No time, or is midnight + Return Value.ToString("d", Nothing) + Else + Return Value.ToString("G", Nothing) + End If + End Function + + Public Shared Shadows Function ToString(ByVal Value As Decimal) As String + Return ToString(Value, Nothing) + End Function + + Public Shared Shadows Function ToString(ByVal Value As Decimal, ByVal NumberFormat As NumberFormatInfo) As String + Return Value.ToString("G", NumberFormat) + End Function + + Public Shared Shadows Function ToString(ByVal Value As Object) As String + + If Value Is Nothing Then + Return Nothing + + Else + Dim StringValue As String = TryCast(Value, String) + + If StringValue IsNot Nothing Then + Return StringValue + End If + End If + + Dim ValueInterface As IConvertible + Dim ValueTypeCode As TypeCode + + ValueInterface = TryCast(Value, IConvertible) + + If Not ValueInterface Is Nothing Then + + ValueTypeCode = ValueInterface.GetTypeCode() + + Select Case ValueTypeCode + Case TypeCode.Boolean + Return CStr(ValueInterface.ToBoolean(Nothing)) + + Case TypeCode.SByte + Return CStr(ValueInterface.ToSByte(Nothing)) + + Case TypeCode.Byte + Return CStr(ValueInterface.ToByte(Nothing)) + + Case TypeCode.Int16 + Return CStr(ValueInterface.ToInt16(Nothing)) + + Case TypeCode.UInt16 + Return CStr(ValueInterface.ToUInt16(Nothing)) + + Case TypeCode.Int32 + Return CStr(ValueInterface.ToInt32(Nothing)) + + Case TypeCode.UInt32 + Return CStr(ValueInterface.ToUInt32(Nothing)) + + Case TypeCode.Int64 + Return CStr(ValueInterface.ToInt64(Nothing)) + + Case TypeCode.UInt64 + Return CStr(ValueInterface.ToUInt64(Nothing)) + + Case TypeCode.Decimal + Return CStr(ValueInterface.ToDecimal(Nothing)) + + Case TypeCode.Single + Return CStr(ValueInterface.ToSingle(Nothing)) + + Case TypeCode.Double + Return CStr(ValueInterface.ToDouble(Nothing)) + + Case TypeCode.Char + Return CStr(ValueInterface.ToChar(Nothing)) + + Case TypeCode.DateTime + Return CStr(ValueInterface.ToDateTime(Nothing)) + + Case TypeCode.String + Return CStr(ValueInterface.ToString(Nothing)) + + Case Else + ' Fall through to error + End Select + + Else + Dim CharArray As Char() = TryCast(Value, Char()) + + If CharArray IsNot Nothing Then + Return New String(CharArray) + End If + End If + + Throw New InvalidCastException(GetResourceString(ResID.InvalidCast_FromTo, VBFriendlyName(Value), "String")) + + End Function + + Public Shared Function ToGenericParameter(Of T)(ByVal Value As Object) As T + + 'Think carefully about this function and how it handles Enums, + 'System.Enum, System.ValueType, Object, etc. This function should not + 'do a latebound conversion. + + If Value Is Nothing Then + Return Nothing + End If + + Dim ReflectedType As Type = GetType(T) + Dim tc As TypeCode = GetTypeCode(ReflectedType) + + Select Case tc + Case TypeCode.Boolean + Return DirectCast(CObj(CBool(Value)), T) + Case TypeCode.SByte + Return DirectCast(CObj(CSByte(Value)), T) + Case TypeCode.Byte + Return DirectCast(CObj(CByte(Value)), T) + Case TypeCode.Int16 + Return DirectCast(CObj(CShort(Value)), T) + Case TypeCode.UInt16 + Return DirectCast(CObj(CUShort(Value)), T) + Case TypeCode.Int32 + Return DirectCast(CObj(CInt(Value)), T) + Case TypeCode.UInt32 + Return DirectCast(CObj(CUInt(Value)), T) + Case TypeCode.Int64 + Return DirectCast(CObj(CLng(Value)), T) + Case TypeCode.UInt64 + Return DirectCast(CObj(CULng(Value)), T) + Case TypeCode.Decimal + Return DirectCast(CObj(CDec(Value)), T) + Case TypeCode.Single + Return DirectCast(CObj(CSng(Value)), T) + Case TypeCode.Double + Return DirectCast(CObj(CDbl(Value)), T) + Case TypeCode.DateTime + Return DirectCast(CObj(CDate(Value)), T) + Case TypeCode.Char + Return DirectCast(CObj(CChar(Value)), T) + Case TypeCode.String + Return DirectCast(CObj(CStr(Value)), T) + Case Else + Return DirectCast(Value, T) + End Select + End Function + + Private Shared Function CastSByteEnum(ByVal Expression As SByte, ByVal TargetType As Type) As Object + If IsEnum(TargetType) Then Return System.Enum.ToObject(TargetType, Expression) + Return Expression + End Function + + Private Shared Function CastByteEnum(ByVal Expression As Byte, ByVal TargetType As Type) As Object + If IsEnum(TargetType) Then Return System.Enum.ToObject(TargetType, Expression) + Return Expression + End Function + + Private Shared Function CastInt16Enum(ByVal Expression As Int16, ByVal TargetType As Type) As Object + If IsEnum(TargetType) Then Return System.Enum.ToObject(TargetType, Expression) + Return Expression + End Function + + Private Shared Function CastUInt16Enum(ByVal Expression As UInt16, ByVal TargetType As Type) As Object + If IsEnum(TargetType) Then Return System.Enum.ToObject(TargetType, Expression) + Return Expression + End Function + + Private Shared Function CastInt32Enum(ByVal Expression As Int32, ByVal TargetType As Type) As Object + If IsEnum(TargetType) Then Return System.Enum.ToObject(TargetType, Expression) + Return Expression + End Function + + Private Shared Function CastUInt32Enum(ByVal Expression As UInt32, ByVal TargetType As Type) As Object + If IsEnum(TargetType) Then Return System.Enum.ToObject(TargetType, Expression) + Return Expression + End Function + + Private Shared Function CastInt64Enum(ByVal Expression As Int64, ByVal TargetType As Type) As Object + If IsEnum(TargetType) Then Return System.Enum.ToObject(TargetType, Expression) + Return Expression + End Function + + Private Shared Function CastUInt64Enum(ByVal Expression As UInt64, ByVal TargetType As Type) As Object + If IsEnum(TargetType) Then Return System.Enum.ToObject(TargetType, Expression) + Return Expression + End Function + + Friend Shared Function ForceValueCopy(ByVal Expression As Object, ByVal TargetType As Type) As Object + 'CONSIDER: any way to get this faster? It's called every time we pass a valuetype to a byref parameter. + + Dim iconv As IConvertible = TryCast(Expression, IConvertible) + + If iconv Is Nothing Then + Return Expression + End If + + Debug.Assert(iconv.GetTypeCode = GetTypeCode(TargetType), "expected types to match") + + Select Case iconv.GetTypeCode() + + Case TypeCode.Boolean : Return iconv.ToBoolean(Nothing) + Case TypeCode.SByte : Return CastSByteEnum(iconv.ToSByte(Nothing), TargetType) + Case TypeCode.Byte : Return CastByteEnum(iconv.ToByte(Nothing), TargetType) + Case TypeCode.Int16 : Return CastInt16Enum(iconv.ToInt16(Nothing), TargetType) + Case TypeCode.UInt16 : Return CastUInt16Enum(iconv.ToUInt16(Nothing), TargetType) + Case TypeCode.Int32 : Return CastInt32Enum(iconv.ToInt32(Nothing), TargetType) + Case TypeCode.UInt32 : Return CastUInt32Enum(iconv.ToUInt32(Nothing), TargetType) + Case TypeCode.Int64 : Return CastInt64Enum(iconv.ToInt64(Nothing), TargetType) + Case TypeCode.UInt64 : Return CastUInt64Enum(iconv.ToUInt64(Nothing), TargetType) + Case TypeCode.Decimal : Return iconv.ToDecimal(Nothing) + Case TypeCode.Single : Return iconv.ToSingle(Nothing) + Case TypeCode.Double : Return iconv.ToDouble(Nothing) + Case TypeCode.DateTime : Return iconv.ToDateTime(Nothing) + Case TypeCode.Char : Return iconv.ToChar(Nothing) + + Case TypeCode.Empty +#If TELESTO Then + Debug.Assert(False,"shouldn't reach here") +#Else + Debug.Fail("shouldn't reach here") +#End If + + Case TypeCode.Object, _ + TypeCode.DBNull, _ + TypeCode.String + + 'fall through + + End Select + + Return Expression + + End Function + + Private Shared Function ChangeIntrinsicType(ByVal Expression As Object, ByVal TargetType As Type) As Object + + 'This function will not handle user-defined conversion resolution, and so handles + 'only conversions between intrinsic types. + Debug.Assert(IsIntrinsicType(Expression.GetType) OrElse IsEnum(Expression.GetType), "this function converts between intrinsic types only") + + Select Case GetTypeCode(TargetType) + + Case TypeCode.Boolean : Return CBool(Expression) + Case TypeCode.SByte : Return CastSByteEnum(CSByte(Expression), TargetType) + Case TypeCode.Byte : Return CastByteEnum(CByte(Expression), TargetType) + Case TypeCode.Int16 : Return CastInt16Enum(CShort(Expression), TargetType) + Case TypeCode.UInt16 : Return CastUInt16Enum(CUShort(Expression), TargetType) + Case TypeCode.Int32 : Return CastInt32Enum(CInt(Expression), TargetType) + Case TypeCode.UInt32 : Return CastUInt32Enum(CUInt(Expression), TargetType) + Case TypeCode.Int64 : Return CastInt64Enum(CLng(Expression), TargetType) + Case TypeCode.UInt64 : Return CastUInt64Enum(CULng(Expression), TargetType) + Case TypeCode.Decimal : Return CDec(Expression) + Case TypeCode.Single : Return CSng(Expression) + Case TypeCode.Double : Return CDbl(Expression) + Case TypeCode.DateTime : Return CDate(Expression) + Case TypeCode.Char : Return CChar(Expression) + Case TypeCode.String : Return CStr(Expression) + + Case TypeCode.Empty, _ + TypeCode.Object, _ + TypeCode.DBNull + 'fall though to error + End Select +#If TELESTO Then + Debug.Assert(False,"Expected intrinsic type only, not: " & TargetType.Name) +#Else + Debug.Fail("Expected intrinsic type only, not: " & TargetType.Name) +#End If + Throw New Exception 'would be nice to have an internal runtime exception or something like that + + End Function + + Public Shared Function ChangeType(ByVal Expression As Object, ByVal TargetType As System.Type) As Object + return ChangeType(Expression, TargetType, False) + End Function + + _ + Friend Shared Function ChangeType(ByVal Expression As Object, ByVal TargetType As System.Type, ByVal Dynamic as Boolean) As Object + If TargetType Is Nothing Then + Throw New ArgumentException(GetResourceString(ResID.Argument_InvalidNullValue1, "TargetType")) + End If + + If Expression Is Nothing Then + If IsValueType(TargetType) Then + 'method.invoke will do this for us, so when casting arguments to param types during + 'latebinding, the createinstance call isn't needed, but ChangeType used in a generalized + 'manner should return a default instance. +#If Not TELESTO Then + ' This demand was added to fix an FxCop violation VSWhidbey 348449. + ' Silverlight uses Transparency Security model, + ' - ReflectionPermission is not available + ' - Activator.CreateInstance is marked with SecuritySafeCritical + Dim Permission As New System.Security.Permissions.ReflectionPermission( _ + System.Security.Permissions.ReflectionPermissionFlag.NoFlags) + Permission.Demand() +#End If + Return Activator.CreateInstance(TargetType) + Else + Return Nothing + End If + End If + + Dim SourceType As Type = Expression.GetType + Debug.Assert(Not SourceType.IsByRef, "never expected to see byref source type") + + 'Dig through ByRef types which might come in. + If TargetType.IsByRef Then TargetType = TargetType.GetElementType + + If TargetType Is SourceType OrElse IsRootObjectType(TargetType) Then + Return Expression + End If + + Dim TargetTypeCode As TypeCode = GetTypeCode(TargetType) + + 'All conversions between intrinsic types are natively built-in + 'and require no user-defined conversion resolution. + If IsIntrinsicType(TargetTypeCode) Then + Dim SourceTypeCode As TypeCode = GetTypeCode(SourceType) + If IsIntrinsicType(SourceTypeCode) Then + Return ChangeIntrinsicType(Expression, TargetType) + End If + End If + + If TargetType.IsInstanceOfType(Expression) Then + Return Expression + End If + + If IsCharArrayRankOne(TargetType) AndAlso IsStringType(SourceType) Then + Return CType(DirectCast(Expression, String), Char()) + End If + If IsStringType(TargetType) AndAlso IsCharArrayRankOne(SourceType) Then + Return CStr(DirectCast(Expression, Char())) + End If + + Debug.Assert(ClassifyPredefinedConversion(TargetType, SourceType) = ConversionClass.None OrElse _ + ClassifyPredefinedConversion(TargetType, SourceType) = ConversionClass.Narrowing, _ + "expected all predefined conversions handled by this point") + + If Dynamic Then + Dim idmop As IDynamicMetaObjectProvider = IDOUtils.TryCastToIDMOP(Expression) + If idmop IsNot Nothing Then + Return IDOBinder.UserDefinedConversion(idmop, TargetType) + End If + End If + Return ObjectUserDefinedConversion(Expression, TargetType) + End Function + + _ + _ + Public Shared Function FallbackUserDefinedConversion( _ + ByVal Expression As Object, ByVal TargetType As Type) As Object + + Return ObjectUserDefinedConversion(Expression, TargetTYpe) + End Function 'FalbackUserDefinedConversion + + _ + Private Shared Function ObjectUserDefinedConversion( _ + ByVal Expression As Object, ByVal TargetType As Type) As Object + + Dim SourceType As Type = Expression.GetType + If ClassifyPredefinedConversion(TargetType, SourceType) = ConversionClass.None AndAlso _ + (IsClassOrValueType(SourceType) OrElse IsClassOrValueType(TargetType)) AndAlso _ + Not (IsIntrinsicType(SourceType) AndAlso IsIntrinsicType(TargetType)) Then + + 'Conversions of the form S-->T use only one user-defined conversion at a time, i.e., + 'user-defined conversions are not chained together. It may be necessary to convert to and + 'from intermediate types using predefined conversions to match the signature of the + 'user-defined conversion exactly, so the conversion "path" is comprised of at most three + 'parts: + ' + ' 1) [ predefined conversion S-->Sx ] + ' 2) User-defined conversion Sx-->Tx + ' 3) [ predefined conversion Tx-->T ] + ' + ' Where Sx is the intermediate source type + ' and Tx is the intermediate target type + ' + ' Steps 1 and 3 are optional given S == Sx or Tx == T. + ' + 'Given the source operand and target type, resolve the conversion operator and invoke it. + 'When invoking the conversion operator, the conversion from S-->Sx is done when matching + 'the arguments. After invocation, we must handle the conversion from the result of the + 'invocation to the target type, i.e., Tx-->T. + + 'Resolve the operator. + Dim OperatorMethod As Method = Nothing + Dim Result As ConversionClass = _ + ClassifyUserDefinedConversion(TargetType, SourceType, OperatorMethod) + + If OperatorMethod IsNot Nothing Then + Debug.Assert(Result = ConversionClass.Widening OrElse Result = ConversionClass.Narrowing, _ + "operator method not expected for invalid conversion") + + 'Invoke the operator. This handles the conversion S-->Sx. + Dim BaseReference As Container = New Container(OperatorMethod.DeclaringType) + Dim InvocationResult As Object = _ + BaseReference.InvokeMethod( _ + OperatorMethod, _ + New Object() {Expression}, _ + Nothing, _ + BindingFlags.InvokeMethod) + + 'Now convert the result of the invocation to the target type, Tx-->T. + +#If DEBUG Then + If InvocationResult IsNot Nothing Then + + + 'disabling the assert below when we're converting to Nullable(Of T) + 'since the Runtime hasn't been updated yet to handle Nullable. In this case the assert + 'is harmless, but ClassifyPredefinedConversion hasn't been updated to consider Nullable conversions, + 'and updating this across the entire runtime would be significant feature work. + + If Not _ + (TargetType.IsGenericType AndAlso _ + Not TargetType.IsGenericTypeDefinition AndAlso _ + TargetType.GetGenericTypeDefinition().Equals(GetType(Nullable(Of ))) AndAlso _ + TargetType.GetGenericArguments().Length > 0 AndAlso _ + InvocationResult.GetType().Equals(TargetType.GetGenericArguments()(0))) Then + + + Dim PostConversion As ConversionClass = ClassifyPredefinedConversion(TargetType, InvocationResult.GetType) + + Debug.Assert( _ + PostConversion = ConversionClass.Narrowing OrElse _ + PostConversion = ConversionClass.Identity OrElse _ + PostConversion = ConversionClass.Widening, _ + "User defined conversion returned unexpected result") + End If + End If +#End If + Return ChangeType(InvocationResult, TargetType) + + ElseIf Result = ConversionClass.Ambiguous Then + Throw New InvalidCastException( _ + GetResourceString( _ + ResID.AmbiguousCast2, _ + VBFriendlyName(SourceType), _ + VBFriendlyName(TargetType))) + End If + + End If + + Throw New InvalidCastException( _ + GetResourceString( _ + ResID.InvalidCast_FromTo, _ + VBFriendlyName(SourceType), _ + VBFriendlyName(TargetType))) + + End Function 'UserDefinedConversion + + ' Simplied version of ObjectUserDefinedConversion, above + ' Determines if conversion is possible + Friend Shared Function CanUserDefinedConvert(ByVal Expression As Object, ByVal TargetType As Type) As Boolean + + Dim SourceType As Type = Expression.GetType + If ClassifyPredefinedConversion(TargetType, SourceType) = ConversionClass.None AndAlso _ + (IsClassOrValueType(SourceType) OrElse IsClassOrValueType(TargetType)) AndAlso _ + Not (IsIntrinsicType(SourceType) AndAlso IsIntrinsicType(TargetType)) Then + + 'Resolve the operator. + Dim OperatorMethod As Method = Nothing + Dim Result As ConversionClass = _ + ClassifyUserDefinedConversion(TargetType, SourceType, OperatorMethod) + + Return OperatorMethod IsNot Nothing + End If + + Return False + End Function 'CanUserDefinedConvert + + End Class + +#If LATEBINDING And TELESTO Then + Public NotInheritable Class LateBinderConversions + Public Shared Function ChangeType(ByVal Expression As Object, ByVal TargetType As System.Type) As Object + Return Conversions.ChangeType(Expression, TargetType, False) + End Function + End Class +#End If + +End Namespace diff --git a/Microsoft.VisualBasic/runtime/msvbalib/Helpers/DateType.vb b/Microsoft.VisualBasic/runtime/msvbalib/Helpers/DateType.vb new file mode 100644 index 000000000..51e4f53f8 --- /dev/null +++ b/Microsoft.VisualBasic/runtime/msvbalib/Helpers/DateType.vb @@ -0,0 +1,91 @@ +' Copyright (c) Microsoft Corporation. All rights reserved. + +Imports System +Imports System.Globalization +Imports Microsoft.VisualBasic.CompilerServices.Utils + +Namespace Microsoft.VisualBasic.CompilerServices + +#Region " BACKWARDS COMPATIBILITY " + + 'WARNING WARNING WARNING WARNING WARNING + 'This code exists to support Everett compiled applications. Make sure you understand + 'the backwards compatibility ramifications of any edit you make in this region. + 'WARNING WARNING WARNING WARNING WARNING + + _ + Public NotInheritable Class DateType + ' Prevent creation. + Private Sub New() + End Sub + + Public Shared Function FromString(ByVal Value As String) As Date + Return DateType.FromString(Value, GetCultureInfo()) + End Function + + Public Shared Function FromString(ByVal Value As String, ByVal culture As Globalization.CultureInfo) As Date + Dim ParsedDate As System.DateTime + + If TryParse(Value, ParsedDate) Then + Return ParsedDate + Else + 'Truncate the string to 32 characters for the message + Throw New InvalidCastException(GetResourceString(ResID.InvalidCast_FromStringTo, Left(Value, 32), "Date")) + End If + End Function + + Public Shared Function FromObject(ByVal Value As Object) As Date + + If Value Is Nothing Then + Exit Function + End If + + Dim ValueInterface As IConvertible + Dim ValueTypeCode As TypeCode + + ValueInterface = TryCast(Value, IConvertible) + + If Not ValueInterface Is Nothing Then + + ValueTypeCode = ValueInterface.GetTypeCode() + + Select Case ValueTypeCode + Case TypeCode.DateTime + Return ValueInterface.ToDateTime(Nothing) + + Case TypeCode.String + Return DateType.FromString(ValueInterface.ToString(Nothing), GetCultureInfo()) + + Case TypeCode.Boolean, _ + TypeCode.Byte, _ + TypeCode.Int16, _ + TypeCode.Int32, _ + TypeCode.Int64, _ + TypeCode.Single, _ + TypeCode.Double, _ + TypeCode.Decimal, _ + TypeCode.Char + ' Fall through to error + + Case Else + ' Fall through to error + End Select + + End If + + Throw New InvalidCastException(GetResourceString(ResID.InvalidCast_FromTo, VBFriendlyName(Value), "Date")) + End Function + + Friend Shared Function TryParse(ByVal Value As String, ByRef Result As System.DateTime) As Boolean + Const ParseStyle As DateTimeStyles = _ + DateTimeStyles.AllowWhiteSpaces Or _ + DateTimeStyles.NoCurrentDateDefault + Dim Culture As CultureInfo = GetCultureInfo() + Return System.DateTime.TryParse(ToHalfwidthNumbers(Value, Culture), Culture, ParseStyle, Result) + End Function + + End Class + +#End Region + +End Namespace diff --git a/Microsoft.VisualBasic/runtime/msvbalib/Helpers/DecimalType.vb b/Microsoft.VisualBasic/runtime/msvbalib/Helpers/DecimalType.vb new file mode 100644 index 000000000..aa2ed321e --- /dev/null +++ b/Microsoft.VisualBasic/runtime/msvbalib/Helpers/DecimalType.vb @@ -0,0 +1,216 @@ +' Copyright (c) Microsoft Corporation. All rights reserved. + +Imports System +Imports System.Globalization + +Imports Microsoft.VisualBasic.CompilerServices.ExceptionUtils +Imports Microsoft.VisualBasic.CompilerServices.Utils + +Namespace Microsoft.VisualBasic.CompilerServices + +#Region " BACKWARDS COMPATIBILITY " + + 'WARNING WARNING WARNING WARNING WARNING + 'This code exists to support Everett compiled applications. Make sure you understand + 'the backwards compatibility ramifications of any edit you make in this region. + 'WARNING WARNING WARNING WARNING WARNING + + _ + Public NotInheritable Class DecimalType + ' Prevent creation. + Private Sub New() + End Sub + + Public Shared Function FromBoolean(ByVal Value As Boolean) As Decimal + If Value Then + Return -1D + Else + Return 0D + End If + End Function + + Public Shared Function FromString(ByVal Value As String) As Decimal + Return FromString(Value, Nothing) + End Function + + Public Shared Function FromString(ByVal Value As String, ByVal NumberFormat As NumberFormatInfo) As Decimal + If Value Is Nothing Then + Return 0D + End If + + Try + Dim i64Value As Int64 + + If IsHexOrOctValue(Value, i64Value) Then + Return CDec(i64Value) + End If + + Return Parse(Value, NumberFormat) + + Catch e1 As OverflowException + Throw VbMakeException(vbErrors.Overflow) + Catch e2 As FormatException + Throw New InvalidCastException(GetResourceString(ResID.InvalidCast_FromStringTo, Left(Value, 32), "Decimal")) + End Try + End Function + + Public Shared Function FromObject(ByVal Value As Object) As Decimal + Return FromObject(Value, Nothing) + End Function + + Public Shared Function FromObject(ByVal Value As Object, ByVal NumberFormat As NumberFormatInfo) As Decimal + + If Value Is Nothing Then + Return 0D + End If + + Dim ValueInterface As IConvertible + Dim ValueTypeCode As TypeCode + + ValueInterface = TryCast(Value, IConvertible) + + If Not ValueInterface Is Nothing Then + + ValueTypeCode = ValueInterface.GetTypeCode() + + Select Case ValueTypeCode + + Case TypeCode.Boolean + Return DecimalType.FromBoolean(ValueInterface.ToBoolean(Nothing)) + + Case TypeCode.Byte + Return CDec(ValueInterface.ToByte(Nothing)) + + Case TypeCode.Int16 + Return CDec(ValueInterface.ToInt16(Nothing)) + + Case TypeCode.Int32 + Return CDec(ValueInterface.ToInt32(Nothing)) + + Case TypeCode.Int64 + Return CDec(ValueInterface.ToInt64(Nothing)) + + Case TypeCode.Single + Return CDec(ValueInterface.ToSingle(Nothing)) + + Case TypeCode.Double + Return CDec(ValueInterface.ToDouble(Nothing)) + + Case TypeCode.Decimal + Return ValueInterface.ToDecimal(Nothing) + + Case TypeCode.String + Return DecimalType.FromString(ValueInterface.ToString(Nothing), NumberFormat) + + Case TypeCode.Char, _ + TypeCode.DateTime + ' Fall through to error + Case Else + ' Fall through to error + End Select + End If + + Throw New InvalidCastException(GetResourceString(ResID.InvalidCast_FromTo, VBFriendlyName(Value), "Decimal")) + End Function + + Public Shared Function Parse(ByVal Value As String, ByVal NumberFormat As NumberFormatInfo) As Decimal + Dim NormalizedNumberFormat As NumberFormatInfo + Dim culture As CultureInfo = GetCultureInfo() + + If NumberFormat Is Nothing Then + NumberFormat = culture.NumberFormat + End If + + ' Normalize number format settings to enable us to first use the numeric settings for both currency and number parsing + ' compatible with VB6 + NormalizedNumberFormat = GetNormalizedNumberFormat(NumberFormat) + + Const flags As NumberStyles = _ + NumberStyles.AllowDecimalPoint Or _ + NumberStyles.AllowExponent Or _ + NumberStyles.AllowLeadingSign Or _ + NumberStyles.AllowLeadingWhite Or _ + NumberStyles.AllowThousands Or _ + NumberStyles.AllowTrailingSign Or _ + NumberStyles.AllowParentheses Or _ + NumberStyles.AllowTrailingWhite Or _ + NumberStyles.AllowCurrencySymbol + + Value = ToHalfwidthNumbers(Value, culture) + + Try + ' Use numeric settings to parse + Return System.Decimal.Parse(Value, flags, NormalizedNumberFormat) + Catch FormatEx As FormatException When Not (NumberFormat Is NormalizedNumberFormat) + ' Use currency settings to parse + Return System.Decimal.Parse(Value, flags, NumberFormat) + Catch Ex As Exception + Throw Ex + End Try + + End Function + + ' This method returns a NumberFormat with the relevant Currency Settings to be the same as the Number Settings + ' In - NumberFormat to be normalized - this is not changed by this Method + ' Return - Normalized NumberFormat + Friend Shared Function GetNormalizedNumberFormat(ByVal InNumberFormat As NumberFormatInfo) As NumberFormatInfo + + Dim OutNumberFormat As NumberFormatInfo + + With InNumberFormat + If (Not .CurrencyDecimalSeparator Is Nothing) AndAlso _ + (Not .NumberDecimalSeparator Is Nothing) AndAlso _ + (Not .CurrencyGroupSeparator Is Nothing) AndAlso _ + (Not .NumberGroupSeparator Is Nothing) AndAlso _ + (.CurrencyDecimalSeparator.Length = 1) AndAlso _ + (.NumberDecimalSeparator.Length = 1) AndAlso _ + (.CurrencyGroupSeparator.Length = 1) AndAlso _ + (.NumberGroupSeparator.Length = 1) AndAlso _ + (.CurrencyDecimalSeparator.Chars(0) = .NumberDecimalSeparator.Chars(0)) AndAlso _ + (.CurrencyGroupSeparator.Chars(0) = .NumberGroupSeparator.Chars(0)) AndAlso _ + (.CurrencyDecimalDigits = .NumberDecimalDigits) Then + Return InNumberFormat + End If + End With + + + With InNumberFormat + If (Not .CurrencyDecimalSeparator Is Nothing) AndAlso _ + (Not .NumberDecimalSeparator Is Nothing) AndAlso _ + (.CurrencyDecimalSeparator.Length = .NumberDecimalSeparator.Length) AndAlso _ + (Not .CurrencyGroupSeparator Is Nothing) AndAlso _ + (Not .NumberGroupSeparator Is Nothing) AndAlso _ + (.CurrencyGroupSeparator.Length = .NumberGroupSeparator.Length) Then + + Dim i As Integer + For i = 0 To .CurrencyDecimalSeparator.Length - 1 + If (.CurrencyDecimalSeparator.Chars(i) <> .NumberDecimalSeparator.Chars(i)) Then GoTo MisMatch + Next + + For i = 0 To .CurrencyGroupSeparator.Length - 1 + If (.CurrencyGroupSeparator.Chars(i) <> .NumberGroupSeparator.Chars(i)) Then GoTo MisMatch + Next + + Return InNumberFormat + End If + End With + +MisMatch: + + OutNumberFormat = DirectCast(InNumberFormat.Clone, NumberFormatInfo) + + ' Set the Currency Settings to be the Same as the Numeric Settings + With OutNumberFormat + .CurrencyDecimalSeparator = .NumberDecimalSeparator + .CurrencyGroupSeparator = .NumberGroupSeparator + .CurrencyDecimalDigits = .NumberDecimalDigits + End With + + Return OutNumberFormat + End Function + + End Class + +#End Region + +End Namespace diff --git a/Microsoft.VisualBasic/runtime/msvbalib/Helpers/DoubleType.vb b/Microsoft.VisualBasic/runtime/msvbalib/Helpers/DoubleType.vb new file mode 100644 index 000000000..7584555df --- /dev/null +++ b/Microsoft.VisualBasic/runtime/msvbalib/Helpers/DoubleType.vb @@ -0,0 +1,242 @@ +' Copyright (c) Microsoft Corporation. All rights reserved. + +Imports System +Imports System.Globalization + +Imports Microsoft.VisualBasic.CompilerServices.DecimalType +Imports Microsoft.VisualBasic.CompilerServices.Utils + +Namespace Microsoft.VisualBasic.CompilerServices + +#Region " BACKWARDS COMPATIBILITY " + + 'WARNING WARNING WARNING WARNING WARNING + 'This code exists to support Everett compiled applications. Make sure you understand + 'the backwards compatibility ramifications of any edit you make in this region. + 'WARNING WARNING WARNING WARNING WARNING + + _ + Public NotInheritable Class DoubleType + ' Prevent creation. + Private Sub New() + End Sub + + Public Shared Function FromString(ByVal Value As String) As Double + Return FromString(Value, Nothing) + End Function + + Public Shared Function FromString(ByVal Value As String, ByVal NumberFormat As NumberFormatInfo) As Double + + If Value Is Nothing Then + Return 0 + End If + + Try + Dim i64Value As Int64 + + If IsHexOrOctValue(Value, i64Value) Then + Return CDbl(i64Value) + End If + Return DoubleType.Parse(Value, NumberFormat) + + Catch e As FormatException + Throw New InvalidCastException(GetResourceString(ResID.InvalidCast_FromStringTo, Left(Value, 32), "Double"), e) + End Try + + End Function + + Public Shared Function FromObject(ByVal Value As Object) As Double + Return FromObject(Value, Nothing) + End Function + + Public Shared Function FromObject(ByVal Value As Object, ByVal NumberFormat As NumberFormatInfo) As Double + + If Value Is Nothing Then + Return 0 + End If + + Dim ValueInterface As IConvertible + Dim ValueTypeCode As TypeCode + + ValueInterface = TryCast(Value, IConvertible) + + If ValueInterface Is Nothing Then + GoTo ThrowInvalidCast + End If + + ValueTypeCode = ValueInterface.GetTypeCode() + + Select Case ValueTypeCode + + Case TypeCode.Boolean + Return CDbl(ValueInterface.ToBoolean(Nothing)) + + Case TypeCode.Byte + If TypeOf Value Is System.Byte Then + Return CDbl(DirectCast(Value, Byte)) + Else + Return CDbl(ValueInterface.ToByte(Nothing)) + End If + + Case TypeCode.Int16 + If TypeOf Value Is System.Int16 Then + Return CDbl(DirectCast(Value, Int16)) + Else + Return CDbl(ValueInterface.ToInt16(Nothing)) + End If + + Case TypeCode.Int32 + If TypeOf Value Is System.Int32 Then + Return CDbl(DirectCast(Value, Int32)) + Else + Return CDbl(ValueInterface.ToInt32(Nothing)) + End If + + Case TypeCode.Int64 + If TypeOf Value Is System.Int64 Then + Return CDbl(DirectCast(Value, Int64)) + Else + Return CDbl(ValueInterface.ToInt64(Nothing)) + End If + + Case TypeCode.Single + If TypeOf Value Is System.Single Then + Return DirectCast(Value, Single) + Else + Return CDbl(ValueInterface.ToSingle(Nothing)) + End If + + Case TypeCode.Double + If TypeOf Value Is System.Double Then + Return CDbl(DirectCast(Value, Double)) + Else + Return CDbl(ValueInterface.ToDouble(Nothing)) + End If + + Case TypeCode.Decimal + 'Do not use .ToDecimal because of jit temp issue effects all perf + Return DecimalToDouble(ValueInterface) + + Case TypeCode.String + Return DoubleType.FromString(ValueInterface.ToString(Nothing), NumberFormat) + + Case TypeCode.Char, _ + TypeCode.DateTime + ' Fall through to error + + Case Else + ' Fall through to error + End Select +ThrowInvalidCast: + Throw New InvalidCastException(GetResourceString(ResID.InvalidCast_FromTo, VBFriendlyName(Value), "Double")) + + End Function + + Private Shared Function DecimalToDouble(ByVal ValueInterface As IConvertible) As Double + Return CDbl(ValueInterface.ToDecimal(Nothing)) + End Function + + Public Shared Function Parse(ByVal Value As String) As Double + Return Parse(Value, Nothing) + End Function + + Friend Shared Function TryParse(ByVal Value As String, ByRef Result As Double) As Boolean + Dim NumberFormat As NumberFormatInfo + Dim NormalizedNumberFormat As NumberFormatInfo + Dim culture As CultureInfo = GetCultureInfo() + + NumberFormat = culture.NumberFormat + NormalizedNumberFormat = GetNormalizedNumberFormat(NumberFormat) + + Const flags As NumberStyles = _ + NumberStyles.AllowDecimalPoint Or _ + NumberStyles.AllowExponent Or _ + NumberStyles.AllowLeadingSign Or _ + NumberStyles.AllowLeadingWhite Or _ + NumberStyles.AllowThousands Or _ + NumberStyles.AllowTrailingSign Or _ + NumberStyles.AllowParentheses Or _ + NumberStyles.AllowTrailingWhite Or _ + NumberStyles.AllowCurrencySymbol + + Value = ToHalfwidthNumbers(Value, culture) + + ' The below code handles the 80% case efficiently and is inefficient only when the numeric and currency settings + ' are different + + If NumberFormat Is NormalizedNumberFormat Then + Return System.Double.TryParse(Value, flags, NormalizedNumberFormat, Result) + Else + Try + ' Use numeric settings to parse + ' Note that we use Parse instead of TryParse in order to distinguish whether the conversion failed + ' due to FormatException or other exception like OverFlowException, etc. + Result = System.Double.Parse(Value, flags, NormalizedNumberFormat) + Return True + Catch FormatEx As FormatException + ' Use currency settings to parse + Try + Return System.Double.TryParse(Value, flags, NumberFormat, Result) + Catch ex As ArgumentException + Return False + End Try + Catch ex As StackOverflowException + Throw ex + Catch ex As OutOfMemoryException + Throw ex + Catch ex As System.Threading.ThreadAbortException + Throw ex + Catch Ex As Exception + Return False + End Try + End If + + End Function + + Public Shared Function Parse(ByVal Value As String, ByVal NumberFormat As NumberFormatInfo) As Double + Dim NormalizedNumberFormat As NumberFormatInfo + Dim culture As CultureInfo = GetCultureInfo() + + If NumberFormat Is Nothing Then + NumberFormat = culture.NumberFormat + End If + + ' Normalize number format settings to enable us to first use the numeric settings for both currency and number parsing + ' compatible with VB6 + NormalizedNumberFormat = GetNormalizedNumberFormat(NumberFormat) + + + Const flags As NumberStyles = _ + NumberStyles.AllowDecimalPoint Or _ + NumberStyles.AllowExponent Or _ + NumberStyles.AllowLeadingSign Or _ + NumberStyles.AllowLeadingWhite Or _ + NumberStyles.AllowThousands Or _ + NumberStyles.AllowTrailingSign Or _ + NumberStyles.AllowParentheses Or _ + NumberStyles.AllowTrailingWhite Or _ + NumberStyles.AllowCurrencySymbol + + + Value = ToHalfwidthNumbers(Value, culture) + + + Try + ' Use numeric settings to parse + Return System.Double.Parse(Value, flags, NormalizedNumberFormat) + Catch FormatEx As FormatException When Not (NumberFormat Is NormalizedNumberFormat) + ' Use currency settings to parse + Return System.Double.Parse(Value, flags, NumberFormat) + Catch Ex As Exception + Throw Ex + End Try + + End Function + + End Class + +#End Region + +End Namespace + + diff --git a/Microsoft.VisualBasic/runtime/msvbalib/Helpers/ExceptionUtils.vb b/Microsoft.VisualBasic/runtime/msvbalib/Helpers/ExceptionUtils.vb new file mode 100644 index 000000000..21acb6878 --- /dev/null +++ b/Microsoft.VisualBasic/runtime/msvbalib/Helpers/ExceptionUtils.vb @@ -0,0 +1,612 @@ +' Copyright (c) Microsoft Corporation. All rights reserved. + +Imports System +Imports System.IO +Imports System.Runtime.InteropServices +Imports System.Diagnostics +Imports System.Security + +Imports Microsoft.VisualBasic.CompilerServices.Utils + +Namespace Microsoft.VisualBasic.CompilerServices + + Friend Enum vbErrors + None = 0 + ReturnWOGoSub = 3 + IllegalFuncCall = 5 + Overflow = 6 + OutOfMemory = 7 + OutOfBounds = 9 + ArrayLocked = 10 + DivByZero = 11 + TypeMismatch = 13 + OutOfStrSpace = 14 + ExprTooComplex = 16 + CantContinue = 17 + UserInterrupt = 18 + ResumeWOErr = 20 + OutOfStack = 28 + UNDONE = 29 + UndefinedProc = 35 + TooManyClients = 47 + DLLLoadErr = 48 + DLLBadCallingConv = 49 + InternalError = 51 + BadFileNameOrNumber = 52 + FileNotFound = 53 + BadFileMode = 54 + FileAlreadyOpen = 55 + IOError = 57 + FileAlreadyExists = 58 + BadRecordLen = 59 + DiskFull = 61 + EndOfFile = 62 + BadRecordNum = 63 + TooManyFiles = 67 + DevUnavailable = 68 + PermissionDenied = 70 + DiskNotReady = 71 + DifferentDrive = 74 + PathFileAccess = 75 + PathNotFound = 76 + ObjNotSet = 91 + IllegalFor = 92 + BadPatStr = 93 + CantUseNull = 94 + UserDefined = 95 + AdviseLimit = 96 + BadCallToFriendFunction = 97 + CantPassPrivateObject = 98 + DLLCallException = 99 + DoesntImplementICollection = 100 + Abort = 287 + InvalidFileFormat = 321 + CantCreateTmpFile = 322 + InvalidResourceFormat = 325 + InvalidPropertyValue = 380 + InvalidPropertyArrayIndex = 381 + SetNotSupportedAtRuntime = 382 + SetNotSupported = 383 + NeedPropertyArrayIndex = 385 + SetNotPermitted = 387 + GetNotSupportedAtRuntime = 393 + GetNotSupported = 394 + PropertyNotFound = 422 + NoSuchControlOrProperty = 423 + NotObject = 424 + CantCreateObject = 429 + OLENotSupported = 430 + OLEFileNotFound = 432 + OLENoPropOrMethod = 438 + OLEAutomationError = 440 + LostTLB = 442 + OLENoDefault = 443 + ActionNotSupported = 445 + NamedArgsNotSupported = 446 + LocaleSettingNotSupported = 447 + NamedParamNotFound = 448 + ParameterNotOptional = 449 + FuncArityMismatch = 450 + NotEnum = 451 + InvalidOrdinal = 452 + InvalidDllFunctionName = 453 + CodeResourceNotFound = 454 + CodeResourceLockError = 455 + DuplicateKey = 457 + InvalidTypeLibVariable = 458 + ObjDoesNotSupportEvents = 459 + InvalidClipboardFormat = 460 + IdentNotMember = 461 + ServerNotFound = 462 + ObjNotRegistered = 463 + InvalidPicture = 481 + PrinterError = 482 + CantSaveFileToTemp = 735 + SearchTextNotFound = 744 + ReplacementsTooLong = 746 + + NotYetImplemented = 32768 + FileNotFoundWithName = 40243 + CantFindDllEntryPoint = 59201 + + SeekErr = 32771 + ReadFault = 32772 + WriteFault = 32773 + BadFunctionId = 32774 + FileLockViolation = 32775 + ShareRequired = 32789 + BufferTooSmall = 32790 + InvDataRead = 32792 + UnsupFormat = 32793 + RegistryAccess = 32796 + LibNotRegistered = 32797 + Usage = 32799 + UndefinedType = 32807 + QualifiedNameDisallowed = 32808 + InvalidState = 32809 + WrongTypeKind = 32810 + ElementNotFound = 32811 + AmbiguousName = 32812 + ModNameConflict = 32813 + UnknownLcid = 32814 + BadModuleKind = 35005 + NoContainingLib = 35009 + BadTypeId = 35010 + BadLibId = 35011 + Eof = 35012 + SizeTooBig = 35013 + ExpectedFuncNotModule = 35015 + ExpectedFuncNotRecord = 35016 + ExpectedFuncNotProject = 35017 + ExpectedFuncNotVar = 35018 + ExpectedTypeNotProj = 35019 + UnsuitableFuncPropMatch = 35020 + BrokenLibRef = 35021 + UnsupportedTypeLibFeature = 35022 + ModuleAsType = 35024 + InvalidTypeInfoKind = 35025 + InvalidTypeLibFunction = 35026 + OperationNotAllowedInDll = 40035 + CompileError = 40036 + CantEvalWatch = 40037 + MissingVbaTypeLib = 40038 + UserReset = 40040 + MissingEndBrack = 40041 + IncorrectTypeChar = 40042 + InvalidNumLit = 40043 + IllegalChar = 40044 + IdTooLong = 40045 + StatementTooComplex = 40046 + ExpectedTokens = 40047 + InconsistentPropFuncs = 40067 + CircularType = 40068 + AccessViolation = &H80004003 'This is E_POINTER. This is what VB6 returns from err.Number when calling into a .NET assembly that throws an AccessViolation + LastTrappable = ReplacementsTooLong + End Enum + +#If TELESTO Then + Friend NotInheritable Class ExceptionUtils 'FIXME: +#Else + _ + Public NotInheritable Class ExceptionUtils +#End If + + ' Prevent creation. + Private Sub New() + End Sub + + Friend Const E_NOTIMPL As Integer = &H80004001I + Friend Const E_NOINTERFACE As Integer = &H80004002I + Friend Const E_POINTER As Integer = &H80004003I + Friend Const E_ABORT As Integer = &H80004004I + ' FACILITY_DISPATCH - IDispatch errors. + Friend Const DISP_E_UNKNOWNINTERFACE As Integer = &H80020001I + Friend Const DISP_E_MEMBERNOTFOUND As Integer = &H80020003I + Friend Const DISP_E_PARAMNOTFOUND As Integer = &H80020004I + Friend Const DISP_E_TYPEMISMATCH As Integer = &H80020005I + Friend Const DISP_E_UNKNOWNNAME As Integer = &H80020006I + Friend Const DISP_E_NONAMEDARGS As Integer = &H80020007I + Friend Const DISP_E_BADVARTYPE As Integer = &H80020008I + Friend Const DISP_E_OVERFLOW As Integer = &H8002000AI + Friend Const DISP_E_BADINDEX As Integer = &H8002000BI + Friend Const DISP_E_UNKNOWNLCID As Integer = &H8002000CI + Friend Const DISP_E_ARRAYISLOCKED As Integer = &H8002000DI + Friend Const DISP_E_BADPARAMCOUNT As Integer = &H8002000EI + Friend Const DISP_E_PARAMNOTOPTIONAL As Integer = &H8002000FI + Friend Const DISP_E_NOTACOLLECTION As Integer = &H80020011I + Friend Const DISP_E_DIVBYZERO As Integer = &H80020012I +#If Not LATEBINDING Then + ' FACILITY_DISPATCH - Typelib errors. + Friend Const TYPE_E_BUFFERTOOSMALL As Integer = &H80028016I + Friend Const TYPE_E_INVDATAREAD As Integer = &H80028018I + Friend Const TYPE_E_UNSUPFORMAT As Integer = &H80028019I + Friend Const TYPE_E_REGISTRYACCESS As Integer = &H8002801CI + Friend Const TYPE_E_LIBNOTREGISTERED As Integer = &H8002801DI + Friend Const TYPE_E_UNDEFINEDTYPE As Integer = &H80028027I + Friend Const TYPE_E_QUALIFIEDNAMEDISALLOWED As Integer = &H80028028I + Friend Const TYPE_E_INVALIDSTATE As Integer = &H80028029I + Friend Const TYPE_E_WRONGTYPEKIND As Integer = &H8002802AI + Friend Const TYPE_E_ELEMENTNOTFOUND As Integer = &H8002802BI + Friend Const TYPE_E_AMBIGUOUSNAME As Integer = &H8002802CI + Friend Const TYPE_E_NAMECONFLICT As Integer = &H8002802DI + Friend Const TYPE_E_UNKNOWNLCID As Integer = &H8002802EI + Friend Const TYPE_E_DLLFUNCTIONNOTFOUND As Integer = &H8002802FI + Friend Const TYPE_E_BADMODULEKIND As Integer = &H800288BDI + Friend Const TYPE_E_SIZETOOBIG As Integer = &H800288C5I + Friend Const TYPE_E_TYPEMISMATCH As Integer = &H80028CA0I + Friend Const TYPE_E_OUTOFBOUNDS As Integer = &H80028CA1I + Friend Const TYPE_E_IOERROR As Integer = &H80028CA2I + Friend Const TYPE_E_CANTCREATETMPFILE As Integer = &H80028CA3I + Friend Const TYPE_E_CANTLOADLIBRARY As Integer = &H80029C4AI + Friend Const TYPE_E_INCONSISTENTPROPFUNCS As Integer = &H80029C83I + Friend Const TYPE_E_CIRCULARTYPE As Integer = &H80029C84I + + ' FACILITY_STORAGE errors + Friend Const STG_E_INVALIDFUNCTION As Integer = &H80030001I + Friend Const STG_E_FILENOTFOUND As Integer = &H80030002I + Friend Const STG_E_PATHNOTFOUND As Integer = &H80030003I + Friend Const STG_E_TOOMANYOPENFILES As Integer = &H80030004I + Friend Const STG_E_ACCESSDENIED As Integer = &H80030005I + Friend Const STG_E_INVALIDHANDLE As Integer = &H80030006I + Friend Const STG_E_INSUFFICIENTMEMORY As Integer = &H80030008I + Friend Const STG_E_NOMOREFILES As Integer = &H80030012I + Friend Const STG_E_DISKISWRITEPROTECTED As Integer = &H80030013I + Friend Const STG_E_SEEKERROR As Integer = &H80030019I + Friend Const STG_E_WRITEFAULT As Integer = &H8003001DI + Friend Const STG_E_READFAULT As Integer = &H8003001EI + Friend Const STG_E_SHAREVIOLATION As Integer = &H80030020I + Friend Const STG_E_LOCKVIOLATION As Integer = &H80030021I + Friend Const STG_E_FILEALREADYEXISTS As Integer = &H80030050I + Friend Const STG_E_MEDIUMFULL As Integer = &H80030070I + Friend Const STG_E_INVALIDHEADER As Integer = &H800300FBI + Friend Const STG_E_INVALIDNAME As Integer = &H800300FCI + Friend Const STG_E_UNKNOWN As Integer = &H800300FDI + Friend Const STG_E_UNIMPLEMENTEDFUNCTION As Integer = &H800300FEI + Friend Const STG_E_INUSE As Integer = &H80030100I + Friend Const STG_E_NOTCURRENT As Integer = &H80030101I + Friend Const STG_E_REVERTED As Integer = &H80030102I + Friend Const STG_E_CANTSAVE As Integer = &H80030103I + Friend Const STG_E_OLDFORMAT As Integer = &H80030104I + Friend Const STG_E_OLDDLL As Integer = &H80030105I + Friend Const STG_E_SHAREREQUIRED As Integer = &H80030106I + Friend Const STG_E_NOTFILEBASEDSTORAGE As Integer = &H80030107I + Friend Const STG_E_EXTANTMARSHALLINGS As Integer = &H80030108I + + ' FACILITY_ITF errors. + Friend Const CLASS_E_NOTLICENSED As Integer = &H80040112I + Friend Const REGDB_E_CLASSNOTREG As Integer = &H80040154I + Friend Const MK_E_UNAVAILABLE As Integer = &H800401E3I + Friend Const MK_E_INVALIDEXTENSION As Integer = &H800401E6I + Friend Const MK_E_CANTOPENFILE As Integer = &H800401EAI + Friend Const CO_E_CLASSSTRING As Integer = &H800401F3I + Friend Const CO_E_APPNOTFOUND As Integer = &H800401F5I + Friend Const CO_E_APPDIDNTREG As Integer = &H800401FEI + + ' FACILITY_WIN32 errors + Friend Const E_ACCESSDENIED As Integer = &H80070005I + Friend Const E_OUTOFMEMORY As Integer = &H8007000EI + Friend Const E_INVALIDARG As Integer = &H80070057I + + ' FACILITY_WINDOWS - I don't know why this differs from FACILITY_WIN32 + Friend Const CO_E_SERVER_EXEC_FAILURE As Integer = &H80080005I + +#If Not TELESTO Then ' used for Everett FlowControl only + Friend Shared Function MakeException1(ByVal hr As Integer, ByVal Parm1 As String) As Exception + + Dim sMsg As String + Dim i As Integer + + If hr > 0 AndAlso hr <= &HFFFFI Then + sMsg = GetResourceString(CType(hr, vbErrors)) + Else + sMsg = "" + End If + + 'Insert Parm1 into message + i = sMsg.IndexOf("%1", StringComparison.OrdinalIgnoreCase) + If i >= 0 Then + sMsg = sMsg.Substring(0, i) + Parm1 + sMsg.Substring(i + 2) + End If + + Return VbMakeExceptionEx(hr, sMsg) + + End Function +#End If +#End If + Friend Shared Function VbMakeException(ByVal hr As Integer) As System.Exception + Dim sMsg As String + + If hr > 0 AndAlso hr <= &HFFFFI Then + sMsg = GetResourceString(CType(hr, vbErrors)) + Else + sMsg = "" + End If + VbMakeException = VbMakeExceptionEx(hr, sMsg) + End Function + +#If Not LATEBINDING Then + Friend Shared Function VbMakeException(ByVal ex As Exception, ByVal hr As Integer) As System.Exception + Err().SetUnmappedError(hr) + Return ex + End Function +#End If + + Friend Shared Function VbMakeExceptionEx(ByVal Number As Integer, ByVal sMsg As String) As System.Exception + Dim VBDefinedError As Boolean + + VbMakeExceptionEx = BuildException(Number, sMsg, VBDefinedError) + + If VBDefinedError Then +#If Not LATEBINDING Then + Err().SetUnmappedError(Number) +#End If + End If + + End Function + + + Friend Shared Function BuildException(ByVal Number As Integer, ByVal Description As String, ByRef VBDefinedError As Boolean) As System.Exception + + VBDefinedError = True + + Select Case Number + + Case vbErrors.None + + Case vbErrors.ReturnWOGoSub, _ + vbErrors.ResumeWOErr, _ + vbErrors.CantUseNull, _ + vbErrors.DoesntImplementICollection + Return New InvalidOperationException(Description) + + Case vbErrors.IllegalFuncCall, _ + vbErrors.NamedParamNotFound, _ + vbErrors.NamedArgsNotSupported, _ + vbErrors.ParameterNotOptional + Return New ArgumentException(Description) + + Case vbErrors.OLENoPropOrMethod + Return New MissingMemberException(Description) + + Case vbErrors.Overflow + Return New OverflowException(Description) + + Case vbErrors.OutOfMemory, vbErrors.OutOfStrSpace + Return New OutOfMemoryException(Description) + + Case vbErrors.OutOfBounds + Return New IndexOutOfRangeException(Description) + + Case vbErrors.DivByZero + Return New DivideByZeroException(Description) + + Case vbErrors.TypeMismatch + Return New InvalidCastException(Description) + + Case vbErrors.OutOfStack + Return New StackOverflowException(Description) + + Case vbErrors.DLLLoadErr + Return New TypeLoadException(Description) + + Case vbErrors.FileNotFound + Return New IO.FileNotFoundException(Description) + + Case vbErrors.EndOfFile + Return New IO.EndOfStreamException(Description) + + Case vbErrors.IOError, _ + vbErrors.BadFileNameOrNumber, _ + vbErrors.BadFileMode, _ + vbErrors.FileAlreadyOpen, _ + vbErrors.FileAlreadyExists, _ + vbErrors.BadRecordLen, _ + vbErrors.DiskFull, _ + vbErrors.BadRecordNum, _ + vbErrors.TooManyFiles, _ + vbErrors.DevUnavailable, _ + vbErrors.PermissionDenied, _ + vbErrors.DiskNotReady, _ + vbErrors.DifferentDrive, _ + vbErrors.PathFileAccess + Return New IO.IOException(Description) + + Case vbErrors.PathNotFound, _ + vbErrors.OLEFileNotFound + Return New IO.FileNotFoundException(Description) + + Case vbErrors.ObjNotSet + Return New NullReferenceException(Description) + + Case vbErrors.PropertyNotFound + Return New MissingFieldException(Description) + + Case vbErrors.CantCreateObject, _ + vbErrors.ServerNotFound + Return New Exception(Description) + + Case vbErrors.AccessViolation + Return New AccessViolationException() 'We never want a custom description here. Use the localized message that comes for free inside the exception + + Case Else + 'Fall below to default + VBDefinedError = False + Return New Exception(Description) + End Select + +#If TELESTO Then + VBDefinedError = False + Return New Exception(Description) +#Else + Debug.Fail("Should not get here") + Return Nothing +#End If + End Function + + '= PUBLIC ============================================================= + + + '= FRIENDS ============================================================ + +#If Not TELESTO Then + + '''************************************************************************** + ''' ;GetArgumentExceptionWithArgName + ''' + ''' Return a new instance of ArgumentException with the message from resource file and the Exception.ArgumentName property set. + ''' + ''' The name of the argument (paramemter). Not localized. + ''' The resource ID. Use CompilerServices.ResID.xxx + ''' Strings that will replace place holders in the resource string, if any. + ''' A new instance of ArgumentException. + ''' This is the prefered way to construct an argument exception. + Friend Shared Function GetArgumentExceptionWithArgName(ByVal ArgumentName As String, _ + ByVal ResourceID As String, ByVal ParamArray PlaceHolders() As String) As ArgumentException + + Return New ArgumentException(GetResourceString(ResourceID, PlaceHolders), ArgumentName) + End Function + + '''************************************************************************** + ''' ;GetArgumentNullException + ''' + ''' Return a new instance of ArgumentNullException with message: "Argument cannot be Nothing." + ''' + ''' The name of the argument (paramemter). Not localized. + ''' A new instance of ArgumentNullException. + Friend Shared Function GetArgumentNullException(ByVal ArgumentName As String) As ArgumentNullException + + Return New ArgumentNullException(ArgumentName, GetResourceString(ResID.MyID.General_ArgumentNullException)) + End Function + + '''************************************************************************** + ''' ;GetArgumentNullException + ''' + ''' Return a new instance of ArgumentNullException with the message from resource file. + ''' + ''' The name of the argument (paramemter). Not localized. + ''' The resource ID. Use CompilerServices.ResID.xxx + ''' Strings that will replace place holders in the resource string, if any. + ''' A new instance of ArgumentNullException. + Friend Shared Function GetArgumentNullException(ByVal ArgumentName As String, _ + ByVal ResourceID As String, ByVal ParamArray PlaceHolders() As String) As ArgumentNullException + + Return New ArgumentNullException(ArgumentName, GetResourceString(ResourceID, PlaceHolders)) + End Function + + '''************************************************************************** + ''' ;GetDirectoryNotFoundException + ''' + ''' Return a new instance of IO.DirectoryNotFoundException with the message from resource file. + ''' + ''' The resource ID. Use CompilerServices.ResID.xxx + ''' Strings that will replace place holders in the resource string, if any. + ''' A new instance of IO.DirectoryNotFoundException. + Friend Shared Function GetDirectoryNotFoundException( _ + ByVal ResourceID As String, ByVal ParamArray PlaceHolders() As String) As IO.DirectoryNotFoundException + + Return New IO.DirectoryNotFoundException(GetResourceString(ResourceID, PlaceHolders)) + End Function + + '''************************************************************************** + ''' ;GetFileNotFoundException + ''' + ''' Return a new instance of IO.FileNotFoundException with the message from resource file. + ''' + ''' The file name (path) of the not found file. + ''' The resource ID. Use CompilerServices.ResID.xxx + ''' Strings that will replace place holders in the resource string, if any. + ''' A new instance of IO.FileNotFoundException. + Friend Shared Function GetFileNotFoundException(ByVal FileName As String, _ + ByVal ResourceID As String, ByVal ParamArray PlaceHolders() As String) As IO.FileNotFoundException + + Return New IO.FileNotFoundException(GetResourceString(ResourceID, PlaceHolders), FileName) + End Function + + '''************************************************************************** + ''' ;GetInvalidOperationException + ''' + ''' Return a new instance of InvalidOperationException with the message from resource file. + ''' + ''' The resource ID. Use CompilerServices.ResID.xxx + ''' Strings that will replace place holders in the resource string, if any. + ''' A new instance of InvalidOperationException. + Friend Shared Function GetInvalidOperationException( _ + ByVal ResourceID As String, ByVal ParamArray PlaceHolders() As String) As InvalidOperationException + + Return New InvalidOperationException(GetResourceString(ResourceID, PlaceHolders)) + End Function + + '''************************************************************************** + ''' ;GetIOException + ''' + ''' Return a new instance of IO.IOException with the message from resource file. + ''' + ''' The resource ID. Use CompilerServices.ResID.xxx + ''' Strings that will replace place holders in the resource string, if any. + ''' A new instance of IO.IOException. + Friend Shared Function GetIOException(ByVal ResourceID As String, ByVal ParamArray PlaceHolders() As String) As IO.IOException + + Return New IO.IOException(GetResourceString(ResourceID, PlaceHolders)) + End Function + +#If 0 Then ': Nobody is using this anymore (fxcop reported it) + '''************************************************************************** + ''' ;GetSecurityException + ''' + ''' Return a new instance of Security.SecurityException with the message from resource file. + ''' + ''' The resource ID. Use CompilerServices.ResID.xxx + ''' Strings that will replace place holders in the resource string, if any. + ''' A new instance of Security.SecurityException. + Friend Shared Function GetSecurityException(ByVal ResourceID As String, ByVal ParamArray PlaceHolders() As String) _ + As Security.SecurityException + + Return New Security.SecurityException(GetResourceString(ResourceID, PlaceHolders)) + End Function +#End If + + '''************************************************************************** + ''' ;GetWin32Exception + ''' + ''' Return a new instance of Win32Exception with the message from resource file and the last Win32 error. + ''' + ''' The resource ID. Use CompilerServices.ResID.xxx + ''' Strings that will replace place holders in the resource string, if any. + ''' A new instance of Win32Exception. + ''' There is no way to exclude the Win32 error so this function will call Marshal.GetLastWin32Error all the time. + _ + Friend Shared Function GetWin32Exception( _ + ByVal ResourceID As String, ByVal ParamArray PlaceHolders() As String) As ComponentModel.Win32Exception + + Return New ComponentModel.Win32Exception(Marshal.GetLastWin32Error(), GetResourceString(ResourceID, PlaceHolders)) + End Function +#End If + End Class + +#If TELESTO Then + 'FIXME _ + 'Note that objects aren't serializable in Telesto + Public NotInheritable Class InternalErrorException +#Else + _ + _ + Public NotInheritable Class InternalErrorException +#End If + + Inherits System.Exception + +#If Not TELESTO Then 'Telesto doesn't support serialization + ' FxCop: deserialization constructor must be defined as Private. + _ + Private Sub New(ByVal info As System.Runtime.Serialization.SerializationInfo, ByVal context As System.Runtime.Serialization.StreamingContext) + MyBase.New(info, context) + End Sub +#End If + +#If TELESTO Then + 'FIXME: + Public Sub New(ByVal message As String) +#Else + _ + Public Sub New(ByVal message As String) +#End If + MyBase.New(message) + End Sub + +#If TELESTO Then + 'FIXME: + Public Sub New(ByVal message As String, ByVal innerException As System.Exception) +#Else + _ + Public Sub New(ByVal message As String, ByVal innerException As System.Exception) +#End If + MyBase.New(message, innerException) + End Sub + + ' default constructor + Public Sub New() + MyBase.New(GetResourceString(ResID.InternalError)) + End Sub + End Class + +End Namespace diff --git a/Microsoft.VisualBasic/runtime/msvbalib/Helpers/FlowControl.vb b/Microsoft.VisualBasic/runtime/msvbalib/Helpers/FlowControl.vb new file mode 100644 index 000000000..2b0fd102b --- /dev/null +++ b/Microsoft.VisualBasic/runtime/msvbalib/Helpers/FlowControl.vb @@ -0,0 +1,766 @@ +' Copyright (c) Microsoft Corporation. All rights reserved. + +Imports System +Imports System.Diagnostics +Imports System.Reflection + +Imports Microsoft.VisualBasic.CompilerServices.ConversionResolution +Imports Microsoft.VisualBasic.CompilerServices.ExceptionUtils +Imports Microsoft.VisualBasic.CompilerServices.Symbols +Imports Microsoft.VisualBasic.CompilerServices.Utils + +Namespace Microsoft.VisualBasic.CompilerServices + +#Region " BACKWARDS COMPATIBILITY " + + 'WARNING WARNING WARNING WARNING WARNING + 'This code exists to support Everett compiled applications. Make sure you understand + 'the backwards compatibility ramifications of any edit you make in this region. + 'WARNING WARNING WARNING WARNING WARNING +#If Not TELESTO Then 'These helpers aren't necessary on Telesto because we don't have an Everett backwards compat issue there. + _ + Public NotInheritable Class FlowControl + ' Prevent creation. + Private Sub New() + End Sub + + Private NotInheritable Class ObjectFor + Public Counter As Object + Public Limit As Object + Public StepValue As Object + Public PositiveStep As Boolean + Public EnumType As Type + + Friend Sub New() + End Sub + End Class + + Public Shared Function ForNextCheckR4(ByVal count As Single, ByVal limit As Single, ByVal StepValue As Single) As Boolean + If StepValue > 0 Then + Return count <= limit + Else + Return count >= limit + End If + End Function + + Public Shared Function ForNextCheckR8(ByVal count As Double, ByVal limit As Double, ByVal StepValue As Double) As Boolean + If StepValue > 0 Then + Return count <= limit + Else + Return count >= limit + End If + End Function + + Public Shared Function ForNextCheckDec(ByVal count As Decimal, ByVal limit As Decimal, ByVal StepValue As Decimal) As Boolean + If System.Decimal.op_LessThan(StepValue, System.Decimal.Zero) Then + 'StepValue <= 0 + 'Return true if count >= limit + Return System.Decimal.op_GreaterThanOrEqual(count, limit) + Else + 'StepValue > 0 + 'Return true if count <= limit + Return System.Decimal.op_LessThanOrEqual(count, limit) + End If + End Function + + Public Shared Function ForLoopInitObj(ByVal Counter As Object, ByVal Start As Object, ByVal Limit As Object, ByVal StepValue As Object, ByRef LoopForResult As Object, ByRef CounterResult As Object) As Boolean + Dim typ As System.TypeCode + Dim LoopFor As ObjectFor + Dim icompare As IComparable + Dim CompareResult As Integer + Dim Zero As Object + + If (Start Is Nothing) Then + Throw New ArgumentException(GetResourceString(ResID.Argument_InvalidNullValue1, "Start")) + ElseIf (Limit Is Nothing) Then + Throw New ArgumentException(GetResourceString(ResID.Argument_InvalidNullValue1, "Limit")) + ElseIf (StepValue Is Nothing) Then + Throw New ArgumentException(GetResourceString(ResID.Argument_InvalidNullValue1, "Step")) + End If + + Dim StartType As Type = Start.GetType() + Dim LimitType As Type = Limit.GetType() + Dim StepType As Type = StepValue.GetType() + + typ = ObjectType.GetWidestType(Start, Limit) + typ = ObjectType.GetWidestType(StepValue, typ) + + If typ = TypeCode.String Then + typ = TypeCode.Double + End If + + If typ = TypeCode.Object Then + Throw New ArgumentException(GetResourceString(ResID.ForLoop_CommonType3, VBFriendlyName(StartType), VBFriendlyName(LimitType), VBFriendlyName(StepValue))) + End If + + LoopFor = New ObjectFor + + Dim StartTypeCode As TypeCode = Type.GetTypeCode(StartType) + Dim LimitTypeCode As TypeCode = Type.GetTypeCode(LimitType) + Dim StepTypeCode As TypeCode = Type.GetTypeCode(StepType) + + ' Funky. If one or more of the three values is an enum of the same underlying + ' type as the loop, and all of the enum types are the same, then make the type + ' of the loop the enum. + Dim CurrentEnumType As Type = Nothing + + If (StartTypeCode = typ) AndAlso StartType.IsEnum Then + CurrentEnumType = StartType + End If + + If (LimitTypeCode = typ) AndAlso LimitType.IsEnum Then + If (Not CurrentEnumType Is Nothing) AndAlso _ + (Not CurrentEnumType Is LimitType) Then + CurrentEnumType = Nothing + GoTo NotEnumType + End If + + CurrentEnumType = LimitType + End If + + If (StepTypeCode = typ) AndAlso StepType.IsEnum Then + If (Not CurrentEnumType Is Nothing) AndAlso _ + (Not CurrentEnumType Is StepType) Then + CurrentEnumType = Nothing + GoTo NotEnumType + End If + + CurrentEnumType = StepType + End If +NotEnumType: + LoopFor.EnumType = CurrentEnumType + + Try + LoopFor.Counter = ObjectType.CTypeHelper(Start, typ) + Catch ex As StackOverflowException + Throw ex + Catch ex As OutOfMemoryException + Throw ex + Catch ex As System.Threading.ThreadAbortException + Throw ex + Catch + Throw New ArgumentException(GetResourceString(ResID.ForLoop_ConvertToType3, "Start", VBFriendlyName(StartType), VBFriendlyName(ObjectType.TypeFromTypeCode(typ)))) + End Try + + Try + LoopFor.Limit = ObjectType.CTypeHelper(Limit, typ) + Catch ex As StackOverflowException + Throw ex + Catch ex As OutOfMemoryException + Throw ex + Catch ex As System.Threading.ThreadAbortException + Throw ex + Catch + Throw New ArgumentException(GetResourceString(ResID.ForLoop_ConvertToType3, "Limit", VBFriendlyName(LimitType), VBFriendlyName(ObjectType.TypeFromTypeCode(typ)))) + End Try + + Try + LoopFor.StepValue = ObjectType.CTypeHelper(StepValue, typ) + Catch ex As StackOverflowException + Throw ex + Catch ex As OutOfMemoryException + Throw ex + Catch ex As System.Threading.ThreadAbortException + Throw ex + Catch + Throw New ArgumentException(GetResourceString(ResID.ForLoop_ConvertToType3, "Step", VBFriendlyName(StepType), VBFriendlyName(ObjectType.TypeFromTypeCode(typ)))) + End Try + + 'Check and save whether this is a positive or negative step + Zero = ObjectType.CTypeHelper(0, typ) + icompare = CType(LoopFor.StepValue, IComparable) + CompareResult = icompare.CompareTo(Zero) + + If CompareResult >= 0 Then + LoopFor.PositiveStep = True + Else + LoopFor.PositiveStep = False + End If + + LoopForResult = LoopFor + If Not LoopFor.EnumType Is Nothing Then + CounterResult = System.Enum.ToObject(LoopFor.EnumType, LoopFor.Counter) + Else + CounterResult = LoopFor.Counter + End If + Return CheckContinueLoop(LoopFor) + End Function + + Public Shared Function ForNextCheckObj(ByVal Counter As Object, ByVal LoopObj As Object, ByRef CounterResult As Object) As Boolean + + Dim LoopFor As ObjectFor + + If LoopObj Is Nothing Then + Throw VbMakeException(vbErrors.IllegalFor) + End If + + If Counter Is Nothing Then + Throw New NullReferenceException(GetResourceString(ResID.Argument_InvalidNullValue1, "Counter")) + End If + + LoopFor = CType(LoopObj, ObjectFor) + + Dim type1, type2 As TypeCode + Dim WidestType, ResultType As TypeCode + + ' At this point, we know it's IConvertible + type1 = CType(Counter, IConvertible).GetTypeCode() + type2 = CType(LoopFor.StepValue, IConvertible).GetTypeCode() + + If (type1 = type2) AndAlso (Not type1 = TypeCode.String) Then + 'Nothing to do now + WidestType = type1 + Else + WidestType = ObjectType.GetWidestType(type1, type2) + If WidestType = TypeCode.String Then + WidestType = TypeCode.Double + End If + + If ResultType = TypeCode.Object Then + Throw New ArgumentException(GetResourceString(ResID.ForLoop_CommonType2, VBFriendlyName(ObjectType.TypeFromTypeCode(type1)), VBFriendlyName(ObjectType.TypeFromTypeCode(type2)))) + End If + + Try + Counter = ObjectType.CTypeHelper(Counter, WidestType) + Catch ex As StackOverflowException + Throw ex + Catch ex As OutOfMemoryException + Throw ex + Catch ex As System.Threading.ThreadAbortException + Throw ex + Catch + Throw New ArgumentException(GetResourceString(ResID.ForLoop_ConvertToType3, "Start", VBFriendlyName(Counter.GetType()), VBFriendlyName(ObjectType.TypeFromTypeCode(WidestType)))) + End Try + + Try + LoopFor.Limit = ObjectType.CTypeHelper(LoopFor.Limit, WidestType) + Catch ex As StackOverflowException + Throw ex + Catch ex As OutOfMemoryException + Throw ex + Catch ex As System.Threading.ThreadAbortException + Throw ex + Catch + Throw New ArgumentException(GetResourceString(ResID.ForLoop_ConvertToType3, "Limit", VBFriendlyName(LoopFor.Limit.GetType()), VBFriendlyName(ObjectType.TypeFromTypeCode(WidestType)))) + End Try + + Try + LoopFor.StepValue = ObjectType.CTypeHelper(LoopFor.StepValue, WidestType) + Catch ex As StackOverflowException + Throw ex + Catch ex As OutOfMemoryException + Throw ex + Catch ex As System.Threading.ThreadAbortException + Throw ex + Catch + Throw New ArgumentException(GetResourceString(ResID.ForLoop_ConvertToType3, "Step", VBFriendlyName(LoopFor.StepValue.GetType()), VBFriendlyName(ObjectType.TypeFromTypeCode(WidestType)))) + End Try + End If + + LoopFor.Counter = ObjectType.AddObj(Counter, LoopFor.StepValue) + ResultType = CType(LoopFor.Counter, IConvertible).GetTypeCode() + + If Not LoopFor.EnumType Is Nothing Then + CounterResult = System.Enum.ToObject(LoopFor.EnumType, LoopFor.Counter) + Else + CounterResult = LoopFor.Counter + End If + + If Not (ResultType = WidestType) Then + 'Overflow to bigger type occurred + LoopFor.Limit = ObjectType.CTypeHelper(LoopFor.Limit, ResultType) + LoopFor.StepValue = ObjectType.CTypeHelper(LoopFor.StepValue, ResultType) + 'If we overflow, then we should always be at the end of the loop + Return False + End If + + ForNextCheckObj = CheckContinueLoop(LoopFor) + End Function + + Public Shared Function ForEachInArr(ByVal ary As System.Array) As Collections.IEnumerator + Dim Result As Collections.IEnumerator = CType(ary, Collections.ICollection).GetEnumerator() + If Result Is Nothing Then + Throw VbMakeException(vbErrors.IllegalFor) + End If + Return Result + End Function + + Public Shared Function ForEachInObj(ByVal obj As Object) As Collections.IEnumerator + Dim Result As Collections.IEnumerator + Dim ienum As System.Collections.IEnumerable + + If obj Is Nothing Then + Throw VbMakeException(vbErrors.ObjNotSet) + End If + + 'Initialize the enumerator + Try + ienum = CType(obj, Collections.IEnumerable) + Catch ex As StackOverflowException + Throw ex + Catch ex As OutOfMemoryException + Throw ex + Catch ex As System.Threading.ThreadAbortException + Throw ex + Catch + Throw MakeException1(vbErrors.DoesntImplementICollection, obj.GetType.ToString) + End Try + + Result = ienum.GetEnumerator() 'Positioned before first element + + If Result Is Nothing Then + Throw MakeException1(vbErrors.DoesntImplementICollection, obj.GetType.ToString) + End If + + Return Result + End Function + + Public Shared Function ForEachNextObj(ByRef obj As Object, ByVal enumerator As Collections.IEnumerator) As Boolean + If enumerator.MoveNext() Then + obj = enumerator.Current + Return True + Else + obj = Nothing + Return False + End If + End Function + + Private Shared Function CheckContinueLoop(ByVal LoopFor As ObjectFor) As Boolean + Dim icompare As IComparable + Dim CompareResult As Integer + + Try + icompare = CType(LoopFor.Counter, IComparable) + CompareResult = icompare.CompareTo(LoopFor.Limit) + + If LoopFor.PositiveStep Then + If CompareResult <= 0 Then + Return True + Else + Return False + End If + Else + If CompareResult >= 0 Then + Return True + Else + Return False + End If + End If + + Catch ex As InvalidCastException + Throw New ArgumentException(GetResourceString(ResID.Argument_IComparable2, "loop control variable", VBFriendlyName(LoopFor.Counter))) + End Try + End Function + + Public Shared Sub CheckForSyncLockOnValueType(ByVal obj As Object) + If Not obj Is Nothing AndAlso obj.GetType.IsValueType() Then + Throw New ArgumentException(GetResourceString(ResID.SyncLockRequiresReferenceType1, VBFriendlyName(obj.GetType))) + End If + End Sub + + End Class +#End If 'not TELESTO +#End Region + + + + 'REVIEW VSW#395750: need to rewrite these FlowControl helpers to handle unsigned types. +#If TELESTO And Not NETCORE Then + 'FIXME: + Friend NotInheritable Class ObjectFlowControl +#Else + _ + Public NotInheritable Class ObjectFlowControl +#End If + + Private Sub New() + End Sub + +#If TELESTO Then + +#If NETCORE + Public Shared Sub CheckForSyncLockOnValueType(ByVal Expression As Object) +#Else + Friend Shared Sub CheckForSyncLockOnValueType(ByVal Expression As Object) +#End If + + If Expression IsNot Nothing AndAlso Expression.GetType.IsValueType() Then + Throw New ArgumentException( _ + GetResourceString(ResID.SyncLockRequiresReferenceType1, VBFriendlyName(Expression.GetType))) + End If + End Sub +#End If + +#If TELESTO And Not NETCORE Then + End Class + + Public NotInheritable Class LateBinderObjectFlowControl + Public NotInheritable Class ForLoopControl 'FIXME: +#Else + _ + Public NotInheritable Class ForLoopControl +#End If + Private Counter As Object + Private Limit As Object + Private StepValue As Object + Private PositiveStep As Boolean + Private EnumType As Type + Private WidestType As Type + Private WidestTypeCode As TypeCode + Private UseUserDefinedOperators As Boolean + Private OperatorPlus As Method + Private OperatorGreaterEqual As Method + Private OperatorLessEqual As Method + + + Private Sub New() + End Sub + + ' CONSIDER: Is there a better way of doing this? + Private Shared Function GetWidestType(ByVal Type1 As System.Type, ByVal Type2 As System.Type) As Type + If Type1 Is Nothing OrElse Type2 Is Nothing Then Return Nothing + + If Not Type1.IsEnum AndAlso Not Type2.IsEnum Then + Dim tc1 As TypeCode = GetTypeCode(Type1) + Dim tc2 As TypeCode = GetTypeCode(Type2) + + If IsNumericType(tc1) AndAlso IsNumericType(tc2) Then + Return MapTypeCodeToType(ForLoopWidestTypeCode(tc1)(tc2)) + End If + End If + + Dim LeftToRight As ConversionClass = ClassifyConversion(Type2, Type1, Nothing) + If LeftToRight = ConversionClass.Identity OrElse LeftToRight = ConversionClass.Widening Then + Return Type2 + End If + + Dim RightToLeft As ConversionClass = ClassifyConversion(Type1, Type2, Nothing) + If RightToLeft = ConversionClass.Widening Then + Return Type1 + End If + + Return Nothing + End Function + + Private Shared Function GetWidestType(ByVal Type1 As System.Type, ByVal Type2 As System.Type, ByVal Type3 As System.Type) As Type + Return GetWidestType(Type1, GetWidestType(Type2, Type3)) + End Function + + Private Shared Function ConvertLoopElement(ByVal ElementName As String, ByVal Value As Object, ByVal SourceType As Type, ByVal TargetType As Type) As Object + Try + Return Conversions.ChangeType(Value, TargetType) + Catch ex As AccessViolationException + Throw ex + Catch ex As StackOverflowException + Throw ex + Catch ex As OutOfMemoryException + Throw ex + Catch ex As System.Threading.ThreadAbortException + Throw ex + Catch + Throw New ArgumentException(GetResourceString(ResID.ForLoop_ConvertToType3, ElementName, VBFriendlyName(SourceType), VBFriendlyName(TargetType))) + End Try + End Function + + Private Shared Function VerifyForLoopOperator( _ + ByVal Op As UserDefinedOperator, _ + ByVal ForLoopArgument As Object, _ + ByVal ForLoopArgumentType As Type) As Method + + Dim OperatorMethod As Method = Operators.GetCallableUserDefinedOperator(Op, ForLoopArgument, ForLoopArgument) + + If OperatorMethod Is Nothing Then + Throw New ArgumentException(GetResourceString( _ + ResID.ForLoop_OperatorRequired2, _ + VBFriendlyNameOfType(ForLoopArgumentType, FullName:=True), _ + Symbols.OperatorNames(Op))) + End If + + Dim OperatorInfo As MethodInfo = TryCast(OperatorMethod.AsMethod, MethodInfo) + Dim Parameters As ParameterInfo() = OperatorInfo.GetParameters + + ' Validate the types + Select Case Op + Case UserDefinedOperator.Plus, UserDefinedOperator.Minus + If Parameters.Length <> 2 OrElse _ + Parameters(0).ParameterType IsNot ForLoopArgumentType OrElse _ + Parameters(1).ParameterType IsNot ForLoopArgumentType OrElse _ + OperatorInfo.ReturnType IsNot ForLoopArgumentType Then + Throw New ArgumentException(GetResourceString( _ + ResID.ForLoop_UnacceptableOperator2, _ + OperatorMethod.ToString, _ + VBFriendlyNameOfType(ForLoopArgumentType, FullName:=True))) + End If + + Case UserDefinedOperator.LessEqual, UserDefinedOperator.GreaterEqual + If Parameters.Length <> 2 OrElse _ + Parameters(0).ParameterType IsNot ForLoopArgumentType OrElse _ + Parameters(1).ParameterType IsNot ForLoopArgumentType Then + Throw New ArgumentException(GetResourceString( _ + ResID.ForLoop_UnacceptableRelOperator2, _ + OperatorMethod.ToString, _ + VBFriendlyNameOfType(ForLoopArgumentType, FullName:=True))) + End If + End Select + + Return OperatorMethod + End Function + + Public Shared Function ForLoopInitObj(ByVal Counter As Object, ByVal Start As Object, ByVal Limit As Object, ByVal StepValue As Object, ByRef LoopForResult As Object, ByRef CounterResult As Object) As Boolean + 'CONSIDER: Find a better way of doing the compare + Dim LoopFor As ForLoopControl + + If (Start Is Nothing) Then + Throw New ArgumentException(GetResourceString(ResID.Argument_InvalidNullValue1, "Start")) + ElseIf (Limit Is Nothing) Then + Throw New ArgumentException(GetResourceString(ResID.Argument_InvalidNullValue1, "Limit")) + ElseIf (StepValue Is Nothing) Then + Throw New ArgumentException(GetResourceString(ResID.Argument_InvalidNullValue1, "Step")) + End If + + Dim StartType As Type = Start.GetType() + Dim LimitType As Type = Limit.GetType() + Dim StepType As Type = StepValue.GetType() + + Dim WidestType As Type = GetWidestType(StepType, StartType, LimitType) + + If WidestType Is Nothing Then + Throw New ArgumentException(GetResourceString(ResID.ForLoop_CommonType3, VBFriendlyName(StartType), VBFriendlyName(LimitType), VBFriendlyName(StepValue))) + End If + + LoopFor = New ForLoopControl + + Dim WidestTypeCode As TypeCode = GetTypeCode(WidestType) + + ' If the widest typecode is Object, try to use user defined conversions. + If WidestTypeCode = TypeCode.Object Then + LoopFor.UseUserDefinedOperators = True + End If + + If WidestTypeCode = TypeCode.String Then + WidestTypeCode = TypeCode.Double + End If + + Dim StartTypeCode As TypeCode = Type.GetTypeCode(StartType) + Dim LimitTypeCode As TypeCode = Type.GetTypeCode(LimitType) + Dim StepTypeCode As TypeCode = Type.GetTypeCode(StepType) + + ' Funky. If one or more of the three values is an enum of the same underlying + ' type as the loop, and all of the enum types are the same, then make the type + ' of the loop the enum. + Dim CurrentEnumType As Type = Nothing + + If (StartTypeCode = WidestTypeCode) AndAlso StartType.IsEnum Then + CurrentEnumType = StartType + End If + + If (LimitTypeCode = WidestTypeCode) AndAlso LimitType.IsEnum Then + If (Not CurrentEnumType Is Nothing) AndAlso _ + (Not CurrentEnumType Is LimitType) Then + CurrentEnumType = Nothing + GoTo NotEnumType + End If + + CurrentEnumType = LimitType + End If + + If (StepTypeCode = WidestTypeCode) AndAlso StepType.IsEnum Then + If (Not CurrentEnumType Is Nothing) AndAlso _ + (Not CurrentEnumType Is StepType) Then + CurrentEnumType = Nothing + GoTo NotEnumType + End If + + CurrentEnumType = StepType + End If +NotEnumType: + LoopFor.EnumType = CurrentEnumType + + If Not LoopFor.UseUserDefinedOperators Then + LoopFor.WidestType = MapTypeCodeToType(WidestTypeCode) + Else + LoopFor.WidestType = WidestType + End If + + LoopFor.WidestTypeCode = WidestTypeCode + + LoopFor.Counter = ConvertLoopElement("Start", Start, StartType, LoopFor.WidestType) + LoopFor.Limit = ConvertLoopElement("Limit", Limit, LimitType, LoopFor.WidestType) + LoopFor.StepValue = ConvertLoopElement("Step", StepValue, StepType, LoopFor.WidestType) + + ' Verify that the required operators are present. + If LoopFor.UseUserDefinedOperators Then + LoopFor.OperatorPlus = VerifyForLoopOperator(UserDefinedOperator.Plus, LoopFor.Counter, LoopFor.WidestType) + VerifyForLoopOperator(UserDefinedOperator.Minus, LoopFor.Counter, LoopFor.WidestType) + LoopFor.OperatorLessEqual = VerifyForLoopOperator(UserDefinedOperator.LessEqual, LoopFor.Counter, LoopFor.WidestType) + LoopFor.OperatorGreaterEqual = VerifyForLoopOperator(UserDefinedOperator.GreaterEqual, LoopFor.Counter, LoopFor.WidestType) + End If + + 'Important: a Zero step is considered Positive. This is consistent with the early-bound behavior. + LoopFor.PositiveStep = Operators.ConditionalCompareObjectGreaterEqual( _ + LoopFor.StepValue, _ + Operators.SubtractObject(LoopFor.StepValue, LoopFor.StepValue), _ + False) + + LoopForResult = LoopFor + + If Not LoopFor.EnumType Is Nothing Then + CounterResult = System.Enum.ToObject(LoopFor.EnumType, LoopFor.Counter) + Else + CounterResult = LoopFor.Counter + End If + + Return CheckContinueLoop(LoopFor) + End Function + + Public Shared Function ForNextCheckObj(ByVal Counter As Object, ByVal LoopObj As Object, ByRef CounterResult As Object) As Boolean + + Dim LoopFor As ForLoopControl + + If LoopObj Is Nothing Then + Throw VbMakeException(vbErrors.IllegalFor) + End If + + If Counter Is Nothing Then + Throw New NullReferenceException(GetResourceString(ResID.Argument_InvalidNullValue1, "Counter")) + End If + + LoopFor = CType(LoopObj, ForLoopControl) + + Dim NeedToChangeType As Boolean = False + + If Not LoopFor.UseUserDefinedOperators Then + ' At this point, we know it's IConvertible + Dim CounterTypeCode As TypeCode = DirectCast(Counter, IConvertible).GetTypeCode() + + If CounterTypeCode <> LoopFor.WidestTypeCode OrElse CounterTypeCode = TypeCode.String Then + If CounterTypeCode = TypeCode.Object Then + Throw New ArgumentException(GetResourceString(ResID.ForLoop_CommonType2, VBFriendlyName(MapTypeCodeToType(CounterTypeCode)), VBFriendlyName(LoopFor.WidestType))) + Else + Dim WidestType As Type = GetWidestType(MapTypeCodeToType(CounterTypeCode), LoopFor.WidestType) + Dim WidestTypeCode As TypeCode = GetTypeCode(WidestType) + + If WidestTypeCode = TypeCode.String Then + WidestTypeCode = TypeCode.Double + End If + + LoopFor.WidestTypeCode = WidestTypeCode + LoopFor.WidestType = MapTypeCodeToType(WidestTypeCode) + NeedToChangeType = True + End If + End If + End If + + If NeedToChangeType OrElse LoopFor.UseUserDefinedOperators Then + Counter = ConvertLoopElement("Start", Counter, Counter.GetType(), LoopFor.WidestType) + + If Not LoopFor.UseUserDefinedOperators Then + LoopFor.Limit = ConvertLoopElement("Limit", LoopFor.Limit, LoopFor.Limit.GetType(), LoopFor.WidestType) + LoopFor.StepValue = ConvertLoopElement("Step", LoopFor.StepValue, LoopFor.StepValue.GetType(), LoopFor.WidestType) + End If + End If + + If Not LoopFor.UseUserDefinedOperators Then + LoopFor.Counter = Operators.AddObject(Counter, LoopFor.StepValue) + + Dim ResultTypeCode As TypeCode = CType(LoopFor.Counter, IConvertible).GetTypeCode() + + If Not LoopFor.EnumType Is Nothing Then + CounterResult = System.Enum.ToObject(LoopFor.EnumType, LoopFor.Counter) + Else + CounterResult = LoopFor.Counter + End If + + If ResultTypeCode <> LoopFor.WidestTypeCode Then + 'Overflow to bigger type occurred + LoopFor.Limit = Conversions.ChangeType(LoopFor.Limit, MapTypeCodeToType(ResultTypeCode)) + LoopFor.StepValue = Conversions.ChangeType(LoopFor.StepValue, MapTypeCodeToType(ResultTypeCode)) + 'If we overflow, then we should always be at the end of the loop + Return False + End If + Else + ' Execute addition. + LoopFor.Counter = Operators.InvokeUserDefinedOperator( _ + LoopFor.OperatorPlus, _ + True, _ + Counter, _ + LoopFor.StepValue) + + If LoopFor.Counter.GetType() IsNot LoopFor.WidestType Then + LoopFor.Counter = ConvertLoopElement("Start", LoopFor.Counter, LoopFor.Counter.GetType(), LoopFor.WidestType) + End If + + CounterResult = LoopFor.Counter + End If + + Return CheckContinueLoop(LoopFor) + End Function + + Public Shared Function ForNextCheckR4(ByVal count As Single, ByVal limit As Single, ByVal StepValue As Single) As Boolean + 'Important: a Zero step is considered Positive. This is consistent with integral For loops. + If StepValue >= 0 Then + Return count <= limit + Else + Return count >= limit + End If + End Function + + Public Shared Function ForNextCheckR8(ByVal count As Double, ByVal limit As Double, ByVal StepValue As Double) As Boolean + 'Important: a Zero step is considered Positive. This is consistent with integral For loops. + If StepValue >= 0 Then + Return count <= limit + Else + Return count >= limit + End If + End Function + + 'CONSIDER: If the compiler used operator overloading for Decimals in For loops, this function would no longer be needed. + Public Shared Function ForNextCheckDec(ByVal count As Decimal, ByVal limit As Decimal, ByVal StepValue As Decimal) As Boolean + 'Important: a Zero step is considered Positive. This is consistent with integral For loops. + If StepValue >= 0 Then + Return count <= limit + Else + Return count >= limit + End If + End Function + + Private Shared Function CheckContinueLoop(ByVal LoopFor As ForLoopControl) As Boolean + + If Not LoopFor.UseUserDefinedOperators Then + Dim icompare As IComparable + Dim CompareResult As Integer + + Try + icompare = CType(LoopFor.Counter, IComparable) + CompareResult = icompare.CompareTo(LoopFor.Limit) + + If LoopFor.PositiveStep Then + Return CompareResult <= 0 + Else + Return CompareResult >= 0 + End If + + Catch ex As InvalidCastException + Throw New ArgumentException(GetResourceString(ResID.Argument_IComparable2, "loop control variable", VBFriendlyName(LoopFor.Counter))) + End Try + Else + If LoopFor.PositiveStep Then + Return CBool(Operators.InvokeUserDefinedOperator( _ + LoopFor.OperatorLessEqual, _ + True, _ + LoopFor.Counter, _ + LoopFor.Limit)) + Else + Return CBool(Operators.InvokeUserDefinedOperator( _ + LoopFor.OperatorGreaterEqual, _ + True, _ + LoopFor.Counter, _ + LoopFor.Limit)) + End If + End If + End Function + + End Class + End Class + +End Namespace diff --git a/Microsoft.VisualBasic/runtime/msvbalib/Helpers/ForEachEnum.vb b/Microsoft.VisualBasic/runtime/msvbalib/Helpers/ForEachEnum.vb new file mode 100644 index 000000000..ddef595a1 --- /dev/null +++ b/Microsoft.VisualBasic/runtime/msvbalib/Helpers/ForEachEnum.vb @@ -0,0 +1,184 @@ +' Copyright (c) Microsoft Corporation. All rights reserved. + +Imports System +Imports System.Collections +Imports System.Diagnostics +Imports Microsoft.VisualBasic.CompilerServices + +Namespace Microsoft.VisualBasic + + 'This is in the helpers directory but not in the compilerservices namespace + + 'The ForEachEnum class is publicly exposed through Collection.GetEnumerator(). +#If TELESTO Then + Friend NotInheritable Class ForEachEnum 'FIXME: +#Else + _ + Friend NotInheritable Class ForEachEnum +#End If + + Implements IEnumerator + Implements IDisposable + + 'No Finalize - this is intentional because we do not want to invoke RemoveIterator in finalize + ' because of threading and synchronization issues involved which would then cause + ' perf degrade. + + Private mDisposed As Boolean = False + Private Sub Dispose() Implements IDisposable.Dispose + If Not mDisposed Then + mCollectionObject.RemoveIterator(WeakRef) + mDisposed = True + End If + mCurrent = Nothing + mNext = Nothing + End Sub + + 'The collection this enumerator is enumerating over + Private mCollectionObject As Microsoft.VisualBasic.Collection + + 'The current element being iterated. Note: this element may no longer be in the list, + ' (if it was deleted), so do *not* assume that its previous and next pointers are valid. + Private mCurrent As Collection.Node + + 'The next item to enumerate. This is always updated on MoveNext and is used to determine where + ' the enumeration goes next (not mCurrent). + Private mNext As Collection.Node + + 'A flag indicating that we are ready to start enumerating but have not done so yet (allows us + ' to delay fixing mNext until the next MoveNext). + Private mAtBeginning As Boolean + + Friend WeakRef As WeakReference + + Public Sub New(ByVal coll As Microsoft.VisualBasic.Collection) + MyBase.New() + mCollectionObject = coll + + Reset() + End Sub + + Public Function MoveNext() As Boolean Implements IEnumerator.MoveNext + If mDisposed Then + Return False + End If + + If mAtBeginning Then + 'We haven't started iterating yet. Start at the beginning. + + mAtBeginning = False + mNext = mCollectionObject.GetFirstListNode() + End If + + Debug.Assert(Not mAtBeginning) + If mNext Is Nothing Then + Dispose() + Return False + End If + + mCurrent = mNext + If mCurrent IsNot Nothing Then + mNext = mCurrent.m_Next + Return True + Else + Debug.Assert(mNext Is Nothing) + Dispose() + Return False + End If + End Function + + Public Sub Reset() Implements IEnumerator.Reset + If mDisposed Then + mCollectionObject.AddIterator(WeakRef) + mDisposed = False + End If + + mCurrent = Nothing + mNext = Nothing + mAtBeginning = True + End Sub + + Public ReadOnly Property Current() As Object Implements IEnumerator.Current + Get + If mCurrent Is Nothing Then + Return Nothing + Else + Return mCurrent.m_Value + End If + End Get + End Property + + Friend Enum AdjustIndexType + Insert + Remove + End Enum + + 'REVIEW VSW#395751 : This a public function on a friend class. Does it need to be public? How can the customer call this function today? Latebinding/Reflection... + + 'Adjusts the enumerator to account for newly-inserted or removed items in/from the collection. + ' For insertion, this call must be made *after* the insertion has taken place. For deletion, + ' this call must have been made before the next/prev pointers in the deleted node have been + ' invalidated (they must still be pointing to the values before the deletion). + Public Sub Adjust(ByVal Node As Collection.Node, ByVal Type As AdjustIndexType) + + If Node Is Nothing Then +#If TELESTO Then + Debug.Assert(False, "Node shouldn't be nothing") +#Else + Debug.Fail("Node shouldn't be nothing") +#End If + Exit Sub 'defensive + End If + + If mDisposed Then + 'Nothing to do + Exit Sub + End If + + Select Case Type + Case AdjustIndexType.Insert + Debug.Assert(Node IsNot mCurrent, "If we just inserted Node, then it couldn't be the current node because it's not in the list yet") + + 'mCurrent may not necessarily be still in the list, so we have to be wary of using mCurrent.m_Next. However, if + ' there is a current node, and its next is pointing to Node, then mCurrent must still be in the list (since Node wasn't + ' in the list before). So in this case we'll go ahead and set our mNext to the newly-inserted node. + 'It would seem to make sense also to set mNext to the new node if mNext is Nothing (i.e., we're at the end of the list), but + ' this would be a difference in RTM/Everett behavior that doesn't seem warranted. + If mCurrent IsNot Nothing AndAlso Node Is mCurrent.m_Next Then + 'The new node was inserted right after our current node. + ' Iterate through it next. + mNext = Node + End If + + 'Note that the case of inserting at the beginning before we've + ' iterated through any nodes will be handled in MoveNext with the + ' mAtBeginning flag. + + Case AdjustIndexType.Remove + If Node Is mCurrent Then + 'Current node was removed. No need to do anything, because we want GetCurrent() to continue to + ' return this same node's data until the next MoveNext(). + ElseIf Node Is mNext Then + 'The next node was removed. Make our next node to iterate through + ' be the one after that instead + mNext = mNext.m_Next + End If + Case Else +#If TELESTO Then + Debug.Assert(False, "Unexpected adjustment type in enumerator") ' Silverlight CLR does not have Debug.Fail. +#Else + Debug.Fail("Unexpected adjustment type in enumerator") +#End If + End Select + End Sub + + 'Should be called if the list this is enumerating is cleared. It will set the + ' enumerator past the end of the list (nothing more to enumerate). + Friend Sub AdjustOnListCleared() + mNext = Nothing + End Sub + + End Class + +End Namespace + diff --git a/Microsoft.VisualBasic/runtime/msvbalib/Helpers/Hosting.vb b/Microsoft.VisualBasic/runtime/msvbalib/Helpers/Hosting.vb new file mode 100644 index 000000000..b8da3ce72 --- /dev/null +++ b/Microsoft.VisualBasic/runtime/msvbalib/Helpers/Hosting.vb @@ -0,0 +1,34 @@ +' Copyright (c) Microsoft Corporation. All rights reserved. + +Imports System +Imports System.Security.Permissions + +Namespace Microsoft.VisualBasic.CompilerServices + + _ + Public Interface IVbHost + Function GetParentWindow() As System.Windows.Forms.IWin32Window + Function GetWindowTitle() As String + End Interface + + _ + _ + Public NotInheritable Class HostServices + + Private Shared m_host As IVbHost + + Public Shared Property VBHost() As IVbHost + Get + Return m_host + End Get + + Set(ByVal Value As IVbHost) + m_host = Value + End Set + End Property + + End Class + +End Namespace + + diff --git a/Microsoft.VisualBasic/runtime/msvbalib/Helpers/IDOBinder.vb b/Microsoft.VisualBasic/runtime/msvbalib/Helpers/IDOBinder.vb new file mode 100644 index 000000000..d75126e6f --- /dev/null +++ b/Microsoft.VisualBasic/runtime/msvbalib/Helpers/IDOBinder.vb @@ -0,0 +1,1552 @@ +' Copyright (c) Microsoft Corporation. All rights reserved. + +Imports System +Imports System.Collections.Generic +Imports System.Collections.ObjectModel +Imports System.Diagnostics +Imports System.Dynamic +Imports System.Linq.Expressions +Imports System.Reflection +Imports System.Runtime.CompilerServices + +Imports Microsoft.VisualBasic.CompilerServices.NewLateBinding +Imports Microsoft.VisualBasic.CompilerServices.Symbols + +Namespace Microsoft.VisualBasic.CompilerServices + + Friend Class IDOBinder + + Private Sub New() + Throw New InternalErrorException() + End Sub + + Private Structure SaveCopyBack + Implements IDisposable + + ' We need to pass the CopyBack value from the VB binder through + ' the DLR and into the Fallback. Unfortunately the DLR APIs provide + ' no obvious way to get the value from one place to the other. So + ' we store its value in a ThreadLocal here. + _ + Private Shared SavedCopyBack As Boolean() + + Private oldCopyBack As Boolean() + + Public Sub New(ByVal copyBack As Boolean()) + ' Save values of thread statics + oldCopyBack = SavedCopyBack + + ' Set new values + SavedCopyBack = copyBack + End Sub + + Public Sub Dispose() Implements System.IDisposable.Dispose + ' Restore values of thread statics + SavedCopyBack = oldCopyBack + End Sub + + Friend Shared Function GetCopyBack() As Boolean() + Return SavedCopyBack + End Function + End Structure + + ' A sentinel returned when no such member is found. + Friend Shared ReadOnly missingMemberSentinel As Object = New Object() + + + Friend Shared Function GetCopyBack() As Boolean() + Return SaveCopyBack.GetCopyBack() + End Function + + Friend Shared Function IDOCall( _ + ByVal Instance As IDynamicMetaObjectProvider, _ + ByVal MemberName As String, _ + ByVal Arguments As Object(), _ + ByVal ArgumentNames As String(), _ + ByVal CopyBack As Boolean(), _ + ByVal IgnoreReturn As Boolean) As Object + + Dim s As New SaveCopyBack(CopyBack) + Using s + Dim CallInfo As CallInfo = Nothing + Dim PackedArguments As Object() = Nothing + IDOUtils.PackArguments(0, ArgumentNames, Arguments, PackedArguments, CallInfo) + Try + Return IDOUtils.CreateRefCallSiteAndInvoke( _ + New VBCallBinder(MemberName, CallInfo, IgnoreReturn), _ + Instance, PackedArguments) + Finally + IDOUtils.CopyBackArguments(CallInfo, PackedArguments, Arguments) + End Try + End Using + End Function 'IDOCall + + Friend Shared Function IDOGet( _ + ByVal Instance As IDynamicMetaObjectProvider, _ + ByVal MemberName As String, _ + ByVal Arguments As Object(), _ + ByVal ArgumentNames As String(), _ + ByVal CopyBack As Boolean()) As Object + + Dim s As New SaveCopyBack(CopyBack) + Using s + Dim PackedArguments As Object() = Nothing + Dim CallInfo As CallInfo = Nothing + IDOUtils.PackArguments(0, ArgumentNames, Arguments, PackedArguments, CallInfo) + Try + Return IDOUtils.CreateRefCallSiteAndInvoke( _ + New VBGetBinder(MemberName, CallInfo), _ + Instance, PackedArguments) + Finally + IDOUtils.CopyBackArguments(CallInfo, PackedArguments, Arguments) + End Try + End Using + End Function 'IDOGet + + Friend Shared Function IDOInvokeDefault( _ + ByVal Instance As IDynamicMetaObjectProvider, _ + ByVal Arguments As Object(), _ + ByVal ArgumentNames As String(), _ + ByVal ReportErrors As Boolean, _ + ByVal CopyBack As Boolean()) As Object + + Dim s As New SaveCopyBack(CopyBack) + Using s + Dim PackedArguments As Object() = Nothing + Dim CallInfo As CallInfo = Nothing + IDOUtils.PackArguments(0, ArgumentNames, Arguments, PackedArguments, CallInfo) + Try + Return IDOUtils.CreateRefCallSiteAndInvoke( _ + New VBInvokeDefaultBinder(CallInfo, ReportErrors), _ + Instance, PackedArguments) + Finally + IDOUtils.CopyBackArguments(CallInfo, PackedArguments, Arguments) + End Try + End Using + End Function 'IDOInvokeDefault + + Friend Shared Function IDOFallbackInvokeDefault( _ + ByVal Instance As IDynamicMetaObjectProvider, _ + ByVal Arguments As Object(), _ + ByVal ArgumentNames As String(), _ + ByVal ReportErrors As Boolean, _ + ByVal CopyBack As Boolean()) As Object + + Dim s As New SaveCopyBack(CopyBack) + Using s + Dim PackedArguments As Object() = Nothing + Dim CallInfo As CallInfo = Nothing + IDOUtils.PackArguments(0, ArgumentNames, Arguments, PackedArguments, CallInfo) + Try + Return IDOUtils.CreateRefCallSiteAndInvoke( _ + New VBInvokeDefaultFallbackBinder(CallInfo, ReportErrors), _ + Instance, PackedArguments) + Finally + IDOUtils.CopyBackArguments(CallInfo, PackedArguments, Arguments) + End Try + End Using + + End Function 'IDOFallbackInvokeDefault + + Friend Shared Sub IDOSet( _ + ByVal Instance As IDynamicMetaObjectProvider, _ + ByVal MemberName As String, _ + ByVal ArgumentNames() As String, _ + ByVal Arguments As Object()) + + Dim s As New SaveCopyBack(Nothing) + Using s + If Arguments.Length = 1 Then + IDOUtils.CreateFuncCallSiteAndInvoke( _ + New VBSetBinder(MemberName), Instance, Arguments) + Else + ' Look for a DLR member that might be an array + Dim member As Object = IDOUtils.CreateFuncCallSiteAndInvoke( _ + New VBGetMemberBinder(MemberName), Instance, NoArguments) + If member Is IDOBinder.missingMemberSentinel Then ' found no DLR member by this name + NewLateBinding.ObjectLateSet( _ + Instance, Nothing, MemberName, Arguments, ArgumentNames, NoTypeArguments) + Else + ' Treat the found DLR member as an array + NewLateBinding.LateIndexSet(member, Arguments, ArgumentNames) + End If + End If + End Using + End Sub 'IDOSet + + Friend Shared Sub IDOSetComplex( _ + ByVal Instance As IDynamicMetaObjectProvider, _ + ByVal MemberName As String, _ + ByVal Arguments As Object(), _ + ByVal ArgumentNames As String(), _ + ByVal OptimisticSet As Boolean, _ + ByVal RValueBase As Boolean) + + Dim s As New SaveCopyBack(Nothing) + Using s + If Arguments.Length = 1 Then + IDOUtils.CreateFuncCallSiteAndInvoke( _ + New VBSetComplexBinder(MemberName, OptimisticSet, RValueBase), Instance, Arguments) + Else + ' Look for a DLR member that might be an array + Dim member As Object = IDOUtils.CreateFuncCallSiteAndInvoke( _ + New VBGetMemberBinder(MemberName), Instance, NoArguments) + If member Is IDOBinder.missingMemberSentinel Then ' found no DLR member by this name + NewLateBinding.ObjectLateSetComplex( _ + Instance, Nothing, MemberName, Arguments, _ + ArgumentNames, NoTypeArguments, OptimisticSet, RValueBase) + Else + ' Treat the found DLR member as an array + NewLateBinding.LateIndexSetComplex( _ + member, Arguments, ArgumentNames, OptimisticSet, RValueBase) + End If + End If + End Using + End Sub 'IDOSetComplex + + Friend Shared Sub IDOIndexSet( _ + ByVal Instance As IDynamicMetaObjectProvider, _ + ByVal Arguments As Object(), _ + ByVal ArgumentNames As String()) + + Dim s As New SaveCopyBack(Nothing) + Using s + Dim PackedArguments As Object() = Nothing + Dim CallInfo As CallInfo = Nothing + IDOUtils.PackArguments(1, ArgumentNames, Arguments, PackedArguments, CallInfo) + IDOUtils.CreateFuncCallSiteAndInvoke( _ + New VBIndexSetBinder(CallInfo), _ + Instance, PackedArguments) + End Using + End Sub 'IDOIndexSet + + Friend Shared Sub IDOIndexSetComplex( _ + ByVal Instance As IDynamicMetaObjectProvider, _ + ByVal Arguments As Object(), _ + ByVal ArgumentNames As String(), _ + ByVal OptimisticSet As Boolean, _ + ByVal RValueBase As Boolean) + + Dim s As New SaveCopyBack(Nothing) + Using s + Dim PackedArguments As Object() = Nothing + Dim CallInfo As CallInfo = Nothing + IDOUtils.PackArguments(1, ArgumentNames, Arguments, PackedArguments, CallInfo) + IDOUtils.CreateFuncCallSiteAndInvoke( _ + New VBIndexSetComplexBinder(CallInfo, OptimisticSet, RValueBase), _ + Instance, PackedArguments) + End Using + End Sub 'IDOIndexSetComplex + + Friend Shared Function UserDefinedConversion( _ + ByVal Expression As IDynamicMetaObjectProvider, _ + ByVal TargetType As System.Type) As Object + + Return IDOUtils.CreateConvertCallSiteAndInvoke( _ + New VBConversionBinder(TargetType), _ + Expression) + End Function 'UserDefinedConversion + + Friend Shared Function InvokeUserDefinedOperator( _ + ByVal Op As UserDefinedOperator, _ + ByVal Arguments As Object()) As Object + + Dim linqOp As ExpressionType? = IDOUtils.LinqOperator(Op) + If linqOp Is Nothing Then + Return Operators.InvokeObjectUserDefinedOperator(Op, Arguments) + Else + Dim linqOperator As ExpressionType = CType(linqOp, ExpressionType) + Dim opBinder As CallSiteBinder + If Arguments.Length = 1 Then + opBinder = New VBUnaryOperatorBinder(Op, linqOperator) + Else + opBinder = New VBBinaryOperatorBinder(Op, linqOperator) + End If + Dim Instance As Object = Arguments(0) + Dim Args As Object() = _ + If(Arguments.Length = 1, NoArguments, New Object() {Arguments(1)}) + Return IDOUtils.CreateFuncCallSiteAndInvoke(opBinder, Instance, Args) + End If + End Function 'InvokeUserDefinedOperator + End Class 'IDOBinder + + Friend Class VBCallBinder + Inherits InvokeMemberBinder + + Private ReadOnly _ignoreReturn As Boolean + + Sub New(ByVal MemberName As String, _ + ByVal CallInfo As CallInfo, _ + ByVal IgnoreReturn As Boolean) + + MyBase.New(MemberName, True, CallInfo) + _ignoreReturn = IgnoreReturn + End Sub + + Public Overloads Overrides Function FallbackInvokeMember( _ + ByVal target As DynamicMetaObject, _ + ByVal packedArgs() As DynamicMetaObject, _ + ByVal errorSuggestion As DynamicMetaObject) As DynamicMetaObject + + If IDOUtils.NeedsDeferral(target, packedArgs) Then + Return Me.Defer(target, packedArgs) + End If + + Dim arguments As Expression() = Nothing + Dim argNames As String() = Nothing + Dim argValues As Object() = Nothing + IDOUtils.UnpackArguments(packedArgs, Me.CallInfo, arguments, argNames, argValues) + + If errorSuggestion IsNot Nothing AndAlso Not CanBindCall(target.Value, Name, argValues, argNames, _ignoreReturn) Then + Return errorSuggestion 'Binding will fail; use the IDO-provided error + End If + + Dim result As ParameterExpression = Expression.Variable(GetType(Object), "result") + Dim array As ParameterExpression = Expression.Variable(GetType(Object()), "array") + + Dim fallback As Expression = _ + Expression.Call( _ + GetType(NewLateBinding).GetMethod("FallbackCall"), _ + target.Expression(), _ + Expression.Constant(Name, GetType(String)), _ + Expression.Assign( _ + array, _ + Expression.NewArrayInit(GetType(Object), arguments) _ + ), _ + Expression.Constant(argNames, GetType(String())), _ + Expression.Constant(_ignoreReturn, GetType(Boolean)) _ + ) + + Return New DynamicMetaObject( _ + Expression.Block( _ + New ParameterExpression() {result, array}, _ + Expression.Assign(result, fallback), _ + IDOUtils.GetWriteBack(arguments, array), _ + result _ + ), _ + IDOUtils.CreateRestrictions(target, packedArgs) _ + ) + End Function 'FallbackInvokeMember + + Public Overloads Overrides Function FallbackInvoke( _ + ByVal target As DynamicMetaObject, _ + ByVal packedArgs() As DynamicMetaObject, _ + ByVal errorSuggestion As DynamicMetaObject) As DynamicMetaObject + + Return New VBInvokeBinder(Me.CallInfo, True).FallbackInvoke(target, packedArgs, errorSuggestion) + End Function 'FallbackInvoke + + ' Implement value equality. This is used so we can discover previously produced rules. + ' See comment at IOUtils.GetCachedBinder, which explains the caching in more detail. + Public Overrides Function Equals(ByVal _other As Object) As Boolean + Dim other As VBCallBinder = TryCast(_other, VBCallBinder) + Return other IsNot Nothing AndAlso String.Equals(Name, other.Name) AndAlso CallInfo.Equals(other.CallInfo) AndAlso _ignoreReturn = other._ignoreReturn + End Function + + Private Shared ReadOnly _hash As Integer = GetType(VBCallBinder).GetHashCode() + Public Overrides Function GetHashCode() As Integer + Return _hash Xor Name.GetHashCode() Xor CallInfo.GetHashCode() Xor _ignoreReturn.GetHashCode() + End Function + End Class 'VBCallBinder + + Friend Class VBGetBinder + Inherits InvokeMemberBinder + + Sub New(ByVal MemberName As String, _ + ByVal CallInfo As CallInfo) + MyBase.New(MemberName, True, CallInfo) + End Sub 'New + + Public Overloads Overrides Function FallbackInvokeMember( _ + ByVal target As DynamicMetaObject, _ + ByVal packedArgs() As DynamicMetaObject, _ + ByVal errorSuggestion As DynamicMetaObject) As DynamicMetaObject + + If IDOUtils.NeedsDeferral(target, packedArgs) Then + Return Me.Defer(target, packedArgs) + End If + + Dim arguments As Expression() = Nothing + Dim argNames As String() = Nothing + Dim argValues As Object() = Nothing + IDOUtils.UnpackArguments(packedArgs, Me.CallInfo, arguments, argNames, argValues) + + If errorSuggestion IsNot Nothing AndAlso Not CanBindGet(target.Value, Name, argValues, argNames) Then + Return errorSuggestion 'Binding will fail; use the IDO-provided error + End If + + Dim result As ParameterExpression = Expression.Variable(GetType(Object), "result") + Dim array As ParameterExpression = Expression.Variable(GetType(Object()), "array") + + Dim fallback As Expression = _ + Expression.Call( _ + GetType(NewLateBinding).GetMethod("FallbackGet"), _ + target.Expression(), _ + Expression.Constant(Name), _ + Expression.Assign( _ + array, _ + Expression.NewArrayInit(GetType(Object), arguments) _ + ), _ + Expression.Constant(argNames, GetType(String())) _ + ) + + Return New DynamicMetaObject( _ + Expression.Block( _ + New ParameterExpression() {result, array}, _ + Expression.Assign(result, fallback), _ + IDOUtils.GetWriteBack(arguments, array), _ + result _ + ), _ + IDOUtils.CreateRestrictions(target, packedArgs) _ + ) + End Function 'FallbackInvokeMember + + Public Overrides Function FallbackInvoke( _ + ByVal target As DynamicMetaObject, _ + ByVal packedArgs() As DynamicMetaObject, _ + ByVal errorSuggestion As DynamicMetaObject) As DynamicMetaObject + + Return New VBInvokeBinder(Me.CallInfo, False).FallbackInvoke(target, packedArgs, errorSuggestion) + End Function 'FallbackInvoke + + ' Implement value equality. This is used so we can discover previously produced rules. + ' See comment at IOUtils.GetCachedBinder, which explains the caching in more detail. + Public Overrides Function Equals(ByVal _other As Object) As Boolean + Dim other As VBGetBinder = TryCast(_other, VBGetBinder) + Return other IsNot Nothing AndAlso String.Equals(Name, other.Name) AndAlso CallInfo.Equals(other.CallInfo) + End Function + + Private Shared ReadOnly _hash As Integer = GetType(VBGetBinder).GetHashCode() + Public Overrides Function GetHashCode() As Integer + Return _hash Xor Name.GetHashCode() Xor CallInfo.GetHashCode() + End Function + End Class 'VBGetBinder + + + ' Implements FallbackInvoke for VBCallBinder and VBGetBinder + Class VBInvokeBinder + Inherits InvokeBinder + + ' True if this is coming from LateCall, false if it's for LateGet + Private ReadOnly _lateCall As Boolean + + Public Sub New(ByVal CallInfo As CallInfo, ByVal LateCall As Boolean) + MyBase.New(CallInfo) + _lateCall = LateCall + End Sub + + Public Overloads Overrides Function FallbackInvoke( _ + ByVal target As DynamicMetaObject, _ + ByVal packedArgs() As DynamicMetaObject, _ + ByVal errorSuggestion As DynamicMetaObject) As DynamicMetaObject + + If IDOUtils.NeedsDeferral(target, packedArgs) Then + Return Me.Defer(target, packedArgs) + End If + + ' The DLR resolved o.member, but not o.member(args). We need to apply + ' the default action. If there are no args and no default action, though, + ' it's an error (hence ReportErrors = True). These semantics are embedded in + ' a new internal-only entry point, "LateCallInvokeDefault". + + Dim arguments As Expression() = Nothing + Dim argNames As String() = Nothing + Dim argValues As Object() = Nothing + IDOUtils.UnpackArguments(packedArgs, Me.CallInfo, arguments, argNames, argValues) + + If errorSuggestion IsNot Nothing AndAlso Not CanBindInvokeDefault(target.Value, argValues, argNames, _lateCall) Then + Return errorSuggestion 'Use the IDO-provided error + End If + + Dim result As ParameterExpression = Expression.Variable(GetType(Object), "result") + Dim array As ParameterExpression = Expression.Variable(GetType(Object()), "array") + + Dim fallback As Expression = Expression.Call( _ + GetType(NewLateBinding).GetMethod(If(_lateCall, "LateCallInvokeDefault", "LateGetInvokeDefault")), _ + target.Expression(), _ + Expression.Assign( _ + array, _ + Expression.NewArrayInit(GetType(Object), arguments) _ + ), _ + Expression.Constant(argNames, GetType(String())), _ + Expression.Constant(_lateCall) _ + ) + + Return New DynamicMetaObject( _ + Expression.Block( _ + New ParameterExpression() {result, array}, _ + Expression.Assign(result, fallback), _ + IDOUtils.GetWriteBack(arguments, array), _ + result _ + ), _ + IDOUtils.CreateRestrictions(target, packedArgs) _ + ) + End Function 'FallbackInvoke + + ' Implement value equality. This is used so we can discover previously produced rules. + ' See comment at IOUtils.GetCachedBinder, which explains the caching in more detail. + Public Overrides Function Equals(ByVal _other As Object) As Boolean + Dim other As VBInvokeBinder = TryCast(_other, VBInvokeBinder) + Return other IsNot Nothing AndAlso CallInfo.Equals(other.CallInfo) AndAlso _lateCall.Equals(other._lateCall) + End Function + + Private Shared ReadOnly _hash As Integer = GetType(VBGetBinder).GetHashCode() + Public Overrides Function GetHashCode() As Integer + Return _hash Xor CallInfo.GetHashCode() Xor _lateCall.GetHashCode() + End Function + End Class + + Class VBInvokeDefaultBinder + Inherits InvokeBinder + + Private ReadOnly _reportErrors As Boolean + + Sub New(ByVal CallInfo As CallInfo, ByVal ReportErrors As Boolean) + MyBase.New(CallInfo) + Me._reportErrors = ReportErrors + End Sub 'New + + Public Overloads Overrides Function FallbackInvoke( _ + ByVal target As DynamicMetaObject, _ + ByVal packedArgs As DynamicMetaObject(), _ + ByVal errorSuggestion As DynamicMetaObject) As DynamicMetaObject + + If IDOUtils.NeedsDeferral(target, packedArgs) Then + Return Me.Defer(target, packedArgs) + End If + + Dim arguments As Expression() = Nothing + Dim argNames As String() = Nothing + Dim argValues As Object() = Nothing + IDOUtils.UnpackArguments(packedArgs, Me.CallInfo, arguments, argNames, argValues) + + If errorSuggestion IsNot Nothing AndAlso Not CanBindInvokeDefault(target.Value, argValues, argNames, _reportErrors) Then + Return errorSuggestion 'Use the IDO-provided error + End If + + Dim result As ParameterExpression = Expression.Variable(GetType(Object), "result") + Dim array As ParameterExpression = Expression.Variable(GetType(Object()), "array") + + Dim fallback As Expression = Expression.Call( _ + GetType(NewLateBinding).GetMethod("FallbackInvokeDefault1"), _ + target.Expression(), _ + Expression.Assign( _ + array, _ + Expression.NewArrayInit(GetType(Object), arguments) _ + ), _ + Expression.Constant(argNames, GetType(String())), _ + Expression.Constant(_reportErrors) _ + ) + + Return New DynamicMetaObject( _ + Expression.Block( _ + New ParameterExpression() {result, array}, _ + Expression.Assign(result, fallback), _ + IDOUtils.GetWriteBack(arguments, array), _ + result _ + ), _ + IDOUtils.CreateRestrictions(target, packedArgs) _ + ) + End Function 'FallbackInvoke + + ' Implement value equality. This is used so we can discover previously produced rules. + ' See comment at IOUtils.GetCachedBinder, which explains the caching in more detail. + Public Overrides Function Equals(ByVal _other As Object) As Boolean + Dim other As VBInvokeDefaultBinder = TryCast(_other, VBInvokeDefaultBinder) + Return other IsNot Nothing AndAlso CallInfo.Equals(other.CallInfo) AndAlso _reportErrors = other._reportErrors + End Function + + Private Shared ReadOnly _hash As Integer = GetType(VBInvokeDefaultBinder).GetHashCode() + Public Overrides Function GetHashCode() As Integer + Return _hash Xor CallInfo.GetHashCode() Xor _reportErrors.GetHashCode() + End Function + End Class 'VBInvokeDefaultBinder + + Class VBInvokeDefaultFallbackBinder + Inherits GetIndexBinder + + Private ReadOnly _reportErrors As Boolean + + Sub New(ByVal CallInfo As CallInfo, ByVal ReportErrors As Boolean) + MyBase.New(CallInfo) + Me._reportErrors = ReportErrors + End Sub 'New + + Public Overrides Function FallbackGetIndex( _ + ByVal target As DynamicMetaObject, _ + ByVal packedArgs As DynamicMetaObject(), _ + ByVal errorSuggestion As DynamicMetaObject) As DynamicMetaObject + + If IDOUtils.NeedsDeferral(target, packedArgs) Then + Return Me.Defer(target, packedArgs) + End If + + Dim arguments As Expression() = Nothing + Dim argNames As String() = Nothing + Dim argValues As Object() = Nothing + IDOUtils.UnpackArguments(packedArgs, Me.CallInfo, arguments, argNames, argValues) + + If errorSuggestion IsNot Nothing AndAlso Not CanBindInvokeDefault(target.Value, argValues, argNames, _reportErrors) Then + Return errorSuggestion 'Use the IDO-provided error + End If + + Dim result As ParameterExpression = Expression.Variable(GetType(Object), "result") + Dim array As ParameterExpression = Expression.Variable(GetType(Object()), "array") + + Dim fallback As Expression = Expression.Call( _ + GetType(NewLateBinding).GetMethod("FallbackInvokeDefault2"), _ + target.Expression(), _ + Expression.Assign( _ + array, _ + Expression.NewArrayInit(GetType(Object), arguments) _ + ), _ + Expression.Constant(argNames, GetType(String())), _ + Expression.Constant(_reportErrors) _ + ) + + Return New DynamicMetaObject( _ + Expression.Block( _ + New ParameterExpression() {result, array}, _ + Expression.Assign(result, fallback), _ + IDOUtils.GetWriteBack(arguments, array), _ + result _ + ), _ + IDOUtils.CreateRestrictions(target, packedArgs) _ + ) + End Function 'FallbackGetIndex + + ' Implement value equality. This is used so we can discover previously produced rules. + ' See comment at IOUtils.GetCachedBinder, which explains the caching in more detail. + Public Overrides Function Equals(ByVal _other As Object) As Boolean + Dim other As VBInvokeDefaultFallbackBinder = TryCast(_other, VBInvokeDefaultFallbackBinder) + Return other IsNot Nothing AndAlso CallInfo.Equals(other.CallInfo) AndAlso _reportErrors = other._reportErrors + End Function + + Private Shared ReadOnly _hash As Integer = GetType(VBInvokeDefaultFallbackBinder).GetHashCode() + Public Overrides Function GetHashCode() As Integer + Return _hash Xor CallInfo.GetHashCode() Xor _reportErrors.GetHashCode() + End Function + End Class 'VBInvokeDefaultFallbackBinder + + Class VBSetBinder + Inherits SetMemberBinder + + Sub New(ByVal MemberName As String) + MyBase.New(Name:=MemberName, IgnoreCase:=True) + End Sub 'New + + Public Overloads Overrides Function FallbackSetMember( _ + ByVal target As DynamicMetaObject, _ + ByVal value As DynamicMetaObject, _ + ByVal errorSuggestion As DynamicMetaObject) As DynamicMetaObject + + If IDOUtils.NeedsDeferral(target, value:=value) Then + Return Me.Defer(target, value) + End If + + If errorSuggestion IsNot Nothing AndAlso Not CanBindSet(target.Value, Name, value.Value, False, False) Then + Return errorSuggestion 'Binding will fail; use the IDO-provided error + End If + + Dim valueExpression As Expression = IDOUtils.ConvertToObject(value.Expression()) + Dim arguments() As Expression = {valueExpression} + + Dim fallback As Expression = Expression.Call( _ + GetType(NewLateBinding).GetMethod("FallbackSet"), _ + target.Expression(), _ + Expression.Constant(Name), _ + Expression.NewArrayInit(GetType(Object), arguments) _ + ) + + Return New DynamicMetaObject( _ + Expression.Block(fallback, valueExpression), _ + IDOUtils.CreateRestrictions(target, value:=value) _ + ) + End Function + + ' Implement value equality. This is used so we can discover previously produced rules. + ' See comment at IOUtils.GetCachedBinder, which explains the caching in more detail. + Public Overrides Function Equals(ByVal _other As Object) As Boolean + Dim other As VBSetBinder = TryCast(_other, VBSetBinder) + Return other IsNot Nothing AndAlso String.Equals(Name, other.Name) + End Function + + Private Shared ReadOnly _hash As Integer = GetType(VBSetBinder).GetHashCode() + Public Overrides Function GetHashCode() As Integer + Return _hash Xor Name.GetHashCode() + End Function + End Class 'VBSetBinder + + Class VBSetComplexBinder + Inherits SetMemberBinder + + Private ReadOnly _optimisticSet As Boolean + Private ReadOnly _rValueBase As Boolean + + Sub New(ByVal MemberName As String, ByVal OptimisticSet As Boolean, ByVal RValueBase As Boolean) + MyBase.New(Name:=MemberName, IgnoreCase:=True) + Me._optimisticSet = OptimisticSet + Me._rValueBase = RValueBase + End Sub 'New + + Public Overloads Overrides Function FallbackSetMember( _ + ByVal target As DynamicMetaObject, _ + ByVal value As DynamicMetaObject, _ + ByVal errorSuggestion As DynamicMetaObject) As DynamicMetaObject + + If IDOUtils.NeedsDeferral(target, value:=value) Then + Return Me.Defer(target, value) + End If + + If errorSuggestion IsNot Nothing AndAlso Not CanBindSet(target.Value, Name, value.Value, _optimisticSet, _rValueBase) Then + Return errorSuggestion 'Binding will fail; use the IDO-provided error + End If + + Dim valueExpression As Expression = IDOUtils.ConvertToObject(value.Expression()) + Dim arguments() As Expression = {valueExpression} + + Dim fallback As Expression = Expression.Call( _ + GetType(NewLateBinding).GetMethod("FallbackSetComplex"), _ + target.Expression(), _ + Expression.Constant(Name), _ + Expression.NewArrayInit(GetType(Object), arguments), _ + Expression.Constant(_optimisticSet), _ + Expression.Constant(_rValueBase) _ + ) + + Return New DynamicMetaObject( _ + Expression.Block(fallback, valueExpression), _ + IDOUtils.CreateRestrictions(target, value:=value) _ + ) + End Function + + ' Implement value equality. This is used so we can discover previously produced rules. + ' See comment at IOUtils.GetCachedBinder, which explains the caching in more detail. + Public Overrides Function Equals(ByVal _other As Object) As Boolean + Dim other As VBSetComplexBinder = TryCast(_other, VBSetComplexBinder) + Return other IsNot Nothing AndAlso String.Equals(Name, other.Name) AndAlso _optimisticSet = other._optimisticSet AndAlso _rValueBase = other._rValueBase + End Function + + Private Shared ReadOnly _hash As Integer = GetType(VBSetComplexBinder).GetHashCode() + Public Overrides Function GetHashCode() As Integer + Return _hash Xor Name.GetHashCode() Xor _optimisticSet.GetHashCode() Xor _rValueBase.GetHashCode() + End Function + End Class 'VBSetComplexBinder + + ' Used to fetch a DLR field + Class VBGetMemberBinder + Inherits GetMemberBinder +#If TELESTO Then + Implements IInvokeOnGetBinder +#End If + Public Sub New(ByVal name As String) + MyBase.New(name, True) + End Sub 'New + + Public Overrides Function FallbackGetMember( _ + ByVal target As DynamicMetaObject, _ + ByVal errorSuggestion As DynamicMetaObject) As DynamicMetaObject + + If errorSuggestion IsNot Nothing Then + Return errorSuggestion + End If + + ' Return a flag indicating no such DLR field exists + Return New DynamicMetaObject(Expression.Constant(IDOBinder.missingMemberSentinel), IDOUtils.CreateRestrictions(target)) + End Function 'FallbackGetMember + + ' Implement value equality. This is used so we can discover previously produced rules. + ' See comment at IOUtils.GetCachedBinder, which explains the caching in more detail. + Public Overrides Function Equals(ByVal _other As Object) As Boolean + Dim other As VBGetMemberBinder = TryCast(_other, VBGetMemberBinder) + Return other IsNot Nothing AndAlso String.Equals(Name, other.Name) + End Function + + Private Shared ReadOnly _hash As Integer = GetType(VBGetMemberBinder).GetHashCode() + Public Overrides Function GetHashCode() As Integer + Return _hash Xor Name.GetHashCode() + End Function + +#If TELESTO Then + ' Silverlight COM binding needs to know that it should not invoke a + ' default property, and instead wait until we provide the indexing arguments + Private ReadOnly Property InvokeOnGet() As Boolean Implements IInvokeOnGetBinder.InvokeOnGet + Get + Return False + End Get + End Property +#End If + End Class 'VBGetMemberBinder + + Class VBConversionBinder + Inherits ConvertBinder + + Sub New(ByVal T As Type) + MyBase.New(T, True) + End Sub 'New + + Public Overrides Function FallbackConvert( _ + ByVal target As DynamicMetaObject, _ + ByVal errorSuggestion As DynamicMetaObject) As DynamicMetaObject + + If IDOUtils.NeedsDeferral(target) Then + Return Me.Defer(target) + End If + + If errorSuggestion IsNot Nothing AndAlso Not Conversions.CanUserDefinedConvert(target.Value, Me.Type()) Then + 'Can't convert, use the error provided by the IDO + Return errorSuggestion + End If + + Dim fallback As Expression = Expression.Call( _ + GetType(Conversions).GetMethod("FallbackUserDefinedConversion"), _ + target.Expression(), _ + Expression.Constant(Me.Type(), GetType(System.Type)) _ + ) + + Return New DynamicMetaObject(Expression.Convert(fallback, ReturnType), IDOUtils.CreateRestrictions(target)) + + End Function 'FallbackConvert + + ' Implement value equality. This is used so we can discover previously produced rules. + ' See comment at IOUtils.GetCachedBinder, which explains the caching in more detail. + Public Overrides Function Equals(ByVal _other As Object) As Boolean + Dim other As VBConversionBinder = TryCast(_other, VBConversionBinder) + ' = Operator is not defined in .net 3.5. TELESTO needs to be built with .net 3.5 tools. +#If TELESTO Then + Return other IsNot Nothing AndAlso Type.Equals(other.Type) +#Else + Return other IsNot Nothing AndAlso Type = other.Type +#End If + End Function + + Private Shared ReadOnly _hash As Integer = GetType(VBConversionBinder).GetHashCode() + Public Overrides Function GetHashCode() As Integer + Return _hash Xor Type.GetHashCode() + End Function + End Class 'VBConversionBinder + + Class VBUnaryOperatorBinder + Inherits UnaryOperationBinder + + Private ReadOnly _Op As UserDefinedOperator + + Sub New(ByVal Op As UserDefinedOperator, ByVal LinqOp As ExpressionType) + MyBase.New(LinqOp) + _Op = Op + End Sub 'New + + Public Overrides Function FallbackUnaryOperation( _ + ByVal target As DynamicMetaObject, _ + ByVal errorSuggestion As DynamicMetaObject) As DynamicMetaObject + + If IDOUtils.NeedsDeferral(target) Then + Return Me.Defer(target) + End If + + If errorSuggestion IsNot Nothing AndAlso Operators.GetCallableUserDefinedOperator(_Op, target.Value) Is Nothing Then + 'Can't bind, use the error provided by the IDO + Return errorSuggestion + End If + + Dim fallback As Expression = Expression.Call( _ + GetType(Operators).GetMethod("FallbackInvokeUserDefinedOperator"), _ + Expression.Constant(_Op, GetType(Object)), _ + Expression.NewArrayInit(GetType(Object), New Expression() {IDOUtils.ConvertToObject(target.Expression)}) _ + ) + + Return New DynamicMetaObject(fallback, IDOUtils.CreateRestrictions(target)) + End Function 'FallbackUnaryOperator + + ' Implement value equality. This is used so we can discover previously produced rules. + ' See comment at IOUtils.GetCachedBinder, which explains the caching in more detail. + Public Overrides Function Equals(ByVal _other As Object) As Boolean + Dim other As VBUnaryOperatorBinder = TryCast(_other, VBUnaryOperatorBinder) + Return other IsNot Nothing AndAlso _Op = other._Op AndAlso Operation = other.Operation + End Function + + Private Shared ReadOnly _hash As Integer = GetType(VBUnaryOperatorBinder).GetHashCode() + Public Overrides Function GetHashCode() As Integer + Return _hash Xor _Op.GetHashCode() Xor Operation.GetHashCode() + End Function + End Class 'VBUnaryOperatorBinder + + Class VBBinaryOperatorBinder + Inherits BinaryOperationBinder + + Private ReadOnly _Op As UserDefinedOperator + + Sub New(ByVal Op As UserDefinedOperator, ByVal LinqOp As ExpressionType) + MyBase.New(LinqOp) + _Op = Op + End Sub 'New + + Public Overrides Function FallbackBinaryOperation( _ + ByVal target As DynamicMetaObject, _ + ByVal arg As DynamicMetaObject, _ + ByVal errorSuggestion As DynamicMetaObject) As DynamicMetaObject + + If IDOUtils.NeedsDeferral(target, value:=arg) Then + Return Me.Defer(target, arg) + End If + + If errorSuggestion IsNot Nothing AndAlso Operators.GetCallableUserDefinedOperator(_Op, target.Value, arg.Value) Is Nothing Then + 'Can't bind, use the error provided by the IDO + Return errorSuggestion + End If + + Dim fallback As Expression = Expression.Call( _ + GetType(Operators).GetMethod("FallbackInvokeUserDefinedOperator"), _ + Expression.Constant(_Op, GetType(Object)), _ + Expression.NewArrayInit(GetType(Object), New Expression() { _ + IDOUtils.ConvertToObject(target.Expression), _ + IDOUtils.ConvertToObject(arg.Expression)}) _ + ) + + Return New DynamicMetaObject(fallback, IDOUtils.CreateRestrictions(target, value:=arg)) + End Function 'FallbackUnaryOperator + + ' Implement value equality. This is used so we can discover previously produced rules. + ' See comment at IOUtils.GetCachedBinder, which explains the caching in more detail. + Public Overrides Function Equals(ByVal _other As Object) As Boolean + Dim other As VBBinaryOperatorBinder = TryCast(_other, VBBinaryOperatorBinder) + Return other IsNot Nothing AndAlso _Op = other._Op AndAlso Operation = other.Operation + End Function + + Private Shared ReadOnly _hash As Integer = GetType(VBBinaryOperatorBinder).GetHashCode() + Public Overrides Function GetHashCode() As Integer + Return _hash Xor _Op.GetHashCode() Xor Operation.GetHashCode() + End Function + End Class 'VBBinaryOperatorBinder + + Class VBIndexSetBinder + Inherits SetIndexBinder + + Sub New(ByVal CallInfo As CallInfo) + MyBase.New(CallInfo) + End Sub 'New + + Public Overrides Function FallbackSetIndex( _ + ByVal target As DynamicMetaObject, _ + ByVal packedIndexes As DynamicMetaObject(), _ + ByVal value As DynamicMetaObject, _ + ByVal errorSuggestion As DynamicMetaObject) As DynamicMetaObject + + If IDOUtils.NeedsDeferral(target, packedIndexes, value) Then + Array.Resize(packedIndexes, packedIndexes.Length + 1) + packedIndexes(packedIndexes.Length - 1) = value + Return Me.Defer(target, packedIndexes) + End If + + Dim indexNames As String() = Nothing + Dim indexes As Expression() = Nothing + Dim indexValues As Object() = Nothing + + IDOUtils.UnpackArguments(packedIndexes, Me.CallInfo, indexes, indexNames, indexValues) + + Dim indexValuesPlusValue(indexValues.Length) As Object + indexValues.CopyTo(indexValuesPlusValue, 0) + indexValuesPlusValue(indexValues.Length) = value.Value + + If errorSuggestion IsNot Nothing AndAlso _ + Not CanIndexSetComplex(target.Value, indexValuesPlusValue, indexNames, False, False) Then + Return errorSuggestion 'Use the IDO-provided error + End If + + Dim valueExpression As Expression = IDOUtils.ConvertToObject(value.Expression) + Dim indexesPlusValue(indexes.Length) As Expression + indexes.CopyTo(indexesPlusValue, 0) + indexesPlusValue(indexes.Length) = valueExpression + + Dim fallback As Expression = Expression.Call( _ + GetType(NewLateBinding).GetMethod("FallbackIndexSet"), _ + target.Expression(), _ + Expression.NewArrayInit(GetType(Object), indexesPlusValue), _ + Expression.Constant(indexNames, GetType(String())) _ + ) + + Return New DynamicMetaObject( _ + Expression.Block(fallback, valueExpression), _ + IDOUtils.CreateRestrictions(target, packedIndexes, value) _ + ) + End Function 'FallbackSetIndex + + ' Implement value equality. This is used so we can discover previously produced rules. + ' See comment at IOUtils.GetCachedBinder, which explains the caching in more detail. + Public Overrides Function Equals(ByVal _other As Object) As Boolean + Dim other As VBIndexSetBinder = TryCast(_other, VBIndexSetBinder) + Return other IsNot Nothing AndAlso CallInfo.Equals(other.CallInfo) + End Function + + Private Shared ReadOnly _hash As Integer = GetType(VBIndexSetBinder).GetHashCode() + Public Overrides Function GetHashCode() As Integer + Return _hash Xor CallInfo.GetHashCode() + End Function + End Class 'VBIndexSetBinder + + Class VBIndexSetComplexBinder + Inherits SetIndexBinder + + Private ReadOnly _optimisticSet As Boolean + Private ReadOnly _rValueBase As Boolean + + Sub New(ByVal CallInfo As CallInfo, ByVal OptimisticSet As Boolean, ByVal RValueBase As Boolean) + MyBase.New(CallInfo) + Me._optimisticSet = OptimisticSet + Me._rValueBase = RValueBase + End Sub 'New + + Public Overrides Function FallbackSetIndex( _ + ByVal target As DynamicMetaObject, _ + ByVal packedIndexes As DynamicMetaObject(), _ + ByVal value As DynamicMetaObject, _ + ByVal errorSuggestion As DynamicMetaObject) As DynamicMetaObject + + If IDOUtils.NeedsDeferral(target, packedIndexes, value) Then + Array.Resize(packedIndexes, packedIndexes.Length + 1) + packedIndexes(packedIndexes.Length - 1) = value + Return Me.Defer(target, packedIndexes) + End If + + Dim indexNames As String() = Nothing + Dim indexes As Expression() = Nothing + Dim indexValues As Object() = Nothing + + IDOUtils.UnpackArguments(packedIndexes, Me.CallInfo, indexes, indexNames, indexValues) + + Dim indexValuesPlusValue(indexValues.Length) As Object + indexValues.CopyTo(indexValuesPlusValue, 0) + indexValuesPlusValue(indexValues.Length) = value.Value + + If errorSuggestion IsNot Nothing AndAlso _ + Not CanIndexSetComplex(target.Value, indexValuesPlusValue, indexNames, _optimisticSet, _rValueBase) Then + Return errorSuggestion 'Use the IDO-provided error + End If + + Dim valueExpression As Expression = IDOUtils.ConvertToObject(value.Expression) + Dim indexesPlusValue(indexes.Length) As Expression + indexes.CopyTo(indexesPlusValue, 0) + indexesPlusValue(indexes.Length) = valueExpression + + Dim fallback As Expression = Expression.Call( _ + GetType(NewLateBinding).GetMethod("FallbackIndexSetComplex"), _ + target.Expression(), _ + Expression.NewArrayInit(GetType(Object), indexesPlusValue), _ + Expression.Constant(indexNames, GetType(String())), _ + Expression.Constant(_optimisticSet), _ + Expression.Constant(_rValueBase) _ + ) + + Return New DynamicMetaObject( _ + Expression.Block(fallback, valueExpression), _ + IDOUtils.CreateRestrictions(target, packedIndexes, value) _ + ) + End Function 'FallbackSetIndex + + ' Implement value equality. This is used so we can discover previously produced rules. + ' See comment at IOUtils.GetCachedBinder, which explains the caching in more detail. + Public Overrides Function Equals(ByVal _other As Object) As Boolean + Dim other As VBIndexSetComplexBinder = TryCast(_other, VBIndexSetComplexBinder) + Return other IsNot Nothing AndAlso CallInfo.Equals(other.CallInfo) AndAlso _optimisticSet = other._optimisticSet AndAlso _rValueBase = other._rValueBase + End Function + + Private Shared ReadOnly _hash As Integer = GetType(VBIndexSetComplexBinder).GetHashCode() + Public Overrides Function GetHashCode() As Integer + Return _hash Xor CallInfo.GetHashCode() Xor _optimisticSet.GetHashCode() Xor _rValueBase.GetHashCode() + End Function + End Class 'VBIndexSetComplexBinder + + Friend Class IDOUtils + + Private Sub New() + Throw New InternalErrorException() + End Sub + + ' Each binder will cache up to 128 of it's most recently used rules. + ' So by caching the 64 most recently used binders, we limit the total + ' number of rules to 8k. + Private Shared binderCache As New CacheSet(Of CallSiteBinder)(64) + + ' + ' Look for an existing compatible binder in the cache. If we find one, + ' we can reuse the rules that it produced. If we don't find a match, + ' then add this binder to the cache. + ' + ' Compatibility is determined by the Equals method on the binders. Two + ' binders should compare equal if they would produce the same rule for + ' the same arguments. In practice, this is true if all of their + ' instance fields are equal. + ' + ' Consider this example: + ' x.Foo(a) + ' y.Foo(b) + ' + ' Both of these call sites are calling "Foo" with one argument, so they + ' can potentially use the same generated rule. Constrast with: + ' z.Foo(c, d) + ' + ' Now we have two arguments, so we can't share rules with the other two + ' call sites. + ' + Private Shared Function GetCachedBinder(ByVal Action As CallSiteBinder) As CallSiteBinder + Return binderCache.GetExistingOrAdd(Action) + End Function 'GetAtomizedBinder + + ' This method checks whether an object is an instance of IDynamicMetaObjectProvider. + ' Apparently, for remote objects (objects in a different process), CLR will report + ' allow cast to an interface (isinst instruction returns non-null) even though the object + ' doesn't implement the interface. Therefore we are checking that the object resides + ' in the same app domain in addition to implementing the IDynamicMetaObjectProcider interface. + Friend Shared Function TryCastToIDMOP(ByVal o As Object) As IDynamicMetaObjectProvider + Dim ido As IDynamicMetaObjectProvider = TryCast(o, IDynamicMetaObjectProvider) +#If Not TELESTO Then + If ido IsNot Nothing AndAlso Not System.Runtime.Remoting.RemotingServices.IsObjectOutOfAppDomain(o) Then +#Else + If ido IsNot Nothing Then 'No RemotingServices in Telesto +#End If + Return ido + Else + Return Nothing + End If + End Function + + ' Convert from VB's UserDefinedOperator to Linq operator type + Friend Shared Function LinqOperator(ByVal vbOperator As UserDefinedOperator) As ExpressionType? + + Select Case vbOperator + Case UserDefinedOperator.Negate + Return ExpressionType.Negate + Case UserDefinedOperator.Not + Return ExpressionType.Not + Case UserDefinedOperator.UnaryPlus + Return ExpressionType.UnaryPlus + Case UserDefinedOperator.Plus + Return ExpressionType.Add + Case UserDefinedOperator.Minus + Return ExpressionType.Subtract + Case UserDefinedOperator.Multiply + Return ExpressionType.Multiply + Case UserDefinedOperator.Divide + Return ExpressionType.Divide + Case UserDefinedOperator.Power + Return ExpressionType.Power + Case UserDefinedOperator.ShiftLeft + Return ExpressionType.LeftShift + Case UserDefinedOperator.ShiftRight + Return ExpressionType.RightShift + Case UserDefinedOperator.Modulus + Return ExpressionType.Modulo + Case UserDefinedOperator.Or + Return ExpressionType.Or + Case UserDefinedOperator.Xor + Return ExpressionType.ExclusiveOr + Case UserDefinedOperator.And + Return ExpressionType.And + Case UserDefinedOperator.Equal + Return ExpressionType.Equal + Case UserDefinedOperator.NotEqual + Return ExpressionType.NotEqual + Case UserDefinedOperator.Less + Return ExpressionType.LessThan + Case UserDefinedOperator.LessEqual + Return ExpressionType.LessThanOrEqual + Case UserDefinedOperator.GreaterEqual + Return ExpressionType.GreaterThanOrEqual + Case UserDefinedOperator.Greater + Return ExpressionType.GreaterThan + Case Else + Return Nothing + End Select + End Function 'LinqOperator + + 'If the CallSite had byref arguments, the values in packedArgs may be updated + 'We need to propegate those changes back to the original arguments array. + Shared Sub CopyBackArguments(ByVal callInfo As CallInfo, ByVal packedArgs As Object(), ByVal args As Object()) + If packedArgs IsNot args Then + 'This works like UnpackArguments, but just copies the values + ' + 'We need to reorder the args if we have any named args so it matches + 'what the Late* entry point expects, which is named args first. + 'Input order is: P1, P2, P3, N1, N2 [, V] + 'Output order is: N1, N2, P1, P2, P3 [, V] + '(where V is an the value argument for things like SetIndex) + Dim argCount As Integer = packedArgs.Length + Dim normalArgCount As Integer = callInfo.ArgumentCount + Dim positionalArgCount As Integer = argCount - callInfo.ArgumentNames.Count + + For i As Integer = 0 To argCount - 1 + args(i) = packedArgs(If(i < normalArgCount, (i + positionalArgCount) Mod normalArgCount, i)) + Next + End If + End Sub + + 'Pack arguments from VB libraries for DLR + Shared Sub PackArguments( _ + ByVal valueArgs As Integer, _ + ByVal argNames As String(), _ + ByVal args As Object(), _ + ByRef packedArgs As Object(), _ + ByRef callInfo As CallInfo) + + 'There is some inconsistency in the handling of argNames, sometimes it + 'has been normalized to non-null by this point, sometimes not. + If argNames Is Nothing Then + argNames = New String(-1) {} + End If + + callInfo = New CallInfo(args.Length - valueArgs, argNames) + + If argNames.Length > 0 Then + 'Arguments are passed to NewLateBinder a counterintuitive way, with + 'named arguments first in the array. So we need to reorder them to get + 'correct interop. + 'See ExpressionSemantics.cpp, ConstructLateBoundArgumentList + packedArgs = New Object(args.Length - 1) {} + + 'Input order is: N1, N2, P1, P2, P3 [, V] + 'Output order is: P1, P2, P3, N1, N2 [, V] + '(where V is an the value argument for things like SetIndex) + Dim normalArgCount As Integer = args.Length - valueArgs + For i As Integer = 0 To normalArgCount - 1 + packedArgs(i) = args((i + argNames.Length) Mod normalArgCount) + Next i + ' Copy the value arguments (for SetIndex*), if any + For i As Integer = normalArgCount To args.Length - 1 + packedArgs(i) = args(i) + Next + Else + packedArgs = args + End If + End Sub 'PackArguments + + 'Unpack arguments from DLR for VB libraries + Shared Sub UnpackArguments( _ + ByVal packedArgs As DynamicMetaObject(), _ + ByVal callInfo As CallInfo, _ + ByRef args As Expression(), _ + ByRef argNames As String(), _ + ByRef argValues As Object()) + + 'See comment for PackArguments + 'We need to reorder the args if we have any named args so it matches + 'what the Late* entry point expects, which is named args first. + 'Input order is: P1, P2, P3, N1, N2 [, V] + 'Output order is: N1, N2, P1, P2, P3 [, V] + '(where V is an the value argument for things like SetIndex) + + Dim argCount As Integer = packedArgs.Length + Dim normalArgCount As Integer = CallInfo.ArgumentCount + args = New Expression(argCount - 1) {} + argValues = New Object(argCount - 1) {} + + Dim namedArgCount As Integer = CallInfo.ArgumentNames.Count + Dim positionalArgCount As Integer = argCount - namedArgCount + + For i As Integer = 0 To normalArgCount - 1 + Dim p As DynamicMetaObject = packedArgs((i + positionalArgCount) Mod normalArgCount) + args(i) = p.Expression + argValues(i) = p.Value + Next + ' Copy the value arguments (for SetIndex*), if any + For i As Integer = normalArgCount To argCount - 1 + Dim p As DynamicMetaObject = packedArgs(i) + args(i) = p.Expression + argValues(i) = p.Value + Next + + ' Binding functions expect non-null names + argNames = New String(namedArgCount - 1) {} + CallInfo.ArgumentNames.CopyTo(argNames, 0) + End Sub 'UnpackArguments + + Shared Function GetWriteBack(ByVal arguments() As Expression, ByVal array As ParameterExpression) As Expression + Dim writeback As New List(Of Expression) + For i As Integer = 0 To arguments.Length - 1 + Dim arg As ParameterExpression = TryCast(arguments(i), ParameterExpression) + If arg IsNot Nothing AndAlso arg.IsByRef Then + writeback.Add(Expression.Assign(arg, Expression.ArrayIndex(array, Expression.Constant(i)))) + End If + Next + Select Case writeback.Count + Case 0 + Return Expression.Empty() + Case 1 + Return writeback(0) + Case Else + Return Expression.Block(writeback) + End Select + End Function + + 'Convert expression to Object if its type is not Object already. + Shared Function ConvertToObject(ByVal valueExpression As Expression) As Expression + Return If(valueExpression.Type.Equals(GetType(Object)), valueExpression, Expression.Convert(valueExpression, GetType(Object))) + End Function 'ConvertToObject + + Friend Delegate Function SiteDelegate0(ByVal Site As CallSite, ByVal Instance As Object) As Object + Friend Delegate Function SiteDelegate1(ByVal Site As CallSite, ByVal Instance As Object, ByRef Arg0 As Object) As Object + Friend Delegate Function SiteDelegate2(ByVal Site As CallSite, ByVal Instance As Object, ByRef Arg0 As Object, ByRef Arg1 As Object) As Object + Friend Delegate Function SiteDelegate3(ByVal Site As CallSite, ByVal Instance As Object, ByRef Arg0 As Object, ByRef Arg1 As Object, ByRef Arg2 As Object) As Object + Friend Delegate Function SiteDelegate4(ByVal Site As CallSite, ByVal Instance As Object, ByRef Arg0 As Object, ByRef Arg1 As Object, ByRef Arg2 As Object, ByRef Arg3 As Object) As Object + Friend Delegate Function SiteDelegate5(ByVal Site As CallSite, ByVal Instance As Object, ByRef Arg0 As Object, ByRef Arg1 As Object, ByRef Arg2 As Object, ByRef Arg3 As Object, ByRef Arg4 As Object) As Object + Friend Delegate Function SiteDelegate6(ByVal Site As CallSite, ByVal Instance As Object, ByRef Arg0 As Object, ByRef Arg1 As Object, ByRef Arg2 As Object, ByRef Arg3 As Object, ByRef Arg4 As Object, ByRef Arg5 As Object) As Object + Friend Delegate Function SiteDelegate7(ByVal Site As CallSite, ByVal Instance As Object, ByRef Arg0 As Object, ByRef Arg1 As Object, ByRef Arg2 As Object, ByRef Arg3 As Object, ByRef Arg4 As Object, ByRef Arg5 As Object, ByRef Arg6 As Object) As Object + + Shared Function CreateRefCallSiteAndInvoke( _ + ByVal Action As CallSiteBinder, _ + ByVal Instance As Object, _ + ByVal Arguments As Object()) As Object + + Action = GetCachedBinder(Action) + + Select Case Arguments.Length + Case 0 + Dim c As CallSite(Of SiteDelegate0) = CallSite(Of SiteDelegate0).Create(Action) + Return c.Target.Invoke(c, Instance) + Case 1 + Dim c As CallSite(Of SiteDelegate1) = CallSite(Of SiteDelegate1).Create(Action) + Return c.Target.Invoke(c, Instance, Arguments(0)) + Case 2 + Dim c As CallSite(Of SiteDelegate2) = CallSite(Of SiteDelegate2).Create(Action) + Return c.Target.Invoke(c, Instance, Arguments(0), Arguments(1)) + Case 3 + Dim c As CallSite(Of SiteDelegate3) = CallSite(Of SiteDelegate3).Create(Action) + Return c.Target.Invoke(c, Instance, Arguments(0), Arguments(1), Arguments(2)) + Case 4 + Dim c As CallSite(Of SiteDelegate4) = CallSite(Of SiteDelegate4).Create(Action) + Return c.Target.Invoke(c, Instance, Arguments(0), Arguments(1), Arguments(2), Arguments(3)) + Case 5 + Dim c As CallSite(Of SiteDelegate5) = CallSite(Of SiteDelegate5).Create(Action) + Return c.Target.Invoke(c, Instance, Arguments(0), Arguments(1), Arguments(2), Arguments(3), Arguments(4)) + Case 6 + Dim c As CallSite(Of SiteDelegate6) = CallSite(Of SiteDelegate6).Create(Action) + Return c.Target.Invoke(c, Instance, Arguments(0), Arguments(1), Arguments(2), Arguments(3), Arguments(4), Arguments(5)) + Case 7 + Dim c As CallSite(Of SiteDelegate7) = CallSite(Of SiteDelegate7).Create(Action) + Return c.Target.Invoke(c, Instance, Arguments(0), Arguments(1), Arguments(2), Arguments(3), Arguments(4), Arguments(5), Arguments(6)) + Case Else + Dim signature(Arguments.Length + 2) As Type + Dim refObject As Type = GetType(Object).MakeByRefType() + signature(0) = GetType(CallSite) ' First argument is a call site + signature(1) = GetType(Object) ' Second is the instance (ByVal) + signature(signature.Length - 1) = GetType(Object) ' Last type is the return type + For i As Integer = 2 To signature.Length - 2 ' All arguments are ByRef + signature(i) = refObject + Next + + Dim c As CallSite = CallSite.Create(Expression.GetDelegateType(signature), Action) + Dim args(Arguments.Length + 1) As Object + args(0) = c + args(1) = Instance + Arguments.CopyTo(args, 2) + Dim siteTarget As System.Delegate = DirectCast(c.GetType().GetField("Target").GetValue(c), System.Delegate) + Try + Dim result As Object = siteTarget.DynamicInvoke(args) + Array.Copy(args, 2, Arguments, 0, Arguments.Length) + Return result + Catch ie As TargetInvocationException + Throw ie.InnerException + End Try + End Select + End Function 'CreateRefCallSiteAndInvoke + + Shared Function CreateFuncCallSiteAndInvoke( _ + ByVal Action As CallSiteBinder, _ + ByVal Instance As Object, _ + ByVal Arguments As Object()) As Object + + Action = GetCachedBinder(Action) + + Select Case Arguments.Length + Case 0 + Dim c As CallSite(Of Func(Of CallSite, Object, Object)) = _ + CallSite(Of Func(Of CallSite, Object, Object)).Create(Action) + Return c.Target.Invoke(c, Instance) + Case 1 + Dim c As CallSite(Of Func(Of CallSite, Object, Object, Object)) = _ + CallSite(Of Func(Of CallSite, Object, Object, Object)).Create(Action) + Return c.Target.Invoke(c, Instance, Arguments(0)) + Case 2 + Dim c As CallSite(Of Func(Of CallSite, Object, Object, Object, Object)) = _ + CallSite(Of Func(Of CallSite, Object, Object, Object, Object)).Create(Action) + Return c.Target.Invoke(c, Instance, Arguments(0), Arguments(1)) + Case 3 + Dim c As CallSite(Of Func(Of CallSite, Object, Object, Object, Object, Object)) = _ + CallSite(Of Func(Of CallSite, Object, Object, Object, Object, Object)).Create(Action) + Return c.Target.Invoke(c, Instance, Arguments(0), Arguments(1), Arguments(2)) + Case 4 + Dim c As CallSite(Of Func(Of CallSite, Object, Object, Object, Object, Object, Object)) = _ + CallSite(Of Func(Of CallSite, Object, Object, Object, Object, Object, Object)).Create(Action) + Return c.Target.Invoke(c, Instance, Arguments(0), Arguments(1), Arguments(2), Arguments(3)) + Case 5 + Dim c As CallSite(Of Func(Of CallSite, Object, Object, Object, Object, Object, Object, Object)) = _ + CallSite(Of Func(Of CallSite, Object, Object, Object, Object, Object, Object, Object)).Create(Action) + Return c.Target.Invoke(c, Instance, Arguments(0), Arguments(1), Arguments(2), Arguments(3), Arguments(4)) + Case 6 + Dim c As CallSite(Of Func(Of CallSite, Object, Object, Object, Object, Object, Object, Object, Object)) = _ + CallSite(Of Func(Of CallSite, Object, Object, Object, Object, Object, Object, Object, Object)).Create(Action) + Return c.Target.Invoke(c, Instance, Arguments(0), Arguments(1), Arguments(2), Arguments(3), Arguments(4), Arguments(5)) + Case 7 + Dim c As CallSite(Of Func(Of CallSite, Object, Object, Object, Object, Object, Object, Object, Object, Object)) = _ + CallSite(Of Func(Of CallSite, Object, Object, Object, Object, Object, Object, Object, Object, Object)).Create(Action) + Return c.Target.Invoke(c, Instance, Arguments(0), Arguments(1), Arguments(2), Arguments(3), Arguments(4), Arguments(5), Arguments(6)) + Case Else + Dim delegateArgTypes(Arguments.Length + 2) As Type + delegateArgTypes(0) = GetType(CallSite) + For i As Integer = 1 To delegateArgTypes.Length - 1 + delegateArgTypes(i) = GetType(Object) + Next + Dim c As CallSite = CallSite.Create(Expression.GetDelegateType(delegateArgTypes), Action) + Dim args(Arguments.Length + 1) As Object + args(0) = c + args(1) = Instance + Arguments.CopyTo(args, 2) + Dim siteTarget As System.Delegate = _ + CType(c.GetType().GetField("Target").GetValue(c), System.Delegate) + Try + Return siteTarget.DynamicInvoke(args) + Catch ie As TargetInvocationException + Throw ie.InnerException + End Try + End Select + End Function 'CreateFuncCallSiteAndInvoke + + ' The type of the Convert call site must match the type we are converting to + Shared Function CreateConvertCallSiteAndInvoke( _ + ByVal Action As ConvertBinder, _ + ByVal Instance As Object) As Object + + ' Create the call site for performing the conversion + Dim delegateArgTypes(2) As Type + delegateArgTypes(0) = GetType(CallSite) + delegateArgTypes(1) = GetType(Object) + delegateArgTypes(2) = Action.Type + Dim c As CallSite = CallSite.Create(Expression.GetFuncType(delegateArgTypes), GetCachedBinder(Action)) + + ' Invoke it through reflection + Dim args(1) As Object + args(0) = c + args(1) = Instance + Dim siteTarget As System.Delegate = _ + CType(c.GetType().GetField("Target").GetValue(c), System.Delegate) + Try + Return siteTarget.DynamicInvoke(args) + Catch ie As TargetInvocationException + Throw ie.InnerException + End Try + End Function 'CreateConvertCallSiteAndInvoke + + + ''' + ''' Adds the exact type restriction on the target of the dynamic operation, and merges all of the + ''' restrictions together + ''' + ''' The DynamicMetaObject representing the target of the operation + ''' The DynamicMetaObjects representing the arguments of the operation + ''' The DynamicMetaObject representing another other argument, usually the value of a set + ''' New set of restrictions which includes the exact type restriction on the target. + ''' + ''' The dynamic binding produced by the binder is applicable to the exact type of the target object. + ''' This method will add the binding restriction on the exact type of the target (all FallbackXXX + ''' methods call this). + ''' + Friend Shared Function CreateRestrictions( _ + ByVal target As DynamicMetaObject, _ + Optional ByVal args As DynamicMetaObject() = Nothing, _ + Optional ByVal value As DynamicMetaObject = Nothing) As BindingRestrictions + + Dim r As BindingRestrictions = CreateRestriction(target) + If args IsNot Nothing Then + For Each arg As DynamicMetaObject In args + r = r.Merge(CreateRestriction(arg)) + Next + End If + If value IsNot Nothing Then + r = r.Merge(CreateRestriction(value)) + End If + Return r + End Function + + Private Shared Function CreateRestriction(ByVal metaObject As DynamicMetaObject) As BindingRestrictions + If metaObject.Value Is Nothing Then + Return metaObject.Restrictions.Merge( _ + BindingRestrictions.GetInstanceRestriction(metaObject.Expression, Nothing)) + Else + Return metaObject.Restrictions.Merge( _ + BindingRestrictions.GetTypeRestriction(metaObject.Expression, metaObject.LimitType)) + End If + End Function + + Friend Shared Function NeedsDeferral( _ + ByVal target As DynamicMetaObject, _ + Optional ByVal args As DynamicMetaObject() = Nothing, _ + Optional ByVal value As DynamicMetaObject = Nothing) As Boolean + + If Not target.HasValue Then + Return True + End If + If value IsNot Nothing AndAlso Not value.HasValue Then + Return True + End If + If args IsNot Nothing Then + For Each a As DynamicMetaObject In args + If Not a.HasValue Then + Return True + End If + Next + End If + Return False + End Function + + End Class 'IDOUtils + + + ''' + ''' Provides a set-like object used for caches which holds onto a maximum + ''' number of elements specified at construction time. + ''' + ''' This class is thread safe. + ''' + Friend NotInheritable Class CacheSet(Of T) + Private ReadOnly _dict As New Dictionary(Of T, LinkedListNode(Of T)) + Private ReadOnly _list As New LinkedList(Of T) + Private ReadOnly _maxSize As Integer + + ''' + ''' Creates a dictionary-like object used for caches. + ''' + ''' The maximum number of elements to store. + Friend Sub New(ByVal maxSize As Integer) + _maxSize = maxSize + End Sub + + ''' + ''' Tries to get the entry associated with 'key'. If it already exists, + ''' the existing value will be returned. Otherwise it will be added, + ''' removing the oldest element in the cache if it has reached capacity. + ''' + Friend Function GetExistingOrAdd(ByVal key As T) As T + SyncLock Me + Dim node As LinkedListNode(Of T) = Nothing + If _dict.TryGetValue(key, node) Then + ' Found a match, move it to the head of the list + If node.Previous IsNot Nothing Then + _list.Remove(node) + _list.AddFirst(node) + End If + Return node.Value + ElseIf _dict.Count = _maxSize Then + ' We're at capacity, remove the last element to make room + _dict.Remove(_list.Last.Value) + _list.RemoveLast() + End If + + ' Add a new entry to the head of the list + node = New LinkedListNode(Of T)(key) + _dict.Add(key, node) + _list.AddFirst(node) + Return key + End SyncLock + End Function + End Class + +End Namespace diff --git a/Microsoft.VisualBasic/runtime/msvbalib/Helpers/IOUtils.vb b/Microsoft.VisualBasic/runtime/msvbalib/Helpers/IOUtils.vb new file mode 100644 index 000000000..9939cd146 --- /dev/null +++ b/Microsoft.VisualBasic/runtime/msvbalib/Helpers/IOUtils.vb @@ -0,0 +1,153 @@ +' Copyright (c) Microsoft Corporation. All rights reserved. + +Imports System +Imports System.Security +Imports System.Security.Permissions +Imports System.IO +Imports System.Text + +Imports Microsoft.VisualBasic.CompilerServices.ExceptionUtils +Imports Microsoft.VisualBasic.CompilerServices.Utils + +Namespace Microsoft.VisualBasic.CompilerServices + + _ + Class IOUtils + ' Prevent creation. + Private Sub New() + End Sub + + _ + Friend Shared Function FindFirstFile(ByVal assem As System.Reflection.Assembly, ByVal PathName As String, ByVal Attributes As IO.FileAttributes) As String + Dim Dir As DirectoryInfo + Dim DirName As String = Nothing + Dim FileName As String + Dim files() As FileSystemInfo + Dim oAssemblyData As AssemblyData + Const DiskNotReadyError As Integer = &H80070015 + + If PathName.Length > 0 AndAlso PathName.Chars(PathName.Length - 1) = Path.DirectorySeparatorChar Then + DirName = Path.GetFullPath(PathName) + FileName = "*.*" + Else + If PathName.Length = 0 Then + FileName = "*.*" + Else + FileName = Path.GetFileName(PathName) + DirName = Path.GetDirectoryName(PathName) + + If (FileName Is Nothing) OrElse (FileName.Length = 0) OrElse (FileName = ".") Then + FileName = "*.*" + End If + End If + + + If (DirName Is Nothing) OrElse (DirName.Length = 0) Then + If Path.IsPathRooted(PathName) Then + DirName = Path.GetPathRoot(PathName) + Else + DirName = Environment.CurrentDirectory + If DirName.Chars(DirName.Length - 1) <> Path.DirectorySeparatorChar Then + DirName = DirName & Path.DirectorySeparatorChar + End If + End If + Else + If DirName.Chars(DirName.Length - 1) <> Path.DirectorySeparatorChar Then + DirName = DirName & Path.DirectorySeparatorChar + End If + End If + + If FileName = ".." Then + DirName = DirName & "..\" + FileName = "*.*" + End If + End If + + + Try + Dir = Directory.GetParent(DirName & FileName) + files = Dir.GetFileSystemInfos(FileName) + Catch ex As SecurityException + Throw ex + Catch IOex As IOException When _ + (System.Runtime.InteropServices.Marshal.GetHRForException(IOex) = DiskNotReadyError) + Throw VbMakeException(vbErrors.BadFileNameOrNumber) + Catch ex As StackOverflowException + Throw ex + Catch ex As OutOfMemoryException + Throw ex + Catch ex As System.Threading.ThreadAbortException + Throw ex + Catch + Return "" + End Try + + oAssemblyData = ProjectData.GetProjectData().GetAssemblyData(assem) + oAssemblyData.m_DirFiles = files + oAssemblyData.m_DirNextFileIndex = 0 + oAssemblyData.m_DirAttributes = Attributes + + If (files Is Nothing) OrElse (files.Length = 0) Then + Return "" + End If + + Return FindFileFilter(oAssemblyData) + End Function + + + + Friend Shared Function FindNextFile(ByVal assem As System.Reflection.Assembly) As String + Dim oAssemblyData As AssemblyData + + oAssemblyData = ProjectData.GetProjectData().GetAssemblyData(assem) + + If oAssemblyData.m_DirFiles Is Nothing Then + Throw New ArgumentException(GetResourceString(ResID.DIR_IllegalCall)) + End If + + If oAssemblyData.m_DirNextFileIndex > oAssemblyData.m_DirFiles.GetUpperBound(0) Then + 'Prevent hitting the security check in this scenario + oAssemblyData.m_DirFiles = Nothing + oAssemblyData.m_DirNextFileIndex = 0 + Return Nothing + End If + + Return FindFileFilter(oAssemblyData) + End Function + + + + 'IMPORTANT: MUST DEMAND THE FOLLOWING PERMISSIONS BEFORE CALLING + ' FileIOPermission( FileIOPermissionAccess.PathDiscovery, oProj.m_DirName & "\." ) + ' + Private Shared Function FindFileFilter(ByVal oAssemblyData As AssemblyData) As String + Dim Index As Integer + Dim files() As FileSystemInfo + Dim file As FileSystemInfo + + files = oAssemblyData.m_DirFiles + Index = oAssemblyData.m_DirNextFileIndex + + Do While True + If Index > files.GetUpperBound(0) Then + oAssemblyData.m_DirFiles = Nothing + oAssemblyData.m_DirNextFileIndex = 0 + Return Nothing + End If + + file = files(Index) + + If ((file.Attributes And (FileAttributes.Directory Or FileAttributes.System Or FileAttributes.Hidden)) = 0) OrElse _ + ((file.Attributes And oAssemblyData.m_DirAttributes) <> 0) Then + oAssemblyData.m_DirNextFileIndex = Index + 1 + Return files(Index).Name + End If + + Index += 1 + Loop + Return Nothing + End Function + + End Class + +End Namespace diff --git a/Microsoft.VisualBasic/runtime/msvbalib/Helpers/IntegerType.vb b/Microsoft.VisualBasic/runtime/msvbalib/Helpers/IntegerType.vb new file mode 100644 index 000000000..6901769e2 --- /dev/null +++ b/Microsoft.VisualBasic/runtime/msvbalib/Helpers/IntegerType.vb @@ -0,0 +1,135 @@ +' Copyright (c) Microsoft Corporation. All rights reserved. + +Imports System +Imports System.Globalization + +Imports Microsoft.VisualBasic.CompilerServices.Utils + +Namespace Microsoft.VisualBasic.CompilerServices + +#Region " BACKWARDS COMPATIBILITY " + + 'WARNING WARNING WARNING WARNING WARNING + 'This code exists to support Everett compiled applications. Make sure you understand + 'the backwards compatibility ramifications of any edit you make in this region. + 'WARNING WARNING WARNING WARNING WARNING + + _ + Public NotInheritable Class IntegerType + ' Prevent creation. + Private Sub New() + End Sub + + Public Shared Function FromString(ByVal Value As String) As Integer + + If Value Is Nothing Then + Return 0 + End If + + Try + Dim i64Value As Int64 + + If IsHexOrOctValue(Value, i64Value) Then + Return CInt(i64Value) + End If + + Return CInt(DoubleType.Parse(Value)) + + Catch e As FormatException + Throw New InvalidCastException(GetResourceString(ResID.InvalidCast_FromStringTo, Left(Value, 32), "Integer"), e) + End Try + + End Function + + Public Shared Function FromObject(ByVal Value As Object) As Integer + + If Value Is Nothing Then + Return 0 + End If + + Dim ValueInterface As IConvertible + Dim ValueTypeCode As TypeCode + + ValueInterface = TryCast(Value, IConvertible) + + If ValueInterface Is Nothing Then + GoTo ThrowInvalidCast + End If + + ValueTypeCode = ValueInterface.GetTypeCode() + + Select Case ValueTypeCode + + Case TypeCode.Boolean + Return CInt(ValueInterface.ToBoolean(Nothing)) + + Case TypeCode.Byte + If TypeOf Value Is System.Byte Then + Return CInt(DirectCast(Value, Byte)) + Else + Return CInt(ValueInterface.ToByte(Nothing)) + End If + + Case TypeCode.Int16 + If TypeOf Value Is System.Int16 Then + Return CInt(DirectCast(Value, Int16)) + Else + Return CInt(ValueInterface.ToInt16(Nothing)) + End If + + Case TypeCode.Int32 + If TypeOf Value Is System.Int32 Then + Return CInt(DirectCast(Value, Int32)) + Else + Return CInt(ValueInterface.ToInt32(Nothing)) + End If + + Case TypeCode.Int64 + If TypeOf Value Is System.Int64 Then + Return CInt(DirectCast(Value, Int64)) + Else + Return CInt(ValueInterface.ToInt64(Nothing)) + End If + + Case TypeCode.Single + If TypeOf Value Is System.Single Then + Return CInt(DirectCast(Value, Single)) + Else + Return CInt(ValueInterface.ToSingle(Nothing)) + End If + + Case TypeCode.Double + If TypeOf Value Is System.Double Then + Return CInt(DirectCast(Value, Double)) + Else + Return CInt(ValueInterface.ToDouble(Nothing)) + End If + + Case TypeCode.Decimal + 'Do not use .ToDecimal because of jit temp issue effects all perf + Return DecimalToInteger(ValueInterface) + + Case TypeCode.String + Return IntegerType.FromString(ValueInterface.ToString(Nothing)) + Case TypeCode.Char, _ + TypeCode.DateTime + ' Fall through to error + + Case Else + ' Fall through to error + End Select +ThrowInvalidCast: + Throw New InvalidCastException(GetResourceString(ResID.InvalidCast_FromTo, VBFriendlyName(Value), "Integer")) + End Function + + Private Shared Function DecimalToInteger(ByVal ValueInterface As IConvertible) As Integer + Return CInt(ValueInterface.ToDecimal(Nothing)) + End Function + + End Class + +#End Region + +End Namespace + + diff --git a/Microsoft.VisualBasic/runtime/msvbalib/Helpers/InternalXmlHelper.vb b/Microsoft.VisualBasic/runtime/msvbalib/Helpers/InternalXmlHelper.vb new file mode 100644 index 000000000..3b5a4fc8e --- /dev/null +++ b/Microsoft.VisualBasic/runtime/msvbalib/Helpers/InternalXmlHelper.vb @@ -0,0 +1,194 @@ +' Copyright (c) Microsoft Corporation. All rights reserved. + +Namespace Microsoft.VisualBasic.CompilerServices + _ + Public NotInheritable Class InternalXmlHelper + _ + Private Sub New() + End Sub + ' _ + Public Shared Property Value(ByVal source As Global.System.Collections.Generic.IEnumerable(Of Global.System.Xml.Linq.XElement)) As String + Get + For Each item As Global.System.Xml.Linq.XElement In source + Return item.Value + Next + Return Nothing + End Get + Set(ByVal value As String) + For Each item As Global.System.Xml.Linq.XElement In source + item.Value = value + Exit For + Next + End Set + End Property + ' _ + Public Shared Property AttributeValue(ByVal source As Global.System.Collections.Generic.IEnumerable(Of Global.System.Xml.Linq.XElement), ByVal name As Global.System.Xml.Linq.XName) As String + Get + For Each item As Global.System.Xml.Linq.XElement In source + Return CType(item.Attribute(name), String) + Next + Return Nothing + End Get + Set(ByVal value As String) + For Each item As Global.System.Xml.Linq.XElement In source + item.SetAttributeValue(name, value) + Exit For + Next + End Set + End Property + ' _ + Public Shared Property AttributeValue(ByVal source As Global.System.Xml.Linq.XElement, ByVal name As Global.System.Xml.Linq.XName) As String + Get + Return CType(source.Attribute(name), String) + End Get + Set(ByVal value As String) + source.SetAttributeValue(name, value) + End Set + End Property + _ + Public Shared Function CreateAttribute(ByVal name As Global.System.Xml.Linq.XName, ByVal value As Object) As Global.System.Xml.Linq.XAttribute + If value Is Nothing Then + Return Nothing + End If + Return New Global.System.Xml.Linq.XAttribute(name, value) + End Function + _ + Public Shared Function CreateNamespaceAttribute(ByVal name As Global.System.Xml.Linq.XName, ByVal ns As Global.System.Xml.Linq.XNamespace) As Global.System.Xml.Linq.XAttribute + Dim a As New Global.System.Xml.Linq.XAttribute(name, ns.NamespaceName) + a.AddAnnotation(ns) + Return a + End Function + _ + Public Shared Function RemoveNamespaceAttributes(ByVal inScopePrefixes() As String, ByVal inScopeNs() As Global.System.Xml.Linq.XNamespace, ByVal attributes As Global.System.Collections.Generic.List(Of Global.System.Xml.Linq.XAttribute), ByVal obj As Object) As Object + If obj IsNot Nothing Then + Dim elem As Global.System.Xml.Linq.XElement = TryCast(obj, Global.System.Xml.Linq.XElement) + If Not elem Is Nothing Then + Return RemoveNamespaceAttributes(inScopePrefixes, inScopeNs, attributes, elem) + Else + Dim elems As Global.System.Collections.IEnumerable = TryCast(obj, Global.System.Collections.IEnumerable) + If elems IsNot Nothing Then + Return RemoveNamespaceAttributes(inScopePrefixes, inScopeNs, attributes, elems) + End If + End If + End If + Return obj + End Function + _ + Public Shared Function RemoveNamespaceAttributes(ByVal inScopePrefixes() As String, ByVal inScopeNs() As Global.System.Xml.Linq.XNamespace, ByVal attributes As Global.System.Collections.Generic.List(Of Global.System.Xml.Linq.XAttribute), ByVal obj As Global.System.Collections.IEnumerable) As Global.System.Collections.IEnumerable + If obj IsNot Nothing Then + Dim elems As Global.System.Collections.Generic.IEnumerable(Of Global.System.Xml.Linq.XElement) = TryCast(obj, Global.System.Collections.Generic.IEnumerable(Of Global.System.Xml.Linq.XElement)) + If elems IsNot Nothing Then + Return Global.System.Linq.Enumerable.Select(elems, AddressOf New RemoveNamespaceAttributesClosure(inScopePrefixes, inScopeNs, attributes).ProcessXElement) + Else + Return Global.System.Linq.Enumerable.Select(Global.System.Linq.Enumerable.Cast(Of Object)(obj), AddressOf New RemoveNamespaceAttributesClosure(inScopePrefixes, inScopeNs, attributes).ProcessObject) + End If + End If + Return obj + End Function + _ + _ + _ + Private NotInheritable Class RemoveNamespaceAttributesClosure + Private ReadOnly m_inScopePrefixes As String() + Private ReadOnly m_inScopeNs As Global.System.Xml.Linq.XNamespace() + Private ReadOnly m_attributes As Global.System.Collections.Generic.List(Of Global.System.Xml.Linq.XAttribute) + _ + Friend Sub New(ByVal inScopePrefixes() As String, ByVal inScopeNs() As Global.System.Xml.Linq.XNamespace, ByVal attributes As Global.System.Collections.Generic.List(Of Global.System.Xml.Linq.XAttribute)) + m_inScopePrefixes = inScopePrefixes + m_inScopeNs = inScopeNs + m_attributes = attributes + End Sub + _ + Friend Function ProcessXElement(ByVal elem As Global.System.Xml.Linq.XElement) As Global.System.Xml.Linq.XElement + Return InternalXmlHelper.RemoveNamespaceAttributes(m_inScopePrefixes, m_inScopeNs, m_attributes, elem) + End Function + _ + Friend Function ProcessObject(ByVal obj As Object) As Object + Dim elem As Global.System.Xml.Linq.XElement = TryCast(obj, Global.System.Xml.Linq.XElement) + If elem IsNot Nothing Then + Return InternalXmlHelper.RemoveNamespaceAttributes(m_inScopePrefixes, m_inScopeNs, m_attributes, elem) + Else + Return obj + End If + End Function + End Class + _ + Public Shared Function RemoveNamespaceAttributes(ByVal inScopePrefixes() As String, ByVal inScopeNs() As Global.System.Xml.Linq.XNamespace, ByVal attributes As Global.System.Collections.Generic.List(Of Global.System.Xml.Linq.XAttribute), ByVal e As Global.System.Xml.Linq.XElement) As Global.System.Xml.Linq.XElement + If e IsNot Nothing Then + Dim a As Global.System.Xml.Linq.XAttribute = e.FirstAttribute + + While a IsNot Nothing + Dim nextA As Global.System.Xml.Linq.XAttribute = a.NextAttribute + + If a.IsNamespaceDeclaration() Then + Dim ns As Global.System.Xml.Linq.XNamespace = a.Annotation(Of Global.System.Xml.Linq.XNamespace)() + Dim prefix As String = a.Name.LocalName + + If ns IsNot Nothing Then + If inScopePrefixes IsNot Nothing AndAlso inScopeNs IsNot Nothing Then + Dim lastIndex As Integer = inScopePrefixes.Length - 1 + + For i As Integer = 0 To lastIndex + Dim currentInScopePrefix As String = inScopePrefixes(i) + Dim currentInScopeNs As Global.System.Xml.Linq.XNamespace = inScopeNs(i) + If prefix.Equals(currentInScopePrefix) Then + If ns = currentInScopeNs Then + 'prefix and namespace match. Remove the unneeded ns attribute + a.Remove() + End If + + 'prefix is in scope but refers to something else. Leave the ns attribute. + a = Nothing + Exit For + End If + Next + End If + + If a IsNot Nothing Then + 'Prefix is not in scope + 'Now check whether it's going to be in scope because it is in the attributes list + + If attributes IsNot Nothing Then + Dim lastIndex As Integer = attributes.Count - 1 + For i As Integer = 0 To lastIndex + Dim currentA As Global.System.Xml.Linq.XAttribute = attributes(i) + Dim currentInScopePrefix As String = currentA.Name.LocalName + Dim currentInScopeNs As Global.System.Xml.Linq.XNamespace = currentA.Annotation(Of Global.System.Xml.Linq.XNamespace)() + If currentInScopeNs IsNot Nothing Then + If prefix.Equals(currentInScopePrefix) Then + If ns = currentInScopeNs Then + 'prefix and namespace match. Remove the unneeded ns attribute + a.Remove() + End If + + 'prefix is in scope but refers to something else. Leave the ns attribute. + a = Nothing + Exit For + End If + End If + Next + End If + + If a IsNot Nothing Then + 'Prefix is definitely not in scope + a.Remove() + 'namespace is not defined either. Add this attributes list + attributes.Add(a) + End If + End If + End If + End If + + a = nextA + End While + End If + Return e + End Function + + End Class + +End Namespace + + diff --git a/Microsoft.VisualBasic/runtime/msvbalib/Helpers/LateBinding.vb b/Microsoft.VisualBasic/runtime/msvbalib/Helpers/LateBinding.vb new file mode 100644 index 000000000..e2aaf8757 --- /dev/null +++ b/Microsoft.VisualBasic/runtime/msvbalib/Helpers/LateBinding.vb @@ -0,0 +1,1291 @@ +' Copyright (c) Microsoft Corporation. All rights reserved. + +Imports System +Imports System.Reflection +Imports System.Globalization +Imports System.Diagnostics +Imports System.Runtime.InteropServices +Imports System.Runtime.Remoting + +Imports Microsoft.VisualBasic.CompilerServices.ExceptionUtils +Imports Microsoft.VisualBasic.CompilerServices.Utils + +Namespace Microsoft.VisualBasic.CompilerServices + +#If DEBUG Then + _ + Friend NotInheritable Class DebuggerHiddenAttribute + Inherits Attribute + End Class + + _ + Friend NotInheritable Class DebuggerStepThroughAttribute + Inherits Attribute + End Class +#End If + +#Region " BACKWARDS COMPATIBILITY " + + 'WARNING WARNING WARNING WARNING WARNING + 'This code exists to support Everett compiled applications. Make sure you understand + 'the backwards compatibility ramifications of any edit you make in this region. + 'WARNING WARNING WARNING WARNING WARNING + + _ + Public NotInheritable Class LateBinding + ' Prevent creation. + Private Sub New() + End Sub + + Private Const DefaultCallType As CallType = CType(0, CallType) + + Private Shared Function GetMostDerivedMemberInfo(ByVal objIReflect As IReflect, ByVal name As String, ByVal flags As BindingFlags) As MemberInfo + Dim mi() As MemberInfo + Dim i As Integer + Dim Selected As MemberInfo + + ' Filter out generic methods for compatibility with the Whidbey framework + mi = GetNonGenericMembers(objIReflect.GetMember(name, flags)) + + If mi Is Nothing OrElse mi.Length = 0 Then + Return Nothing + End If + + 'Only the outermost definition can be called + Selected = mi(0) + For i = 1 To mi.GetUpperBound(0) + If mi(i).DeclaringType.IsSubclassOf(Selected.DeclaringType) Then + Selected = mi(i) + End If + Next i + + Return Selected + + End Function + + + + _ + Public Shared Function LateGet(ByVal o As Object, _ + ByVal objType As Type, _ + ByVal name As String, _ + ByVal args() As Object, _ + ByVal paramnames() As String, _ + ByVal CopyBack() As Boolean) As Object + + Dim flags As BindingFlags + + flags = BindingFlags.IgnoreCase Or _ + BindingFlags.GetProperty Or _ + BindingFlags.InvokeMethod Or _ + BindingFlags.FlattenHierarchy Or _ + BindingFlags.OptionalParamBinding Or _ + BindingFlags.Static Or _ + BindingFlags.Instance Or _ + BindingFlags.Public + + + If objType Is Nothing Then + If o Is Nothing Then + Throw VbMakeException(vbErrors.ObjNotSet) + End If + + objType = o.GetType() + End If + + Dim objIReflect As IReflect = GetCorrectIReflect(o, objType) + + If name Is Nothing Then + name = "" + End If + + + If objType.IsCOMObject Then + CheckForClassExtendingCOMClass(objType) + + Else + 'Fields must be checked for and returned here + ' shadowing causes behavior that must be + Dim mi As MemberInfo = GetMostDerivedMemberInfo(objIReflect, name, flags Or BindingFlags.GetField) + If (Not mi Is Nothing) AndAlso (mi.MemberType = MemberTypes.Field) Then + 'SECURITY CHECK + VBBinder.SecurityCheckForLateboundCalls(mi, objType, objIReflect) + 'SECURITY CHECK + + ' If we use the System.Type's IReflect Implementation + Dim ValueOfField As Object + If objType Is objIReflect OrElse CType(mi, FieldInfo).IsStatic OrElse _ + DoesTargetObjectMatch(o, mi) Then + + VerifyObjRefPresentForInstanceCall(o, mi) + + ValueOfField = CType(mi, FieldInfo).GetValue(o) + Else + ValueOfField = InvokeMemberOnIReflect(objIReflect, mi, BindingFlags.GetField, o, Nothing) + End If + + If (args Is Nothing OrElse args.Length = 0) Then + Return ValueOfField + Else + Return LateIndexGet(ValueOfField, args, paramnames) + End If + End If + End If + + Dim binder As VBBinder + + binder = New VBBinder(CopyBack) + + Try + Return binder.InvokeMember(name, flags, objType, objIReflect, o, args, paramnames) + + ' + ' + ' There may be a property or field that returns an array or object with a default member + ' We get the field or property then try using a LateIndexGet + 'UNDONE: handle this in the binder code once the com+ team has completed the Beta2 DCR work + Catch ex As Exception When IsMissingMemberException(ex) + + If objType.IsCOMObject() OrElse ((Not args Is Nothing) AndAlso (args.Length > 0)) Then + Dim oTmp As Object + + flags = BindingFlags.IgnoreCase Or _ + BindingFlags.GetProperty Or _ + BindingFlags.InvokeMethod Or _ + BindingFlags.FlattenHierarchy Or _ + BindingFlags.OptionalParamBinding Or _ + BindingFlags.Static Or _ + BindingFlags.Instance Or _ + BindingFlags.Public + + If Not objType.IsCOMObject() Then + flags = flags Or BindingFlags.GetField + End If + + Try + oTmp = binder.InvokeMember(name, flags, objType, objIReflect, o, Nothing, Nothing) + Catch exInner As AccessViolationException + Throw exInner + Catch exInner As StackOverflowException + Throw exInner + Catch exInner As OutOfMemoryException + Throw exInner + Catch exInner As System.Threading.ThreadAbortException + Throw exInner + Catch + oTmp = Nothing + End Try + If oTmp Is Nothing Then + Throw New MissingMemberException(GetResourceString(ResID.MissingMember_MemberNotFoundOnType2, name, VBFriendlyName(objType, o))) + Else + Try + Return LateIndexGet(oTmp, args, paramnames) + Catch exInner As Exception When IsMissingMemberException(exInner) AndAlso (TypeOf ex Is MissingMemberException) + Throw ex + End Try + End If + Else + Throw New MissingMemberException(GetResourceString(ResID.MissingMember_MemberNotFoundOnType2, name, VBFriendlyName(objType, o))) + End If + + Catch ex As TargetInvocationException + Throw ex.InnerException + + End Try + + End Function + + Private Shared Function IsMissingMemberException(ByVal ex As Exception) As Boolean + + If TypeOf ex Is MissingMemberException Then + Return True + + ElseIf TypeOf ex Is MemberAccessException Then + Return True + + Else + Dim cex As COMException = TryCast(ex, COMException) + + If cex IsNot Nothing Then + If cex.ErrorCode = DISP_E_UNKNOWNNAME Then + Return True + + ElseIf cex.ErrorCode = (SEVERITY_ERROR Or FACILITY_CONTROL Or 438) Then + Return True + End If + + ElseIf TypeOf ex Is TargetInvocationException AndAlso _ + (TypeOf ex.InnerException Is COMException AndAlso _ + CType(ex.InnerException, COMException).ErrorCode = DISP_E_NOTACOLLECTION) Then + Return True + + End If + End If + + Return False + + End Function + + + + _ + Public Shared Sub LateSetComplex(ByVal o As Object, ByVal objType As Type, ByVal name As String, _ + ByVal args() As Object, ByVal paramnames() As String, _ + ByVal OptimisticSet As Boolean, ByVal RValueBase As Boolean) + + 'CONSIDER (4/12/2001): rewrite the binder to suppress the exception in this case + Try + ' - We can't change this now - + ' this really needs to be done in two steps: + ' step 1: can the Set succeed? + ' step 2: perform the Set + ' the rvaluebase check would be done between 1 and 2 + InternalLateSet(o, objType, name, args, paramnames, OptimisticSet, DefaultCallType) + + If RValueBase AndAlso objType.IsValueType Then + 'note that objType is passed byref to InternalLateSet and that it + 'should be valid by the time we get to this point + Throw New Exception(GetResourceString(ResID.RValueBaseForValueType, VBFriendlyName(objType, o), VBFriendlyName(objType, o))) + End If + ' UNDONE - - replace below with 'when IsMissingException' - also why the 'when OptimisticSet = True' clause below ? + Catch ex As System.MissingMemberException When OptimisticSet = True + 'A missing member exception means it has no Set member. Silently handle the exception. + End Try + + End Sub + + + _ + Public Shared Sub LateSet(ByVal o As Object, ByVal objType As Type, ByVal name As String, _ + ByVal args() As Object, ByVal paramnames() As String) + + InternalLateSet(o, objType, name, args, paramnames, False, DefaultCallType) + + End Sub + + _ + Friend Shared Sub InternalLateSet(ByVal o As Object, _ + ByRef objType As Type, _ + ByVal name As String, _ + ByVal args() As Object, _ + ByVal paramnames() As String, _ + ByVal OptimisticSet As Boolean, _ + ByVal UseCallType As CallType) + + Dim flags As BindingFlags + Dim binder As VBBinder + + flags = BindingFlags.IgnoreCase Or _ + BindingFlags.FlattenHierarchy Or _ + BindingFlags.OptionalParamBinding Or _ + BindingFlags.Static Or _ + BindingFlags.Instance Or _ + BindingFlags.Public + + If objType Is Nothing Then + If o Is Nothing Then + Throw VbMakeException(vbErrors.ObjNotSet) + End If + + objType = o.GetType() + End If + + Dim objIReflect As IReflect = GetCorrectIReflect(o, objType) + + If name Is Nothing Then + name = "" + End If + + If objType.IsCOMObject() Then + CheckForClassExtendingCOMClass(objType) + If UseCallType = CallType.Set Then + flags = flags Or BindingFlags.PutRefDispProperty + If args(args.GetUpperBound(0)) Is Nothing Then + args(args.GetUpperBound(0)) = New DispatchWrapper(Nothing) + End If + ElseIf UseCallType = CallType.Let Then + flags = flags Or BindingFlags.PutDispProperty + Else + flags = flags Or GetPropertyPutFlags(args(args.GetUpperBound(0))) + End If + Else + flags = flags Or BindingFlags.SetProperty + 'Fields must be checked for and returned here + ' shadowing causes behavior that must be + Dim mi As MemberInfo = GetMostDerivedMemberInfo(objIReflect, name, flags Or BindingFlags.SetField) + If (Not mi Is Nothing) AndAlso (mi.MemberType = MemberTypes.Field) Then + Dim fi As FieldInfo = CType(mi, FieldInfo) + Dim NewValue As Object + + If fi.IsInitOnly Then + 'REVIEW: Should this be MissingMember? + Throw New MissingMemberException(GetResourceString(ResID.MissingMember_ReadOnlyField2, name, VBFriendlyName(objType, o))) + End If + + If (args Is Nothing OrElse args.Length = 0) Then + 'Everything must be shadowed + Throw New MissingMemberException(GetResourceString(ResID.MissingMember_MemberNotFoundOnType2, name, VBFriendlyName(objType, o))) + + ElseIf args.Length = 1 Then + NewValue = args(0) + 'SECURITY CHECK + VBBinder.SecurityCheckForLateboundCalls(fi, objType, objIReflect) + 'SECURITY CHECK + + Dim FieldValue As Object + If NewValue Is Nothing Then + FieldValue = Nothing + Else + FieldValue = ObjectType.CTypeHelper(args(0), fi.FieldType) + End If + + If objType Is objIReflect OrElse fi.IsStatic OrElse _ + DoesTargetObjectMatch(o, fi) Then + + VerifyObjRefPresentForInstanceCall(o, fi) + + fi.SetValue(o, FieldValue) + Else + InvokeMemberOnIReflect(objIReflect, fi, BindingFlags.SetField, o, New Object() {FieldValue}) + End If + + Return + + ElseIf args.Length > 1 Then + 'SECURITY CHECK + VBBinder.SecurityCheckForLateboundCalls(mi, objType, objIReflect) + 'SECURITY CHECK + + Dim FieldValue As Object = Nothing + If objType Is objIReflect OrElse CType(mi, FieldInfo).IsStatic OrElse _ + DoesTargetObjectMatch(o, mi) Then + + VerifyObjRefPresentForInstanceCall(o, mi) + + FieldValue = CType(mi, FieldInfo).GetValue(o) + + Else + FieldValue = InvokeMemberOnIReflect(objIReflect, mi, BindingFlags.GetField, o, New Object() {FieldValue}) + End If + + LateIndexSet(FieldValue, args, paramnames) + Return + + End If + + End If + End If + + binder = New VBBinder(Nothing) + + If (OptimisticSet AndAlso args.GetUpperBound(0) > 0) Then + 'Check for an overloaded property. + ' We need to see what property needs to be set + ' overloaded properties can cause problems because + ' of ReadOnly/WriteOnly + Dim pi As PropertyInfo + Dim propflags As BindingFlags = _ + BindingFlags.GetProperty Or _ + BindingFlags.IgnoreCase Or _ + BindingFlags.FlattenHierarchy Or _ + BindingFlags.OptionalParamBinding Or _ + BindingFlags.Static Or _ + BindingFlags.Instance Or _ + BindingFlags.Public + + Dim indexTypes As System.Type() + Dim i As Integer + Dim oArg As Object + + indexTypes = New System.Type(args.GetUpperBound(0) - 1) {} + + For i = 0 To indexTypes.GetUpperBound(0) + oArg = args(i) + If oArg Is Nothing Then + indexTypes(i) = Nothing + Else + indexTypes(i) = oArg.GetType() + End If + Next i + Try + pi = objIReflect.GetProperty(name, propflags, binder, GetType(Integer), indexTypes, Nothing) + If pi Is Nothing OrElse (Not pi.CanWrite) Then + 'Property has no setter, so bail + Return + End If + Catch ex As MissingMemberException + 'No set for this + Return + End Try + End If + + + Try + binder.InvokeMember(name, flags, objType, objIReflect, o, args, paramnames) + Catch ex As Exception When IsMissingMemberException(ex) + ' There may be a property or field that returns an array or object with a default member + ' We get the field or property then try using a LateIndexGet + 'UNDONE: handle this in the binder code once the com+ team has completed the Beta2 DCR work + If (Not args Is Nothing) AndAlso (args.Length > 1) Then + Dim oTmp As Object + + flags = BindingFlags.IgnoreCase Or _ + BindingFlags.GetProperty Or _ + BindingFlags.FlattenHierarchy Or _ + BindingFlags.OptionalParamBinding Or _ + BindingFlags.Static Or _ + BindingFlags.Instance Or _ + BindingFlags.Public + + If Not objType.IsCOMObject() Then + flags = flags Or BindingFlags.GetField + End If + + + Try + oTmp = binder.InvokeMember(name, flags, objType, objIReflect, o, Nothing, Nothing) + Catch exInner As Exception When IsMissingMemberException(exInner) AndAlso (TypeOf ex Is MissingMemberException) + 'Throw the exception thrown by VBBinder.InvokeMember + Throw ex + Catch exInner As AccessViolationException + Throw exInner + Catch exInner As StackOverflowException + Throw exInner + Catch exInner As OutOfMemoryException + Throw exInner + Catch exInner As System.Threading.ThreadAbortException + Throw exInner + Catch exInner As Exception + oTmp = Nothing + End Try + + If oTmp Is Nothing Then + Throw New MissingMemberException(GetResourceString(ResID.MissingMember_MemberNotFoundOnType2, name, VBFriendlyName(objType, o))) + Else + Try + LateIndexSet(oTmp, args, paramnames) + Catch exInner As Exception When IsMissingMemberException(exInner) AndAlso (TypeOf ex Is MissingMemberException) + Throw ex + End Try + End If + Else + Throw New MissingMemberException(GetResourceString(ResID.MissingMember_MemberNotFoundOnType2, name, VBFriendlyName(objType, o))) + End If + + Catch ex As TargetInvocationException + If ex.InnerException Is Nothing Then + Throw ex + ElseIf TypeOf ex.InnerException Is TargetParameterCountException Then + If (flags And BindingFlags.PutRefDispProperty) <> 0 Then + 'Set was being called + Throw New MissingMemberException(GetResourceString(ResID.MissingMember_MemberSetNotFoundOnType2, name, VBFriendlyName(objType, o))) + Else + 'Let was being called + Throw New MissingMemberException(GetResourceString(ResID.MissingMember_MemberLetNotFoundOnType2, name, VBFriendlyName(objType, o))) + End If + Else + Throw ex.InnerException + End If + End Try + + End Sub + + Private Shared Sub CheckForClassExtendingCOMClass(ByVal objType As Type) + + If Not objType.IsCOMObject OrElse objType.FullName = "System.__ComObject" Then + Return + End If + + If objType.BaseType.FullName = "System.__ComObject" Then + Return + End If + Throw New InvalidOperationException(GetResourceString(ResID.LateboundCallToInheritedComClass)) + + End Sub + + _ + Public Shared Function LateIndexGet(ByVal o As Object, ByVal args() As Object, ByVal paramnames() As String) As Object + + Dim objType As Type + Dim binder As VBBinder + Dim DefaultName As String = Nothing + + If o Is Nothing Then + Throw VbMakeException(vbErrors.ObjNotSet) + End If + + objType = o.GetType() + + Dim objIReflect As IReflect = GetCorrectIReflect(o, objType) + + If objType.IsArray() Then + + 'Named arguments are not allowed as indexers + If paramnames IsNot Nothing AndAlso paramnames.Length <> 0 Then + Throw New ArgumentException(GetResourceString(ResID.Argument_InvalidNamedArgs)) + End If + + Dim ArgCount As Integer + Dim ary As Array + + ary = CType(o, Array) + + 'Optimized cases + ArgCount = args.Length + + 'Check for valid dimensions + If ArgCount <> ary.Rank Then + 'UNDONE: message text + Throw New RankException + End If + + If ArgCount = 1 Then + Return ary.GetValue(CInt(args(0))) + ElseIf ArgCount = 2 Then + Return ary.GetValue(CInt(args(0)), CInt(args(1))) + Else + Dim IndexArray() As Integer + Dim ArgIndex As Integer + + ReDim IndexArray(ArgCount - 1) + + For ArgIndex = 0 To ArgCount - 1 + IndexArray(ArgIndex) = CInt(args(ArgIndex)) + Next ArgIndex + + Return ary.GetValue(IndexArray) + End If + Else + Dim flags As BindingFlags + Dim member As MemberInfo + Dim members As MemberInfo() + Dim match As MethodBase() = Nothing + + flags = BindingFlags.IgnoreCase Or _ + BindingFlags.GetProperty Or _ + BindingFlags.InvokeMethod Or _ + BindingFlags.FlattenHierarchy Or _ + BindingFlags.OptionalParamBinding Or _ + BindingFlags.Instance Or _ + BindingFlags.Static Or _ + BindingFlags.Public + + If Not objType.IsCOMObject() Then + If (args Is Nothing OrElse args.Length = 0) Then + 'how can this be? how can we have an indexed late get with no arguments? + flags = flags Or BindingFlags.GetField + End If + + Dim i, iNext As Integer + + members = GetDefaultMembers(objType, objIReflect, DefaultName) + + If Not members Is Nothing Then + For i = 0 To members.GetUpperBound(0) + member = members(i) + If member.MemberType = MemberTypes.Property Then + member = CType(member, PropertyInfo).GetGetMethod() + End If + + If Not member Is Nothing AndAlso member.MemberType <> MemberTypes.Field Then + members(iNext) = member + iNext += 1 + End If + Next i + End If + + 'Catch the missing method here, Invoke below will throw an ArgumentException + If members Is Nothing Or iNext = 0 Then + Throw New MissingMemberException(GetResourceString(ResID.MissingMember_NoDefaultMemberFound1, VBFriendlyName(objType, o))) + End If + match = New MethodBase(iNext - 1) {} + For i = 0 To iNext - 1 + Try + match(i) = CType(members(i), MethodBase) + Catch ex As StackOverflowException + Throw ex + Catch ex As OutOfMemoryException + Throw ex + Catch ex As System.Threading.ThreadAbortException + Throw ex + Catch ex As Exception + ' If this assert is triggered due to an AccessViolationException, + ' report a bug to the CLR. + Debug.Assert(False, ex.Message) + End Try + Next i + + + Else + CheckForClassExtendingCOMClass(objType) + End If + + binder = New VBBinder(Nothing) + + Try + If objType.IsCOMObject() Then + Return binder.InvokeMember("", flags, objType, objIReflect, o, args, paramnames) + + Else + Dim state As Object = Nothing + Dim retValue As Object + Dim method As MethodBase + + 'Give binder necessary information for creating error text + binder.m_BindToName = DefaultName + binder.m_objType = objType + method = binder.BindToMethod(flags, match, args, Nothing, Nothing, paramnames, state) + + ' + 'BEGIN: SECURITY SECURITY SECURITY SECURITY SECURITY SECURITY SECURITY SECURITY + ' + VBBinder.SecurityCheckForLateboundCalls(method, objType, objIReflect) + + If objType Is objIReflect OrElse method.IsStatic OrElse _ + DoesTargetObjectMatch(o, method) Then + + VerifyObjRefPresentForInstanceCall(o, method) + + retValue = method.Invoke(o, args) + + Else + retValue = InvokeMemberOnIReflect(objIReflect, method, BindingFlags.InvokeMethod, o, args) + End If + + ' + 'END: SECURITY SECURITY SECURITY SECURITY SECURITY SECURITY SECURITY SECURITY + ' + binder.ReorderArgumentArray(args, state) + Return retValue + + End If + + Catch ex As Exception When IsMissingMemberException(ex) + Throw New MissingMemberException(GetResourceString(ResID.MissingMember_NoDefaultMemberFound1, VBFriendlyName(objType, o))) + + Catch ex As TargetInvocationException + Throw ex.InnerException + End Try + End If + End Function + + + Private Shared Function GetDefaultMembers(ByVal typ As Type, ByVal objIReflect As IReflect, ByRef DefaultName As String) As MemberInfo() + + Dim attributeList As Object() + Dim members As MemberInfo() + + If typ Is objIReflect Then + Do + attributeList = typ.GetCustomAttributes(GetType(DefaultMemberAttribute), False) + If (Not attributeList Is Nothing) AndAlso (attributeList.Length <> 0) Then + DefaultName = CType(attributeList(0), DefaultMemberAttribute).MemberName + members = typ.GetMember(DefaultName, BindingFlags.IgnoreCase Or _ + BindingFlags.FlattenHierarchy Or _ + BindingFlags.Instance Or _ + BindingFlags.Static Or _ + BindingFlags.Public) + + ' Filter out generic methods for compatibility with the Whidbey framework + members = GetNonGenericMembers(members) + + If members Is Nothing OrElse members.Length = 0 Then + DefaultName = "" + Return Nothing + End If + Return members + End If + typ = typ.BaseType + Loop While (Not typ Is Nothing) + + DefaultName = "" + + Return Nothing + Else + + members = objIReflect.GetMember("", BindingFlags.IgnoreCase Or _ + BindingFlags.FlattenHierarchy Or _ + BindingFlags.Instance Or _ + BindingFlags.Static Or _ + BindingFlags.Public) + + ' Filter out generic methods for compatibility with the Whidbey framework + members = GetNonGenericMembers(members) + + If members Is Nothing OrElse members.Length = 0 Then + DefaultName = "" + Return Nothing + End If + + DefaultName = members(0).Name + Return members + End If + + End Function + + _ + Public Shared Sub LateIndexSetComplex(ByVal o As Object, ByVal args() As Object, ByVal paramnames() As String, _ + ByVal OptimisticSet As Boolean, ByVal RValueBase As Boolean) + + 'CONSIDER (4/12/2001): rewrite the binder to suppress the exception in this case + Try + 'CONSIDER (5/9/2001): + ' this really needs to be done in two steps: + ' step 1: can the Set succeed? + ' step 2: perform the Set + ' the rvaluebase check would be done between 1 and 2 + LateIndexSet(o, args, paramnames) + + If RValueBase AndAlso o.GetType().IsValueType Then + Throw New Exception(GetResourceString(ResID.RValueBaseForValueType, o.GetType().Name, o.GetType().Name)) + End If + Catch ex As System.MissingMemberException When OptimisticSet = True + 'A missing member exception means it has no Set member. Silently handle the exception. + End Try + End Sub + + _ + Public Shared Sub LateIndexSet(ByVal o As Object, ByVal args() As Object, ByVal paramnames() As String) + + Dim objType As Type + Dim DefaultName As String = Nothing + + If o Is Nothing Then + Throw VbMakeException(vbErrors.ObjNotSet) + End If + + objType = o.GetType() + + Dim objIReflect As IReflect = GetCorrectIReflect(o, objType) + + If objType.IsArray Then + + 'Named arguments are not allowed as indexers + If paramnames IsNot Nothing AndAlso paramnames.Length <> 0 Then + Throw New ArgumentException(GetResourceString(ResID.Argument_InvalidNamedArgs)) + End If + + Dim ArgCount As Integer + Dim ary As Array + Dim NewValue As Object + + ary = CType(o, Array) + + 'Optimized cases + ArgCount = args.Length - 1 + + NewValue = args(ArgCount) + If Not NewValue Is Nothing Then + Dim elemType As Type + elemType = objType.GetElementType() + + 'Check that the type is valid for the assignment + If Not NewValue.GetType() Is elemType Then + NewValue = ObjectType.CTypeHelper(NewValue, elemType) + End If + End If + + 'Check for valid dimensions + If ArgCount <> ary.Rank Then + 'UNDONE: message text + Throw New RankException + End If + + If ArgCount = 1 Then + ary.SetValue(NewValue, CInt(args(0))) + + ElseIf ArgCount = 2 Then + ary.SetValue(NewValue, CInt(args(0)), CInt(args(1))) + + Else + Dim IndexArray() As Integer + Dim ArgIndex As Integer + ReDim IndexArray(ArgCount - 1) + + For ArgIndex = 0 To ArgCount - 1 + IndexArray(ArgIndex) = CInt(args(ArgIndex)) + Next ArgIndex + + ary.SetValue(NewValue, IndexArray) + End If + Else + Dim flags As BindingFlags + Dim member As MemberInfo + Dim members As MemberInfo() + Dim match As MethodBase() = Nothing + + flags = BindingFlags.IgnoreCase Or _ + BindingFlags.FlattenHierarchy Or _ + BindingFlags.OptionalParamBinding Or _ + BindingFlags.Instance Or _ + BindingFlags.Static Or _ + BindingFlags.Public + + If objType.IsCOMObject() Then + CheckForClassExtendingCOMClass(objType) + flags = flags Or GetPropertyPutFlags(args(args.GetUpperBound(0))) + + Else + flags = flags Or BindingFlags.SetProperty + If (args.Length = 1) Then + flags = flags Or BindingFlags.SetField + End If + + Dim i, iNext As Integer + + members = GetDefaultMembers(objType, objIReflect, DefaultName) + + If Not members Is Nothing Then + For i = 0 To members.GetUpperBound(0) + member = members(i) + If member.MemberType = MemberTypes.Property Then + member = CType(member, PropertyInfo).GetSetMethod() + End If + + If Not member Is Nothing AndAlso member.MemberType <> MemberTypes.Field Then + members(iNext) = member + iNext += 1 + End If + Next i + End If + + 'Catch the missing method here, Invoke below will throw an ArgumentException + If members Is Nothing Or iNext = 0 Then + Throw New MissingMemberException(GetResourceString(ResID.MissingMember_NoDefaultMemberFound1, VBFriendlyName(objType, o))) + End If + + match = New MethodBase(iNext - 1) {} + For i = 0 To iNext - 1 + Try + match(i) = CType(members(i), MethodBase) + Catch ex As StackOverflowException + Throw ex + Catch ex As OutOfMemoryException + Throw ex + Catch ex As System.Threading.ThreadAbortException + Throw ex + Catch + ' If this assert is triggered because of an AccessViolation exception, + ' report a bug to the CLR. + Debug.Assert(False) + End Try + Next i + End If + + Dim binder As VBBinder + binder = New VBBinder(Nothing) + Try + If objType.IsCOMObject Then + binder.InvokeMember("", flags, objType, objIReflect, o, args, paramnames) + Else + Dim state As Object = Nothing + Dim method As MethodBase + + binder.m_BindToName = DefaultName + binder.m_objType = objType + method = binder.BindToMethod(flags, match, args, Nothing, Nothing, paramnames, state) + + ' + 'BEGIN: SECURITY SECURITY SECURITY SECURITY SECURITY SECURITY SECURITY SECURITY + ' + VBBinder.SecurityCheckForLateboundCalls(method, objType, objIReflect) + + If objType Is objIReflect OrElse method.IsStatic OrElse _ + DoesTargetObjectMatch(o, method) Then + + VerifyObjRefPresentForInstanceCall(o, method) + + method.Invoke(o, args) + + Else + InvokeMemberOnIReflect(objIReflect, method, BindingFlags.InvokeMethod, o, args) + End If + + ' + 'END: SECURITY SECURITY SECURITY SECURITY SECURITY SECURITY SECURITY SECURITY + ' + + binder.ReorderArgumentArray(args, state) + + End If + + Catch ex As Exception When IsMissingMemberException(ex) + 'Override the message so that we're consistent with above Throw + Throw New MissingMemberException(GetResourceString(ResID.MissingMember_NoDefaultMemberFound1, VBFriendlyName(objType, o))) + + Catch ex As TargetInvocationException + Throw ex.InnerException + End Try + End If + End Sub + + + Private Shared Function GetPropertyPutFlags(ByVal NewValue As Object) As BindingFlags + If (NewValue Is Nothing) Then + Return BindingFlags.SetProperty + ElseIf (TypeOf NewValue Is System.ValueType) OrElse _ + (TypeOf NewValue Is String) OrElse _ + (TypeOf NewValue Is DBNull) OrElse _ + (TypeOf NewValue Is Missing) OrElse _ + (TypeOf NewValue Is System.Array) OrElse _ + (TypeOf NewValue Is System.Runtime.InteropServices.CurrencyWrapper) Then + Return BindingFlags.PutDispProperty + End If + Return BindingFlags.PutRefDispProperty + End Function + + + _ + Public Shared Sub LateCall(ByVal o As Object, ByVal objType As Type, ByVal name As String, _ + ByVal args() As Object, ByVal paramnames() As String, ByVal CopyBack() As Boolean) + + InternalLateCall(o, objType, name, args, paramnames, CopyBack, True) + End Sub + + + _ + Friend Shared Function InternalLateCall(ByVal o As Object, ByVal objType As Type, ByVal name As String, _ + ByVal args() As Object, ByVal paramnames() As String, ByVal CopyBack() As Boolean, ByVal IgnoreReturn As Boolean) As Object + Dim flags As BindingFlags + + flags = BindingFlags.IgnoreCase Or _ + BindingFlags.InvokeMethod Or _ + BindingFlags.FlattenHierarchy Or _ + BindingFlags.OptionalParamBinding Or _ + BindingFlags.Static Or _ + BindingFlags.Instance Or _ + BindingFlags.Public + + If IgnoreReturn Then + flags = flags Or BindingFlags.IgnoreReturn + End If + + If objType Is Nothing Then + If o Is Nothing Then + Throw VbMakeException(vbErrors.ObjNotSet) + End If + + objType = o.GetType() + End If + + Dim objIReflect As IReflect = GetCorrectIReflect(o, objType) + + If objType.IsCOMObject Then + CheckForClassExtendingCOMClass(objType) + End If + + If name Is Nothing Then + name = "" + End If + + Dim binder As VBBinder = New VBBinder(CopyBack) + + If Not objType.IsCOMObject Then + Dim mi() As MemberInfo + mi = GetMembersByName(objIReflect, name, flags) + + If (mi Is Nothing) OrElse (mi.Length = 0) Then + Throw New MissingMemberException(GetResourceString(ResID.MissingMember_MemberNotFoundOnType2, name, VBFriendlyName(objType, o))) + End If + If MemberIsField(mi) Then + 'This expression is not a procedure, but occurs as the target of a procedure call. + Throw New ArgumentException(GetResourceString(ResID.ExpressionNotProcedure, name, VBFriendlyName(objType, o))) + End If + + 'Try a FastCall + If (mi.Length = 1 AndAlso (paramnames Is Nothing OrElse paramnames.Length = 0)) Then + Dim Parameters As ParameterInfo() + Dim member As MemberInfo + Dim method As MethodBase + Dim ArgsLength, ParametersLength As Integer + + member = mi(0) + If member.MemberType = MemberTypes.Property Then + member = CType(member, PropertyInfo).GetGetMethod() + If member Is Nothing Then + Throw New MissingMemberException(GetResourceString(ResID.MissingMember_MemberNotFoundOnType2, name, VBFriendlyName(objType, o))) + End If + End If + + method = CType(member, MethodBase) + Parameters = method.GetParameters() + + ArgsLength = args.Length + ParametersLength = Parameters.Length + + If ParametersLength = ArgsLength Then + If ParametersLength = 0 Then + Return FastCall(o, method, Parameters, args, objType, objIReflect) + ElseIf (CopyBack Is Nothing) AndAlso NoByrefs(Parameters) Then + 'Check that we don't have a param array here + Dim LastParam As ParameterInfo + LastParam = Parameters(ParametersLength - 1) + If LastParam.ParameterType.IsArray() Then + 'Check for ParamArray attribute + Dim ca() As Object + ca = LastParam.GetCustomAttributes(GetType(ParamArrayAttribute), False) + If (ca Is Nothing) OrElse (ca.Length = 0) Then + Return FastCall(o, method, Parameters, args, objType, objIReflect) + End If + Else + Return FastCall(o, method, Parameters, args, objType, objIReflect) + End If + End If + End If + + End If + + End If + + Try + Return binder.InvokeMember(name, flags, objType, objIReflect, o, args, paramnames) + + Catch ex As MissingMemberException + 'Keep existing exception text + Throw + + 'Some exceptions can occur that need to be mapped to MissingMemberException + Catch ex As Exception When IsMissingMemberException(ex) + Throw New MissingMemberException(GetResourceString(ResID.MissingMember_MemberNotFoundOnType2, name, VBFriendlyName(objType, o))) + + Catch ex As TargetInvocationException + Throw ex.InnerException + + End Try + + End Function + + Private Shared Function NoByrefs(ByVal parameters As ParameterInfo()) As Boolean + Dim i As Integer + For i = 0 To parameters.Length - 1 + If parameters(i).ParameterType.IsByRef Then + Return False + End If + Next i + Return True + End Function + + _ + Private Shared Function FastCall(ByVal o As Object, ByVal method As MethodBase, ByVal Parameters As ParameterInfo(), ByVal args As Object(), ByVal objType As Type, ByVal objIReflect As IReflect) As Object + + Dim oArg As Object + Dim Parameter As ParameterInfo + Dim i As Integer + + For i = 0 To args.GetUpperBound(0) + Parameter = Parameters(i) + oArg = args(i) + If TypeOf oArg Is Missing AndAlso Parameter.IsOptional Then + oArg = Parameter.DefaultValue + End If + args(i) = ObjectType.CTypeHelper(oArg, Parameter.ParameterType) + Next i + ' + 'BEGIN: SECURITY SECURITY SECURITY SECURITY SECURITY SECURITY SECURITY SECURITY + ' + VBBinder.SecurityCheckForLateboundCalls(method, objType, objIReflect) + + If objType Is objIReflect OrElse method.IsStatic OrElse _ + DoesTargetObjectMatch(o, method) Then + + VerifyObjRefPresentForInstanceCall(o, method) + + Return method.Invoke(o, args) + + Else + '' UNDONE - passing the binder to user code ok ? seems ok, but check with + Return InvokeMemberOnIReflect(objIReflect, method, BindingFlags.InvokeMethod, o, args) + End If + + ' + 'END: SECURITY SECURITY SECURITY SECURITY SECURITY SECURITY SECURITY SECURITY + ' + + End Function + + Private Shared Function GetMembersByName(ByVal objIReflect As IReflect, ByVal name As String, ByVal flags As BindingFlags) As MemberInfo() + + ' Filter out generic methods for compatibility with the WHidbey framework + GetMembersByName = GetNonGenericMembers(objIReflect.GetMember(name, flags)) + + If (Not GetMembersByName Is Nothing) AndAlso (GetMembersByName.Length = 0) Then + Return Nothing + End If + + End Function + + Private Shared Function MemberIsField(ByVal mi As MemberInfo()) As Boolean + + Dim MemberIndex As Integer + Dim ThisMember As MemberInfo + + For MemberIndex = 0 To mi.GetUpperBound(0) + + ThisMember = mi(MemberIndex) + + If ThisMember Is Nothing Then + 'Skip this one + + ElseIf ThisMember.MemberType = MemberTypes.Field Then + + ' + 'Run through the list and remove all the inherited members of this type + ' + Dim j As Integer + For j = 0 To mi.GetUpperBound(0) + + If MemberIndex <> j AndAlso (Not mi(j) Is Nothing) AndAlso _ + ThisMember.DeclaringType.IsSubclassOf(mi(j).DeclaringType) Then + ' ThisMember Shadows the baseclass and ThatMethod should not be accessible + ' to the caller + mi(j) = Nothing + End If + + Next j + + End If + + Next + + 'UNDONE: Add test case to verify this code cannot return invalid value + For Each ThisMember In mi + If Not ThisMember Is Nothing Then + If ThisMember.MemberType <> MemberTypes.Field Then + Return False + End If + End If + Next + Return True + End Function + + Friend Shared Function DoesTargetObjectMatch(ByVal Value As Object, ByVal Member As MemberInfo) As Boolean + If (Value Is Nothing) OrElse Member.DeclaringType.IsAssignableFrom(Value.GetType) Then + Return True + End If + + Return False + End Function + + + ' Used for invoking the InvokeMember of IReflect. We don't want to use the flags already because + ' we don't want clashes like between InvokeMethod and SetProperty. + Const VBLateBindingFlags As BindingFlags = BindingFlags.IgnoreCase Or _ + BindingFlags.FlattenHierarchy Or _ + BindingFlags.OptionalParamBinding Or _ + BindingFlags.Static Or _ + BindingFlags.Instance Or _ + BindingFlags.Public + + Friend Shared Function InvokeMemberOnIReflect(ByVal objIReflect As IReflect, ByVal member As MemberInfo, ByVal flags As BindingFlags, ByVal target As Object, ByVal args As Object()) As Object + ' UNDONE - - 06-06-2002 - consider if we can reuse the same binder instance used for the rest of this latebound call + + Dim binder As New VBBinder(Nothing) + binder.CacheMember(member) + + Return objIReflect.InvokeMember(member.Name, VBLateBindingFlags Or flags, binder, target, args, Nothing, Nothing, Nothing) + + End Function + + Private Shared Function GetCorrectIReflect(ByVal o As Object, ByVal objType As Type) As IReflect + + ' For a System.Type Object, we always use the underlying System.Type's IReflect implementation, because a System.Type's Implementation + ' returns information about the Type it represents and not its own information. If we did not do this, latebound calls to a System.Type + ' Object would fail. + + ' We don't support this For COM Objects because this is not a valid COM scenario and the perf. degrade is intolerable + If (Not (o Is Nothing)) AndAlso (Not objType.IsCOMObject) AndAlso (Not RemotingServices.IsTransparentProxy(o)) AndAlso (Not (TypeOf o Is System.Type)) Then + Dim IReflectObject As IReflect = TryCast(o, IReflect) + + If IReflectObject IsNot Nothing Then + Return IReflectObject + End If + End If + Return DirectCast(objType, IReflect) + End Function + + + Friend Shared Sub VerifyObjRefPresentForInstanceCall(ByVal Value As Object, ByVal Member As MemberInfo) + If Value Is Nothing Then + Dim IsStatic As Boolean = True + + Debug.Assert(Not Member Is Nothing, "How can this be Nothing ?") + + Select Case Member.MemberType + Case MemberTypes.Method + IsStatic = DirectCast(Member, MethodInfo).IsStatic + + Case MemberTypes.Field + IsStatic = DirectCast(Member, FieldInfo).IsStatic + + Case MemberTypes.Constructor + IsStatic = DirectCast(Member, ConstructorInfo).IsStatic + + Case MemberTypes.Property + ' We always decide based on the context and use the get or the set accessor + ' appropriately. So by the time this method is invoked, we should just see + ' the MethodInfos of the Getter or the Setter + + Debug.Assert(False, "How can a property get here ?") + + Case Else + 'this should never happen + + Debug.Assert(False, "How did we get here ?") + End Select + + + If Not IsStatic Then + 'Reference to non-shared member '|1' requires an object reference. + Throw New NullReferenceException( _ + GetResourceString(ResID.NullReference_InstanceReqToAccessMember1, MemberToString(Member))) + End If + End If + End Sub + + Friend Shared Function GetNonGenericMembers(ByVal Members As MemberInfo()) As MemberInfo() + If Members IsNot Nothing AndAlso Members.Length > 0 Then + Dim NonGenericMemberCount As Integer = 0 + + For MemberIndex As Integer = 0 To Members.GetUpperBound(0) + If LegacyIsGeneric(Members(MemberIndex)) Then + Members(MemberIndex) = Nothing + Else + NonGenericMemberCount += 1 + End If + Next + + If NonGenericMemberCount = Members.GetUpperBound(0) + 1 Then + ' There weren't any generic members. Return the original array. + Return Members + ElseIf NonGenericMemberCount > 0 Then + Dim NonGenericMembers(NonGenericMemberCount - 1) As MemberInfo + + Dim NonGenericIndex As Integer = 0 + + ' Collect the non-generic methods + For MemberIndex As Integer = 0 To Members.GetUpperBound(0) + If Members(MemberIndex) IsNot Nothing Then + NonGenericMembers(NonGenericIndex) = Members(MemberIndex) + NonGenericIndex += 1 + End If + Next + + Return NonGenericMembers + End If + End If + + Return Nothing + End Function + + Friend Shared Function LegacyIsGeneric(ByVal Member As MemberInfo) As Boolean + 'Returns True whether Method is an instantiated or uninstantiated generic method. + Dim Method As MethodBase = TryCast(Member, MethodBase) + If Method Is Nothing Then Return False + Return Method.IsGenericMethod + End Function + + End Class + +#End Region + +End Namespace diff --git a/Microsoft.VisualBasic/runtime/msvbalib/Helpers/LikeOperator.vb b/Microsoft.VisualBasic/runtime/msvbalib/Helpers/LikeOperator.vb new file mode 100644 index 000000000..431f38be2 --- /dev/null +++ b/Microsoft.VisualBasic/runtime/msvbalib/Helpers/LikeOperator.vb @@ -0,0 +1,1826 @@ +' Copyright (c) Microsoft Corporation. All rights reserved. + +Option Explicit On +Option Strict On + +Imports System +Imports System.Globalization +Imports System.Collections.Generic +Imports System.Diagnostics + +Imports Microsoft.VisualBasic.Strings +Imports Microsoft.VisualBasic.CompilerServices.Symbols +Imports Microsoft.VisualBasic.CompilerServices.Operators +Imports Microsoft.VisualBasic.CompilerServices.Utils + + +Namespace Microsoft.VisualBasic.CompilerServices + +#If TELESTO Then + 'FIXME: _ + Public NotInheritable Class LikeOperator +#Else + _ + Public NotInheritable Class LikeOperator +#End If + + Private Sub New() + End Sub + + ' The list of ligatures + ' + Private Enum Ligatures + Invalid = 0 + Min = &HC6 + ssBeta = &HDF + szBeta = &HDF + aeUpper = &HC6 + ae = &HE6 + thUpper = &HDE + th = &HFE + oeUpper = &H152 + oe = &H153 + Max = &H153 + End Enum + + ' The expansions for the ligatures. Note that the order of these is the same as their + ' order in the Ligatures enum + Shared LigatureExpansions() As String = {"", "ss", "sz", "AE", "ae", "TH", "th", "OE", "oe"} + + Shared LigatureMap() As Byte + + Shared Sub New() + LigatureMap = New Byte(Ligatures.Max - Ligatures.Min) {} + + LigatureMap(Ligatures.ssBeta - Ligatures.Min) = 1 + LigatureMap(Ligatures.szBeta - Ligatures.Min) = 2 + LigatureMap(Ligatures.aeUpper - Ligatures.Min) = 3 + LigatureMap(Ligatures.ae - Ligatures.Min) = 4 + LigatureMap(Ligatures.thUpper - Ligatures.Min) = 5 + LigatureMap(Ligatures.th - Ligatures.Min) = 6 + LigatureMap(Ligatures.oeUpper - Ligatures.Min) = 7 + LigatureMap(Ligatures.oe - Ligatures.Min) = 8 + + End Sub + +#if TELESTO + Private Shared Function LigatureIndex(ByVal ch As Char) As Byte + + If AscW(ch) < Ligatures.Min OrElse AscW(ch) > Ligatures.Max Then + Return 0 + End If + + Return LigatureMap(AscW(ch) - Ligatures.Min) + End Function +#else + Private Shared Function LigatureIndex(ByVal ch As Char) As Byte + + If Asc(ch) < Ligatures.Min OrElse Asc(ch) > Ligatures.Max Then + Return 0 + End If + + Return LigatureMap(Asc(ch) - Ligatures.Min) + End Function +#end if + + Private Shared Function CanCharExpand _ + ( _ + ByVal ch As Char, _ + ByVal LocaleSpecificLigatureTable As Byte(), _ + ByVal Comparer As CompareInfo, _ + ByVal Options As CompareOptions _ + ) As Integer + + Debug.Assert(Options <> CompareOptions.Ordinal, "Char expansion check unexpected during binary compare!!!") + + Dim Index As Byte = LigatureIndex(ch) + + If Index = 0 Then + Return 0 + End If + + If LocaleSpecificLigatureTable(Index) = 0 Then + If Comparer.Compare(ch, LigatureExpansions(Index)) = 0 Then + LocaleSpecificLigatureTable(Index) = 1 + Else + LocaleSpecificLigatureTable(Index) = 2 + End If + End If + + If LocaleSpecificLigatureTable(Index) = 1 Then + Return Index + End If + End Function + + Private Shared Function GetCharExpansion _ + ( _ + ByVal ch As Char, _ + ByVal LocaleSpecificLigatureTable As Byte(), _ + ByVal Comparer As CompareInfo, _ + ByVal Options As CompareOptions _ + ) As String + + Dim Index As Integer = CanCharExpand(ch, LocaleSpecificLigatureTable, Comparer, Options) + + If Index = 0 Then + Return ch + End If + + Return LigatureExpansions(Index) + End Function + + Private Enum CharKind + None + ExpandedChar1 + ExpandedChar2 + End Enum + + Private Structure LigatureInfo + Friend Kind As CharKind + Friend CharBeforeExpansion As Char + End Structure + + ': What I've been able to divine about this function is that its purpose is to normalize the string + 'that is going to be used in the Like operator. The string may contain liguratures (two letters being represented by + 'a single glpyh) that need to be expanded. It also may contain Katakana characters that need to be mapped to + 'narrow width characters. + Private Shared Sub ExpandString _ + ( _ + ByRef Input As String, _ + ByRef Length As Integer, _ + ByRef InputLigatureInfo As LigatureInfo(), _ + ByVal LocaleSpecificLigatureTable As Byte(), _ + ByVal Comparer As CompareInfo, _ + ByVal Options As CompareOptions, _ + ByRef WidthChanged As Boolean, _ + ByVal UseFullWidth As Boolean _ + ) + + WidthChanged = False + + If Length = 0 Then Return +#If TELESTO Then + 'There is no operating system support for doing the katakana, full/half width mapping on Telesto. + 'Telesto works cross platform (MAC) so we can't use LCMapString() as per below, anyway. + 'Our attempt at normalization will be simply to lower case the string. This means we don't have the same + 'behavior as the desktop for Japan. + Input = Input.ToLower(System.Globalization.CultureInfo.InvariantCulture) +#Else 'Desktop + + Const CODEPAGE_JAPANESE As Integer = 932 + + Dim Culture As CultureInfo = GetCultureInfo() + Dim Encoding As Text.Encoding = Text.Encoding.GetEncoding(Culture.TextInfo.ANSICodePage) + Dim Flags As Integer = NativeTypes.LCMAP_LOWERCASE + Dim MapDone As Boolean = False + + If Not Encoding.IsSingleByte Then + Flags = NativeTypes.LCMAP_LOWERCASE Or NativeTypes.LCMAP_HALFWIDTH + + If IsValidCodePage(CODEPAGE_JAPANESE) Then + If UseFullWidth Then + Flags = NativeTypes.LCMAP_LOWERCASE Or NativeTypes.LCMAP_FULLWIDTH Or NativeTypes.LCMAP_KATAKANA + Else + Flags = NativeTypes.LCMAP_LOWERCASE Or NativeTypes.LCMAP_HALFWIDTH Or NativeTypes.LCMAP_KATAKANA + End If + + ' Mapping certain wide Katakana characters to narrow can + ' increase the string length. See if this is going to happen. + ' + Input = vbLCMapString(Culture, Flags, Input) + MapDone = True + + If Input.Length <> Length Then + Length = Input.Length + WidthChanged = True + End If + End If + End If + + If Not MapDone Then + Input = vbLCMapString(Culture, Flags, Input) + +#If DEBUG + Debug.Assert(Input.Length = Length) +#End If + End If +#End If 'Not Telesto + + + Dim ExtraChars As Integer + + For i As Integer = 0 To Length - 1 + Dim ch As Char = Input.Chars(i) + + If CanCharExpand(ch, LocaleSpecificLigatureTable, Comparer, Options) <> 0 Then + ExtraChars += 1 + End If + Next + + + If ExtraChars > 0 Then + InputLigatureInfo = New LigatureInfo(Length + ExtraChars - 1) {} + Dim NewInput As New Text.StringBuilder(Length + ExtraChars - 1) + + + Dim NewCharIndex As Integer = 0 + + For i As Integer = 0 to Length - 1 + + Dim ch As Char = Input.Chars(i) + + If CanCharExpand(ch, LocaleSpecificLigatureTable, Comparer, Options) <> 0 Then + + Dim Expansion As String = GetCharExpansion(ch, LocaleSpecificLigatureTable, Comparer, Options) + NewInput.Append(Expansion) + + InputLigatureInfo(NewCharIndex).Kind = CharKind.ExpandedChar1 + InputLigatureInfo(NewCharIndex).CharBeforeExpansion = ch + + NewCharIndex += 1 + + InputLigatureInfo(NewCharIndex).Kind = CharKind.ExpandedChar2 + InputLigatureInfo(NewCharIndex).CharBeforeExpansion = ch + + Else + NewInput.Append(ch) + End If + + NewCharIndex += 1 + Next + + Input = NewInput.ToString() + Length = NewInput.Length + End If + End Sub + + Public Shared Function LikeObject(ByVal Source As Object, ByVal Pattern As Object, ByVal CompareOption As CompareMethod) As Object + + Dim conv1, conv2 As IConvertible + Dim tc1, tc2 As TypeCode + + conv1 = TryCast(Source, IConvertible) + If conv1 Is Nothing Then + If Source Is Nothing Then + tc1 = TypeCode.Empty + Else + tc1 = TypeCode.Object + End If + Else + tc1 = conv1.GetTypeCode() + End If + + conv2 = TryCast(Pattern, IConvertible) + If conv2 Is Nothing Then + If Pattern Is Nothing Then + tc2 = TypeCode.Empty + Else + tc2 = TypeCode.Object + End If + Else + tc2 = conv2.GetTypeCode() + End If + + 'Special cases for Char() + If (tc1 = TypeCode.Object) AndAlso (TypeOf Source Is Char()) Then + tc1 = TypeCode.String + End If + + If (tc2 = TypeCode.Object) AndAlso (TypeOf Pattern Is Char()) Then + tc2 = TypeCode.String + End If + + If tc1 = TypeCode.Object OrElse tc2 = TypeCode.Object Then + Return InvokeUserDefinedOperator(UserDefinedOperator.Like, Source, Pattern) + End If + + Return LikeString(CStr(Source), CStr(Pattern), CompareOption) + End Function + + Public Shared Function LikeString(ByVal Source As String, ByVal Pattern As String, ByVal CompareOption As CompareMethod) As Boolean + Dim SourceIndex As Integer + Dim PatternIndex As Integer + + Dim SourceLength As Integer + Dim PatternLength As Integer + + Dim SourceLigatureInfo As LigatureInfo() = Nothing + Dim PatternLigatureInfo As LigatureInfo() = Nothing + + Dim Options As CompareOptions + Dim Comparer As CompareInfo + + If Pattern Is Nothing Then + PatternLength = 0 + Else + PatternLength = Pattern.Length + End If + + If Source Is Nothing Then + SourceLength = 0 + Else + SourceLength = Source.Length + End If + + + ' + ' We expand ligatures up front, but we need to keep track of + ' where they were. We need the source ligature positions so + ' that "?" in the pattern will match both characters of the + ' ligature. We need the pattern ligature positions for + ' bracketed character lists (e.g. [abc0-9]), because a + ' ligature would look like two separate characters. But note + ' that we do this only for option compare text mode. + ' + + If CompareOption = CompareMethod.Binary Then + Options = CompareOptions.Ordinal + Comparer = Nothing + Else + Comparer = GetCultureInfo().CompareInfo + Options = CompareOptions.IgnoreCase Or _ + CompareOptions.IgnoreWidth Or _ + CompareOptions.IgnoreKanaType + + Dim LocaleSpecificLigatureTable As Byte() = New Byte(LigatureExpansions.Length - 1) {} + + ExpandString(Source, SourceLength, SourceLigatureInfo, LocaleSpecificLigatureTable, Comparer, Options, False, False) + ExpandString(Pattern, PatternLength, PatternLigatureInfo, LocaleSpecificLigatureTable, Comparer, Options, False, False) + End If + + + ' The first phase is an optimization for anything in the pattern + ' before the first "*". (If the pattern has no "*" in it, this + ' will do the whole thing.) + ' + ' Visit each character in the pattern, and see if it matches the + ' source. + ' + ' + Dim p As Char + + Do While (PatternIndex < PatternLength AndAlso SourceIndex < SourceLength) + p = Pattern.Chars(PatternIndex) + + Select Case p + Case "?"c, ChrW(&HFF1F) + 'AdvanceToNextChar(Source, SourceLength, SourceIndex, Options) + SkipToEndOfExpandedChar(SourceLigatureInfo, SourceLength, SourceIndex) + + Case "#"c, ChrW(&HFF03) + + If Not System.Char.IsDigit(Source.Chars(SourceIndex)) Then + Return False + End If + + Case "["c, ChrW(&HFF3B) + 'Match ranges like "[ACE-TZ]" + + Dim RangePatternEmpty, RangeMismatch, RangePatternError As Boolean + MatchRange( _ + Source, _ + SourceLength, _ + SourceIndex, _ + SourceLigatureInfo, _ + Pattern, _ + PatternLength, _ + PatternIndex, _ + PatternLigatureInfo, _ + RangePatternEmpty, _ + RangeMismatch, _ + RangePatternError, _ + Comparer, _ + Options) + + If RangePatternError Then + Throw New ArgumentException(GetResourceString(ResID.Argument_InvalidValue1, "Pattern")) + End If + + If RangeMismatch Then + Return False + End If + + If RangePatternEmpty Then + PatternIndex += 1 + Continue Do + End If + + Case "*"c, ChrW(&HFF0A) + Dim AsteriskMismatch, AsteriskPatternError As Boolean + + MatchAsterisk( _ + Source, _ + SourceLength, _ + SourceIndex, _ + SourceLigatureInfo, _ + Pattern, _ + PatternLength, _ + PatternIndex, _ + PatternLigatureInfo, _ + AsteriskMismatch, _ + AsteriskPatternError, _ + Comparer, _ + Options) + + If AsteriskPatternError Then + Throw New ArgumentException(GetResourceString(ResID.Argument_InvalidValue1, "Pattern")) + End If + + Return Not AsteriskMismatch + + Case Else + ' Not a special pattern character. Just see if we have a match. + ' + If CompareChars( _ + Source, _ + SourceLength, _ + SourceIndex, _ + SourceIndex, _ + SourceLigatureInfo, _ + Pattern, _ + PatternLength, _ + PatternIndex, _ + PatternIndex, _ + PatternLigatureInfo, _ + Comparer, _ + Options) <> 0 Then + Return False + End If + + End Select + + PatternIndex += 1 + SourceIndex += 1 + Loop + + + ' Check for the special case that we're at the end of the source, + ' and the pattern has nothing left in it but *'s or empty []'s. + ' + While PatternIndex < PatternLength + p = Pattern.Chars(PatternIndex) + + If p = "*"c OrElse p = ChrW(&HFF0A) Then + PatternIndex += 1 + + ElseIf PatternIndex + 1 < PatternLength AndAlso _ + ((p = "["c AndAlso Pattern.Chars(PatternIndex + 1) = "]"c) OrElse _ + (p = ChrW(&HFF3B) AndAlso Pattern.Chars(PatternIndex + 1) = ChrW(&HFF3D))) Then + + PatternIndex += 2 + Else + Exit While + End If + End While + + Return PatternIndex >= PatternLength AndAlso SourceIndex >= SourceLength + End Function + + Private Shared Sub SkipToEndOfExpandedChar(ByVal InputLigatureInfo As LigatureInfo(), ByVal Length As Integer, ByRef Current As Integer) + + If InputLigatureInfo Is Nothing Then + 'Nothing to do for the option compare binary case or the simple option compare text case + Else + If Current < Length AndAlso InputLigatureInfo(Current).Kind = CharKind.ExpandedChar1 Then + Current = Current + 1 + End If + End If + End Sub + + Private Shared Function CompareChars _ + ( _ + ByVal Left As String, _ + ByVal LeftLength As Integer, _ + ByVal LeftStart As Integer, _ + ByRef LeftEnd As Integer, _ + ByVal LeftLigatureInfo As LigatureInfo(), _ + ByVal Right As String, _ + ByVal RightLength As Integer, _ + ByVal RightStart As Integer, _ + ByRef RightEnd As Integer, _ + ByVal RightLigatureInfo As LigatureInfo(), _ + ByVal Comparer As CompareInfo, _ + ByVal Options As CompareOptions, _ + Optional ByVal MatchBothCharsOfExpandedCharInRight As Boolean = False, _ + Optional ByVal UseUnexpandedCharForRight As Boolean = False _ + ) As Integer + + LeftEnd = LeftStart + RightEnd = RightStart + + If Options = CompareOptions.Ordinal Then + ' Ordinal compare + ' + Return AscW(Left.Chars(LeftStart)) - AscW(Right.Chars(RightStart)) + End If + + Debug.Assert(Comparer IsNot Nothing, "Like Operator - Comparer expected for option compare text!!!") + Debug.Assert(Not MatchBothCharsOfExpandedCharInRight OrElse Not UseUnexpandedCharForRight, "Conflicting compare options!!!") + + + If UseUnexpandedCharForRight Then + If RightLigatureInfo IsNot Nothing AndAlso RightLigatureInfo(RightEnd).Kind = CharKind.ExpandedChar1 Then + + Right = Right.Substring(RightStart, RightEnd - RightStart) + Right = Right & RightLigatureInfo(RightEnd).CharBeforeExpansion + RightEnd += 1 + Return CompareChars(Left.Substring(LeftStart, LeftEnd - LeftStart + 1), Right, Comparer, Options) + + End If + + ElseIf MatchBothCharsOfExpandedCharInRight Then + + Dim SavedRightEnd As Integer = RightEnd + SkipToEndOfExpandedChar(RightLigatureInfo, RightLength, RightEnd) + + ' If matching both expanded characters on the right, then consider multiple characters on the left too + ' + If SavedRightEnd < RightEnd Then + + Dim NumberOfExtraCharsToCompare As Integer = 0 + If LeftEnd + 1 < LeftLength Then + NumberOfExtraCharsToCompare = 1 + End If + + Dim MatchResult As Integer = _ + CompareChars(Left.Substring(LeftStart, LeftEnd - LeftStart + 1 + NumberOfExtraCharsToCompare), Right.Substring(RightStart, RightEnd - RightStart + 1), Comparer, Options) + + If MatchResult = 0 Then + LeftEnd = LeftEnd + NumberOfExtraCharsToCompare + End If + + Return MatchResult + End If + End If + + Debug.Assert(LeftEnd < LeftLength AndAlso RightEnd < RightLength, "Comparing chars beyond end of string!!!") + + If LeftEnd = LeftStart AndAlso RightEnd = RightStart Then + Return Comparer.Compare(Left.Chars(LeftStart), Right.Chars(RightStart), Options) + End If + + Return CompareChars(Left.Substring(LeftStart, LeftEnd - LeftStart + 1), Right.Substring(RightStart, RightEnd - RightStart + 1), Comparer, Options) + + End Function + + Private Shared Function CompareChars _ + ( _ + ByVal Left As String, _ + ByVal Right As String, _ + ByVal Comparer As CompareInfo, _ + ByVal Options As CompareOptions _ + ) As Integer + + If Options = CompareOptions.Ordinal Then + ' Ordinal compare + ' + Return AscW(Left.Chars(0)) - AscW(Right.Chars(0)) + End If + + Debug.Assert(Comparer IsNot Nothing, "Like Operator - Comparer expected for option compare text!!!") + + Return Comparer.Compare(Left, Right, Options) + + End Function + + Private Shared Function CompareChars _ + ( _ + ByVal Left As Char, _ + ByVal Right As Char, _ + ByVal Comparer As CompareInfo, _ + ByVal Options As CompareOptions _ + ) As Integer + + If Options = CompareOptions.Ordinal Then + ' Ordinal compare + ' + Return AscW(Left) - AscW(Right) + End If + + Debug.Assert(Comparer IsNot Nothing, "Like Operator - Comparer expected for option compare text!!!") + + Return Comparer.Compare(Left, Right, Options) + + End Function + + Private Shared Sub MatchRange _ + ( _ + ByVal Source As String, _ + ByVal SourceLength As Integer, _ + ByRef SourceIndex As Integer, _ + ByVal SourceLigatureInfo As LigatureInfo(), _ + ByVal Pattern As String, _ + ByVal PatternLength As Integer, _ + ByRef PatternIndex As Integer, _ + ByVal PatternLigatureInfo As LigatureInfo(), _ + ByRef RangePatternEmpty As Boolean, _ + ByRef Mismatch As Boolean, _ + ByRef PatternError As Boolean, _ + ByVal Comparer As CompareInfo, _ + ByVal Options As CompareOptions, _ + Optional ByRef SeenNot As Boolean = False, _ + Optional ByVal RangeList As List(Of Range) = Nothing, _ + Optional ByVal ValidatePatternWithoutMatching As Boolean = False _ + ) + + Debug.Assert(PatternIndex <= PatternLength AndAlso _ + (Pattern.Chars(PatternIndex) = "[" OrElse Pattern.Chars(PatternIndex) = ChrW(&HFF3B)), _ + "Like operator - Unexpected range matching!!!") + + Debug.Assert(RangeList Is Nothing OrElse ValidatePatternWithoutMatching, "Unexpected options to MatchRange!!!") + + Dim RangeStart, RangeEnd As String + Dim Range As Range + + RangePatternEmpty = False + Mismatch = False + PatternError = False + SeenNot = False + + PatternIndex += 1 + + If PatternIndex >= PatternLength Then + PatternError = True + Return + End If + + Dim p As Char = Pattern.Chars(PatternIndex) + + If p = "!"c OrElse p = ChrW(&HFF01) Then + SeenNot = True + PatternIndex += 1 + + If PatternIndex >= PatternLength Then + Mismatch = True + Return + End If + + p = Pattern.Chars(PatternIndex) + End If + + If p = "]"c OrElse p = ChrW(&HFF3D) Then + + If SeenNot Then + 'We got "[!]" ? Treat it as the single literal character "!". + ' + SeenNot = False + + If (Not ValidatePatternWithoutMatching) Then + Mismatch = Not (CompareChars(Source.Chars(SourceIndex), "!"c, Comparer, Options) = 0) + End If + + If RangeList IsNot Nothing Then + Range.Start = PatternIndex - 1 + Range.StartLength = 1 + Range.End = -1 + Range.EndLength = 0 + RangeList.Add(Range) + End If + + Return + End If + + ' Ignore empty brackets + RangePatternEmpty = True + Return + End If + + ' Scan through character list + ' + Do + RangeStart = Nothing + RangeEnd = Nothing + + If p = "]"c OrElse p = ChrW(&HFF3D) Then + Mismatch = Not SeenNot + Return 'End of "[...]" match + End If + + ' Try to match the expanded ligature + ' + Dim SourceNextIndex, PatternNextIndex As Integer + Dim CompareResult As Integer + + If Not ValidatePatternWithoutMatching AndAlso _ + PatternLigatureInfo IsNot Nothing AndAlso _ + PatternLigatureInfo(PatternIndex).Kind = CharKind.ExpandedChar1 Then + + ' VB6 compat - Match expanded char and return in this case without even validating RangeStart > RangeEnd + ' + CompareResult = _ + CompareChars( _ + Source, _ + SourceLength, _ + SourceIndex, _ + SourceNextIndex, _ + SourceLigatureInfo, _ + Pattern, _ + PatternLength, _ + PatternIndex, _ + PatternNextIndex, _ + PatternLigatureInfo, _ + Comparer, _ + Options, _ + MatchBothCharsOfExpandedCharInRight:=True) + + If CompareResult = 0 Then + SourceIndex = SourceNextIndex + PatternIndex = PatternNextIndex + GoTo OneCharMatch + End If + + Else + PatternNextIndex = PatternIndex + SkipToEndOfExpandedChar(PatternLigatureInfo, PatternLength, PatternNextIndex) + End If + + Range.Start = PatternIndex + Range.StartLength = PatternNextIndex - PatternIndex + 1 + + ' Store the range start char + ' + If Options = CompareOptions.Ordinal Then + RangeStart = Pattern.Chars(PatternIndex) + ElseIf PatternLigatureInfo IsNot Nothing AndAlso PatternLigatureInfo(PatternIndex).Kind = CharKind.ExpandedChar1 Then + RangeStart = PatternLigatureInfo(PatternIndex).CharBeforeExpansion + PatternIndex = PatternNextIndex + Else + RangeStart = Pattern.Substring(PatternIndex, PatternNextIndex - PatternIndex + 1) + PatternIndex = PatternNextIndex + End If + + + If PatternNextIndex + 2 < PatternLength AndAlso _ + (Pattern.Chars(PatternNextIndex + 1) = "-"c OrElse Pattern.Chars(PatternNextIndex + 1) = ChrW(&HFF0D)) AndAlso _ + (Pattern.Chars(PatternNextIndex + 2) <> "]"c AndAlso Pattern.Chars(PatternNextIndex + 2) <> ChrW(&HFF3D)) Then + + ' We're at the last character of a range. + ' + PatternIndex += 2 + + ' Try to match one char + ' + If Not ValidatePatternWithoutMatching AndAlso _ + PatternLigatureInfo IsNot Nothing AndAlso _ + PatternLigatureInfo(PatternIndex).Kind = CharKind.ExpandedChar1 Then + + ' VB6 compat - Match expanded char and return in this case without even validating RangeStart > RangeEnd + ' + CompareResult = _ + CompareChars( _ + Source, _ + SourceLength, _ + SourceIndex, _ + SourceNextIndex, _ + SourceLigatureInfo, _ + Pattern, _ + PatternLength, _ + PatternIndex, _ + PatternNextIndex, _ + PatternLigatureInfo, _ + Comparer, _ + Options, _ + MatchBothCharsOfExpandedCharInRight:=True) + + If CompareResult = 0 Then + PatternIndex = PatternNextIndex + GoTo OneCharMatch + End If + + Else + PatternNextIndex = PatternIndex + SkipToEndOfExpandedChar(PatternLigatureInfo, PatternLength, PatternNextIndex) + End If + + Range.End = PatternIndex + Range.EndLength = PatternNextIndex - PatternIndex + 1 + + ' Store the range end char + ' + If Options = CompareOptions.Ordinal Then + RangeEnd = Pattern.Chars(PatternIndex) + ElseIf PatternLigatureInfo IsNot Nothing AndAlso PatternLigatureInfo(PatternIndex).Kind = CharKind.ExpandedChar1 Then + RangeEnd = PatternLigatureInfo(PatternIndex).CharBeforeExpansion + PatternIndex = PatternNextIndex + Else + RangeEnd = Pattern.Substring(PatternIndex, PatternNextIndex - PatternIndex + 1) + PatternIndex = PatternNextIndex + End If + + + If CompareChars(RangeStart, RangeEnd, Comparer, Options) > 0 Then + PatternError = True + Return + End If + + If Not ValidatePatternWithoutMatching AndAlso _ + CompareChars( _ + Source, _ + SourceLength, _ + SourceIndex, _ + SourceNextIndex, _ + SourceLigatureInfo, _ + Pattern, _ + Range.Start + Range.StartLength, _ + Range.Start, _ + Nothing, _ + PatternLigatureInfo, _ + Comparer, _ + Options, _ + UseUnexpandedCharForRight:=True) >= 0 AndAlso _ + CompareChars( _ + Source, _ + SourceLength, _ + SourceIndex, _ + SourceNextIndex, _ + SourceLigatureInfo, _ + Pattern, _ + Range.End + Range.EndLength, _ + Range.End, _ + Nothing, _ + PatternLigatureInfo, _ + Comparer, _ + Options, _ + UseUnexpandedCharForRight:=True) <= 0 Then + 'Character was within range +OneCharMatch: + Debug.Assert(Not ValidatePatternWithoutMatching, "Unexpected string matching when validating pattern string!!!") + + If SeenNot Then + Mismatch = True + Return + End If + + Do + PatternIndex += 1 + + If PatternIndex >= PatternLength Then + PatternError = True + Return + End If + + Loop While Pattern.Chars(PatternIndex) <> "]"c AndAlso _ + Pattern.Chars(PatternIndex) <> ChrW(&HFF3D) + + SourceIndex = SourceNextIndex + Return 'Match + End If + + Else + ' Single character match + ' + ' + If Not ValidatePatternWithoutMatching AndAlso _ + CompareChars( _ + Source, _ + SourceLength, _ + SourceIndex, _ + SourceNextIndex, _ + SourceLigatureInfo, _ + Pattern, _ + Range.Start + Range.StartLength, _ + Range.Start, _ + Nothing, _ + PatternLigatureInfo, _ + Comparer, _ + Options, _ + UseUnexpandedCharForRight:=True) = 0 Then + + GoTo OneCharMatch + End If + + ' No range end for single characters in list + ' + Range.End = -1 + Range.EndLength = 0 + + End If + + + If RangeList IsNot Nothing Then + RangeList.Add(Range) + End If + + PatternIndex += 1 + + If PatternIndex >= PatternLength Then + PatternError = True + Return + End If + + p = Pattern.Chars(PatternIndex) + Loop + + End Sub + + Private Shared Function ValidateRangePattern _ + ( _ + ByVal Pattern As String, _ + ByVal PatternLength As Integer, _ + ByRef PatternIndex As Integer, _ + ByVal PatternLigatureInfo As LigatureInfo(), _ + ByVal Comparer As CompareInfo, _ + ByVal Options As CompareOptions, _ + ByRef SeenNot As Boolean, _ + ByRef RangeList As List(Of Range) _ + ) As Boolean + + Const ValidPatternWithoutMatching As Boolean = True + + Dim PatternError As Boolean + + MatchRange( _ + Nothing, _ + -1, _ + -1, _ + Nothing, _ + Pattern, _ + PatternLength, _ + PatternIndex, _ + PatternLigatureInfo, _ + Nothing, _ + Nothing, _ + PatternError, _ + Comparer, _ + Options, _ + SeenNot, _ + RangeList, _ + ValidPatternWithoutMatching) + + Return Not PatternError + End Function + + Private Enum PatternType + [STRING] + EXCLIST + INCLIST + DIGIT + ANYCHAR + STAR + NONE + End Enum + + Private Structure PatternGroup + Friend PatType As PatternType + Friend MaxSourceIndex As Integer + Friend CharCount As Integer + + ' StringPatternStart, StringPatternEnd - there are the indices into the original source string + ' and are NOT indices into StringPattern. + ' +#If DEBUG Then + Private m_StringPatternStart As Integer 'For PatternType.[STRING] + Friend Property StringPatternStart() As Integer + Get + Debug.Assert(PatType = PatternType.STRING, "Unexpected pattern group type!!!") + Return m_StringPatternStart + End Get + Set(ByVal Value As Integer) + Debug.Assert(PatType = PatternType.STRING, "Unexpected pattern group type!!!") + m_StringPatternStart = Value + End Set + End Property + + Private m_StringPatternEnd As Integer 'For PatternType.[STRING] + Friend Property StringPatternEnd() As Integer + Get + Debug.Assert(PatType = PatternType.STRING, "Unexpected pattern group type!!!") + Return m_StringPatternEnd + End Get + Set(ByVal Value As Integer) + Debug.Assert(PatType = PatternType.STRING, "Unexpected pattern group type!!!") + m_StringPatternEnd = Value + End Set + End Property + +#Else + Friend StringPatternStart As Integer + Friend StringPatternEnd As Integer +#End If + +#If DEBUG Then + Private m_MinSourceIndex As Integer + Friend Property MinSourceIndex() As Integer + Get + Debug.Assert(PatType = PatternType.STAR OrElse PatType = PatternType.NONE, "Unexpected pattern group type!!!") + Return m_MinSourceIndex + End Get + Set(ByVal Value As Integer) + Debug.Assert(PatType = PatternType.STAR OrElse PatType = PatternType.NONE, "Unexpected pattern group type!!!") + m_MinSourceIndex = Value + End Set + End Property +#Else + Friend MinSourceIndex As Integer +#End If + +#If DEBUG Then + Private m_RangeStarts As String() + Property RangeStarts() As String() + Get + Debug.Assert(PatType = PatternType.EXCLIST OrElse PatType = PatternType.INCLIST, "Unexpected pattern group type!!!") + Return m_RangeStarts + End Get + Set(ByVal value As String()) + Debug.Assert(PatType = PatternType.EXCLIST OrElse PatType = PatternType.INCLIST, "Unexpected pattern group type!!!") + m_RangeStarts = value + End Set + End Property + + + Private m_RangeList As List(Of Range) + Property RangeList() As List(Of Range) + Get + Debug.Assert(PatType = PatternType.EXCLIST OrElse PatType = PatternType.INCLIST, "Unexpected pattern group type!!!") + Return m_RangeList + End Get + Set(ByVal Value As List(Of Range)) + Debug.Assert(PatType = PatternType.EXCLIST OrElse PatType = PatternType.INCLIST, "Unexpected pattern group type!!!") + m_RangeList = Value + End Set + End Property +#Else + Friend RangeList As List(Of Range) +#End If + + Public StartIndexOfPossibleMatch As Integer + End Structure + + Private Structure Range + Friend Start As Integer 'Index into the pattern string + Friend StartLength As Integer + + Friend [End] As Integer 'Index into the pattern string + Friend EndLength As Integer + End Structure + + Private Shared Sub BuildPatternGroups _ + ( _ + ByVal Source As String, _ + ByVal SourceLength As Integer, _ + ByRef SourceIndex As Integer, _ + ByVal SourceLigatureInfo As LigatureInfo(), _ + ByVal Pattern As String, _ + ByVal PatternLength As Integer, _ + ByRef PatternIndex As Integer, _ + ByVal PatternLigatureInfo As LigatureInfo(), _ + ByRef PatternError As Boolean, _ + ByRef PGIndexForLastAsterisk As Integer, _ + ByVal Comparer As CompareInfo, _ + ByVal Options As CompareOptions, _ + ByRef PatternGroups() As PatternGroup _ + ) + ' Pattern groups: + ' + ' 1. A string of characters not containing a special pattern + ' character. + ' + ' 2. Any number of consecutive "?". + ' + ' 3. Any number of consecutive "#". + ' + ' 4. A bracketed character list. + ' + ' 5. Any number of consecutive "*" (collapsed together). + ' + ' We have a local array that is good for small patterns. + ' If the pattern gets large, we allocate additional memory. + ' + ' PG - pattern group + + PatternError = False + PGIndexForLastAsterisk = 0 + + Dim PGIndex As Integer + + Const PGMaxCount As Integer = 16 + PatternGroups = New PatternGroup(PGMaxCount - 1) {} + + Dim PGLast As Integer = PGMaxCount - 1 + Dim PrevPatType As PatternType = PatternType.NONE + PGIndex = 0 + + Do + ' Increase the size of the Pattern groups array if required + ' + If PGIndex >= PGLast Then + Dim NewPatternGroups(PGLast + PGMaxCount) As PatternGroup + PatternGroups.CopyTo(NewPatternGroups, 0) + PatternGroups = NewPatternGroups + PGLast = PGLast + PGMaxCount + End If + + Dim p As Char = Pattern.Chars(PatternIndex) + + Select Case p + + Case "*"c, ChrW(&HFF0A) + ' Record the "*" pattern and collapse multiple contiguous "*"'s if possible + ' + If PrevPatType <> PatternType.STAR Then + PrevPatType = PatternType.STAR + PatternGroups(PGIndex).PatType = PatternType.STAR + PGIndexForLastAsterisk = PGIndex + PGIndex += 1 + End If + + Case "["c, ChrW(&HFF3B) + Dim SeenNot As Boolean = False + Dim RangeList As New List(Of Range) + + If Not ValidateRangePattern(Pattern, PatternLength, PatternIndex, PatternLigatureInfo, Comparer, Options, SeenNot, RangeList) Then + PatternError = True + Return + End If + + ' Ignore empty "[]" and don't build a pattern group for it + ' + If RangeList.Count = 0 Then + Exit Select + End If + + If SeenNot Then + PrevPatType = PatternType.EXCLIST + Else + PrevPatType = PatternType.INCLIST + End If + + PatternGroups(PGIndex).PatType = PrevPatType + PatternGroups(PGIndex).CharCount = 1 + PatternGroups(PGIndex).RangeList = RangeList + + PGIndex += 1 + + Case "#"c, ChrW(&HFF03) + + If PrevPatType = PatternType.DIGIT Then + PatternGroups(PGIndex - 1).CharCount += 1 + Else + PatternGroups(PGIndex).PatType = PatternType.DIGIT + PatternGroups(PGIndex).CharCount = 1 + PGIndex += 1 + PrevPatType = PatternType.DIGIT + End If + + Case "?"c, ChrW(&HFF1F) + + If PrevPatType = PatternType.ANYCHAR Then + PatternGroups(PGIndex - 1).CharCount += 1 + Else + PatternGroups(PGIndex).PatType = PatternType.ANYCHAR + PatternGroups(PGIndex).CharCount = 1 + PGIndex += 1 + PrevPatType = PatternType.ANYCHAR + End If + + Case Else + + Dim StringPatternStart As Integer = PatternIndex + + Dim StringPatternEnd As Integer = PatternIndex + + If StringPatternEnd >= PatternLength Then + StringPatternEnd = PatternLength - 1 + End If + + If PrevPatType = PatternType.STRING Then + PatternGroups(PGIndex - 1).CharCount += 1 + PatternGroups(PGIndex - 1).StringPatternEnd = StringPatternEnd + Else + PatternGroups(PGIndex).PatType = PatternType.STRING + PatternGroups(PGIndex).CharCount = 1 + PatternGroups(PGIndex).StringPatternStart = StringPatternStart + PatternGroups(PGIndex).StringPatternEnd = StringPatternEnd + + PGIndex += 1 + PrevPatType = PatternType.STRING + End If + + End Select + + PatternIndex += 1 + + Loop While PatternIndex < PatternLength + + 'Add ending mark + ' + PatternGroups(PGIndex).PatType = PatternType.NONE + PatternGroups(PGIndex).MinSourceIndex = SourceLength + + ' Pattern is compiled into an array of Pattern groups. Walk backward through list to assign max positions. + ' + Dim MaxPossibleStart As Integer = SourceLength + Do While PGIndex > 0 + Select Case PatternGroups(PGIndex).PatType + Case PatternType.STRING + MaxPossibleStart -= PatternGroups(PGIndex).CharCount + + Case PatternType.DIGIT, PatternType.ANYCHAR + MaxPossibleStart -= PatternGroups(PGIndex).CharCount + + Case PatternType.EXCLIST, PatternType.INCLIST + MaxPossibleStart -= 1 + + Case PatternType.STAR, PatternType.NONE + 'Can start anywhere + + Case Else +#If TELESTO Then + Debug.Assert(False, "Unexpected pattern kind!!!") ' Silverlight CLR does not have Debug.Fail. +#Else + Debug.Fail("Unexpected pattern kind!!!") +#End If + End Select + + PatternGroups(PGIndex).MaxSourceIndex = MaxPossibleStart + PGIndex -= 1 + Loop + + End Sub + + Private Shared Sub MatchAsterisk _ + ( _ + ByVal Source As String, _ + ByVal SourceLength As Integer, _ + ByVal SourceIndex As Integer, _ + ByVal SourceLigatureInfo As LigatureInfo(), _ + ByVal Pattern As String, _ + ByVal PatternLength As Integer, _ + ByVal PatternIndex As Integer, _ + ByVal PattternLigatureInfo As LigatureInfo(), _ + ByRef Mismatch As Boolean, _ + ByRef PatternError As Boolean, _ + ByVal Comparer As CompareInfo, _ + ByVal Options As CompareOptions _ + ) + + Debug.Assert(PatternIndex <= PatternLength AndAlso _ + (Pattern.Chars(PatternIndex) = "*"c OrElse Pattern.Chars(PatternIndex) = ChrW(&HFF0A)), _ + "Like operator - Unexpected asterisk matching!!!") + + Mismatch = False + PatternError = False + + If PatternIndex >= PatternLength Then + Return 'Successful match + End If + + ' We've found a "*" in the pattern that is not at the end. + ' Now we need to scan ahead in the pattern and compile it + ' into an array of structs describing each pattern group. + ' + + Dim PatternGroups() As PatternGroup = Nothing + Dim PGIndex As Integer + Dim PGIndexForLastAsterisk As Integer + + BuildPatternGroups( _ + Source, _ + SourceLength, _ + SourceIndex, _ + SourceLigatureInfo, _ + Pattern, _ + PatternLength, _ + PatternIndex, _ + PattternLigatureInfo, _ + PatternError, _ + PGIndexForLastAsterisk, _ + Comparer, _ + Options, _ + PatternGroups) + + If PatternError Then + Return + End If + + Debug.Assert(PatternGroups IsNot Nothing AndAlso _ + PatternGroups.Length > 0 AndAlso _ + PatternGroups(0).PatType = PatternType.STAR, "Pattern parsing failed!!!") + + + ' Start the search + ' + + If PatternGroups(PGIndexForLastAsterisk + 1).PatType <> PatternType.NONE Then + ' + ' Optimize for the "*" case + ' Helps discard mismatches faster and in some cases, the match are also + ' faster + ' + Dim SavedSourceIndex As Integer = SourceIndex + Dim NumberOfCharsToMatch As Integer + + PGIndex = PGIndexForLastAsterisk + 1 + Do + NumberOfCharsToMatch += PatternGroups(PGIndex).CharCount + PGIndex += 1 + Loop While PatternGroups(PGIndex).PatType <> PatternType.NONE + + SourceIndex = SourceLength + SubtractChars(Source, SourceLength, SourceIndex, NumberOfCharsToMatch, SourceLigatureInfo, Options) + + MatchAsterisk( _ + Source, _ + SourceLength, _ + SourceIndex, _ + SourceLigatureInfo, _ + Pattern, _ + PattternLigatureInfo, _ + PatternGroups, _ + PGIndexForLastAsterisk, _ + Mismatch, _ + PatternError, _ + Comparer, _ + Options) + + If PatternError OrElse Mismatch Then + Return + End If + + SourceLength = PatternGroups(PGIndexForLastAsterisk + 1).StartIndexOfPossibleMatch + + If SourceLength <= 0 Then + Return + End If + + ' Move the end marker to just after the last asterisk because everything afterwards have been + ' matched successfully. + ' + Debug.Assert(PatternGroups(PGIndex).PatType = PatternType.NONE, "Unexpected pattern end!!!") + PatternGroups(PGIndex).MaxSourceIndex = SourceLength + PatternGroups(PGIndex).MinSourceIndex = SourceLength + PatternGroups(PGIndex).StartIndexOfPossibleMatch = 0 + PatternGroups(PGIndexForLastAsterisk + 1) = PatternGroups(PGIndex) + + ' Reset the pattern group corresponding to the last asterisk because it needs to be reused in + ' the next phase of matching + ' + PatternGroups(PGIndexForLastAsterisk).MinSourceIndex = 0 + PatternGroups(PGIndexForLastAsterisk).StartIndexOfPossibleMatch = 0 + + PGIndex = PGIndexForLastAsterisk + 1 + Dim MaxPossibleStart As Integer = SourceLength + Do While PGIndex > 0 + Select Case PatternGroups(PGIndex).PatType + Case PatternType.STRING + MaxPossibleStart -= PatternGroups(PGIndex).CharCount + + Case PatternType.DIGIT, PatternType.ANYCHAR + MaxPossibleStart -= PatternGroups(PGIndex).CharCount + + Case PatternType.EXCLIST, PatternType.INCLIST + MaxPossibleStart -= 1 + + Case PatternType.STAR, PatternType.NONE + 'Can start anywhere + + Case Else +#If TELESTO Then + Debug.Assert(False, "Unexpected pattern kind!!!") ' Silverlight CLR does not have Debug.Fail. +#Else + Debug.Fail("Unexpected pattern kind!!!") +#End If + End Select + + PatternGroups(PGIndex).MaxSourceIndex = MaxPossibleStart + PGIndex -= 1 + Loop + + SourceIndex = SavedSourceIndex + End If + + MatchAsterisk( _ + Source, _ + SourceLength, _ + SourceIndex, _ + SourceLigatureInfo, _ + Pattern, _ + PattternLigatureInfo, _ + PatternGroups, _ + 0, _ + Mismatch, _ + PatternError, _ + Comparer, _ + Options) + End Sub + + Private Shared Sub MatchAsterisk _ + ( _ + ByVal Source As String, _ + ByVal SourceLength As Integer, _ + ByVal SourceIndex As Integer, _ + ByVal SourceLigatureInfo As LigatureInfo(), _ + ByVal Pattern As String, _ + ByVal PatternLigatureInfo As LigatureInfo(), _ + ByVal PatternGroups() As PatternGroup, _ + ByVal PGIndex As Integer, _ + ByRef Mismatch As Boolean, _ + ByRef PatternError As Boolean, _ + ByVal Comparer As CompareInfo, _ + ByVal Options As CompareOptions _ + ) + + Dim PGPrevMismatchIndex As Integer = PGIndex + Dim PrevMismatchSourceIndex As Integer = SourceIndex + Dim PGSaved As Integer = -1 + Dim PGRestartAsteriskIndex As Integer = -1 + + Debug.Assert(PatternGroups(PGIndex).PatType = PatternType.STAR, "Unexpected start of pattern groups list!!!") + + PatternGroups(PGIndex).MinSourceIndex = SourceIndex + PatternGroups(PGIndex).StartIndexOfPossibleMatch = SourceIndex + PGIndex += 1 + + Do + Dim PGCurrent As PatternGroup = PatternGroups(PGIndex) + + Select Case PGCurrent.PatType + + Case PatternType.STRING +MatchString: + If SourceIndex > PGCurrent.MaxSourceIndex Then + Mismatch = True + Return + End If + + PatternGroups(PGIndex).StartIndexOfPossibleMatch = SourceIndex + + Dim StringPatternIndex As Integer = PGCurrent.StringPatternStart + Dim SourceSecondCharIndex As Integer = 0 + Dim SourceMatchIndex As Integer = SourceIndex + Dim FirstIteration As Boolean = True + + Do + Dim CompareResult As Integer = _ + CompareChars( _ + Source, _ + SourceLength, _ + SourceMatchIndex, _ + SourceMatchIndex, _ + SourceLigatureInfo, _ + Pattern, _ + PGCurrent.StringPatternEnd + 1, _ + StringPatternIndex, _ + StringPatternIndex, _ + PatternLigatureInfo, _ + Comparer, _ + Options) + + If FirstIteration Then + FirstIteration = False + SourceSecondCharIndex = SourceMatchIndex + 1 + End If + + If CompareResult <> 0 Then + SourceIndex = SourceSecondCharIndex + PGPrevMismatchIndex = PGIndex - 1 + PrevMismatchSourceIndex = SourceIndex + GoTo MatchString + End If + + StringPatternIndex += 1 + SourceMatchIndex += 1 + + If StringPatternIndex > PGCurrent.StringPatternEnd Then + SourceIndex = SourceMatchIndex + Exit Select + End If + + If SourceMatchIndex >= SourceLength Then + Mismatch = True + Return + End If + + Loop + + Case PatternType.DIGIT +MatchDigits: + If SourceIndex > PGCurrent.MaxSourceIndex Then + Mismatch = True + Return + End If + + PatternGroups(PGIndex).StartIndexOfPossibleMatch = SourceIndex + + For i As Integer = 1 To PGCurrent.CharCount + + Dim c As Char = Source.Chars(SourceIndex) + SourceIndex += 1 + + If Not Char.IsDigit(c) Then + PGPrevMismatchIndex = PGIndex - 1 + PrevMismatchSourceIndex = SourceIndex + GoTo MatchDigits + End If + + Next + + 'Match + + Case PatternType.EXCLIST, PatternType.INCLIST +MatchList: + If SourceIndex > PGCurrent.MaxSourceIndex Then + Mismatch = True + Return + End If + + PatternGroups(PGIndex).StartIndexOfPossibleMatch = SourceIndex + + If Not MatchRangeAfterAsterisk( _ + Source, _ + SourceLength, _ + SourceIndex, _ + SourceLigatureInfo, _ + Pattern, _ + PatternLigatureInfo, _ + PGCurrent, _ + Comparer, _ + Options) Then + + PGPrevMismatchIndex = PGIndex - 1 + PrevMismatchSourceIndex = SourceIndex + GoTo MatchList + End If + + 'Match + + Case PatternType.ANYCHAR + + If SourceIndex > PGCurrent.MaxSourceIndex Then + Mismatch = True + Return + End If + + PatternGroups(PGIndex).StartIndexOfPossibleMatch = SourceIndex + + For i As Integer = 1 To PGCurrent.CharCount + If SourceIndex >= SourceLength Then + Mismatch = True + Return + End If + + SkipToEndOfExpandedChar(SourceLigatureInfo, SourceLength, SourceIndex) + SourceIndex += 1 + Next + + Case PatternType.NONE + + PatternGroups(PGIndex).StartIndexOfPossibleMatch = PGCurrent.MaxSourceIndex + + Debug.Assert(SourceIndex <= PGCurrent.MaxSourceIndex, "Pattern matching lost!!!") + + If SourceIndex < PGCurrent.MaxSourceIndex Then + 'TODO - rename PGPrevMismatchIndex to something more appropriate, it is actually the match before the first mismatch + ' maybe LastForwardShift + ' + PGPrevMismatchIndex = PGIndex - 1 + PrevMismatchSourceIndex = PGCurrent.MaxSourceIndex + End If + + If PatternGroups(PGPrevMismatchIndex).PatType <> PatternType.STAR AndAlso _ + PatternGroups(PGPrevMismatchIndex).PatType <> PatternType.NONE Then + GoTo ShiftPosition + End If + + Return 'Match + + + Case PatternType.STAR + + PatternGroups(PGIndex).StartIndexOfPossibleMatch = SourceIndex + PGCurrent.MinSourceIndex = SourceIndex + + ' See if we've moved our starting point. If so, we + ' back up from the last place it moved, assigning a + ' new minimum position in the source string. Then + ' we can start the search over from the new minimum + ' position. + ' + + Debug.Assert(PatternGroups(PGPrevMismatchIndex).PatType <> PatternType.NONE, "Bad previous mismatch index!!!") + + If PatternGroups(PGPrevMismatchIndex).PatType <> PatternType.STAR Then + + If SourceIndex > PGCurrent.MaxSourceIndex Then + Mismatch = True + Return + End If + +ShiftPosition: + PGSaved = PGIndex + SourceIndex = PrevMismatchSourceIndex + PGIndex = PGPrevMismatchIndex + + Do + SubtractChars(Source, SourceLength, SourceIndex, PatternGroups(PGIndex).CharCount, SourceLigatureInfo, Options) + PGIndex -= 1 + Loop While PatternGroups(PGIndex).PatType <> PatternType.STAR + + SourceIndex = Math.Max(SourceIndex, PatternGroups(PGIndex).MinSourceIndex + 1) + PatternGroups(PGIndex).MinSourceIndex = SourceIndex + PGRestartAsteriskIndex = PGIndex + End If + + PGIndex += 1 + + Continue Do + + End Select + + If PGIndex = PGPrevMismatchIndex Then + ' Reached a point where we've matched before. + + If SourceIndex = PrevMismatchSourceIndex Then + ' Reached a point where we've matched before. Jump ahead + ' to where that left off. + ' + SourceIndex = PatternGroups(PGSaved).MinSourceIndex + PGIndex = PGSaved + PGPrevMismatchIndex = PGSaved + + ElseIf SourceIndex < PrevMismatchSourceIndex Then + ' In certain cases involving ligatures/modifiers, the source + ' index could be moved too far back and thus result in this + ' scenario. + ' + PatternGroups(PGRestartAsteriskIndex).MinSourceIndex += 1 + SourceIndex = PatternGroups(PGRestartAsteriskIndex).MinSourceIndex + PGIndex = PGRestartAsteriskIndex + 1 + + Else 'SourceIndex > PrevMismatchSourceIndex + ' In certain cases involving ligatures/modifiers, more source + ' chars than the number of chars between the previous match + ' corresponding to a "*" and the start of match for the pattern + ' groups where PGIndex > PGPrevMismatchIndex may be matched + ' against the pattern groups between the "*" and PGPrevMismatchIndex + ' and thus result in this scenario. + ' + PGIndex += 1 + PGPrevMismatchIndex = PGRestartAsteriskIndex + End If + Else + PGIndex += 1 + End If + + Loop + End Sub + + Private Shared Function MatchRangeAfterAsterisk _ + ( _ + ByVal Source As String, _ + ByVal SourceLength As Integer, _ + ByRef SourceIndex As Integer, _ + ByVal SourceLigatureInfo As LigatureInfo(), _ + ByVal Pattern As String, _ + ByVal PatternLigatureInfo As LigatureInfo(), _ + ByVal PG As PatternGroup, _ + ByVal Comparer As CompareInfo, _ + ByVal Options As CompareOptions _ + ) As Boolean + + Debug.Assert(PG.PatType = PatternType.EXCLIST OrElse PG.PatType = PatternType.INCLIST, "Unexpected pattern group!!!") + + 'CONSIDER: - improve performance, maybe store the ranges as a table/array lookup for some special cases. + + Dim RangeList As List(Of Range) = PG.RangeList + + 'empty [] match can be ignored + ' + Debug.Assert(RangeList IsNot Nothing AndAlso RangeList.Count > 0, "Empty RangeList unexpected!!!") + + Dim SourceNextIndex As Integer = SourceIndex + Dim Match As Boolean = False + + For Each Range As Range In RangeList + Debug.Assert(Range.Start >= 0, "NULL Range start unexpected!!!") + Dim CompareResultEnd As Integer = 1 + Dim CompareResultStart As Integer + + If PatternLigatureInfo IsNot Nothing AndAlso PatternLigatureInfo(Range.Start).Kind = CharKind.ExpandedChar1 Then + CompareResultStart = _ + CompareChars( _ + Source, _ + SourceLength, _ + SourceIndex, _ + SourceNextIndex, _ + SourceLigatureInfo, _ + Pattern, _ + Range.Start + Range.StartLength, _ + Range.Start, _ + 0, _ + PatternLigatureInfo, _ + Comparer, _ + Options, _ + MatchBothCharsOfExpandedCharInRight:=True) + + If CompareResultStart = 0 Then + Match = True + Exit For + End If + End If + + CompareResultStart = _ + CompareChars( _ + Source, _ + SourceLength, _ + SourceIndex, _ + SourceNextIndex, _ + SourceLigatureInfo, _ + Pattern, _ + Range.Start + Range.StartLength, _ + Range.Start, _ + 0, _ + PatternLigatureInfo, _ + Comparer, _ + Options, _ + UseUnexpandedCharForRight:=True) + + If CompareResultStart > 0 AndAlso Range.End >= 0 Then + CompareResultEnd = _ + CompareChars( _ + Source, _ + SourceLength, _ + SourceIndex, _ + SourceNextIndex, _ + SourceLigatureInfo, _ + Pattern, _ + Range.End + Range.EndLength, _ + Range.End, _ + 0, _ + PatternLigatureInfo, _ + Comparer, _ + Options, _ + UseUnexpandedCharForRight:=True) + End If + + If CompareResultStart = 0 OrElse _ + (CompareResultStart > 0 AndAlso CompareResultEnd <= 0) Then + Match = True + Exit For + End If + + Next + + If PG.PatType = PatternType.EXCLIST Then + Match = Not Match + End If + + SourceIndex = SourceNextIndex + 1 + Return Match + End Function + + Private Shared Sub SubtractChars _ + ( _ + ByVal Input As String, _ + ByVal InputLength As Integer, _ + ByRef Current As Integer, _ + ByVal CharsToSubtract As Integer, _ + ByVal InputLigatureInfo As LigatureInfo(), _ + ByVal Options As CompareOptions _ + ) + + If Options = CompareOptions.Ordinal Then + Current -= CharsToSubtract + If Current < 0 Then Current = 0 + Return + End If + + For i As Integer = 1 To CharsToSubtract + SubtractOneCharInTextCompareMode(Input, InputLength, Current, InputLigatureInfo, Options) + + If Current < 0 Then + Current = 0 + Exit For + End If + Next + + End Sub + + Private Shared Sub SubtractOneCharInTextCompareMode _ + ( _ + ByVal Input As String, _ + ByVal InputLength As Integer, _ + ByRef Current As Integer, _ + ByVal InputLigatureInfo As LigatureInfo(), _ + ByVal Options As CompareOptions _ + ) + Debug.Assert(Options <> CompareOptions.Ordinal, "This method should not be invoked in Option compare binary mode!!!") + + If Current >= InputLength Then + Current -= 1 + Return + End If + + If InputLigatureInfo IsNot Nothing AndAlso _ + InputLigatureInfo(Current).Kind = CharKind.ExpandedChar2 Then + Current -= 2 + Else + Current -= 1 + End If + + End Sub + + End Class + +End Namespace + diff --git a/Microsoft.VisualBasic/runtime/msvbalib/Helpers/LongType.vb b/Microsoft.VisualBasic/runtime/msvbalib/Helpers/LongType.vb new file mode 100644 index 000000000..fdc306004 --- /dev/null +++ b/Microsoft.VisualBasic/runtime/msvbalib/Helpers/LongType.vb @@ -0,0 +1,138 @@ +' Copyright (c) Microsoft Corporation. All rights reserved. + +Imports System +Imports System.Globalization + +Imports Microsoft.VisualBasic.CompilerServices.Utils + +Namespace Microsoft.VisualBasic.CompilerServices + +#Region " BACKWARDS COMPATIBILITY " + + 'WARNING WARNING WARNING WARNING WARNING + 'This code exists to support Everett compiled applications. Make sure you understand + 'the backwards compatibility ramifications of any edit you make in this region. + 'WARNING WARNING WARNING WARNING WARNING + + _ + Public NotInheritable Class LongType + ' Prevent creation. + Private Sub New() + End Sub + + Public Shared Function FromString(ByVal Value As String) As Long + + If (Value Is Nothing) Then + Return 0 + End If + + Try + Dim i64Value As Int64 + + If IsHexOrOctValue(Value, i64Value) Then + Return CLng(i64Value) + End If + + 'Using Decimal parse so that we full range of Int64 + ' and still get currency and thousands parsing + Return CLng(DecimalType.Parse(Value, Nothing)) + + + Catch e As FormatException + Throw New InvalidCastException(GetResourceString(ResID.InvalidCast_FromStringTo, Left(Value, 32), "Long"), e) + End Try + + End Function + + Public Shared Function FromObject(ByVal Value As Object) As Long + + If Value Is Nothing Then + Return 0L + End If + + Dim ValueInterface As IConvertible + Dim ValueTypeCode As TypeCode + + ValueInterface = TryCast(Value, IConvertible) + + If ValueInterface Is Nothing Then + GoTo ThrowInvalidCast + End If + + ValueTypeCode = ValueInterface.GetTypeCode() + + Select Case ValueTypeCode + + Case TypeCode.Boolean + Return CLng(ValueInterface.ToBoolean(Nothing)) + + Case TypeCode.Byte + If TypeOf Value Is System.Byte Then + Return CLng(DirectCast(Value, Byte)) + Else + Return CLng(ValueInterface.ToByte(Nothing)) + End If + + Case TypeCode.Int16 + If TypeOf Value Is System.Int16 Then + Return CLng(DirectCast(Value, Int16)) + Else + Return CLng(ValueInterface.ToInt16(Nothing)) + End If + + Case TypeCode.Int32 + If TypeOf Value Is System.Int32 Then + Return CLng(DirectCast(Value, Int32)) + Else + Return CLng(ValueInterface.ToInt32(Nothing)) + End If + + Case TypeCode.Int64 + If TypeOf Value Is System.Int64 Then + Return CLng(DirectCast(Value, Int64)) + Else + Return CLng(ValueInterface.ToInt64(Nothing)) + End If + + Case TypeCode.Single + If TypeOf Value Is System.Single Then + Return CLng(DirectCast(Value, Single)) + Else + Return CLng(ValueInterface.ToSingle(Nothing)) + End If + + Case TypeCode.Double + If TypeOf Value Is System.Double Then + Return CLng(DirectCast(Value, Double)) + Else + Return CLng(ValueInterface.ToDouble(Nothing)) + End If + + Case TypeCode.Decimal + 'Do not use .ToDecimal because of jit temp issue effects all perf + Return DecimalToLong(ValueInterface) + + Case TypeCode.String + Return LongType.FromString(ValueInterface.ToString(Nothing)) + Case TypeCode.Char, _ + TypeCode.DateTime + ' Fall through to error + + Case Else + ' Fall through to error + End Select +ThrowInvalidCast: + Throw New InvalidCastException(GetResourceString(ResID.InvalidCast_FromTo, VBFriendlyName(Value), "Long")) + End Function + + Private Shared Function DecimalToLong(ByVal ValueInterface As IConvertible) As Long + Return CLng(ValueInterface.ToDecimal(Nothing)) + End Function + + End Class + +#End Region + +End Namespace + + diff --git a/Microsoft.VisualBasic/runtime/msvbalib/Helpers/NativeMethods.vb b/Microsoft.VisualBasic/runtime/msvbalib/Helpers/NativeMethods.vb new file mode 100644 index 000000000..682489d79 --- /dev/null +++ b/Microsoft.VisualBasic/runtime/msvbalib/Helpers/NativeMethods.vb @@ -0,0 +1,530 @@ +' Copyright (c) Microsoft Corporation. All rights reserved. + +Imports System +Imports System.Security +Imports System.Security.Permissions +Imports System.Text +Imports System.Runtime.ConstrainedExecution +Imports System.Runtime.InteropServices +Imports System.Runtime.Versioning + +Namespace Microsoft.VisualBasic.CompilerServices + + _ + Friend NotInheritable Class NativeMethods + + _ + _ + _ + Friend Declare Auto Function _ + WaitForInputIdle _ + Lib "user32" (ByVal Process As NativeTypes.LateInitSafeHandleZeroOrMinusOneIsInvalid, ByVal Milliseconds As Integer) As Integer + + _ + _ + _ + Friend Declare Function _ + GetWindow _ + Lib "user32" (ByVal hwnd As IntPtr, ByVal wFlag As Integer) As IntPtr + + _ + _ + _ + Friend Declare Function _ + GetDesktopWindow _ + Lib "user32" () As IntPtr + + _ + _ + _ + Friend Shared Function GetWindowText(ByVal hWnd As IntPtr, ByVal lpString As StringBuilder, ByVal nMaxCount As Integer) As Integer + End Function + + _ + _ + _ + Friend Declare Function _ + AttachThreadInput _ + Lib "user32" (ByVal idAttach As Integer, ByVal idAttachTo As Integer, ByVal fAttach As Integer) As Integer + + _ + _ + _ + Friend Declare Function _ + SetForegroundWindow _ + Lib "user32" (ByVal hwnd As IntPtr) As Boolean + + _ + _ + _ + Friend Declare Function _ + SetFocus _ + Lib "user32" (ByVal hwnd As IntPtr) As IntPtr + + _ + _ + _ + Friend Declare Auto Function _ + FindWindow _ + Lib "user32" (ByVal lpClassName As String, ByVal lpWindowName As String) As IntPtr + + _ + _ + _ + _ + Friend Declare Function _ + CloseHandle _ + Lib "kernel32" (ByVal hObject As IntPtr) As Integer + + _ + _ + _ + Friend Declare Function _ + WaitForSingleObject _ + Lib "kernel32" (ByVal hHandle As NativeTypes.LateInitSafeHandleZeroOrMinusOneIsInvalid, ByVal dwMilliseconds As Integer) As Integer + + _ + _ + _ + Friend Shared Sub GetStartupInfo( ByVal lpStartupInfo As NativeTypes.STARTUPINFO) + End Sub + + _ + _ + _ + Friend Shared Function CreateProcess( _ + ByVal lpApplicationName As String, _ + ByVal lpCommandLine As String, _ + ByVal lpProcessAttributes As NativeTypes.SECURITY_ATTRIBUTES, _ + ByVal lpThreadAttributes As NativeTypes.SECURITY_ATTRIBUTES, _ + ByVal bInheritHandles As Boolean, _ + ByVal dwCreationFlags As Integer, _ + ByVal lpEnvironment As IntPtr, _ + ByVal lpCurrentDirectory As String, _ + ByVal lpStartupInfo As NativeTypes.STARTUPINFO, _ + ByVal lpProcessInformation As NativeTypes.PROCESS_INFORMATION) As Integer + End Function + + _ + _ + _ + Friend Shared Function GetVolumeInformation( _ + ByVal lpRootPathName As String, _ + ByVal lpVolumeNameBuffer As StringBuilder, _ + ByVal nVolumeNameSize As Integer, _ + ByRef lpVolumeSerialNumber As Integer, _ + ByRef lpMaximumComponentLength As Integer, _ + ByRef lpFileSystemFlags As Integer, _ + ByVal lpFileSystemNameBuffer As IntPtr, _ + ByVal nFileSystemNameSize As Integer) As Integer + End Function + + '''************************************************************************** + ''' ;SHFileOperation + ''' + ''' Given a 32-bit SHFILEOPSTRUCT, call the appropriate SHFileOperation function + ''' to perform shell file operation. + ''' + ''' 32-bit SHFILEOPSTRUCT + ''' 0 if successful, non-zero otherwise. + _ + _ + _ + Friend Shared Function SHFileOperation(ByRef lpFileOp As SHFILEOPSTRUCT) As Int32 + If (IntPtr.Size = 4) Then ' 32-bit platforms + Return SHFileOperation32(lpFileOp) + Else ' 64-bit plaforms + + ' Create a new SHFILEOPSTRUCT64. The only difference is the packing, so copy all fields. + Dim lpFileOp64 As New SHFILEOPSTRUCT64 + lpFileOp64.hwnd = lpFileOp.hwnd + lpFileOp64.wFunc = lpFileOp.wFunc + lpFileOp64.pFrom = lpFileOp.pFrom + lpFileOp64.pTo = lpFileOp.pTo + lpFileOp64.fFlags = lpFileOp.fFlags + lpFileOp64.fAnyOperationsAborted = lpFileOp.fAnyOperationsAborted + lpFileOp64.hNameMappings = lpFileOp.hNameMappings + lpFileOp64.lpszProgressTitle = lpFileOp.lpszProgressTitle + + ' P/Invoke SHFileOperation with the 64 bit structure. + Dim result As Int32 = SHFileOperation64(lpFileOp64) + + ' Only need to check if any operations were aborted. + lpFileOp.fAnyOperationsAborted = lpFileOp64.fAnyOperationsAborted + + Return result + End If + End Function + + '''************************************************************************** + ''' ;SHFileOperation32 + ''' + ''' Copies, moves, renames or deletes a file system object on 32-bit platforms. + ''' + ''' Pointer to an SHFILEOPSTRUCT structure that contains information this function needs + ''' to carry out the specified operation. This parameter must contain a valid value that is not NULL. + ''' You are responsible for validating the value. If you do not, you will experience unexpected result. + ''' Returns zero if successful, non zero otherwise. + ''' + ''' You should use fully-qualified path names with this function. Using it with relative path names is not thread safe. + ''' You cannot use SHFileOperation to move special folders My Documents and My Pictures from a local drive to a remote computer. + ''' File deletion is recursive unless you set the FOF_NORECURSION flag. + ''' + _ + _ + _ + Private Shared Function SHFileOperation32(ByRef lpFileOp As SHFILEOPSTRUCT) As Int32 + End Function + + + '''************************************************************************** + ''' ;SHFILEOPSTRUCT + ''' + ''' Contains information that the SHFileOperation function uses to perform file operations + ''' on 32-bit platforms. + ''' + ''' + ''' * For detail documentation: http://msdn.microsoft.com/library/default.asp?url=/library/en-us/shellcc/platform/shell/reference/structures/shfileopstruct.asp. + ''' Members: + ''' hwnd: Window handle to the dialog box to display information about the status of the operation. + ''' wFunc: Value indicates which operation (copy, move, rename, delete) to perform. + ''' pFrom: Buffer for 1 or more source file names. Each name ends with a NULL separator + additional NULL at the end. + ''' pTo: Buffer for destination name(s). Same rule as pFrom. + ''' fFlags: Flags that control details of the operation. + ''' fAnyOperationsAborted: Out param. TRUE if user aborted any file operations. Otherwise, FALSE. + ''' hNameMappings: Handle to name mapping object containing old and new names of renamed files (not used). + ''' lpszProgressTitle: Address of a string to use as title of progress dialog box. (not used). + ''' typedef struct _SHFILEOPSTRUCT { + ''' HWND hwnd; + ''' UINT wFunc; + ''' LPCTSTR pFrom; + ''' LPCTSTR pTo; + ''' FILEOP_FLAGS fFlags; (WORD) + ''' BOOL fAnyOperationsAborted; + ''' LPVOID hNameMappings; + ''' LPCTSTR lpszProgressTitle; + ''' } SHFILEOPSTRUCT, *LPSHFILEOPSTRUCT; + ''' * VSWhidbey 321345, 321346: From KB151799 - The SHFILEOPSTRUCT is not double-word aligned. + ''' If no steps are taken, the last 3 variables will not be passed correctly. Hence the Pack:=1. + ''' + _ + Friend Structure SHFILEOPSTRUCT + Friend hwnd As IntPtr + Friend wFunc As UInt32 + Friend pFrom As String + Friend pTo As String + Friend fFlags As UInt16 + Friend fAnyOperationsAborted As Boolean + Friend hNameMappings As IntPtr + Friend lpszProgressTitle As String + End Structure + + '''************************************************************************** + ''' ;SHFileOperation64 + ''' + ''' Copies, moves, renames or deletes a file system object on 64-bit platforms. + ''' + _ + _ + _ + Private Shared Function SHFileOperation64(ByRef lpFileOp As SHFILEOPSTRUCT64) As Int32 + End Function + + '''************************************************************************** + ''' ;SHFILEOPSTRUCT64 + ''' + ''' Contains information that the SHFileOperation function uses to perform file operations + ''' on 64-bit platforms, where the structure is unpacked. VSWhidbey 421265, + ''' + _ + Private Structure SHFILEOPSTRUCT64 + Friend hwnd As IntPtr + Friend wFunc As UInt32 + Friend pFrom As String + Friend pTo As String + Friend fFlags As UInt16 + Friend fAnyOperationsAborted As Boolean + Friend hNameMappings As IntPtr + Friend lpszProgressTitle As String + End Structure + + '''************************************************************************** + ''' ;SHFileOperationType + ''' + ''' Values that indicate which file operation to perform. Used in SHFILEOPSTRUCT + ''' + Friend Enum SHFileOperationType As UInt32 + FO_MOVE = &H1 + FO_COPY = &H2 + FO_DELETE = &H3 + FO_RENAME = &H4 + End Enum + + + '''************************************************************************** + ''' ;ShFileOperationFlags + ''' + ''' Flags that control the file operation. Used in SHFILEOPSTRUCT. + ''' + _ + Friend Enum ShFileOperationFlags As UInt16 + ' The pTo member specifies multiple destination files (one for each source file) + ' rather than one directory where all source files are to be deposited. + FOF_MULTIDESTFILES = &H1 + ' Not currently used. + FOF_CONFIRMMOUSE = &H2 + ' Do not display a progress dialog box. + FOF_SILENT = &H4 + ' Give the file being operated on a new name in a move, copy, or rename operation + ' if a file with the target name already exists. + FOF_RENAMEONCOLLISION = &H8 + ' Respond with "Yes to All" for any dialog box that is displayed. + FOF_NOCONFIRMATION = &H10 + ' If FOF_RENAMEONCOLLISION is specified and any files were renamed, + ' assign a name mapping object containing their old and new names to the hNameMappings member. + FOF_WANTMAPPINGHANDLE = &H20 + ' Preserve Undo information, if possible. Undone can only be done from the same process. + ' If pFrom does not contain fully qualified path and file names, this flag is ignored. + ' NOTE: Not setting this flag will let the file be deleted permanently, unlike the doc says. + FOF_ALLOWUNDO = &H40 + ' Perform the operation on files only if a wildcard file name (*.*) is specified. + FOF_FILESONLY = &H80 + ' Display a progress dialog box but do not show the file names. + FOF_SIMPLEPROGRESS = &H100 + ' Do not confirm the creation of a new directory if the operation requires one to be created. + FOF_NOCONFIRMMKDIR = &H200 + ' Do not display a user interface if an error occurs. + FOF_NOERRORUI = &H400 + ' Do not copy the security attributes of the file. + FOF_NOCOPYSECURITYATTRIBS = &H800 + ' Only operate in the local directory. Don't operate recursively into subdirectories. + FOF_NORECURSION = &H1000 + ' Do not move connected files as a group. Only move the specified files. + FOF_NO_CONNECTED_ELEMENTS = &H2000 + ' Send a warning if a file is being destroyed during a delete operation rather than recycled. + ' This flag partially overrides FOF_NOCONFIRMATION. + FOF_WANTNUKEWARNING = &H4000 + ' Treat reparse points as objects, not containers. + FOF_NORECURSEREPARSE = &H8000 + End Enum 'FileOperationFlags + + + '''************************************************************************** + ''' ;SHChangeNotify + ''' + ''' Notifies the system of an event that an application has performed. + ''' An appliation should use this function if it performs an action that may affect the shell. + ''' + ''' Describes the event that has occured. Typically, only one event is specified at at a time. + ''' If more than one event is specified, the values contained in dwItem1 and dwItem2 must be the same, + ''' respectively, for all specified events. See ShellChangeNotificationEvents. + ''' Flags that indicate the meaning of the dwItem1 and dwItem2 parameter. See ShellChangeNotificationFlags. + ''' First event-dependent value. + ''' Second event-dependent value. + ''' + ''' Win 95/98/Me: SHChangeNotify is supported by Microsoft Layer for Unicode. + ''' To use this http://msdn.microsoft.com/library/default.asp?url=/library/en-us/mslu/winprog/microsoft_layer_for_unicode_on_windows_95_98_me_systems.asp + ''' + _ + _ + _ + Friend Shared Sub SHChangeNotify(ByVal wEventId As UInt32, ByVal uFlags As UInt32, _ + ByVal dwItem1 As IntPtr, ByVal dwItem2 As IntPtr) + End Sub + + + '''************************************************************************** + ''' ;SHChangeEventTypes + ''' + ''' Describes the event that has occured. Used in SHChangeNotify. + ''' There are more values in shellapi.h. Only include the relevant ones. + ''' + Friend Enum SHChangeEventTypes As UInt32 + ' Specifies a combination of all of the disk event identifiers. + SHCNE_DISKEVENTS = &H2381F + ' All events have occurred. + SHCNE_ALLEVENTS = &H7FFFFFFF + End Enum + + + '''************************************************************************** + ''' ;SHChangeEventParameterFlags + ''' + ''' Indicates the meaning of dwItem1 and dwItem2 parameters in SHChangeNotify method. + ''' There are more values in shellapi.h. Only include the relevant one. + ''' + Friend Enum SHChangeEventParameterFlags As UInt32 + ' The dwItem1 and dwItem2 parameters are DWORD values. + SHCNF_DWORD = &H3 + End Enum + + '''************************************************************************** + ''' ;MEMORYSTATUS + ''' + ''' Contains information about the current state of both physical and virtual memory. + ''' + _ + Friend Structure MEMORYSTATUS + 'typedef struct _MEMORYSTATUS { + ' DWORD dwLength; Size of the MEMORYSTATUS data structure, in bytes. You do not need to set this member before calling the GlobalMemoryStatus function; the function sets it. + ' DWORD dwMemoryLoad; Number between 0 and 100 that specifies the approximate percentage of physical memory that is in use + ' SIZE_T dwTotalPhys; Total size of physical memory, in bytes. + ' SIZE_T dwAvailPhys; Size of physical memory available, in bytes. + ' SIZE_T dwTotalPageFile; Size of the committed memory limit, in bytes. + ' SIZE_T dwAvailPageFile; Size of available memory to commit, in bytes. + ' SIZE_T dwTotalVirtual; Total size of the user mode portion of the virtual address space of the calling process, in bytes. + ' SIZE_T dwAvailVirtual; Size of unreserved and uncommitted memory in the user mode portion of the virtual address space of the calling process, in bytes. + '} MEMORYSTATUS, *LPMEMORYSTATUS; + + Friend dwLength As UInt32 + Friend dwMemoryLoad As UInt32 + Friend dwTotalPhys As UInt32 + Friend dwAvailPhys As UInt32 + Friend dwTotalPageFile As UInt32 + Friend dwAvailPageFile As UInt32 + Friend dwTotalVirtual As UInt32 + Friend dwAvailVirtual As UInt32 + End Structure + + '''************************************************************************** + ''' ;GlobalMemoryStatus + ''' + ''' Obtains information about the system's current usage of both physical and virtual memory. + ''' + ''' Pointer to a MEMORYSTATUS structure. + ''' + ''' Requirement: + ''' Client: Requires Windows XP, Windows 2000 Professional, Windows NT Workstation, Windows Me, Windows 98, or Windows 95. + ''' Server: Requires Windows Server 2003, Windows 2000 Server, or Windows NT Server. + ''' + _ + _ + _ + Friend Shared Sub GlobalMemoryStatus(ByRef lpBuffer As MEMORYSTATUS) + End Sub + + '''************************************************************************** + ''' ;MEMORYSTATUSEX + ''' + ''' Contains information about the current state of both physical and virtual memory, including extended memory. + ''' + _ + Friend Structure MEMORYSTATUSEX + 'typedef struct _MEMORYSTATUSEX { + ' DWORD dwLength; Size of the structure. Must set before calling GlobalMemoryStatusEx. + ' DWORD dwMemoryLoad; Number between 0 and 100 on current memory utilization. + ' DWORDLONG ullTotalPhys; Total size of physical memory. + ' DWORDLONG ullAvailPhys; Total size of available physical memory. + ' DWORDLONG ullTotalPageFile; Size of committed memory limit. + ' DWORDLONG ullAvailPageFile; Size of available memory to committed (ullTotalPageFile max). + ' DWORDLONG ullTotalVirtual; Total size of user potion of virtual address space of calling process. + ' DWORDLONG ullAvailVirtual; Total size of unreserved and uncommitted memory in virtual address space. + ' DWORDLONG ullAvailExtendedVirtual; Total size of unreserved and uncommitted memory in extended portion of virual address. + '} MEMORYSTATUSEX, *LPMEMORYSTATUSEX; + + Friend dwLength As UInt32 + Friend dwMemoryLoad As UInt32 + Friend ullTotalPhys As UInt64 + Friend ullAvailPhys As UInt64 + Friend ullTotalPageFile As UInt64 + Friend ullAvailPageFile As UInt64 + Friend ullTotalVirtual As UInt64 + Friend ullAvailVirtual As UInt64 + Friend ullAvailExtendedVirtual As UInt64 + + Friend Sub Init() + dwLength = CType(Marshal.SizeOf(GetType(MEMORYSTATUSEX)), UInt32) + End Sub + End Structure + + '''************************************************************************** + ''' ;GlobalMemoryStatusEx + ''' + ''' Obtains information about the system's current usage of both physical and virtual memory. + ''' + ''' Pointer to a MEMORYSTATUSEX structure. + ''' True if the function successes. Otherwise, False. + ''' + ''' Requirement: + ''' Client: Requires Windows XP or Windows 2000 Professional. + ''' Server: Requires Windows Server 2003 or Windows 2000 Server. + ''' + _ + _ + _ + Friend Shared Function GlobalMemoryStatusEx(ByRef lpBuffer As MEMORYSTATUSEX) As Boolean + End Function + + + + '''**************************************************************************** + ''';ConvertStringSecurityDescriptorToSecurityDescriptor + ''' + ''' Takes a SDDL string and converts it to a pointer to a security descriptor + ''' + ''' + ''' + ''' + ''' + ''' + ''' Only supported on Win2000+ + ''' see p. 186-187 in writing secure code vol 2 on the SDDL string + '''see winerror.h for getlastwin32error meanings + '''see sddl.h for info on SDL_VERSION + '''see http://msdn.microsoft.com/library/default.asp?url=/library/en-us/secauthz/security/ace_strings.asp for how to build the SDL string + ''' + _ + _ + _ + Friend Shared Function ConvertStringSecurityDescriptorToSecurityDescriptor(ByVal StringSecurityDescriptor As String, ByVal StringSDRevision As UInteger, ByRef SecurityDescriptor As IntPtr, ByVal SecurityDescriptorSize As IntPtr) As Boolean + End Function + + '''**************************************************************************** + ''';MoveFileEx + ''' + ''' The MoveFileEx function moves an existing file or directory. + ''' http://msdn.microsoft.com/library/default.asp?url=/library/en-us/fileio/fs/movefileex.asp + ''' + _ + _ + _ + Friend Shared Function MoveFileEx( _ + ByVal lpExistingFileName As String, _ + ByVal lpNewFileName As String, _ + ByVal dwFlags As Integer) As Boolean + End Function + + ''' ;New + ''' + ''' FxCop violation: Avoid uninstantiated internal class. + ''' Adding a private constructor to prevent the compiler from generating a default constructor. + ''' + Private Sub New() + End Sub + End Class + + + +End Namespace + diff --git a/Microsoft.VisualBasic/runtime/msvbalib/Helpers/NativeTypes.vb b/Microsoft.VisualBasic/runtime/msvbalib/Helpers/NativeTypes.vb new file mode 100644 index 000000000..652866563 --- /dev/null +++ b/Microsoft.VisualBasic/runtime/msvbalib/Helpers/NativeTypes.vb @@ -0,0 +1,289 @@ +' Copyright (c) Microsoft Corporation. All rights reserved. + +Imports System +Imports System.Diagnostics +Imports System.Security +Imports System.Security.Permissions +Imports System.Text +Imports System.Runtime.ConstrainedExecution +Imports System.Runtime.InteropServices +Imports Microsoft.Win32.SafeHandles + +Namespace Microsoft.VisualBasic.CompilerServices + + _ + Friend NotInheritable Class NativeTypes + + _ + Friend NotInheritable Class SECURITY_ATTRIBUTES + Implements IDisposable + + Friend Sub New() + nLength = System.Runtime.InteropServices.Marshal.SizeOf(GetType(SECURITY_ATTRIBUTES)) + End Sub + + Public nLength As Integer + Public lpSecurityDescriptor As IntPtr + Public bInheritHandle As Boolean + + _ + _ + Public Overloads Sub Dispose() Implements IDisposable.Dispose + If lpSecurityDescriptor <> IntPtr.Zero Then + UnsafeNativeMethods.LocalFree(lpSecurityDescriptor) + lpSecurityDescriptor = IntPtr.Zero + End If + GC.SuppressFinalize(Me) + End Sub + + Protected Overrides Sub Finalize() + Dispose() + MyBase.Finalize() + End Sub + End Class + +#If 0 Then + _ + _ + Friend NotInheritable Class VBSafeHandle + Inherits SafeHandle + + _ + Friend Sub New() + MyBase.New(True) + End Sub + + Friend Declare Function _ + CloseHandle _ + Lib "kernel32" (ByVal hObject As IntPtr) As Integer + End Class +#End If + + ''' ;LateInitSafeHandleZeroOrMinusOneIsInvalid + ''' + ''' Inherits SafeHandleZeroOrMinusOneIsInvalid, with additional InitialSetHandle method. + ''' This is required because call to constructor of SafeHandle is not allowed in constrained region. + ''' + ''' VSWhidbey 544308 + _ + _ + Friend NotInheritable Class LateInitSafeHandleZeroOrMinusOneIsInvalid + Inherits SafeHandleZeroOrMinusOneIsInvalid + + _ + Friend Sub New() + MyBase.New(True) + End Sub + + _ + Friend Sub InitialSetHandle(ByVal h As IntPtr) + Debug.Assert(MyBase.IsInvalid, "Safe handle should only be set once.") + MyBase.SetHandle(h) + End Sub + + _ + Protected Overrides Function ReleaseHandle() As Boolean + Return NativeMethods.CloseHandle(Me.handle) <> 0 + End Function + End Class + + ''' ;PROCESS_INFORMATION + ''' + ''' Represent Win32 PROCESS_INFORMATION structure. IMPORTANT: Copy the handles to a SafeHandle before use them. + ''' + ''' + ''' See ndp\fx\src\compmod\microsoft\win32\safenativemethod.cs. + ''' The handles in PROCESS_INFORMATION are initialized in unmanaged function. + ''' We can't use SafeHandle here because Interop doesn't support [out] SafeHandles in structure / classes yet. + ''' This class makes no attempt to free the handles. To use the handle, first copy it to a SafeHandle class + ''' (using LateInitSafeHandleZeroOrMinusOneIsInvalid.InitialSetHandle) to correctly use and dispose the handle. + ''' + _ + _ + _ + Friend NotInheritable Class PROCESS_INFORMATION + Public hProcess As IntPtr = IntPtr.Zero + Public hThread As IntPtr = IntPtr.Zero + Public dwProcessId As Integer + Public dwThreadId As Integer + + Friend Sub New() + End Sub + End Class + + ''' + ''' Important! This class should be used where the API being called has allocated the strings. That is why lpReserved, etc. are declared as IntPtrs instead + ''' of Strings - so that the marshalling layer won't release the memory. This caused us problems in the shell() functions. We would call GetStartupInfo() + ''' which doesn't expect the memory for the strings to be freed. But because the strings were previously defined as type String, the marshaller would + ''' and we got memory corruption problems detectable while running AppVerifier. + ''' If you use this structure with an API like CreateProcess() then you are supplying the strings so you'll need another version of this class that defines lpReserved, etc. + ''' as String so that the memory will get cleaned up. + ''' See VSWhidbey 445195, 418202, 434146 + ''' + ''' + _ + _ + _ + Friend NotInheritable Class STARTUPINFO + Implements IDisposable + + Public cb As Integer + Public lpReserved As IntPtr = IntPtr.Zero 'not string - see summary + Public lpDesktop As IntPtr = IntPtr.Zero 'not string - see summary + Public lpTitle As IntPtr = IntPtr.Zero 'not string - see summary + Public dwX As Integer + Public dwY As Integer + Public dwXSize As Integer + Public dwYSize As Integer + Public dwXCountChars As Integer + Public dwYCountChars As Integer + Public dwFillAttribute As Integer + Public dwFlags As Integer + Public wShowWindow As Short + Public cbReserved2 As Short + Public lpReserved2 As IntPtr = IntPtr.Zero + Public hStdInput As IntPtr = IntPtr.Zero + Public hStdOutput As IntPtr = IntPtr.Zero + Public hStdError As IntPtr = IntPtr.Zero + + Friend Sub New() + End Sub + + Private m_HasBeenDisposed As Boolean ' To detect redundant calls. Default initialize = False. + + _ + Protected Overrides Sub Finalize() + Dispose(False) + End Sub + + ' IDisposable + _ + Private Sub Dispose(ByVal disposing As Boolean) + If Not m_HasBeenDisposed Then + If disposing Then + m_HasBeenDisposed = True + + Const STARTF_USESTDHANDLES As Integer = 256 'Defined in windows.h + If (Me.dwFlags And STARTF_USESTDHANDLES) <> 0 Then + If hStdInput <> IntPtr.Zero AndAlso hStdInput <> NativeTypes.INVALID_HANDLE Then + NativeMethods.CloseHandle(hStdInput) + hStdInput = NativeTypes.INVALID_HANDLE + End If + + If hStdOutput <> IntPtr.Zero AndAlso hStdOutput <> NativeTypes.INVALID_HANDLE Then + NativeMethods.CloseHandle(hStdOutput) + hStdOutput = NativeTypes.INVALID_HANDLE + End If + + If hStdError <> IntPtr.Zero AndAlso hStdError <> NativeTypes.INVALID_HANDLE Then + NativeMethods.CloseHandle(hStdError) + hStdError = NativeTypes.INVALID_HANDLE + End If + End If 'Me.dwFlags and STARTF_USESTDHANDLES + + End If + End If + End Sub + + ' This code correctly implements the disposable pattern. + _ + _ + Friend Sub Dispose() Implements IDisposable.Dispose + ' Do not change this code. Put cleanup code in Dispose(ByVal disposing As Boolean) above. + Dispose(True) + GC.SuppressFinalize(Me) + End Sub + End Class + + Friend NotInheritable Class SystemTime + Public wYear As Short + Public wMonth As Short + Public wDayOfWeek As Short + Public wDay As Short + Public wHour As Short + Public wMinute As Short + Public wSecond As Short + Public wMilliseconds As Short + + Friend Sub New() + End Sub + End Class + +#If 0 Then + Friend Structure _ + NUMPARSE + Public cDig As Integer + Public dwInFlags As Integer + Public dwOutFlags As Integer + Public cchUsed As Integer + Public nBaseShift As Integer + Public nPwr10 As Integer + End Structure +#End If + + '''************************************************************************** + ''' ;MoveFileExFlags + ''' + ''' Flags for MoveFileEx. + ''' See http://msdn.microsoft.com/library/default.asp?url=/library/en-us/fileio/fs/movefileex.asp + ''' and public\sdk\inc\winbase.h. + ''' + _ + Friend Enum MoveFileExFlags As Integer + MOVEFILE_REPLACE_EXISTING = &H1 + MOVEFILE_COPY_ALLOWED = &H2 + MOVEFILE_DELAY_UNTIL_REBOOT = &H4 + MOVEFILE_WRITE_THROUGH = &H8 + End Enum + + ' Handle Values + Friend Shared ReadOnly INVALID_HANDLE As IntPtr = New IntPtr(-1) + + ' GetWindow() Constants + Friend Const GW_HWNDFIRST As Integer = 0 + Friend Const GW_HWNDLAST As Integer = 1 + Friend Const GW_HWNDNEXT As Integer = 2 + Friend Const GW_HWNDPREV As Integer = 3 + Friend Const GW_OWNER As Integer = 4 + Friend Const GW_CHILD As Integer = 5 + Friend Const GW_MAX As Integer = 5 + + 'Friend Const EVENTLOG_INFORMATION_TYPE As Integer = 0 + + Friend Const STARTF_USESHOWWINDOW As Integer = 1 + + Friend Const NORMAL_PRIORITY_CLASS As Integer = &H20 + + Friend Const LCMAP_TRADITIONAL_CHINESE As Integer = &H4000000I + Friend Const LCMAP_SIMPLIFIED_CHINESE As Integer = &H2000000I + Friend Const LCMAP_UPPERCASE As Integer = &H200I + Friend Const LCMAP_LOWERCASE As Integer = &H100I + Friend Const LCMAP_FULLWIDTH As Integer = &H800000I + Friend Const LCMAP_HALFWIDTH As Integer = &H400000I + Friend Const LCMAP_KATAKANA As Integer = &H200000I + Friend Const LCMAP_HIRAGANA As Integer = &H100000I + + ' Error code from public\sdk\inc\winerror.h + Friend Const ERROR_FILE_NOT_FOUND As Integer = 2 + Friend Const ERROR_PATH_NOT_FOUND As Integer = 3 + Friend Const ERROR_ACCESS_DENIED As Integer = 5 + Friend Const ERROR_ALREADY_EXISTS As Integer = 183 + Friend Const ERROR_FILENAME_EXCED_RANGE As Integer = 206 + Friend Const ERROR_INVALID_DRIVE As Integer = 15 + Friend Const ERROR_INVALID_PARAMETER As Integer = 87 + Friend Const ERROR_SHARING_VIOLATION As Integer = 32 + Friend Const ERROR_FILE_EXISTS As Integer = 80 + Friend Const ERROR_OPERATION_ABORTED As Integer = 995 + Friend Const ERROR_CANCELLED As Integer = 1223 + + ''' ;New + ''' + ''' FxCop violation: Avoid uninstantiated internal class. + ''' Adding a private constructor to prevent the compiler from generating a default constructor. + ''' + Private Sub New() + End Sub + End Class + +End Namespace + diff --git a/Microsoft.VisualBasic/runtime/msvbalib/Helpers/NewLateBinder.vb b/Microsoft.VisualBasic/runtime/msvbalib/Helpers/NewLateBinder.vb new file mode 100644 index 000000000..6d1dc4630 --- /dev/null +++ b/Microsoft.VisualBasic/runtime/msvbalib/Helpers/NewLateBinder.vb @@ -0,0 +1,1706 @@ +' Copyright (c) Microsoft Corporation. All rights reserved. + +Imports System +Imports System.Collections +Imports System.Collections.Generic +Imports System.Diagnostics +Imports System.Dynamic +Imports System.Globalization +Imports System.Reflection +Imports System.Runtime.InteropServices +Imports System.Security.Permissions + +Imports Microsoft.VisualBasic.CompilerServices.Symbols +Imports Microsoft.VisualBasic.CompilerServices.OverloadResolution +Imports Microsoft.VisualBasic.CompilerServices.ExceptionUtils +Imports Microsoft.VisualBasic.CompilerServices.Utils +Imports System.Runtime.Versioning + +#Const NEW_BINDER = True +#Const BINDING_LOG = False + +'REVIEW VSW#395752: set up logging mechanism + +'REVIEW VSW#395753: attributes on all the members +'REVIEW VSW#395754: thread safety for everything + + +Namespace Microsoft.VisualBasic.CompilerServices + +#If TELESTO Then + 'FIXME: + Public NotInheritable Class NewLateBinding +#Else + _ + Public NotInheritable Class NewLateBinding +#End If + ' Prevent creation. + Private Sub New() + End Sub + + _ + Public Shared Function LateCanEvaluate( _ + ByVal instance As Object, _ + ByVal type As System.Type, _ + ByVal memberName As String, _ + ByVal arguments As Object(), _ + ByVal allowFunctionEvaluation As Boolean, _ + ByVal allowPropertyEvaluation As Boolean) As Boolean + + Dim BaseReference As Container + If type IsNot Nothing Then + BaseReference = New Container(type) + Else + BaseReference = New Container(instance) + End If + + Dim Members As MemberInfo() = BaseReference.GetMembers(memberName, False) + + If Members.Length = 0 Then + Return True + End If + + ' This is a field access + If Members(0).MemberType = MemberTypes.Field Then + If arguments.Length = 0 Then + Return True + Else + Dim FieldValue As Object = BaseReference.GetFieldValue(DirectCast(Members(0), FieldInfo)) + BaseReference = New Container(FieldValue) + If BaseReference.IsArray Then + Return True + End If + Return allowPropertyEvaluation + End If + End If + + ' This is a method invocation + If Members(0).MemberType = MemberTypes.Method Then + Return allowFunctionEvaluation + End If + + ' This is a property access + If Members(0).MemberType = MemberTypes.Property Then + Return allowPropertyEvaluation + End If + + Return True + End Function + + + 'CONSIDER: Consider a pair of overloads - one that takes an instance, one that takes a type, since they are mutually exclusive. + _ + Public Shared Function LateCall( _ + ByVal Instance As Object, _ + ByVal Type As System.Type, _ + ByVal MemberName As String, _ + ByVal Arguments As Object(), _ + ByVal ArgumentNames As String(), _ + ByVal TypeArguments As System.Type(), _ + ByVal CopyBack As Boolean(), _ + ByVal IgnoreReturn As Boolean) As Object + +#If Not NEW_BINDER Then + Return LateBinding.InternalLateCall(Instance, Type, MemberName, Arguments, ArgumentNames, CopyBack, False) +#End If + If Arguments Is Nothing Then Arguments = NoArguments + If ArgumentNames Is Nothing Then ArgumentNames = NoArgumentNames + If TypeArguments Is Nothing Then TypeArguments = NoTypeArguments + + Dim BaseReference As Container + If Type IsNot Nothing Then + BaseReference = New Container(Type) + Else + BaseReference = New Container(Instance) + End If + + If BaseReference.IsCOMObject AndAlso Not BaseReference.IsWindowsRuntimeObject Then +#If Not TELESTO Then + 'UNDONE: BAIL for now -- call the old binder. + Return LateBinding.InternalLateCall(Instance, _ + Type, MemberName, Arguments, ArgumentNames, CopyBack, IgnoreReturn) +#Else + Throw New InvalidOperationException("Never expected to see a COM object in Telesto") ' Unexpected scenario. No need to loc this. +#End If 'Not TELESTO + Else + Dim idmop As IDynamicMetaObjectProvider = IDOUtils.TryCastToIDMOP(Instance) + If idmop IsNot Nothing AndAlso TypeArguments Is NoTypeArguments Then + Return IDOBinder.IDOCall(idmop, MemberName, Arguments, ArgumentNames, CopyBack, IgnoreReturn) + Else + Return ObjectLateCall(Instance, Type, MemberName, Arguments, _ + ArgumentNames, TypeArguments, CopyBack, IgnoreReturn) + End If + End If + End Function + + 'This method is only called from DynamicMethods generated at runtime + _ + _ + _ + Public Shared Function FallbackCall( _ + ByVal Instance As Object, _ + ByVal MemberName As String, _ + ByVal Arguments As Object(), _ + ByVal ArgumentNames As String(), _ + ByVal IgnoreReturn As Boolean) As Object + + Return ObjectLateCall(Instance, Nothing, MemberName, Arguments, _ + ArgumentNames, NoTypeArguments, IDOBinder.GetCopyBack(), IgnoreReturn) + End Function 'FallbackCall + + _ + Private Shared Function ObjectLateCall( _ + ByVal Instance As Object, _ + ByVal Type As System.Type, _ + ByVal MemberName As String, _ + ByVal Arguments As Object(), _ + ByVal ArgumentNames As String(), _ + ByVal TypeArguments As System.Type(), _ + ByVal CopyBack As Boolean(), _ + ByVal IgnoreReturn As Boolean) As Object + + Dim BaseReference As Container + If Type IsNot Nothing Then + BaseReference = New Container(Type) + Else + BaseReference = New Container(Instance) + End If + + Dim InvocationFlags As BindingFlags = BindingFlags.InvokeMethod Or BindingFlags.GetProperty + If IgnoreReturn Then InvocationFlags = InvocationFlags Or BindingFlags.IgnoreReturn + + Dim Failure As ResolutionFailure + + Return _ + CallMethod( _ + BaseReference, _ + MemberName, _ + Arguments, _ + ArgumentNames, _ + TypeArguments, _ + CopyBack, _ + InvocationFlags, _ + True, _ + Failure) + End Function 'ObjectLateCall + + 'Quick check to determine if FallbackCall will succeed + Friend Shared Function CanBindCall(ByVal Instance As Object, ByVal MemberName As String, ByVal Arguments As Object(), ByVal ArgumentNames As String(), ByVal IgnoreReturn As Boolean) As Boolean + Dim BaseReference As New Container(Instance) + Dim InvocationFlags As BindingFlags = BindingFlags.InvokeMethod Or BindingFlags.GetProperty + If IgnoreReturn Then InvocationFlags = InvocationFlags Or BindingFlags.IgnoreReturn + + Dim Failure As ResolutionFailure + Dim Members As MemberInfo() = BaseReference.GetMembers(MemberName, False) + If Members Is Nothing OrElse Members.Length = 0 Then + Return False + End If + + Dim TargetProcedure As Method = _ + ResolveCall( _ + BaseReference, _ + MemberName, _ + Members, _ + Arguments, _ + ArgumentNames, _ + NoTypeArguments, _ + InvocationFlags, _ + False, _ + Failure) + + Return Failure = ResolutionFailure.None + End Function + + ' LateCallInvokeDefault is used to optionally invoke the default action on a call target. + ' If the arguemnts are non-empty, then it isn't optional, and is treated + ' as an error if there is no default action. + ' Currently we can get here only in the process of execution of NewLateBinding.LateCall. + _ + _ + Public Shared Function LateCallInvokeDefault( _ + ByVal Instance As Object, _ + ByVal Arguments As Object(), _ + ByVal ArgumentNames As String(), _ + ByVal ReportErrors As Boolean) As Object + + Return InternalLateInvokeDefault(Instance, Arguments, ArgumentNames, ReportErrors, IDOBinder.GetCopyBack()) + End Function 'LateCallInvokeDefault + + ' LateGetInvokeDefault is used to optionally invoke the default action. + ' If the arguemnts are non-empty, then it isn't optional, and is treated + ' as an error if there is no default action. + ' Currently we can get here only in the process of execution of NewLateBinding.LateGet. + _ + _ + Public Shared Function LateGetInvokeDefault( _ + ByVal Instance As Object, _ + ByVal Arguments As Object(), _ + ByVal ArgumentNames As String(), _ + ByVal ReportErrors As Boolean) As Object + + ' Dev10 #614719 + ' According to a comment in VBGetBinder.FallbackInvoke, this function is called when + ' "The DLR was able to resolve o.member, but not o.member(args)" + ' When NewLateBinding.LateGet is evaluating similar expression itself, it never tries to invoke default action + ' if arguments are not empty. It simply returns result of evaluating o.member. I believe, it makes sense + ' to follow the same logic here. I.e., if there are no arguments, simply return the instance unless it is an IDO. + + If IDOUtils.TryCastToIDMOP(Instance) IsNot Nothing OrElse _ + (Arguments IsNot Nothing AndAlso Arguments.Length > 0) _ + Then + Return InternalLateInvokeDefault(Instance, Arguments, ArgumentNames, ReportErrors, IDOBinder.GetCopyBack()) + Else + Return Instance + End If + End Function 'LateGetInvokeDefault + + Private Shared Function InternalLateInvokeDefault( _ + ByVal Instance As Object, _ + ByVal Arguments As Object(), _ + ByVal ArgumentNames As String(), _ + ByVal ReportErrors As Boolean, _ + ByVal CopyBack As Boolean()) As Object + + Dim idmop As IDynamicMetaObjectProvider = IDOUtils.TryCastToIDMOP(Instance) + If idmop IsNot Nothing Then + Return IDOBinder.IDOInvokeDefault(idmop, Arguments, ArgumentNames, ReportErrors, CopyBack) + Else + Return ObjectLateInvokeDefault(Instance, Arguments, ArgumentNames, ReportErrors, CopyBack) + End If + End Function 'InternalLateInvokeDefault + + 'This method is only called from DynamicMethods generated at runtime + _ + _ + _ + Public Shared Function FallbackInvokeDefault1( _ + ByVal Instance As Object, _ + ByVal Arguments As Object(), _ + ByVal ArgumentNames As String(), _ + ByVal ReportErrors As Boolean) As Object + + ' Try using the IDO index operation (in case it's an IDO array) + Return IDOBinder.IDOFallbackInvokeDefault(DirectCast(Instance, IDynamicMetaObjectProvider), Arguments, ArgumentNames, ReportErrors, IDOBinder.GetCopyBack()) + End Function 'FallbackInvokeDefault + + 'This method is only called from DynamicMethods generated at runtime + _ + _ + _ + Public Shared Function FallbackInvokeDefault2( _ + ByVal Instance As Object, _ + ByVal Arguments As Object(), _ + ByVal ArgumentNames As String(), _ + ByVal ReportErrors As Boolean) As Object + + Return ObjectLateInvokeDefault(Instance, Arguments, ArgumentNames, ReportErrors, IDOBinder.GetCopyBack()) + End Function 'FallbackInvokeDefault + + _ + Private Shared Function ObjectLateInvokeDefault( _ + ByVal Instance As Object, _ + ByVal Arguments As Object(), _ + ByVal ArgumentNames As String(), _ + ByVal ReportErrors As Boolean, _ + ByVal CopyBack As Boolean()) As Object + + Dim BaseReference As Container = New Container(Instance) + Dim Failure As ResolutionFailure + Dim Result As Object = InternalLateIndexGet( _ + Instance, Arguments, ArgumentNames, _ + ReportErrors OrElse Arguments.Length <> 0 OrElse BaseReference.IsArray, _ + Failure, CopyBack) + Return If(Failure = ResolutionFailure.None, Result, Instance) + End Function 'ObjectLateInvokeDefault + + _ + Public Shared Function LateIndexGet( _ + ByVal Instance As Object, _ + ByVal Arguments() As Object, _ + ByVal ArgumentNames() As String) As Object + + Return InternalLateInvokeDefault(Instance, Arguments, ArgumentNames, True, Nothing) + End Function 'LateIndexGet + + Private Shared Function LateIndexGet( _ + ByVal Instance As Object, _ + ByVal Arguments() As Object, _ + ByVal ArgumentNames() As String, _ + ByVal CopyBack As Boolean()) As Object + + Return InternalLateInvokeDefault(Instance, Arguments, ArgumentNames, True, CopyBack) + End Function 'LateIndexGet + + Private Shared Function InternalLateIndexGet( _ + ByVal Instance As Object, _ + ByVal Arguments() As Object, _ + ByVal ArgumentNames() As String, _ + ByVal ReportErrors As Boolean, _ + ByRef Failure As ResolutionFailure, _ + ByVal CopyBack As Boolean()) As Object + + Failure = ResolutionFailure.None + +#If Not NEW_BINDER Then + Return LateBinding.LateIndexGet(Instance, Arguments, ArgumentNames) +#End If + If Arguments Is Nothing Then Arguments = NoArguments + If ArgumentNames Is Nothing Then ArgumentNames = NoArgumentNames + + Dim BaseReference As Container = New Container(Instance) + + If BaseReference.IsCOMObject AndAlso Not BaseReference.IsWindowsRuntimeObject Then +#If Not TELESTO Then + 'UNDONE: BAIL for now -- call the old binder. + Return LateBinding.LateIndexGet(Instance, Arguments, ArgumentNames) +#Else + Throw New InvalidOperationException("Never expected to see a COM object in Telesto") ' Unexpected scenario. No need to loc this. +#End If 'Not TELESTO + End If + + 'An r-value expression o(a) has two possible forms: + ' 1: o(a) array lookup--where o is an array object and a is a set of indices + ' 2: o.d(a) default member access--where o has default method/property d + + If BaseReference.IsArray Then + 'This is an array lookup o(a). + + If ArgumentNames.Length > 0 Then + Failure = ResolutionFailure.InvalidArgument + + If ReportErrors Then + Throw New ArgumentException(GetResourceString(ResID.Argument_InvalidNamedArgs)) + End If + + Return Nothing + End If + '#579308 Initialize the copy back array to all ByVal + ResetCopyback(CopyBack) + Return BaseReference.GetArrayValue(Arguments) + End If + + 'This is a default member access o.d(a), which is a call to method "". + + Return _ + CallMethod( _ + BaseReference, _ + "", _ + Arguments, _ + ArgumentNames, _ + NoTypeArguments, _ + CopyBack, _ + BindingFlags.InvokeMethod Or BindingFlags.GetProperty, _ + ReportErrors, _ + Failure) + End Function 'InternalLateIndexGet + + Friend Shared Function CanBindInvokeDefault( _ + ByVal Instance As Object, _ + ByVal Arguments As Object(), _ + ByVal ArgumentNames As String(), _ + ByVal ReportErrors As Boolean) As Boolean + + Dim BaseReference As Container = New Container(Instance) + ReportErrors = ReportErrors OrElse Arguments.Length <> 0 OrElse BaseReference.IsArray + + If Not ReportErrors Then + Return True + End If + + 'An r-value expression o(a) has two possible forms: + ' 1: o(a) array lookup--where o is an array object and a is a set of indices + ' 2: o.d(a) default member access--where o has default method/property d + + If BaseReference.IsArray Then + 'This is an array lookup o(a). + Return ArgumentNames.Length = 0 + End If + + 'This is a default member access o.d(a), which is a call to method "". + Return CanBindCall(Instance, "", Arguments, ArgumentNames, False) + End Function + + Friend Shared Sub ResetCopyback(ByVal CopyBack As Boolean()) + If CopyBack IsNot Nothing Then + ' Initialize the copy back array to all ByVal. + For Index As Integer = 0 To CopyBack.Length - 1 + CopyBack(Index) = False + Next + End If + End Sub 'ResetCopyback + + _ + Public Shared Function LateGet( _ + ByVal Instance As Object, _ + ByVal Type As System.Type, _ + ByVal MemberName As String, _ + ByVal Arguments As Object(), _ + ByVal ArgumentNames As String(), _ + ByVal TypeArguments As Type(), _ + ByVal CopyBack As Boolean()) As Object + +#If Not NEW_BINDER Then + Return LateBinding.LateGet(Instance, Type, MemberName, Arguments, ArgumentNames, CopyBack) +#End If + + If Arguments Is Nothing Then Arguments = NoArguments + If ArgumentNames Is Nothing Then ArgumentNames = NoArgumentNames + If TypeArguments Is Nothing Then TypeArguments = NoTypeArguments + + Dim BaseReference As Container + If Type IsNot Nothing Then + BaseReference = New Container(Type) + Else + BaseReference = New Container(Instance) + End If + + Dim InvocationFlags As BindingFlags = BindingFlags.InvokeMethod Or BindingFlags.GetProperty + + If BaseReference.IsCOMObject AndAlso Not BaseReference.IsWindowsRuntimeObject Then +#If Not TELESTO Then + 'UNDONE: BAIL for now -- call the old binder. + Return LateBinding.LateGet(Instance, Type, MemberName, Arguments, ArgumentNames, CopyBack) + 'Return BaseReference.InvokeCOMMethod2(MemberName, Arguments, ArgumentNames, CopyBack, InvocationFlags) +#Else + Throw New InvalidOperationException("Never expected to see a COM object in Telesto") ' Unexpected scenario. No need to loc this. +#End If 'Not TELESTO + Else + Dim idmop As IDynamicMetaObjectProvider = IDOUtils.TryCastToIDMOP(Instance) + If idmop IsNot Nothing AndAlso TypeArguments Is NoTypeArguments Then + Return IDOBinder.IDOGet(idmop, MemberName, Arguments, ArgumentNames, CopyBack) + Else + Return ObjectLateGet(Instance, Type, MemberName, Arguments, ArgumentNames, TypeArguments, CopyBack) + End If + End If + End Function 'LateGet + + 'This method is only called from DynamicMethods generated at runtime + _ + _ + _ + Public Shared Function FallbackGet( _ + ByVal Instance As Object, _ + ByVal MemberName As String, _ + ByVal Arguments As Object(), _ + ByVal ArgumentNames As String()) As Object + + Return ObjectLateGet(Instance, Nothing, MemberName, Arguments, ArgumentNames, NoTypeArguments, IDOBinder.GetCopyBack()) + End Function 'FallbackGet + + _ + Private Shared Function ObjectLateGet( _ + ByVal Instance As Object, _ + ByVal Type As System.Type, _ + ByVal MemberName As String, _ + ByVal Arguments As Object(), _ + ByVal ArgumentNames As String(), _ + ByVal TypeArguments As Type(), _ + ByVal CopyBack As Boolean()) As Object + + 'TODO(ngafter): should probably pass in InvocationFlags and BaseReference + Dim BaseReference As Container + If Type IsNot Nothing Then + BaseReference = New Container(Type) + Else + BaseReference = New Container(Instance) + End If + + Dim InvocationFlags As BindingFlags = BindingFlags.InvokeMethod Or BindingFlags.GetProperty + + Dim Members As MemberInfo() = BaseReference.GetMembers(MemberName, True) + + If Members(0).MemberType = MemberTypes.Field Then + If TypeArguments.Length > 0 Then + Throw New ArgumentException(GetResourceString(ResID.Argument_InvalidValue)) 'CONSIDER: a better error message + End If + + Dim FieldValue As Object = BaseReference.GetFieldValue(DirectCast(Members(0), FieldInfo)) + If Arguments.Length = 0 Then + 'This is a simple field access. + Return FieldValue + Else + 'This is an indexed field access. + Return LateIndexGet(FieldValue, Arguments, ArgumentNames, CopyBack) + End If + End If + + If ArgumentNames.Length > Arguments.Length OrElse _ + (CopyBack IsNot Nothing AndAlso CopyBack.Length <> Arguments.Length) Then + Throw New ArgumentException(GetResourceString(ResID.Argument_InvalidValue)) 'CONSIDER: a better error message + End If + + Dim Failure As OverloadResolution.ResolutionFailure + Dim TargetProcedure As Method = _ + ResolveCall( _ + BaseReference, _ + MemberName, _ + Members, _ + Arguments, _ + ArgumentNames, _ + TypeArguments, _ + InvocationFlags, _ + False, _ + Failure) + + If Failure = OverloadResolution.ResolutionFailure.None Then + Return BaseReference.InvokeMethod(TargetProcedure, Arguments, CopyBack, InvocationFlags) + + ElseIf Arguments.Length > 0 AndAlso Members.Length = 1 AndAlso IsZeroArgumentCall(Members(0)) Then + ' Dev10 #579405: For default property transformation the group should contain just 1 item + ' and that item should take no arguments. + + TargetProcedure = _ + ResolveCall( _ + BaseReference, _ + MemberName, _ + Members, _ + NoArguments, _ + NoArgumentNames, _ + TypeArguments, _ + InvocationFlags, _ + False, _ + Failure) + + If Failure = OverloadResolution.ResolutionFailure.None Then + Dim Result As Object = BaseReference.InvokeMethod(TargetProcedure, NoArguments, Nothing, InvocationFlags) + + 'For backwards compatibility, throw a missing member exception if the intermediate result is Nothing. + If Result Is Nothing Then + Throw New _ + MissingMemberException( _ + GetResourceString( _ + ResID.IntermediateLateBoundNothingResult1, _ + TargetProcedure.ToString, _ + BaseReference.VBFriendlyName)) + End If + + Result = InternalLateIndexGet( _ + Result, _ + Arguments, _ + ArgumentNames, _ + False, _ + Failure, _ + CopyBack) + + If Failure = ResolutionFailure.None Then + Return Result + End If + End If + + End If + + 'Every attempt to make this work failed. Redo the original call resolution to generate errors. + ResolveCall( _ + BaseReference, _ + MemberName, _ + Members, _ + Arguments, _ + ArgumentNames, _ + TypeArguments, _ + InvocationFlags, _ + True, _ + Failure) +#If TELESTO Then + Debug.Assert(False, "the resolution should have thrown an exception") +#Else + Debug.Fail("the resolution should have thrown an exception") +#End If + Throw New InternalErrorException() + End Function 'ObjectLateGet + + 'Quick check to determine if FallbackGet will succeed + Friend Shared Function CanBindGet(ByVal Instance As Object, ByVal MemberName As String, ByVal Arguments As Object(), ByVal ArgumentNames As String()) As Boolean + Dim BaseReference As New Container(Instance) + Dim InvocationFlags As BindingFlags = BindingFlags.InvokeMethod Or BindingFlags.GetProperty + + Dim Failure As ResolutionFailure + Dim Members As MemberInfo() = BaseReference.GetMembers(MemberName, False) + If Members Is Nothing OrElse Members.Length = 0 Then + Return False + End If + + If Members(0).MemberType = MemberTypes.Field Then + 'There may be additional work after the field get, but as far + 'as we're concerned the binding succeeded + Return True + End If + + Dim TargetProcedure As Method = _ + ResolveCall( _ + BaseReference, _ + MemberName, _ + Members, _ + Arguments, _ + ArgumentNames, _ + NoTypeArguments, _ + InvocationFlags, _ + False, _ + Failure) + + If Failure = OverloadResolution.ResolutionFailure.None Then + Return True + End If + + If Arguments.Length > 0 AndAlso Members.Length = 1 AndAlso IsZeroArgumentCall(Members(0)) Then + ' Dev10 #579405: For default property transformation the group should contain just 1 item + ' and that item should take no arguments. + + TargetProcedure = _ + ResolveCall( _ + BaseReference, _ + MemberName, _ + Members, _ + NoArguments, _ + NoArgumentNames, _ + NoTypeArguments, _ + InvocationFlags, _ + False, _ + Failure) + + If Failure = OverloadResolution.ResolutionFailure.None Then + 'There will be additional work after the first call, but as + 'far as we're concerned the binding succeeded. + Return True + End If + End If + + 'Every attempt at binding failed, return false so we use the IDO's error + Return False + End Function + + ' Determines if the member is a zero argument method or property + Friend Shared Function IsZeroArgumentCall(ByVal Member As MemberInfo) As Boolean + Return ((Member.MemberType = MemberTypes.Method AndAlso _ + DirectCast(Member, MethodInfo).GetParameters().Length = 0) OrElse _ + (Member.MemberType = MemberTypes.Property AndAlso _ + DirectCast(Member, PropertyInfo).GetIndexParameters().Length = 0)) + End Function + + 'UNDONE: temporary entry point to get the compiler hookup working. + _ + Public Shared Sub LateIndexSetComplex( _ + ByVal Instance As Object, _ + ByVal Arguments As Object(), _ + ByVal ArgumentNames As String(), _ + ByVal OptimisticSet As Boolean, _ + ByVal RValueBase As Boolean) + + Dim idmop As IDynamicMetaObjectProvider = IDOUtils.TryCastToIDMOP(Instance) + If idmop IsNot Nothing Then + Call IDOBinder.IDOIndexSetComplex(idmop, Arguments, ArgumentNames, OptimisticSet, RValueBase) + Else + Call ObjectLateIndexSetComplex(Instance, Arguments, ArgumentNames, OptimisticSet, RValueBase) + Return + End If + End Sub + + 'This method is only called from DynamicMethods generated at runtime + _ + _ + _ + Public Shared Sub FallbackIndexSetComplex( _ + ByVal Instance As Object, _ + ByVal Arguments As Object(), _ + ByVal ArgumentNames As String(), _ + ByVal OptimisticSet As Boolean, _ + ByVal RValueBase As Boolean) + + ObjectLateIndexSetComplex(Instance, Arguments, ArgumentNames, OptimisticSet, RValueBase) + End Sub 'FallbackIndexSetComplex + + _ + Friend Shared Sub ObjectLateIndexSetComplex( _ + ByVal Instance As Object, _ + ByVal Arguments As Object(), _ + ByVal ArgumentNames As String(), _ + ByVal OptimisticSet As Boolean, _ + ByVal RValueBase As Boolean) + +#If Not NEW_BINDER Then + LateBinding.LateIndexSetComplex(Instance, Arguments, ArgumentNames, OptimisticSet, RValueBase) + Return +#End If + 'UNDONE: compiler is still loading Optimistic set, but this should be renamed to + 'Report Errors. But changing that while turning on the new latebinder would require + 'a toolset update, which would be bad. For now, make a temp. + 'Dim ReportErrors As Boolean = Not OptimisticSet + + If Arguments Is Nothing Then Arguments = NoArguments + If ArgumentNames Is Nothing Then ArgumentNames = NoArgumentNames + + Dim BaseReference As Container = New Container(Instance) + + 'An l-value expression o(a) has two possible forms: + ' 1: o(a) = v array lookup--where o is an array object and a is a set of indices + ' 2: o.d(a) = v default member access--where o has default method/property d + + If BaseReference.IsArray Then + 'This is an array lookup and assignment o(a) = v. + + If ArgumentNames.Length > 0 Then + Throw New ArgumentException(GetResourceString(ResID.Argument_InvalidNamedArgs)) + End If + + BaseReference.SetArrayValue(Arguments) + Return + End If + + If ArgumentNames.Length > Arguments.Length Then + Throw New ArgumentException(GetResourceString(ResID.Argument_InvalidValue)) 'CONSIDER: a better error message + End If + + If Arguments.Length < 1 Then + 'We're binding to a Set, we must have at least the Value argument. + Throw New ArgumentException(GetResourceString(ResID.Argument_InvalidValue)) 'CONSIDER: a better error message + End If + + Dim MethodName As String = "" + + If BaseReference.IsCOMObject AndAlso Not BaseReference.IsWindowsRuntimeObject Then +#If Not TELESTO Then + 'UNDONE: BAIL for now -- call the old binder. + LateBinding.LateIndexSetComplex(Instance, Arguments, ArgumentNames, OptimisticSet, RValueBase) + Return +#Else + Throw New InvalidOperationException("Never expected to see a COM object in Telesto") ' Unexpected scenario. No need to loc this. +#End If +#If 0 Then + Try + BaseReference.InvokeCOMMethod( _ + MethodName, _ + Arguments, _ + ArgumentNames, _ + Nothing, _ + GetPropertyPutFlags(Arguments(Arguments.Length - 1))) + + If RValueBase AndAlso BaseReference.IsValueType Then + Throw New Exception( _ + GetResourceString( _ + ResID.RValueBaseForValueType, _ + BaseReference.VBFriendlyName, _ + BaseReference.VBFriendlyName)) + End If + Catch ex As System.MissingMemberException When OptimisticSet = True + 'A missing member exception means it has no Set member. Silently handle the exception. + End Try + Return +#End If + + Else + Dim InvocationFlags As BindingFlags = BindingFlags.SetProperty + + Dim Members As MemberInfo() = BaseReference.GetMembers(MethodName, True) 'MethodName is set during this call. + + Dim Failure As OverloadResolution.ResolutionFailure + Dim TargetProcedure As Method = _ + ResolveCall( _ + BaseReference, _ + MethodName, _ + Members, _ + Arguments, _ + ArgumentNames, _ + NoTypeArguments, _ + InvocationFlags, _ + False, _ + Failure) + + If Failure = OverloadResolution.ResolutionFailure.None Then + + If RValueBase AndAlso BaseReference.IsValueType Then + Throw New Exception( _ + GetResourceString( _ + ResID.RValueBaseForValueType, _ + BaseReference.VBFriendlyName, _ + BaseReference.VBFriendlyName)) + End If + + BaseReference.InvokeMethod(TargetProcedure, Arguments, Nothing, InvocationFlags) + Return + + ElseIf OptimisticSet Then + Return + + Else + 'Redo the resolution to generate errors. + ResolveCall( _ + BaseReference, _ + MethodName, _ + Members, _ + Arguments, _ + ArgumentNames, _ + NoTypeArguments, _ + InvocationFlags, _ + True, _ + Failure) + End If + End If + + +#If TELESTO Then + Debug.Assert(False, "the resolution should have thrown an exception - should never reach here") +#Else + Debug.Fail("the resolution should have thrown an exception - should never reach here") +#End If + Throw New InternalErrorException() + + End Sub + + 'Determines if ObjectLateIndexSetComplex can succeed + 'Used by IDOBinder + Friend Shared Function CanIndexSetComplex( _ + ByVal Instance As Object, _ + ByVal Arguments As Object(), _ + ByVal ArgumentNames As String(), _ + ByVal OptimisticSet As Boolean, _ + ByVal RValueBase As Boolean) As Boolean + + Dim BaseReference As Container = New Container(Instance) + + 'An l-value expression o(a) has two possible forms: + ' 1: o(a) = v array lookup--where o is an array object and a is a set of indices + ' 2: o.d(a) = v default member access--where o has default method/property d + + If BaseReference.IsArray Then + 'This is an array lookup and assignment o(a) = v. + Return ArgumentNames.Length = 0 + End If + + Dim MethodName As String = "" + Dim InvocationFlags As BindingFlags = BindingFlags.SetProperty + + Dim Members As MemberInfo() = BaseReference.GetMembers(MethodName, False) 'MethodName is set during this call. + If Members Is Nothing OrElse Members.Length = 0 Then + Return False + End If + + Dim Failure As OverloadResolution.ResolutionFailure + Dim TargetProcedure As Method = _ + ResolveCall( _ + BaseReference, _ + MethodName, _ + Members, _ + Arguments, _ + ArgumentNames, _ + NoTypeArguments, _ + InvocationFlags, _ + False, _ + Failure) + + If Failure = OverloadResolution.ResolutionFailure.None Then + If RValueBase AndAlso BaseReference.IsValueType Then + Return False + End If + + Return True + End If + + Return OptimisticSet + End Function + + 'UNDONE: should remove this helper. + _ + Public Shared Sub LateIndexSet( _ + ByVal Instance As Object, _ + ByVal Arguments() As Object, _ + ByVal ArgumentNames() As String) + + Dim idmop As IDynamicMetaObjectProvider = IDOUtils.TryCastToIDMOP(Instance) + If idmop IsNot Nothing Then + IDOBinder.IDOIndexSet(idmop, Arguments, ArgumentNames) + Return + Else + ObjectLateIndexSet(Instance, Arguments, ArgumentNames) + Return + End If + End Sub 'LateIndexSet + + 'This method is only called from DynamicMethods generated at runtime + _ + _ + _ + Public Shared Sub FallbackIndexSet( _ + ByVal Instance As Object, _ + ByVal Arguments() As Object, _ + ByVal ArgumentNames() As String) + + ObjectLateIndexSet(Instance, Arguments, ArgumentNames) + End Sub 'FallbackIndexSet + + _ + Private Shared Sub ObjectLateIndexSet( _ + ByVal Instance As Object, _ + ByVal Arguments() As Object, _ + ByVal ArgumentNames() As String) + +#If Not NEW_BINDER Then + LateBinding.LateIndexSet(Instance, Arguments, ArgumentNames) + Return +#End If + + ObjectLateIndexSetComplex(Instance, Arguments, ArgumentNames, False, False) + Return + End Sub 'ObjectLateIndexSet + + _ + Public Shared Sub LateSetComplex( _ + ByVal Instance As Object, _ + ByVal Type As Type, _ + ByVal MemberName As String, _ + ByVal Arguments() As Object, _ + ByVal ArgumentNames() As String, _ + ByVal TypeArguments() As Type, _ + ByVal OptimisticSet As Boolean, _ + ByVal RValueBase As Boolean) + + Dim idmop As IDynamicMetaObjectProvider = IDOUtils.TryCastToIDMOP(Instance) + If idmop IsNot Nothing AndAlso TypeArguments Is Nothing Then + IDOBinder.IDOSetComplex(idmop, MemberName, Arguments, ArgumentNames, OptimisticSet, RValueBase) + Else + ObjectLateSetComplex(Instance, Type, _ + MemberName, Arguments, ArgumentNames, TypeArguments, OptimisticSet, RValueBase) + Return + End If + End Sub + + 'This method is only called from DynamicMethods generated at runtime + _ + _ + _ + Public Shared Sub FallbackSetComplex( _ + ByVal Instance As Object, _ + ByVal MemberName As String, _ + ByVal Arguments() As Object, _ + ByVal OptimisticSet As Boolean, _ + ByVal RValueBase As Boolean) + + ObjectLateSetComplex( _ + Instance, Nothing, MemberName, Arguments, New String() {}, _ + NoTypeArguments, OptimisticSet, RValueBase) + End Sub 'FallbackSetComplex + + _ + Friend Shared Sub ObjectLateSetComplex( _ + ByVal Instance As Object, _ + ByVal Type As Type, _ + ByVal MemberName As String, _ + ByVal Arguments() As Object, _ + ByVal ArgumentNames() As String, _ + ByVal TypeArguments() As Type, _ + ByVal OptimisticSet As Boolean, _ + ByVal RValueBase As Boolean) + +#If Not NEW_BINDER Then + LateBinding.LateSetComplex(Instance, Type, MemberName, Arguments, ArgumentNames, OptimisticSet, RValueBase) + Return +#End If + Const DefaultCallType As CallType = CType(0, CallType) + LateSet(Instance, Type, MemberName, Arguments, ArgumentNames, TypeArguments, OptimisticSet, RValueBase, DefaultCallType) + End Sub + + 'UNDONE: temporary entry point to get the compiler hookup working. + _ + Public Shared Sub LateSet( _ + ByVal Instance As Object, _ + ByVal Type As Type, _ + ByVal MemberName As String, _ + ByVal Arguments() As Object, _ + ByVal ArgumentNames() As String, _ + ByVal TypeArguments As Type()) + +#If Not NEW_BINDER Then + LateBinding.LateSet(Instance, Type, MemberName, Arguments, ArgumentNames) + Return +#End If + Dim idmop As IDynamicMetaObjectProvider = IDOUtils.TryCastToIDMOP(Instance) + If idmop IsNot Nothing AndAlso TypeArguments Is Nothing Then + IDOBinder.IDOSet(idmop, MemberName, ArgumentNames, Arguments) + Else + ObjectLateSet(Instance, Type, MemberName, Arguments, ArgumentNames, TypeArguments) + Return + End If + End Sub + + 'This method is only called from DynamicMethods generated at runtime + _ + _ + _ + Public Shared Sub FallbackSet( _ + ByVal Instance As Object, _ + ByVal MemberName As String, _ + ByVal Arguments() As Object) + + ObjectLateSet(Instance, Nothing, MemberName, Arguments, NoArgumentNames, NoTypeArguments) + End Sub 'FallbackSet + + Friend Shared Sub ObjectLateSet( _ + ByVal Instance As Object, _ + ByVal Type As Type, _ + ByVal MemberName As String, _ + ByVal Arguments() As Object, _ + ByVal ArgumentNames() As String, _ + ByVal TypeArguments As Type()) + +#If Not NEW_BINDER Then + LateBinding.LateSet(Instance, Type, MemberName, Arguments, ArgumentNames) + Return +#End If + + Const DefaultCallType As CallType = CType(0, CallType) + LateSet(Instance, Type, MemberName, Arguments, ArgumentNames, _ + TypeArguments, False, False, DefaultCallType) + Return + End Sub + + _ + Public Shared Sub LateSet( _ + ByVal Instance As Object, _ + ByVal Type As Type, _ + ByVal MemberName As String, _ + ByVal Arguments As Object(), _ + ByVal ArgumentNames As String(), _ + ByVal TypeArguments As Type(), _ + ByVal OptimisticSet As Boolean, _ + ByVal RValueBase As Boolean, _ + ByVal CallType As CallType) + + 'UNDONE: compiler is still loading Optimistic set, but this should be renamed to + 'Report Errors. But changing that while turning on the new latebinder would require + 'a toolset update, which would be bad. For now, make a temp. + 'Dim ReportErrors As Boolean = Not OptimisticSet + + + If Arguments Is Nothing Then Arguments = NoArguments + If ArgumentNames Is Nothing Then ArgumentNames = NoArgumentNames + If TypeArguments Is Nothing Then TypeArguments = NoTypeArguments + + Dim BaseReference As Container + If Type IsNot Nothing Then + BaseReference = New Container(Type) + Else + BaseReference = New Container(Instance) + End If + + Dim InvocationFlags As BindingFlags + + If BaseReference.IsCOMObject AndAlso Not BaseReference.IsWindowsRuntimeObject Then +#If Not TELESTO Then + 'UNDONE: BAIL for now -- call the old binder. + Try + 'CONSIDER (5/9/2001): + ' this really needs to be done in two steps: + ' step 1: can the Set succeed? + ' step 2: perform the Set + ' the rvaluebase check would be done between 1 and 2 + + LateBinding.InternalLateSet(Instance, Type, MemberName, Arguments, ArgumentNames, OptimisticSet, CallType) + + If RValueBase AndAlso Type.IsValueType Then + 'note that objType is passed byref to InternalLateSet and that it + 'should be valid by the time we get to this point + Throw New Exception(GetResourceString(ResID.RValueBaseForValueType, VBFriendlyName(Type, Instance), VBFriendlyName(Type, Instance))) + End If + Catch ex As System.MissingMemberException When OptimisticSet = True + 'A missing member exception means it has no Set member. Silently handle the exception. + End Try + + Return +#Else + Throw New InvalidOperationException +#End If 'Not TELESTO +#If 0 Then + If CallType = CallType.Set Then + InvocationFlags = InvocationFlags Or BindingFlags.PutRefDispProperty + If Arguments(Arguments.GetUpperBound(0)) Is Nothing Then + Arguments(Arguments.GetUpperBound(0)) = New DispatchWrapper(Nothing) + End If + ElseIf CallType = CallType.Let Then + InvocationFlags = InvocationFlags Or BindingFlags.PutDispProperty + Else + InvocationFlags = InvocationFlags Or GetPropertyPutFlags(Arguments(Arguments.GetUpperBound(0))) + End If + + 'UNDONE + BaseReference.InvokeCOMMethod2(MemberName, Arguments, ArgumentNames, Nothing, InvocationFlags) + Return +#End If + End If + + ' If we have a IDO that implements TryGetMember for a property but not TrySetMember then we could land up + ' here and with an optimistic set but we don't want to throw an exception if the property is not found. + ' Swallow the exception and return quietly if this is a readonly IDO property doing an optimistic set. + Dim Members As MemberInfo() = BaseReference.GetMembers(MemberName, Not OptimisticSet) + + If Members.Length = 0 And OptimisticSet Then + Return + End If + + If Members(0).MemberType = MemberTypes.Field Then + + If TypeArguments.Length > 0 Then + Throw New ArgumentException(GetResourceString(ResID.Argument_InvalidValue)) 'CONSIDER: a better error message + End If + + If Arguments.Length = 1 Then + If RValueBase AndAlso BaseReference.IsValueType Then + Throw New Exception( _ + GetResourceString( _ + ResID.RValueBaseForValueType, _ + BaseReference.VBFriendlyName, _ + BaseReference.VBFriendlyName)) + End If + 'This is a simple field set. + BaseReference.SetFieldValue(DirectCast(Members(0), FieldInfo), Arguments(0)) + Return + Else + 'This is an indexed field set. + Dim FieldValue As Object = BaseReference.GetFieldValue(DirectCast(Members(0), FieldInfo)) + LateIndexSetComplex(FieldValue, Arguments, ArgumentNames, OptimisticSet, True) + Return + End If + End If + + InvocationFlags = BindingFlags.SetProperty + + If ArgumentNames.Length > Arguments.Length Then + Throw New ArgumentException(GetResourceString(ResID.Argument_InvalidValue)) 'CONSIDER: a better error message + End If + + Dim Failure As OverloadResolution.ResolutionFailure + Dim TargetProcedure As Method + + If TypeArguments.Length = 0 Then + + TargetProcedure = _ + ResolveCall( _ + BaseReference, _ + MemberName, _ + Members, _ + Arguments, _ + ArgumentNames, _ + NoTypeArguments, _ + InvocationFlags, _ + False, _ + Failure) + + If Failure = OverloadResolution.ResolutionFailure.None Then + If RValueBase AndAlso BaseReference.IsValueType Then + Throw New Exception( _ + GetResourceString( _ + ResID.RValueBaseForValueType, _ + BaseReference.VBFriendlyName, _ + BaseReference.VBFriendlyName)) + End If + + BaseReference.InvokeMethod(TargetProcedure, Arguments, Nothing, InvocationFlags) + Return + End If + + End If + + Dim SecondaryInvocationFlags As BindingFlags = _ + BindingFlags.InvokeMethod Or BindingFlags.GetProperty + + If Failure = OverloadResolution.ResolutionFailure.None OrElse Failure = OverloadResolution.ResolutionFailure.MissingMember Then + + TargetProcedure = _ + ResolveCall( _ + BaseReference, _ + MemberName, _ + Members, _ + NoArguments, _ + NoArgumentNames, _ + TypeArguments, _ + SecondaryInvocationFlags, _ + False, _ + Failure) + + If Failure = OverloadResolution.ResolutionFailure.None Then + Dim Result As Object = _ + BaseReference.InvokeMethod(TargetProcedure, NoArguments, Nothing, SecondaryInvocationFlags) + + 'For backwards compatibility, throw a missing member exception if the intermediate result is Nothing. + If Result Is Nothing Then + Throw New _ + MissingMemberException( _ + GetResourceString( _ + ResID.IntermediateLateBoundNothingResult1, _ + TargetProcedure.ToString, _ + BaseReference.VBFriendlyName)) + End If + + LateIndexSetComplex(Result, Arguments, ArgumentNames, OptimisticSet, True) + Return + End If + End If + + If OptimisticSet Then + Return + End If + + 'Everything failed, so give errors. Redo the first attempt to generate the errors. + If TypeArguments.Length = 0 Then + ResolveCall( _ + BaseReference, _ + MemberName, _ + Members, _ + Arguments, _ + ArgumentNames, _ + TypeArguments, _ + InvocationFlags, _ + True, _ + Failure) + + Else + ResolveCall( _ + BaseReference, _ + MemberName, _ + Members, _ + NoArguments, _ + NoArgumentNames, _ + TypeArguments, _ + SecondaryInvocationFlags, _ + True, _ + Failure) + End If + +#If TELESTO Then + Debug.Assert(False, "the resolution should have thrown an exception") +#Else + Debug.Fail("the resolution should have thrown an exception") +#End If + Throw New InternalErrorException() + Return + End Sub + + 'Determines if LateSet will succeed. Used by IDOBinder. + Friend Shared Function CanBindSet(ByVal Instance As Object, ByVal MemberName As String, ByVal Value As Object, ByVal OptimisticSet As Boolean, ByVal RValueBase As Boolean) As Boolean + Dim BaseReference As New Container(Instance) + Dim Arguments As Object() = {Value} + + Dim Members As MemberInfo() = BaseReference.GetMembers(MemberName, False) + If Members Is Nothing OrElse Members.Length = 0 Then + Return False + End If + + If Members(0).MemberType = MemberTypes.Field Then + If Arguments.Length = 1 AndAlso RValueBase AndAlso BaseReference.IsValueType Then + Return False + End If + + 'There may be more work (for indexed fields), but if we got + 'this far we consider it success. + Return True + End If + + Dim Failure As OverloadResolution.ResolutionFailure + Dim TargetProcedure As Method = _ + ResolveCall( _ + BaseReference, _ + MemberName, _ + Members, _ + Arguments, _ + NoArgumentNames, _ + NoTypeArguments, _ + BindingFlags.SetProperty, _ + False, _ + Failure) + + If Failure = OverloadResolution.ResolutionFailure.None Then + If RValueBase AndAlso BaseReference.IsValueType Then + Return False + End If + + Return True + End If + + Dim SecondaryInvocationFlags As BindingFlags = BindingFlags.InvokeMethod Or BindingFlags.GetProperty + + If Failure = OverloadResolution.ResolutionFailure.MissingMember Then + TargetProcedure = _ + ResolveCall( _ + BaseReference, _ + MemberName, _ + Members, _ + NoArguments, _ + NoArgumentNames, _ + NoTypeArguments, _ + SecondaryInvocationFlags, _ + False, _ + Failure) + + If Failure = OverloadResolution.ResolutionFailure.None Then + 'There is work (to call the method/prop), but if we got + 'this far we consider it success. + Return True + End If + End If + + 'Everything failed, so use the IDO's error if any + 'Unless we're doing an optimistic set, in which case this is considered success. + Return OptimisticSet + End Function + + Private Shared Function CallMethod( _ + ByVal BaseReference As Container, _ + ByVal MethodName As String, _ + ByVal Arguments As Object(), _ + ByVal ArgumentNames As String(), _ + ByVal TypeArguments As System.Type(), _ + ByVal CopyBack As Boolean(), _ + ByVal InvocationFlags As BindingFlags, _ + ByVal ReportErrors As Boolean, _ + ByRef Failure As ResolutionFailure) As Object + + Debug.Assert(BaseReference IsNot Nothing, "Nothing unexpected") + Debug.Assert(Arguments IsNot Nothing, "Nothing unexpected") + Debug.Assert(ArgumentNames IsNot Nothing, "Nothing unexpected") + Debug.Assert(TypeArguments IsNot Nothing, "Nothing unexpected") + + Failure = ResolutionFailure.None + + If ArgumentNames.Length > Arguments.Length OrElse _ + (CopyBack IsNot Nothing AndAlso CopyBack.Length <> Arguments.Length) Then + Failure = ResolutionFailure.InvalidArgument + + If ReportErrors Then + Throw New ArgumentException(GetResourceString(ResID.Argument_InvalidValue)) 'CONSIDER: a better error message + End If + + Return Nothing + End If + + If HasFlag(InvocationFlags, BindingFlags.SetProperty) AndAlso Arguments.Length < 1 Then + Failure = ResolutionFailure.InvalidArgument + + If ReportErrors Then + 'If we're binding to a Set, we must have at least the Value argument. + Throw New ArgumentException(GetResourceString(ResID.Argument_InvalidValue)) 'CONSIDER: a better error message + End If + + Return Nothing + End If + +#If 0 Then + If BaseReference.IsCOMObject Then + Return _ + BaseReference.InvokeCOMMethod( _ + MethodName, _ + Arguments, _ + ArgumentNames, _ + CopyBack, _ + InvocationFlags) + End If +#End If + + Dim Members As MemberInfo() = BaseReference.GetMembers(MethodName, ReportErrors) + + If Members Is Nothing OrElse Members.Length = 0 Then + Failure = ResolutionFailure.MissingMember + + If ReportErrors Then +#If TELESTO Then + Debug.Assert(False, "If ReportErrors is True, GetMembers should have thrown above") +#Else + Debug.Fail("If ReportErrors is True, GetMembers should have thrown above") +#End If + Members = BaseReference.GetMembers(MethodName, True) + End If + + Return Nothing + End If + + Dim TargetProcedure As Method = _ + ResolveCall( _ + BaseReference, _ + MethodName, _ + Members, _ + Arguments, _ + ArgumentNames, _ + TypeArguments, _ + InvocationFlags, _ + ReportErrors, _ + Failure) + + If Failure = ResolutionFailure.None Then + Return BaseReference.InvokeMethod(TargetProcedure, Arguments, CopyBack, InvocationFlags) + End If + + Return Nothing + End Function + + Friend Shared Function MatchesPropertyRequirements(ByVal TargetProcedure As Method, ByVal Flags As BindingFlags) As MethodInfo + Debug.Assert(TargetProcedure.IsProperty, "advertised property method isn't.") + + If HasFlag(Flags, BindingFlags.SetProperty) Then + Return TargetProcedure.AsProperty.GetSetMethod + Else + Return TargetProcedure.AsProperty.GetGetMethod + End If + End Function + + Friend Shared Function ReportPropertyMismatch(ByVal TargetProcedure As Method, ByVal Flags As BindingFlags) As Exception + Debug.Assert(TargetProcedure.IsProperty, "advertised property method isn't.") + + If HasFlag(Flags, BindingFlags.SetProperty) Then + Debug.Assert(TargetProcedure.AsProperty.GetSetMethod Is Nothing, "expected error condition") + 'UNDONE: what's the right type of exception to throw for invalid targets? It shouldn't be MissingMemberException. + Return New MissingMemberException( _ + GetResourceString(ResID.NoSetProperty1, TargetProcedure.AsProperty.Name)) + Else + Debug.Assert(TargetProcedure.AsProperty.GetGetMethod Is Nothing, "expected error condition") + 'UNDONE: what's the right type of exception to throw for invalid targets? It shouldn't be MissingMemberException. + Return New MissingMemberException( _ + GetResourceString(ResID.NoGetProperty1, TargetProcedure.AsProperty.Name)) + End If + End Function + + Friend Shared Function ResolveCall( _ + ByVal BaseReference As Container, _ + ByVal MethodName As String, _ + ByVal Members As MemberInfo(), _ + ByVal Arguments As Object(), _ + ByVal ArgumentNames As String(), _ + ByVal TypeArguments As Type(), _ + ByVal LookupFlags As BindingFlags, _ + ByVal ReportErrors As Boolean, _ + ByRef Failure As OverloadResolution.ResolutionFailure) As Method + + Debug.Assert(BaseReference IsNot Nothing, "expected a base reference") + Debug.Assert(MethodName IsNot Nothing, "expected method name") + Debug.Assert(Members IsNot Nothing AndAlso Members.Length > 0, "expected members") + Debug.Assert(Arguments IsNot Nothing AndAlso _ + ArgumentNames IsNot Nothing AndAlso _ + TypeArguments IsNot Nothing AndAlso _ + ArgumentNames.Length <= Arguments.Length, _ + "expected valid argument arrays") + + Failure = OverloadResolution.ResolutionFailure.None + + If Members(0).MemberType <> MemberTypes.Method AndAlso _ + Members(0).MemberType <> MemberTypes.Property Then + + Failure = OverloadResolution.ResolutionFailure.InvalidTarget + If ReportErrors Then + 'This expression is not a procedure, but occurs as the target of a procedure call. + Throw New ArgumentException( _ + GetResourceString(ResID.ExpressionNotProcedure, MethodName, BaseReference.VBFriendlyName)) + End If + Return Nothing + End If + + 'When binding to Property Set accessors, strip off the last Value argument + 'because it does not participate in overload resolution. + + Dim SavedArguments As Object() + Dim ArgumentCount As Integer = Arguments.Length + Dim LastArgument As Object = Nothing + + If HasFlag(LookupFlags, BindingFlags.SetProperty) Then + If Arguments.Length = 0 Then + Failure = OverloadResolution.ResolutionFailure.InvalidArgument + + If ReportErrors Then + Throw New InvalidCastException( _ + GetResourceString(ResID.PropertySetMissingArgument1, MethodName)) + End If + + Return Nothing + End If + + SavedArguments = Arguments + Arguments = New Object(ArgumentCount - 2) {} + System.Array.Copy(SavedArguments, Arguments, Arguments.Length) + LastArgument = SavedArguments(ArgumentCount - 1) + End If + + Dim ResolutionResult As Method = _ + ResolveOverloadedCall( _ + MethodName, _ + Members, _ + Arguments, _ + ArgumentNames, _ + TypeArguments, _ + LookupFlags, _ + ReportErrors, _ + Failure, + BaseReference) + + Debug.Assert(Failure = OverloadResolution.ResolutionFailure.None OrElse Not ReportErrors, _ + "if resolution failed, an exception should have been thrown") + + If Failure <> OverloadResolution.ResolutionFailure.None Then + Debug.Assert(ResolutionResult Is Nothing, "resolution failed so should have no result") + Return Nothing + End If + + Debug.Assert(ResolutionResult IsNot Nothing, "resolution didn't fail, so should have result") + +#If BINDING_LOG Then + Console.WriteLine("== RESULT ==") + Console.WriteLine(ResolutionResult.DeclaringType.Name & "::" & ResolutionResult.ToString) + Console.WriteLine() +#End If + + 'Overload resolution will potentially select one method before validating arguments. + 'Validate those arguments now. + 'CONSIDER: move the overload list construction up and out of overload resolution and into this function. + If Not ResolutionResult.ArgumentsValidated Then + + If Not CanMatchArguments(ResolutionResult, Arguments, ArgumentNames, TypeArguments, False, Nothing) Then + + Failure = OverloadResolution.ResolutionFailure.InvalidArgument + + If ReportErrors Then + Dim ErrorMessage As String = "" + Dim Errors As New List(Of String) + + Dim Result As Boolean = _ + CanMatchArguments(ResolutionResult, Arguments, ArgumentNames, TypeArguments, False, Errors) + + Debug.Assert(Result = False AndAlso Errors.Count > 0, "expected this candidate to fail") + + For Each ErrorString As String In Errors + ErrorMessage &= vbCrLf & " " & ErrorString + Next + + ErrorMessage = GetResourceString(ResID.MatchArgumentFailure2, ResolutionResult.ToString, ErrorMessage) + 'We are missing a member which can match the arguments, so throw a missing member exception. + 'CONSIDER 2/26/2004: InvalidCastException is thrown only for back compat. It would + 'be nice if the latebinder had its own set of exceptions to throw. + Throw New InvalidCastException(ErrorMessage) + End If + + Return Nothing + End If + + End If + + 'Once we've gotten this far, we've selected a member. From this point on, we determine + 'if the member can be called given the context. + + 'Check that the resulting binding makes sense in the current context. + If ResolutionResult.IsProperty Then + If MatchesPropertyRequirements(ResolutionResult, LookupFlags) Is Nothing Then + Failure = OverloadResolution.ResolutionFailure.InvalidTarget + If ReportErrors Then + Throw ReportPropertyMismatch(ResolutionResult, LookupFlags) + End If + Return Nothing + End If + Else + Debug.Assert(ResolutionResult.IsMethod, "must be a method") + If HasFlag(LookupFlags, BindingFlags.SetProperty) Then + Failure = OverloadResolution.ResolutionFailure.InvalidTarget + If ReportErrors Then + 'Methods can't be targets of assignments. + 'UNDONE: what's the right type of exception to throw for invalid targets? It shouldn't be MissingMemberException. + Throw New MissingMemberException( _ + GetResourceString(ResID.MethodAssignment1, ResolutionResult.AsMethod.Name)) + End If + Return Nothing + End If + End If + + If HasFlag(LookupFlags, BindingFlags.SetProperty) Then + 'Need to match the Value argument for the property set call. + Debug.Assert(GetCallTarget(ResolutionResult, LookupFlags).Name.StartsWith("set_"), "expected set accessor") + + Dim Parameters As ParameterInfo() = GetCallTarget(ResolutionResult, LookupFlags).GetParameters + Dim LastParameter As ParameterInfo = Parameters(Parameters.Length - 1) + If Not CanPassToParameter( _ + ResolutionResult, _ + LastArgument, _ + LastParameter, _ + False, _ + False, _ + Nothing, _ + Nothing, _ + Nothing) Then + + Failure = OverloadResolution.ResolutionFailure.InvalidArgument + + If ReportErrors Then + Dim ErrorMessage As String = "" + Dim Errors As New List(Of String) + + Dim Result As Boolean = _ + CanPassToParameter( _ + ResolutionResult, _ + LastArgument, _ + LastParameter, _ + False, _ + False, _ + Errors, _ + Nothing, _ + Nothing) + + Debug.Assert(Result = False AndAlso Errors.Count > 0, "expected this candidate to fail") + + For Each ErrorString As String In Errors + ErrorMessage &= vbCrLf & " " & ErrorString + Next + + ErrorMessage = GetResourceString(ResID.MatchArgumentFailure2, ResolutionResult.ToString, ErrorMessage) + 'The selected member can't handle the type of the Value argument, so this is an argument exception. + 'CONSIDER 2/26/2004: InvalidCastException is thrown only for back compat. It would + 'be nice if the latebinder had its own set of exceptions to throw. + Throw New InvalidCastException(ErrorMessage) + End If + + Return Nothing + End If + End If + + Return ResolutionResult + End Function + + Friend Shared Function GetCallTarget(ByVal TargetProcedure As Method, ByVal Flags As BindingFlags) As MethodBase + If TargetProcedure.IsMethod Then Return TargetProcedure.AsMethod + If TargetProcedure.IsProperty Then Return MatchesPropertyRequirements(TargetProcedure, Flags) +#If TELESTO Then + Debug.Assert(False, "not a method or property??") +#Else + Debug.Fail("not a method or property??") +#End If + Return Nothing + End Function + + Friend Shared Function ConstructCallArguments( _ + ByVal TargetProcedure As Method, _ + ByVal Arguments As Object(), _ + ByVal LookupFlags As BindingFlags) As Object() + + Debug.Assert(TargetProcedure IsNot Nothing AndAlso Arguments IsNot Nothing, "expected arguments") + + + Dim Parameters As ParameterInfo() = GetCallTarget(TargetProcedure, LookupFlags).GetParameters + Dim CallArguments As Object() = New Object(Parameters.Length - 1) {} + + Dim SavedArguments As Object() + Dim ArgumentCount As Integer = Arguments.Length + Dim LastArgument As Object = Nothing + + If HasFlag(LookupFlags, BindingFlags.SetProperty) Then + Debug.Assert(Arguments.Length > 0, "must have an argument for property set Value") + SavedArguments = Arguments + Arguments = New Object(ArgumentCount - 2) {} + System.Array.Copy(SavedArguments, Arguments, Arguments.Length) + LastArgument = SavedArguments(ArgumentCount - 1) + End If + + MatchArguments(TargetProcedure, Arguments, CallArguments) + + If HasFlag(LookupFlags, BindingFlags.SetProperty) Then + 'Need to match the Value argument for the property set call. + Debug.Assert(GetCallTarget(TargetProcedure, LookupFlags).Name.StartsWith("set_"), "expected set accessor") + + Dim LastParameter As ParameterInfo = Parameters(Parameters.Length - 1) + CallArguments(Parameters.Length - 1) = _ + PassToParameter(LastArgument, LastParameter, LastParameter.ParameterType) + End If + + Return CallArguments + End Function + + End Class +End Namespace diff --git a/Microsoft.VisualBasic/runtime/msvbalib/Helpers/ObjectType.vb b/Microsoft.VisualBasic/runtime/msvbalib/Helpers/ObjectType.vb new file mode 100644 index 000000000..1931e164a --- /dev/null +++ b/Microsoft.VisualBasic/runtime/msvbalib/Helpers/ObjectType.vb @@ -0,0 +1,3760 @@ +' Copyright (c) Microsoft Corporation. All rights reserved. + +Imports System +Imports System.Diagnostics +Imports System.Globalization + +Imports Microsoft.VisualBasic.CompilerServices.StringType +Imports Microsoft.VisualBasic.CompilerServices.Utils + +Namespace Microsoft.VisualBasic.CompilerServices + +#Region " BACKWARDS COMPATIBILITY " + + 'WARNING WARNING WARNING WARNING WARNING + 'This code exists to support Everett compiled applications. Make sure you understand + 'the backwards compatibility ramifications of any edit you make in this region. + 'WARNING WARNING WARNING WARNING WARNING + + _ + Public NotInheritable Class ObjectType + + Private Const TCMAX As Integer = 19 + + Private Enum VType + t_bad + t_bool + t_ui1 + t_i2 + t_i4 + t_i8 + t_dec + t_r4 + t_r8 + t_char + t_str + t_date + End Enum + + '*** + '*** Enum for indexing into ConversionClassTable + '*** NOTE: Post RTM revisions should merge VType and VType2 usage + '*** into a single enum and remove use of WiderType table + '*** + Private Enum VType2 + t_bad + t_bool + t_ui1 + t_char + t_i2 + t_i4 + t_i8 + t_r4 + t_r8 + t_date + t_dec + t_ref + t_str + End Enum + + ' ' t_bad , t_bool, t_ui1 , t_i2 , t_i4 , t_i8 , t_dec , t_r4 , t_r8 , t_char, t_str , t_date + Private Shared ReadOnly WiderType(,) As VType = { _ + {VType.t_bad, VType.t_bad, VType.t_bad, VType.t_bad, VType.t_bad, VType.t_bad, VType.t_bad, VType.t_bad, VType.t_bad, VType.t_bad, VType.t_bad, VType.t_bad}, _ + {VType.t_bad, VType.t_bool, VType.t_bool, VType.t_i2, VType.t_i4, VType.t_i8, VType.t_dec, VType.t_r4, VType.t_r8, VType.t_bad, VType.t_r8, VType.t_bad}, _ + {VType.t_bad, VType.t_bool, VType.t_ui1, VType.t_i2, VType.t_i4, VType.t_i8, VType.t_dec, VType.t_r4, VType.t_r8, VType.t_bad, VType.t_r8, VType.t_bad}, _ + {VType.t_bad, VType.t_i2, VType.t_i2, VType.t_i2, VType.t_i4, VType.t_i8, VType.t_dec, VType.t_r4, VType.t_r8, VType.t_bad, VType.t_r8, VType.t_bad}, _ + {VType.t_bad, VType.t_i4, VType.t_i4, VType.t_i4, VType.t_i4, VType.t_i8, VType.t_dec, VType.t_r4, VType.t_r8, VType.t_bad, VType.t_r8, VType.t_bad}, _ + {VType.t_bad, VType.t_i8, VType.t_i8, VType.t_i8, VType.t_i8, VType.t_i8, VType.t_dec, VType.t_r4, VType.t_r8, VType.t_bad, VType.t_r8, VType.t_bad}, _ + {VType.t_bad, VType.t_dec, VType.t_dec, VType.t_dec, VType.t_dec, VType.t_dec, VType.t_dec, VType.t_r4, VType.t_r8, VType.t_bad, VType.t_r8, VType.t_bad}, _ + {VType.t_bad, VType.t_r4, VType.t_r4, VType.t_r4, VType.t_r4, VType.t_r4, VType.t_r4, VType.t_r4, VType.t_r8, VType.t_bad, VType.t_r8, VType.t_bad}, _ + {VType.t_bad, VType.t_r8, VType.t_r8, VType.t_r8, VType.t_r8, VType.t_r8, VType.t_r8, VType.t_r8, VType.t_r8, VType.t_bad, VType.t_r8, VType.t_bad}, _ + {VType.t_bad, VType.t_bad, VType.t_bad, VType.t_bad, VType.t_bad, VType.t_bad, VType.t_bad, VType.t_bad, VType.t_bad, VType.t_char, VType.t_str, VType.t_bad}, _ + {VType.t_bad, VType.t_r8, VType.t_r8, VType.t_r8, VType.t_r8, VType.t_r8, VType.t_r8, VType.t_r8, VType.t_r8, VType.t_str, VType.t_str, VType.t_date}, _ + {VType.t_bad, VType.t_bad, VType.t_bad, VType.t_bad, VType.t_bad, VType.t_bad, VType.t_bad, VType.t_bad, VType.t_bad, VType.t_bad, VType.t_date, VType.t_date} _ + } + + Private Enum CC As Byte + Err = 0 + Same = 1 + Narr = 2 + Wide = 3 + End Enum + + ' '*** This table comes from compiler sources in vb\bc\OverloadResolution.cpp + ' '*** From->bad bool ui1 char i2 i4 i8 r4 r8 date dec ref str + ' '*** Access using CC(totype, fromtype) + Private Shared ReadOnly ConversionClassTable(,) As CC = { _ + {CC.Err, CC.Err, CC.Err, CC.Err, CC.Err, CC.Err, CC.Err, CC.Err, CC.Err, CC.Err, CC.Err, CC.Err, CC.Err}, _ + {CC.Err, CC.Same, CC.Narr, CC.Err, CC.Narr, CC.Narr, CC.Narr, CC.Narr, CC.Narr, CC.Err, CC.Narr, CC.Err, CC.Narr}, _ + {CC.Err, CC.Narr, CC.Same, CC.Err, CC.Narr, CC.Narr, CC.Narr, CC.Narr, CC.Narr, CC.Err, CC.Narr, CC.Err, CC.Narr}, _ + {CC.Err, CC.Err, CC.Err, CC.Same, CC.Err, CC.Err, CC.Err, CC.Err, CC.Err, CC.Err, CC.Err, CC.Err, CC.Narr}, _ + {CC.Err, CC.Narr, CC.Wide, CC.Err, CC.Same, CC.Narr, CC.Narr, CC.Narr, CC.Narr, CC.Err, CC.Narr, CC.Err, CC.Narr}, _ + {CC.Err, CC.Narr, CC.Wide, CC.Err, CC.Wide, CC.Same, CC.Narr, CC.Narr, CC.Narr, CC.Err, CC.Narr, CC.Err, CC.Narr}, _ + {CC.Err, CC.Narr, CC.Wide, CC.Err, CC.Wide, CC.Wide, CC.Same, CC.Narr, CC.Narr, CC.Err, CC.Narr, CC.Err, CC.Narr}, _ + {CC.Err, CC.Narr, CC.Wide, CC.Err, CC.Wide, CC.Wide, CC.Wide, CC.Same, CC.Narr, CC.Err, CC.Wide, CC.Err, CC.Narr}, _ + {CC.Err, CC.Narr, CC.Wide, CC.Err, CC.Wide, CC.Wide, CC.Wide, CC.Wide, CC.Same, CC.Err, CC.Wide, CC.Err, CC.Narr}, _ + {CC.Err, CC.Err, CC.Err, CC.Err, CC.Err, CC.Err, CC.Err, CC.Err, CC.Err, CC.Same, CC.Err, CC.Err, CC.Narr}, _ + {CC.Err, CC.Narr, CC.Wide, CC.Err, CC.Wide, CC.Wide, CC.Wide, CC.Narr, CC.Narr, CC.Err, CC.Same, CC.Err, CC.Narr}, _ + {CC.Err, CC.Err, CC.Err, CC.Err, CC.Err, CC.Err, CC.Err, CC.Err, CC.Err, CC.Err, CC.Err, CC.Err, CC.Err}, _ + {CC.Err, CC.Narr, CC.Narr, CC.Wide, CC.Narr, CC.Narr, CC.Narr, CC.Narr, CC.Narr, CC.Narr, CC.Narr, CC.Err, CC.Same} _ + } + + Private Shared Function VTypeFromTypeCode(ByVal typ As TypeCode) As VType + + Select Case typ + + Case TypeCode.Boolean + Return VType.t_bool + + Case TypeCode.Byte + Return VType.t_ui1 + + Case TypeCode.Int16 + Return VType.t_i2 + + Case TypeCode.Int32 + Return VType.t_i4 + + Case TypeCode.Int64 + Return VType.t_i8 + + Case TypeCode.Decimal + Return VType.t_dec + + Case TypeCode.Single + Return VType.t_r4 + + Case TypeCode.Double + Return VType.t_r8 + + Case TypeCode.Char + Return VType.t_char + + Case TypeCode.String + Return VType.t_str + + Case TypeCode.DateTime + Return VType.t_date + + Case Else + Return VType.t_bad + + End Select + + End Function + + '*** FUNCTION: VType2FromTypeCode + '*** + '*** Helper for indexing into ConversionClassTable + '*** NOTE: Post RTM revisions should merge VType and VType2 usage + '*** into a single enum and remove use of WiderType table + '*** + Private Shared Function VType2FromTypeCode(ByVal typ As TypeCode) As VType2 + + Select Case typ + + Case TypeCode.Boolean + Return VType2.t_bool + + Case TypeCode.Byte + Return VType2.t_ui1 + + Case TypeCode.Int16 + Return VType2.t_i2 + + Case TypeCode.Int32 + Return VType2.t_i4 + + Case TypeCode.Int64 + Return VType2.t_i8 + + Case TypeCode.Decimal + Return VType2.t_dec + + Case TypeCode.Single + Return VType2.t_r4 + + Case TypeCode.Double + Return VType2.t_r8 + + Case TypeCode.Char + Return VType2.t_char + + Case TypeCode.String + Return VType2.t_str + + Case TypeCode.DateTime + Return VType2.t_date + + Case Else + Return VType2.t_bad + + End Select + + End Function + + Private Shared Function TypeCodeFromVType(ByVal vartyp As VType) As TypeCode + + Select Case vartyp + + Case VType.t_bool + Return TypeCode.Boolean + + Case VType.t_ui1 + Return TypeCode.Byte + + Case VType.t_i2 + Return TypeCode.Int16 + + Case VType.t_i4 + Return TypeCode.Int32 + + Case VType.t_i8 + Return TypeCode.Int64 + + Case VType.t_dec + Return TypeCode.Decimal + + Case VType.t_r4 + Return TypeCode.Single + + Case VType.t_r8 + Return TypeCode.Double + + Case VType.t_char + Return TypeCode.Char + + Case VType.t_str + Return TypeCode.String + + Case VType.t_date + Return TypeCode.DateTime + + Case Else + Return TypeCode.Object + + End Select + + End Function + + Friend Shared Function TypeFromTypeCode(ByVal vartyp As TypeCode) As Type + + Select Case vartyp + + Case TypeCode.Boolean + Return GetType(Boolean) + + Case TypeCode.Byte + Return GetType(Byte) + + Case TypeCode.Int16 + Return GetType(Int16) + + Case TypeCode.Int32 + Return GetType(Int32) + + Case TypeCode.Int64 + Return GetType(Int64) + + Case TypeCode.Decimal + Return GetType(Decimal) + + Case TypeCode.Single + Return GetType(Single) + + Case TypeCode.Double + Return GetType(Double) + + Case TypeCode.Char + Return GetType(Char) + + Case TypeCode.String + Return GetType(String) + + Case TypeCode.DateTime + Return GetType(DateTime) + + Case TypeCode.SByte + Return GetType(System.SByte) + + Case TypeCode.UInt16 + Return GetType(System.UInt16) + + Case TypeCode.UInt32 + Return GetType(System.UInt32) + + Case TypeCode.UInt64 + Return GetType(System.UInt64) + + Case TypeCode.Object + Return GetType(System.Object) + + Case TypeCode.DBNull + Return GetType(System.DBNull) + + Case Else + Return Nothing + + End Select + + End Function + + '*** Type1 - Type converting To + '*** Type2 - Type converting From + ' + Friend Shared Function IsWiderNumeric(ByVal Type1 As Type, ByVal Type2 As Type) As Boolean + + Dim TypeCode1, TypeCode2 As TypeCode + + TypeCode1 = System.Type.GetTypeCode(Type1) + TypeCode2 = System.Type.GetTypeCode(Type2) + + ' We can't just return here if the two type codes are the same because one + ' or both might be enums + + If IsOldNumericTypeCode(TypeCode1) AndAlso IsOldNumericTypeCode(TypeCode2) Then + + If TypeCode1 = TypeCode.Boolean OrElse TypeCode2 = TypeCode.Boolean Then + ' No conversion to or from a boolean is widening + Return False + End If + + If Type1.IsEnum() Then + ' No conversion to an enum is widening + Return False + End If + + ' Type2 can be an enum, because then we want to know if it's widening + + Return (WiderType(VTypeFromTypeCode(TypeCode1), VTypeFromTypeCode(TypeCode2)) = VTypeFromTypeCode(TypeCode1)) + End If + Return False + + End Function + + Friend Shared Function IsWideningConversion(ByVal FromType As Type, ByVal ToType As Type) As Boolean + Dim FromTypeCode, ToTypeCode As TypeCode + + Diagnostics.Debug.Assert(Not FromType Is ToType, "IsWideningConversion invalid for like types") + + FromTypeCode = System.Type.GetTypeCode(FromType) + ToTypeCode = System.Type.GetTypeCode(ToType) + + If FromTypeCode = TypeCode.Object Then + If FromType Is GetType(Char()) Then + If ToTypeCode = TypeCode.String OrElse ToType Is GetType(Char()) Then + Return True + End If + End If + + If ToTypeCode = TypeCode.Object Then + If FromType.IsArray AndAlso ToType.IsArray Then + If FromType.GetArrayRank() = ToType.GetArrayRank() Then + Return ToType.GetElementType().IsAssignableFrom(FromType.GetElementType()) + Else + Return False + End If + Else + Return ToType.IsAssignableFrom(FromType) + End If + End If + Return False + End If + + If ToTypeCode = TypeCode.Object Then + If ToType Is GetType(Char()) Then + If FromTypeCode = TypeCode.String Then + Return False + End If + End If + Return ToType.IsAssignableFrom(FromType) + End If + + If ToType.IsEnum() Then + ' No conversion to an enum is widening + Return False + End If + + Dim ConversionType As CC = ConversionClassTable(VType2FromTypeCode(ToTypeCode), VType2FromTypeCode(FromTypeCode)) + Return (ConversionType = CC.Wide OrElse ConversionType = CC.Same) + + End Function + + Friend Overloads Shared Function GetWidestType(ByVal obj1 As Object, ByVal obj2 As Object, Optional ByVal IsAdd As Boolean = False) As TypeCode + Dim type1, type2 As TypeCode + Dim conv1, conv2 As IConvertible + + conv1 = TryCast(obj1, IConvertible) + conv2 = TryCast(obj2, IConvertible) + + If Not conv1 Is Nothing Then + type1 = conv1.GetTypeCode() + Else + If obj1 Is Nothing Then + type1 = TypeCode.Empty + ElseIf TypeOf obj1 Is Char() AndAlso CType(obj1, Array).Rank = 1 Then + type1 = TypeCode.String + Else + type1 = TypeCode.Object + End If + End If + + If Not conv2 Is Nothing Then + type2 = conv2.GetTypeCode() + Else + If obj2 Is Nothing Then + type2 = TypeCode.Empty + ElseIf TypeOf obj2 Is Char() AndAlso CType(obj2, Array).Rank = 1 Then + type2 = TypeCode.String + Else + type2 = TypeCode.Object + End If + End If + + If obj1 Is Nothing Then + Return type2 + ElseIf obj2 Is Nothing Then + Return type1 + Else + ' An ugly hack. If we do x + y and one of them is DBNull and one of them is String, + ' then we convert DBNull to "" and do concatenation. We communicate this by passing + ' back TypeCode.DBNull + If IsAdd AndAlso _ + (((type1 = TypeCode.DBNull) AndAlso (type2 = TypeCode.String)) OrElse _ + ((type1 = TypeCode.String) AndAlso (type2 = TypeCode.DBNull))) Then + Return TypeCode.DBNull + Else + Return TypeCodeFromVType(WiderType(VTypeFromTypeCode(type1), VTypeFromTypeCode(type2))) + End If + End If + End Function + + Friend Overloads Shared Function GetWidestType(ByVal obj1 As Object, ByVal type2 As TypeCode) As TypeCode + Dim type1 As TypeCode + Dim conv1 As IConvertible + + conv1 = TryCast(obj1, IConvertible) + + If Not conv1 Is Nothing Then + type1 = conv1.GetTypeCode() + ElseIf obj1 Is Nothing Then + type1 = TypeCode.Empty + ElseIf TypeOf obj1 Is Char() AndAlso CType(obj1, Array).Rank = 1 Then + type1 = TypeCode.String + Else + type1 = TypeCode.Object + End If + + If obj1 Is Nothing Then + Return type2 + Else + Return TypeCodeFromVType(WiderType(VTypeFromTypeCode(type1), VTypeFromTypeCode(type2))) + End If + + End Function + + Public Shared Function ObjTst(ByVal o1 As Object, ByVal o2 As Object, ByVal TextCompare As Boolean) As Integer + Dim conv1, conv2 As IConvertible + Dim tc1, tc2 As TypeCode + + conv1 = TryCast(o1, IConvertible) + + If conv1 Is Nothing Then + If o1 Is Nothing Then + tc1 = TypeCode.Empty + Else + tc1 = TypeCode.Object + End If + Else + tc1 = conv1.GetTypeCode() + End If + + conv2 = TryCast(o2, IConvertible) + + If conv2 Is Nothing Then + If o2 Is Nothing Then + tc2 = TypeCode.Empty + Else + tc2 = TypeCode.Object + End If + Else + tc2 = conv2.GetTypeCode() + End If + + 'Special cases for Char() + If (tc1 = TypeCode.Object) AndAlso (TypeOf o1 Is Char()) Then + If tc2 = TypeCode.String OrElse tc2 = TypeCode.Empty OrElse ((tc2 = TypeCode.Object) AndAlso (TypeOf o2 Is Char())) Then + 'Treat Char() as String for these cases + o1 = CStr(CharArrayType.FromObject(o1)) + conv1 = CType(o1, IConvertible) + tc1 = TypeCode.String + End If + End If + + If (tc2 = TypeCode.Object) AndAlso (TypeOf o2 Is Char()) Then + If tc1 = TypeCode.String OrElse tc1 = TypeCode.Empty Then + o2 = CStr(CharArrayType.FromObject(o2)) + conv2 = DirectCast(o2, IConvertible) + tc2 = TypeCode.String + End If + End If + + Select Case tc1 * TCMAX + tc2 + + Case TypeCode.Empty * TCMAX + TypeCode.String + Return ObjTstStringString(Nothing, o2.ToString(), TextCompare) + + Case TypeCode.String * TCMAX + TypeCode.Empty + Return ObjTstStringString(o1.ToString(), Nothing, TextCompare) + + Case TypeCode.Empty * TCMAX + TypeCode.Empty + Return CInt(0) + + Case TypeCode.Byte * TCMAX + TypeCode.Empty + Return ObjTstByte(conv1.ToByte(Nothing), 0) + + Case TypeCode.Empty * TCMAX + TypeCode.Byte + Return ObjTstByte(0, conv2.ToByte(Nothing)) + + Case TypeCode.Boolean * TCMAX + TypeCode.Empty + Return ObjTstInt32(ToVBBool(conv1), 0) + + Case TypeCode.Empty * TCMAX + TypeCode.Boolean + Return ObjTstInt32(0, ToVBBool(conv2)) + + Case TypeCode.Int16 * TCMAX + TypeCode.Empty + Return ObjTstInt16(conv1.ToInt16(Nothing), 0) + + Case TypeCode.Empty * TCMAX + TypeCode.Int16 + Return ObjTstInt16(0, conv2.ToInt16(Nothing)) + + Case TypeCode.Int32 * TCMAX + TypeCode.Empty + Return ObjTstInt32(conv1.ToInt32(Nothing), 0) + + Case TypeCode.Empty * TCMAX + TypeCode.Int32 + Return ObjTstInt32(0, conv2.ToInt32(Nothing)) + + Case TypeCode.Int64 * TCMAX + TypeCode.Empty + Return ObjTstInt64(conv1.ToInt64(Nothing), 0) + + Case TypeCode.Empty * TCMAX + TypeCode.Int64 + Return ObjTstInt64(0, conv2.ToInt64(Nothing)) + + Case TypeCode.Single * TCMAX + TypeCode.Empty + Return ObjTstSingle(conv1.ToSingle(Nothing), 0) + + Case TypeCode.Empty * TCMAX + TypeCode.Single + Return ObjTstSingle(0, conv2.ToSingle(Nothing)) + + Case TypeCode.Double * TCMAX + TypeCode.Empty + Return ObjTstDouble(conv1.ToDouble(Nothing), 0) + + Case TypeCode.Empty * TCMAX + TypeCode.Double + Return ObjTstDouble(0, conv2.ToDouble(Nothing)) + + Case TypeCode.Decimal * TCMAX + TypeCode.Empty + Return ObjTstDecimal(conv1, 0) + + Case TypeCode.Empty * TCMAX + TypeCode.Decimal + Return ObjTstDecimal(0, conv2) + + Case TypeCode.Char * TCMAX + TypeCode.Empty + Return ObjTstChar(conv1.ToChar(Nothing), ChrW(0)) + + Case TypeCode.Empty * TCMAX + TypeCode.Char + Return ObjTstChar(ChrW(0), conv2.ToChar(Nothing)) + + Case TypeCode.DateTime * TCMAX + TypeCode.Empty + Return ObjTstDateTime(conv1.ToDateTime(Nothing), DateType.FromObject(Nothing)) + + Case TypeCode.Empty * TCMAX + TypeCode.DateTime + Return ObjTstDateTime(DateType.FromObject(Nothing), conv2.ToDateTime(Nothing)) + + Case TypeCode.Decimal * TCMAX + TypeCode.Byte, _ + TypeCode.Decimal * TCMAX + TypeCode.Int16, _ + TypeCode.Decimal * TCMAX + TypeCode.Int32, _ + TypeCode.Decimal * TCMAX + TypeCode.Int64, _ + TypeCode.Decimal * TCMAX + TypeCode.Decimal, _ + TypeCode.Int16 * TCMAX + TypeCode.Decimal, _ + TypeCode.Byte * TCMAX + TypeCode.Decimal, _ + TypeCode.Int32 * TCMAX + TypeCode.Decimal, _ + TypeCode.Int64 * TCMAX + TypeCode.Decimal + Return ObjTstDecimal(conv1, conv2) + + Case TypeCode.Boolean * TCMAX + TypeCode.Decimal + Return ObjTstDecimal(ToVBBool(conv1), conv2) + + Case TypeCode.Decimal * TCMAX + TypeCode.Boolean + Return ObjTstDecimal(conv1, ToVBBool(conv2)) + + Case TypeCode.String * TCMAX + TypeCode.Byte, _ + TypeCode.String * TCMAX + TypeCode.Int16, _ + TypeCode.String * TCMAX + TypeCode.Int32, _ + TypeCode.String * TCMAX + TypeCode.Int64, _ + TypeCode.String * TCMAX + TypeCode.Single, _ + TypeCode.String * TCMAX + TypeCode.Double, _ + TypeCode.String * TCMAX + TypeCode.Decimal + Return ObjTstString(conv1, tc1, conv2, tc2) + + Case TypeCode.Byte * TCMAX + TypeCode.String, _ + TypeCode.Int16 * TCMAX + TypeCode.String, _ + TypeCode.Int32 * TCMAX + TypeCode.String, _ + TypeCode.Int64 * TCMAX + TypeCode.String, _ + TypeCode.Single * TCMAX + TypeCode.String, _ + TypeCode.Double * TCMAX + TypeCode.String, _ + TypeCode.Decimal * TCMAX + TypeCode.String + Return ObjTstString(conv1, tc1, conv2, tc2) + + + Case TypeCode.Empty * TCMAX + TypeCode.DateTime, _ + TypeCode.DateTime * TCMAX + TypeCode.Empty + Return ObjTstDateTime(CDate(conv1.ToDateTime(Nothing)), conv2.ToDateTime(Nothing)) + + Case TypeCode.DateTime * TCMAX + TypeCode.DateTime + Return ObjTstDateTime(CDate(conv1.ToDateTime(Nothing)), conv2.ToDateTime(Nothing)) + + Case TypeCode.String * TCMAX + TypeCode.DateTime + Return ObjTstDateTime(DateType.FromString(conv1.ToString(Nothing), GetCultureInfo()), conv2.ToDateTime(Nothing)) + + Case TypeCode.DateTime * TCMAX + TypeCode.String + Return ObjTstDateTime(conv1.ToDateTime(Nothing), DateType.FromString(conv2.ToString(Nothing), GetCultureInfo())) + + Case TypeCode.String * TCMAX + TypeCode.String + Return ObjTstStringString(conv1.ToString(Nothing), conv2.ToString(Nothing), TextCompare) + + Case TypeCode.Boolean * TCMAX + TypeCode.String + Return ObjTstBoolean(conv1.ToBoolean(Nothing), BooleanType.FromString(conv2.ToString(Nothing))) + + Case TypeCode.String * TCMAX + TypeCode.Boolean + Return ObjTstBoolean(BooleanType.FromString(conv1.ToString(Nothing)), conv2.ToBoolean(Nothing)) + + Case TypeCode.String * TCMAX + TypeCode.Char, _ + TypeCode.Char * TCMAX + TypeCode.String + Return ObjTstStringString(conv1.ToString(Nothing), conv2.ToString(Nothing), TextCompare) + + Case TypeCode.Double * TCMAX + TypeCode.Byte, _ + TypeCode.Double * TCMAX + TypeCode.Int16, _ + TypeCode.Double * TCMAX + TypeCode.Int32, _ + TypeCode.Double * TCMAX + TypeCode.Int64, _ + TypeCode.Double * TCMAX + TypeCode.Single, _ + TypeCode.Double * TCMAX + TypeCode.Double, _ + TypeCode.Byte * TCMAX + TypeCode.Double, _ + TypeCode.Int16 * TCMAX + TypeCode.Double, _ + TypeCode.Int32 * TCMAX + TypeCode.Double, _ + TypeCode.Int64 * TCMAX + TypeCode.Double, _ + TypeCode.Single * TCMAX + TypeCode.Double, _ + TypeCode.Double * TCMAX + TypeCode.Decimal, _ + TypeCode.Decimal * TCMAX + TypeCode.Double + Return ObjTstDouble(conv1.ToDouble(Nothing), conv2.ToDouble(Nothing)) + + Case TypeCode.Double * TCMAX + TypeCode.Boolean + Return ObjTstDouble(conv1.ToDouble(Nothing), ToVBBool(conv2)) + + Case TypeCode.Boolean * TCMAX + TypeCode.Double + Return ObjTstDouble(ToVBBool(conv1), conv2.ToDouble(Nothing)) + + Case TypeCode.Single * TCMAX + TypeCode.Byte, _ + TypeCode.Single * TCMAX + TypeCode.Int16, _ + TypeCode.Single * TCMAX + TypeCode.Int32, _ + TypeCode.Single * TCMAX + TypeCode.Int64, _ + TypeCode.Single * TCMAX + TypeCode.Single, _ + TypeCode.Byte * TCMAX + TypeCode.Single, _ + TypeCode.Int16 * TCMAX + TypeCode.Single, _ + TypeCode.Int32 * TCMAX + TypeCode.Single, _ + TypeCode.Int64 * TCMAX + TypeCode.Single, _ + TypeCode.Decimal * TCMAX + TypeCode.Single, _ + TypeCode.Single * TCMAX + TypeCode.Decimal + Return ObjTstSingle(conv1.ToSingle(Nothing), conv2.ToSingle(Nothing)) + + Case TypeCode.Single * TCMAX + TypeCode.Boolean + Return ObjTstSingle(conv1.ToSingle(Nothing), ToVBBool(conv2)) + + Case TypeCode.Boolean * TCMAX + TypeCode.Single + Return ObjTstSingle(ToVBBool(conv1), conv2.ToSingle(Nothing)) + + Case TypeCode.Byte * TCMAX + TypeCode.Int64, _ + TypeCode.Int64 * TCMAX + TypeCode.Byte, _ + TypeCode.Int64 * TCMAX + TypeCode.Int16, _ + TypeCode.Int64 * TCMAX + TypeCode.Int32, _ + TypeCode.Int64 * TCMAX + TypeCode.Int64, _ + TypeCode.Int16 * TCMAX + TypeCode.Int64, _ + TypeCode.Int32 * TCMAX + TypeCode.Int64 + Return ObjTstInt64(conv1.ToInt64(Nothing), conv2.ToInt64(Nothing)) + + Case TypeCode.Int64 * TCMAX + TypeCode.Boolean + Return ObjTstInt64(conv1.ToInt64(Nothing), ToVBBool(conv2)) + + Case TypeCode.Boolean * TCMAX + TypeCode.Int64 + Return ObjTstInt64(ToVBBool(conv1), conv2.ToInt64(Nothing)) + + Case TypeCode.Int16 * TCMAX + TypeCode.Int32, _ + TypeCode.Int32 * TCMAX + TypeCode.Int16, _ + TypeCode.Int32 * TCMAX + TypeCode.Int32, _ + TypeCode.Int32 * TCMAX + TypeCode.Byte, _ + TypeCode.Byte * TCMAX + TypeCode.Int32 + Return ObjTstInt32(conv1.ToInt32(Nothing), conv2.ToInt32(Nothing)) + + Case TypeCode.Int32 * TCMAX + TypeCode.Boolean + Return ObjTstInt32(conv1.ToInt32(Nothing), ToVBBool(conv2)) + + Case TypeCode.Boolean * TCMAX + TypeCode.Int32 + Return ObjTstInt32(ToVBBool(conv1), conv2.ToInt32(Nothing)) + + Case TypeCode.Int16 * TCMAX + TypeCode.Byte, _ + TypeCode.Int16 * TCMAX + TypeCode.Int16, _ + TypeCode.Byte * TCMAX + TypeCode.Int16 + Return ObjTstInt16(conv1.ToInt16(Nothing), conv2.ToInt16(Nothing)) + + Case TypeCode.Boolean * TCMAX + TypeCode.Byte, _ + TypeCode.Boolean * TCMAX + TypeCode.Int16 + Return ObjTstInt16(CShort(ToVBBool(conv1)), conv2.ToInt16(Nothing)) + + Case TypeCode.Byte * TCMAX + TypeCode.Boolean, _ + TypeCode.Int16 * TCMAX + TypeCode.Boolean + Return ObjTstInt16(conv1.ToInt16(Nothing), CShort(ToVBBool(conv2))) + + Case TypeCode.Boolean * TCMAX + TypeCode.Boolean + Return ObjTstInt16(CShort(ToVBBool(conv1)), CShort(ToVBBool(conv2))) + + Case TypeCode.Byte * TCMAX + TypeCode.Byte + Return ObjTstByte(conv1.ToByte(Nothing), conv2.ToByte(Nothing)) + + Case TypeCode.Char * TCMAX + TypeCode.Char + Return ObjTstChar(conv1.ToChar(Nothing), conv2.ToChar(Nothing)) + + Case Else + + End Select + + Throw GetNoValidOperatorException(o1, o2) + + End Function + + Private Shared Function ObjTstDateTime(ByVal var1 As DateTime, ByVal var2 As DateTime) As Integer + + Dim ticks1, ticks2 As Int64 + + ticks1 = var1.Ticks + ticks2 = var2.Ticks + + If ticks1 < ticks2 Then + Return -1 + ElseIf ticks1 > ticks2 Then + Return 1 + End If + Return 0 + End Function + + Private Shared Function ObjTstBoolean(ByVal b1 As Boolean, ByVal b2 As Boolean) As Integer + If b1 = b2 Then + Return 0 + ElseIf b1 > b2 Then + Return 1 + Else + Return -1 + End If + End Function + + Private Shared Function ObjTstDouble(ByVal d1 As Double, ByVal d2 As Double) As Integer + If d1 < d2 Then + Return -1 + ElseIf d1 > d2 Then + Return 1 + End If + Return 0 + End Function + + Private Shared Function ObjTstChar(ByVal ch1 As Char, ByVal ch2 As Char) As Integer + If ch1 < ch2 Then + Return -1 + ElseIf ch1 > ch2 Then + Return 1 + End If + Return 0 + End Function + + Private Shared Function ObjTstByte(ByVal by1 As Byte, ByVal by2 As Byte) As Integer + If by1 < by2 Then + Return -1 + ElseIf by1 > by2 Then + Return 1 + End If + Return 0 + End Function + + Private Shared Function ObjTstSingle(ByVal d1 As Single, ByVal d2 As Single) As Integer + If d1 < d2 Then + Return -1 + ElseIf d1 > d2 Then + Return 1 + End If + Return 0 + End Function + + Private Shared Function ObjTstInt16(ByVal d1 As Int16, ByVal d2 As Int16) As Integer + If d1 < d2 Then + Return -1 + ElseIf d1 > d2 Then + Return 1 + End If + Return 0 + End Function + + Private Shared Function ObjTstInt32(ByVal d1 As Int32, ByVal d2 As Int32) As Integer + If d1 < d2 Then + Return -1 + ElseIf d1 > d2 Then + Return 1 + End If + Return 0 + End Function + + Private Shared Function ObjTstInt64(ByVal d1 As Int64, ByVal d2 As Int64) As Integer + If d1 < d2 Then + Return -1 + ElseIf d1 > d2 Then + Return 1 + End If + Return 0 + End Function + + 'This function takes IConvertible because the JIT does not behave properly with Decimal temps + Private Shared Function ObjTstDecimal(ByVal i1 As IConvertible, ByVal i2 As IConvertible) As Integer + Dim d1, d2 As Decimal + d1 = i1.ToDecimal(Nothing) + d2 = i2.ToDecimal(Nothing) + If d1 < d2 Then + Return -1 + ElseIf d1 > d2 Then + Return 1 + End If + Return 0 + End Function + + Private Shared Function ObjTstString(ByVal conv1 As IConvertible, ByVal tc1 As TypeCode, ByVal conv2 As IConvertible, ByVal tc2 As TypeCode) As Integer + Dim dbl1, dbl2 As Double + + If tc1 = TypeCode.String Then + dbl1 = DoubleType.FromString(conv1.ToString(Nothing)) + ElseIf tc1 = TypeCode.Boolean Then + dbl1 = ToVBBool(conv1) + Else + dbl1 = conv1.ToDouble(Nothing) + End If + + If tc2 = TypeCode.String Then + dbl2 = DoubleType.FromString(conv2.ToString(Nothing)) + ElseIf tc2 = TypeCode.Boolean Then + dbl2 = ToVBBool(conv2) + Else + dbl2 = conv2.ToDouble(Nothing) + End If + + Return ObjTstDouble(dbl1, dbl2) + End Function + + Private Shared Function ObjTstStringString(ByVal s1 As String, ByVal s2 As String, ByVal TextCompare As Boolean) As Integer + + If s1 Is Nothing Then + If s2.Length() > 0 Then + Return -1 + Else + Return 0 + End If + ElseIf s2 Is Nothing Then + If s1.Length() > 0 Then + Return 1 + Else + Return 0 + End If + Else + If TextCompare Then + Return GetCultureInfo().CompareInfo.Compare(s1, s2, OptionCompareTextFlags) + Else + Return System.String.CompareOrdinal(s1, s2) + End If + End If + + End Function + + ' Plus ( +x ) + Public Shared Function PlusObj(ByVal obj As Object) As Object + + If obj Is Nothing Then + Return +0I + End If + + Dim conv As IConvertible + Dim typ As TypeCode + + conv = TryCast(obj, IConvertible) + + If conv Is Nothing Then + If obj Is Nothing Then + typ = TypeCode.Empty + Else + typ = TypeCode.Object + End If + Else + typ = conv.GetTypeCode() + End If + + + Select Case typ + + Case TypeCode.Boolean + If TypeOf obj Is Boolean Then + Return CShort(DirectCast(obj, Boolean)) + Else + Return CShort(conv.ToBoolean(Nothing)) + End If + + Case TypeCode.Byte, _ + TypeCode.Int16, _ + TypeCode.Int32, _ + TypeCode.Int64, _ + TypeCode.Decimal, _ + TypeCode.Single, _ + TypeCode.Double + Return obj + + Case TypeCode.String + Return DoubleType.FromObject(obj) + + Case TypeCode.Empty + Return CInt(0) + + Case TypeCode.Char + ' Fall through to error + + Case TypeCode.DateTime + ' Fall through to error + + Case Else + ' Fall through to error + End Select + + Throw GetNoValidOperatorException(obj) + End Function + + ' Negation ( -x ) + Public Shared Function NegObj(ByVal obj As Object) As Object + + Dim conv As IConvertible + Dim tc As TypeCode + + conv = TryCast(obj, IConvertible) + + If conv Is Nothing Then + If obj Is Nothing Then + tc = TypeCode.Empty + Else + tc = TypeCode.Object + End If + Else + tc = conv.GetTypeCode() + End If + + Return InternalNegObj(obj, conv, tc) + + End Function + + + Private Shared Function InternalNegObj(ByVal obj As Object, ByVal conv As IConvertible, ByVal tc As TypeCode) As Object + + Dim Int16Result As Int16 + Dim Int32Result As Int32 + Dim Int64Result As Int64 + Dim DecimalResult As Decimal + Dim DoubleResult As Double + + Select Case tc + + Case TypeCode.Empty + Return -0I + + Case TypeCode.Boolean + If TypeOf obj Is Boolean Then + Int16Result = -CShort(DirectCast(obj, Boolean)) + Else + Int16Result = -CShort(conv.ToBoolean(Nothing)) + End If + GoTo Int16Exit + + Case TypeCode.Byte + If TypeOf obj Is Byte Then + Int16Result = -CType(DirectCast(obj, Byte), Int16) + Else + Int16Result = -CType(conv.ToByte(Nothing), Int16) + End If + GoTo Int16Exit + + Case TypeCode.Int16 + If TypeOf obj Is Int16 Then + Int32Result = -CType(DirectCast(obj, Int16), Int32) + Else + Int32Result = -CType(conv.ToInt16(Nothing), Int32) + End If + GoTo Int32Int16Exit + + Case TypeCode.Int32 + If TypeOf obj Is Int32 Then + Int64Result = -CType(DirectCast(obj, Int32), Int64) + Else + Int64Result = -CType(conv.ToInt32(Nothing), Int64) + End If + GoTo Int64Int32Exit + + Case TypeCode.Int64 + 'Using try/catch instead of check with MinValue + ' since the overflow case should be very rare + ' and a compare would be a big cost for the normal case + Try + If TypeOf obj Is Int64 Then + Int64Result = -DirectCast(obj, Int64) + Else + Int64Result = -conv.ToInt64(Nothing) + End If + GoTo Int64Exit + Catch ex As StackOverflowException + Throw ex + Catch ex As OutOfMemoryException + Throw ex + Catch ex As System.Threading.ThreadAbortException + Throw ex + Catch + DecimalResult = -conv.ToDecimal(Nothing) + GoTo DecimalExit + End Try + + Case TypeCode.Decimal + 'Using try/catch instead of check with MinValue + ' since the overflow case should be very rare + ' and a compare would be a big cost for the normal case + Try + If TypeOf obj Is Decimal Then + DecimalResult = -DirectCast(obj, Decimal) + Else + DecimalResult = -conv.ToDecimal(Nothing) + End If + Return DecimalResult + Catch ex As StackOverflowException + Throw ex + Catch ex As OutOfMemoryException + Throw ex + Catch ex As System.Threading.ThreadAbortException + Throw ex + Catch + DoubleResult = -conv.ToDouble(Nothing) + GoTo DoubleExit + End Try + + Case TypeCode.Single + If TypeOf obj Is Single Then + Return -DirectCast(obj, Single) + Else + Return -conv.ToSingle(Nothing) + End If + + Case TypeCode.Double + If TypeOf obj Is Double Then + DoubleResult = -DirectCast(obj, Double) + Else + DoubleResult = -conv.ToDouble(Nothing) + End If + + GoTo DoubleExit + + Case TypeCode.String + Dim ObjString As String = TryCast(obj, String) + + If ObjString IsNot Nothing Then + DoubleResult = -DoubleType.FromString(ObjString) + Else + DoubleResult = -DoubleType.FromString(conv.ToString(Nothing)) + End If + GoTo DoubleExit + + Case TypeCode.Char + ' Fall through to error + + Case TypeCode.DateTime + ' Fall through to error + + Case Else + ' Fall through to error + + End Select + + Throw GetNoValidOperatorException(obj) + + Exit Function + +Int16ByteExit: + '- Byte can only be zero or negative, so the OrElse is unnecessary + Diagnostics.Debug.Assert(Int16Result <= 0, "Invalid result") + If Int16Result < System.Byte.MinValue Then 'OrElse Int16Result > System.Byte.MaxValue Then + Return Int16Result + End If + Return CType(Int16Result, Byte) + +Int32Int16Exit: + If Int32Result < System.Int16.MinValue OrElse Int32Result > System.Int16.MaxValue Then + Return Int32Result + End If + Return CType(Int32Result, Int16) + +Int64Int32Exit: + If Int64Result < System.Int32.MinValue OrElse Int64Result > System.Int32.MaxValue Then + Return Int64Result + End If + Return CType(Int64Result, Int32) + +Int16Exit: + Return Int16Result + +Int32Exit: + Return Int32Result + +Int64Exit: + Return Int64Result + +DoubleExit: + Return DoubleResult + +DecimalExit: + Return DecimalResult + End Function + + ' NotObj performs a BitNot or Not, depending on the contained type + ' Binary Not (BitNot x) + ' Logical not (Not x) + Public Shared Function NotObj(ByVal obj As Object) As Object + + Dim byteValue As Byte + Dim int16Value As Int16 + Dim int32Value As Int32 + Dim int64Value As Int64 + Dim Type1 As Type + Dim iconv As IConvertible + Dim TypeCode1 As TypeCode + + If obj Is Nothing Then + Return (Not 0I) + End If + + iconv = TryCast(obj, IConvertible) + + If Not iconv Is Nothing Then + TypeCode1 = iconv.GetTypeCode() + Else + TypeCode1 = TypeCode.Object + End If + + Select Case TypeCode1 + + Case TypeCode.Boolean + Return Not iconv.ToBoolean(Nothing) + + Case TypeCode.Byte + Type1 = obj.GetType() + byteValue = Not iconv.ToByte(Nothing) + If Type1.IsEnum Then + Return System.Enum.ToObject(Type1, byteValue) + End If + Return byteValue + + Case TypeCode.Int16 + Type1 = obj.GetType() + int16Value = Not iconv.ToInt16(Nothing) + If Type1.IsEnum Then + Return System.Enum.ToObject(Type1, int16Value) + End If + Return int16Value + + Case TypeCode.Int32 + Type1 = obj.GetType() + int32Value = Not iconv.ToInt32(Nothing) + If Type1.IsEnum Then + Return System.Enum.ToObject(Type1, int32Value) + End If + Return int32Value + + Case TypeCode.Int64 + Type1 = obj.GetType() + int64Value = Not iconv.ToInt64(Nothing) + If Type1.IsEnum Then + Return System.Enum.ToObject(Type1, int64Value) + End If + Return int64Value + + Case TypeCode.Decimal + Return Not CType(iconv.ToDecimal(Nothing), Int64) + + Case TypeCode.Single + Return Not CType(iconv.ToDecimal(Nothing), Int64) + + Case TypeCode.Double + Return Not CType(iconv.ToDecimal(Nothing), Int64) + + Case TypeCode.String + Return Not LongType.FromString(iconv.ToString(Nothing)) + + Case TypeCode.Char + ' Fall through to error + + Case TypeCode.DateTime + ' Fall through to error + + Case Else + ' Fall through to error + + End Select + + Throw GetNoValidOperatorException(obj) + + End Function + + ' Binary And (BitAnd x) + Public Shared Function BitAndObj(ByVal obj1 As Object, ByVal obj2 As Object) As Object + + If obj1 Is Nothing AndAlso obj2 Is Nothing Then + Return 0I + End If + + Dim Type1 As Type = Nothing + Dim Type2 As Type = Nothing + + Dim Type1IsEnum, Type2IsEnum As Boolean + + If Not obj1 Is Nothing Then + Type1 = obj1.GetType() + Type1IsEnum = Type1.IsEnum() + End If + If Not obj2 Is Nothing Then + Type2 = obj2.GetType() + Type2IsEnum = Type2.IsEnum() + End If + + Select Case GetWidestType(obj1, obj2) + + Case TypeCode.Boolean + If Type1 Is Type2 Then + 'Both Boolean + Return BooleanType.FromObject(obj1) And BooleanType.FromObject(obj2) + Else + Return ShortType.FromObject(obj1) And ShortType.FromObject(obj2) + End If + + Case TypeCode.Byte + Dim Result As Byte = ByteType.FromObject(obj1) And ByteType.FromObject(obj2) + + If ((Type1IsEnum AndAlso Type2IsEnum) AndAlso (Not (Type1 Is Type2))) OrElse _ + (Not (Type1IsEnum AndAlso Type2IsEnum)) Then + Return Result + ElseIf Type1IsEnum Then + Return System.Enum.ToObject(Type1, Result) + ElseIf Type2IsEnum Then + Return System.Enum.ToObject(Type2, Result) + End If + Case TypeCode.Int16 + Dim Result As Short = ShortType.FromObject(obj1) And ShortType.FromObject(obj2) + + If ((Type1IsEnum AndAlso Type2IsEnum) AndAlso (Not (Type1 Is Type2))) OrElse _ + (Not (Type1IsEnum AndAlso Type2IsEnum)) Then + Return Result + ElseIf Type1IsEnum Then + Return System.Enum.ToObject(Type1, Result) + ElseIf Type2IsEnum Then + Return System.Enum.ToObject(Type2, Result) + End If + Case TypeCode.Int32 + Dim Result As Integer = IntegerType.FromObject(obj1) And IntegerType.FromObject(obj2) + + If ((Type1IsEnum AndAlso Type2IsEnum) AndAlso (Not (Type1 Is Type2))) OrElse _ + (Not (Type1IsEnum AndAlso Type2IsEnum)) Then + Return Result + ElseIf Type1IsEnum Then + Return System.Enum.ToObject(Type1, Result) + ElseIf Type2IsEnum Then + Return System.Enum.ToObject(Type2, Result) + End If + Case TypeCode.Int64 + Dim Result As Long = LongType.FromObject(obj1) And LongType.FromObject(obj2) + + If ((Type1IsEnum AndAlso Type2IsEnum) AndAlso (Not (Type1 Is Type2))) OrElse _ + (Not (Type1IsEnum AndAlso Type2IsEnum)) Then + Return Result + ElseIf Type1IsEnum Then + Return System.Enum.ToObject(Type1, Result) + ElseIf Type2IsEnum Then + Return System.Enum.ToObject(Type2, Result) + End If + + Case TypeCode.Decimal, _ + TypeCode.Single, _ + TypeCode.Double, _ + TypeCode.String + Return LongType.FromObject(obj1) And LongType.FromObject(obj2) + + Case TypeCode.Char + ' Fall through to error + + Case TypeCode.DateTime + ' Fall through to error + + Case Else + ' Fall through to error + + End Select + + Throw GetNoValidOperatorException(obj1, obj2) + + End Function + + + + ' Binary OR (BitOr x) + Public Shared Function BitOrObj(ByVal obj1 As Object, ByVal obj2 As Object) As Object + + If obj1 Is Nothing AndAlso obj2 Is Nothing Then + Return 0I + End If + + Dim Type1 As Type = Nothing + Dim Type2 As Type = Nothing + + Dim Type1IsEnum, Type2IsEnum As Boolean + + If Not obj1 Is Nothing Then + Type1 = obj1.GetType() + Type1IsEnum = Type1.IsEnum() + End If + If Not obj2 Is Nothing Then + Type2 = obj2.GetType() + Type2IsEnum = Type2.IsEnum() + End If + + Select Case GetWidestType(obj1, obj2) + + Case TypeCode.Boolean + If Type1 Is Type2 Then + 'Both Boolean + Return BooleanType.FromObject(obj1) Or BooleanType.FromObject(obj2) + Else + Return ShortType.FromObject(obj1) Or ShortType.FromObject(obj2) + End If + + Case TypeCode.Byte + Dim Result As Byte = ByteType.FromObject(obj1) Or ByteType.FromObject(obj2) + + If ((Type1IsEnum AndAlso Type2IsEnum) AndAlso (Not (Type1 Is Type2))) OrElse _ + (Not (Type1IsEnum AndAlso Type2IsEnum)) Then + Return Result + ElseIf Type1IsEnum Then + Return System.Enum.ToObject(Type1, Result) + ElseIf Type2IsEnum Then + Return System.Enum.ToObject(Type2, Result) + End If + Case TypeCode.Int16 + Dim Result As Short = ShortType.FromObject(obj1) Or ShortType.FromObject(obj2) + + If ((Type1IsEnum AndAlso Type2IsEnum) AndAlso (Not (Type1 Is Type2))) OrElse _ + (Not (Type1IsEnum AndAlso Type2IsEnum)) Then + Return Result + ElseIf Type1IsEnum Then + Return System.Enum.ToObject(Type1, Result) + ElseIf Type2IsEnum Then + Return System.Enum.ToObject(Type2, Result) + End If + Case TypeCode.Int32 + Dim Result As Integer = IntegerType.FromObject(obj1) Or IntegerType.FromObject(obj2) + + If ((Type1IsEnum AndAlso Type2IsEnum) AndAlso (Not (Type1 Is Type2))) OrElse _ + (Not (Type1IsEnum AndAlso Type2IsEnum)) Then + Return Result + ElseIf Type1IsEnum Then + Return System.Enum.ToObject(Type1, Result) + ElseIf Type2IsEnum Then + Return System.Enum.ToObject(Type2, Result) + End If + Case TypeCode.Int64 + Dim Result As Long = LongType.FromObject(obj1) Or LongType.FromObject(obj2) + + If ((Type1IsEnum AndAlso Type2IsEnum) AndAlso (Not (Type1 Is Type2))) OrElse _ + (Not (Type1IsEnum AndAlso Type2IsEnum)) Then + Return Result + ElseIf Type1IsEnum Then + Return System.Enum.ToObject(Type1, Result) + ElseIf Type2IsEnum Then + Return System.Enum.ToObject(Type2, Result) + End If + + Case TypeCode.Decimal, _ + TypeCode.Single, _ + TypeCode.Double, _ + TypeCode.String + Return LongType.FromObject(obj1) Or LongType.FromObject(obj2) + + Case TypeCode.Char + ' Fall through to error + + Case TypeCode.DateTime + ' Fall through to error + + Case Else + ' Fall through to error + + End Select + + Throw GetNoValidOperatorException(obj1, obj2) + + End Function + + + + ' Binary Xor (BitXor x) + Public Shared Function BitXorObj(ByVal obj1 As Object, ByVal obj2 As Object) As Object + + If obj1 Is Nothing AndAlso obj2 Is Nothing Then + Return 0I + End If + + Dim Type1 As Type = Nothing + Dim Type2 As Type = Nothing + + Dim Type1IsEnum, Type2IsEnum As Boolean + + If Not obj1 Is Nothing Then + Type1 = obj1.GetType() + Type1IsEnum = Type1.IsEnum() + End If + If Not obj2 Is Nothing Then + Type2 = obj2.GetType() + Type2IsEnum = Type2.IsEnum() + End If + + Select Case GetWidestType(obj1, obj2) + + Case TypeCode.Boolean + If Type1 Is Type2 Then + 'Both Boolean + Return BooleanType.FromObject(obj1) Xor BooleanType.FromObject(obj2) + Else + Return ShortType.FromObject(obj1) Xor ShortType.FromObject(obj2) + End If + + Case TypeCode.Byte + Dim Result As Byte = ByteType.FromObject(obj1) Xor ByteType.FromObject(obj2) + + If ((Type1IsEnum AndAlso Type2IsEnum) AndAlso (Not (Type1 Is Type2))) OrElse _ + (Not (Type1IsEnum AndAlso Type2IsEnum)) Then + Return Result + ElseIf Type1IsEnum Then + Return System.Enum.ToObject(Type1, Result) + ElseIf Type2IsEnum Then + Return System.Enum.ToObject(Type2, Result) + End If + + Case TypeCode.Int16 + Dim Result As Short = ShortType.FromObject(obj1) Xor ShortType.FromObject(obj2) + + If ((Type1IsEnum AndAlso Type2IsEnum) AndAlso (Not (Type1 Is Type2))) OrElse _ + (Not (Type1IsEnum AndAlso Type2IsEnum)) Then + Return Result + ElseIf Type1IsEnum Then + Return System.Enum.ToObject(Type1, Result) + ElseIf Type2IsEnum Then + Return System.Enum.ToObject(Type2, Result) + End If + + Case TypeCode.Int32 + Dim Result As Integer = IntegerType.FromObject(obj1) Xor IntegerType.FromObject(obj2) + + If ((Type1IsEnum AndAlso Type2IsEnum) AndAlso (Not (Type1 Is Type2))) OrElse _ + (Not (Type1IsEnum AndAlso Type2IsEnum)) Then + Return Result + ElseIf Type1IsEnum Then + Return System.Enum.ToObject(Type1, Result) + ElseIf Type2IsEnum Then + Return System.Enum.ToObject(Type2, Result) + End If + + Case TypeCode.Int64 + Dim Result As Long = LongType.FromObject(obj1) Xor LongType.FromObject(obj2) + + If ((Type1IsEnum AndAlso Type2IsEnum) AndAlso (Not (Type1 Is Type2))) OrElse _ + (Not (Type1IsEnum AndAlso Type2IsEnum)) Then + Return Result + ElseIf Type1IsEnum Then + Return System.Enum.ToObject(Type1, Result) + ElseIf Type2IsEnum Then + Return System.Enum.ToObject(Type2, Result) + End If + + Case TypeCode.Decimal, _ + TypeCode.Single, _ + TypeCode.Double, _ + TypeCode.String + Return LongType.FromObject(obj1) Xor LongType.FromObject(obj2) + + Case TypeCode.Char + ' Fall through to error + Case TypeCode.DateTime + ' Fall through to error + Case Else + ' Fall through to error + + End Select + + Throw GetNoValidOperatorException(obj1, obj2) + + End Function + + + + Public Shared Function AddObj(ByVal o1 As Object, ByVal o2 As Object) As Object + Dim conv1, conv2 As IConvertible + Dim tc1, tc2 As TypeCode + + conv1 = TryCast(o1, IConvertible) + + If conv1 Is Nothing Then + If o1 Is Nothing Then + tc1 = TypeCode.Empty + Else + tc1 = TypeCode.Object + End If + Else + tc1 = conv1.GetTypeCode() + End If + + conv2 = TryCast(o2, IConvertible) + + If conv2 Is Nothing Then + If o2 Is Nothing Then + tc2 = TypeCode.Empty + Else + tc2 = TypeCode.Object + End If + Else + tc2 = conv2.GetTypeCode() + End If + + 'Special cases for Char() + If (tc1 = TypeCode.Object) AndAlso (TypeOf o1 Is Char()) Then + If tc2 = TypeCode.String OrElse tc2 = TypeCode.Empty OrElse ((tc2 = TypeCode.Object) AndAlso (TypeOf o2 Is Char())) Then + 'Treat Char() as String for these cases + o1 = CStr(CharArrayType.FromObject(o1)) + conv1 = CType(o1, IConvertible) + tc1 = TypeCode.String + End If + End If + + If (tc2 = TypeCode.Object) AndAlso (TypeOf o2 Is Char()) Then + If tc1 = TypeCode.String OrElse tc1 = TypeCode.Empty Then + o2 = CStr(CharArrayType.FromObject(o2)) + conv2 = DirectCast(o2, IConvertible) + tc2 = TypeCode.String + End If + End If + + Select Case tc1 * TCMAX + tc2 + + 'STRING + Case TypeCode.String * TCMAX + TypeCode.Empty, _ + TypeCode.String * TCMAX + TypeCode.DBNull + Return o1 + + Case TypeCode.Empty * TCMAX + TypeCode.String, _ + TypeCode.DBNull * TCMAX + TypeCode.String + Return o2 + + Case TypeCode.String * TCMAX + TypeCode.Byte, _ + TypeCode.String * TCMAX + TypeCode.Int16, _ + TypeCode.String * TCMAX + TypeCode.Int32, _ + TypeCode.String * TCMAX + TypeCode.Int64, _ + TypeCode.String * TCMAX + TypeCode.Single, _ + TypeCode.String * TCMAX + TypeCode.Double, _ + TypeCode.String * TCMAX + TypeCode.Decimal + Return AddString(conv1, tc1, conv2, tc2) + + Case TypeCode.Byte * TCMAX + TypeCode.String, _ + TypeCode.Int16 * TCMAX + TypeCode.String, _ + TypeCode.Int32 * TCMAX + TypeCode.String, _ + TypeCode.Int64 * TCMAX + TypeCode.String, _ + TypeCode.Single * TCMAX + TypeCode.String, _ + TypeCode.Double * TCMAX + TypeCode.String, _ + TypeCode.Decimal * TCMAX + TypeCode.String + Return AddString(conv1, tc1, conv2, tc2) + + Case TypeCode.String * TCMAX + TypeCode.String, _ + TypeCode.String * TCMAX + TypeCode.Char, _ + TypeCode.String * TCMAX + TypeCode.DateTime, _ + TypeCode.Char * TCMAX + TypeCode.String, _ + TypeCode.Char * TCMAX + TypeCode.Char, _ + TypeCode.DateTime * TCMAX + TypeCode.DateTime, _ + TypeCode.DateTime * TCMAX + TypeCode.String + Return StringType.FromObject(o1) + StringType.FromObject(o2) + + Case TypeCode.Boolean * TCMAX + TypeCode.String, _ + TypeCode.String * TCMAX + TypeCode.Boolean + Return AddString(conv1, tc1, conv2, tc2) + + 'EMPTY + Case TypeCode.Empty * TCMAX + TypeCode.Empty + Return CInt(0) + + Case TypeCode.Boolean * TCMAX + TypeCode.Empty, _ + TypeCode.Byte * TCMAX + TypeCode.Empty, _ + TypeCode.Int16 * TCMAX + TypeCode.Empty, _ + TypeCode.Int32 * TCMAX + TypeCode.Empty, _ + TypeCode.Int64 * TCMAX + TypeCode.Empty, _ + TypeCode.Single * TCMAX + TypeCode.Empty, _ + TypeCode.Double * TCMAX + TypeCode.Empty, _ + TypeCode.Decimal * TCMAX + TypeCode.Empty + Return o1 + + Case TypeCode.Empty * TCMAX + TypeCode.Boolean, _ + TypeCode.Empty * TCMAX + TypeCode.Byte, _ + TypeCode.Empty * TCMAX + TypeCode.Int16, _ + TypeCode.Empty * TCMAX + TypeCode.Int32, _ + TypeCode.Empty * TCMAX + TypeCode.Int64, _ + TypeCode.Empty * TCMAX + TypeCode.Single, _ + TypeCode.Empty * TCMAX + TypeCode.Double, _ + TypeCode.Empty * TCMAX + TypeCode.Decimal + Return o2 + + Case TypeCode.Decimal * TCMAX + TypeCode.Byte, _ + TypeCode.Decimal * TCMAX + TypeCode.Int16, _ + TypeCode.Decimal * TCMAX + TypeCode.Int32, _ + TypeCode.Decimal * TCMAX + TypeCode.Int64, _ + TypeCode.Decimal * TCMAX + TypeCode.Decimal, _ + TypeCode.Int16 * TCMAX + TypeCode.Decimal, _ + TypeCode.Byte * TCMAX + TypeCode.Decimal, _ + TypeCode.Int32 * TCMAX + TypeCode.Decimal, _ + TypeCode.Int64 * TCMAX + TypeCode.Decimal + Return AddDecimal(conv1, conv2) + + Case TypeCode.Boolean * TCMAX + TypeCode.Decimal + Return AddDecimal(ToVBBoolConv(conv1), conv2) + + Case TypeCode.Decimal * TCMAX + TypeCode.Boolean + Return AddDecimal(conv1, ToVBBoolConv(conv2)) + + Case TypeCode.Double * TCMAX + TypeCode.Byte, _ + TypeCode.Double * TCMAX + TypeCode.Int16, _ + TypeCode.Double * TCMAX + TypeCode.Int32, _ + TypeCode.Double * TCMAX + TypeCode.Int64, _ + TypeCode.Double * TCMAX + TypeCode.Single, _ + TypeCode.Double * TCMAX + TypeCode.Double, _ + TypeCode.Byte * TCMAX + TypeCode.Double, _ + TypeCode.Int16 * TCMAX + TypeCode.Double, _ + TypeCode.Int32 * TCMAX + TypeCode.Double, _ + TypeCode.Int64 * TCMAX + TypeCode.Double, _ + TypeCode.Single * TCMAX + TypeCode.Double, _ + TypeCode.Double * TCMAX + TypeCode.Decimal, _ + TypeCode.Decimal * TCMAX + TypeCode.Double + Return AddDouble(conv1.ToDouble(Nothing), conv2.ToDouble(Nothing)) + + Case TypeCode.Double * TCMAX + TypeCode.Boolean + Return AddDouble(conv1.ToDouble(Nothing), ToVBBool(conv2)) + + Case TypeCode.Boolean * TCMAX + TypeCode.Double + Return AddDouble(ToVBBool(conv1), conv2.ToDouble(Nothing)) + + Case TypeCode.Single * TCMAX + TypeCode.Byte, _ + TypeCode.Single * TCMAX + TypeCode.Int16, _ + TypeCode.Single * TCMAX + TypeCode.Int32, _ + TypeCode.Single * TCMAX + TypeCode.Int64, _ + TypeCode.Single * TCMAX + TypeCode.Single, _ + TypeCode.Byte * TCMAX + TypeCode.Single, _ + TypeCode.Int16 * TCMAX + TypeCode.Single, _ + TypeCode.Int32 * TCMAX + TypeCode.Single, _ + TypeCode.Int64 * TCMAX + TypeCode.Single, _ + TypeCode.Decimal * TCMAX + TypeCode.Single, _ + TypeCode.Single * TCMAX + TypeCode.Decimal + Return AddSingle(conv1.ToSingle(Nothing), conv2.ToSingle(Nothing)) + + Case TypeCode.Single * TCMAX + TypeCode.Boolean + Return AddSingle(conv1.ToSingle(Nothing), ToVBBool(conv2)) + + Case TypeCode.Boolean * TCMAX + TypeCode.Single + Return AddSingle(ToVBBool(conv1), conv2.ToSingle(Nothing)) + + Case TypeCode.Byte * TCMAX + TypeCode.Int64, _ + TypeCode.Int64 * TCMAX + TypeCode.Byte, _ + TypeCode.Int64 * TCMAX + TypeCode.Int16, _ + TypeCode.Int64 * TCMAX + TypeCode.Int32, _ + TypeCode.Int64 * TCMAX + TypeCode.Int64, _ + TypeCode.Int16 * TCMAX + TypeCode.Int64, _ + TypeCode.Int32 * TCMAX + TypeCode.Int64 + Return AddInt64(conv1.ToInt64(Nothing), conv2.ToInt64(Nothing)) + + Case TypeCode.Int64 * TCMAX + TypeCode.Boolean + Return AddInt64(conv1.ToInt64(Nothing), ToVBBool(conv2)) + + Case TypeCode.Boolean * TCMAX + TypeCode.Int64 + Return AddInt64(ToVBBool(conv1), conv2.ToInt64(Nothing)) + + Case TypeCode.Int16 * TCMAX + TypeCode.Int32, _ + TypeCode.Int32 * TCMAX + TypeCode.Int16, _ + TypeCode.Int32 * TCMAX + TypeCode.Int32, _ + TypeCode.Int32 * TCMAX + TypeCode.Byte, _ + TypeCode.Byte * TCMAX + TypeCode.Int32 + Return AddInt32(conv1.ToInt32(Nothing), conv2.ToInt32(Nothing)) + + Case TypeCode.Int32 * TCMAX + TypeCode.Boolean + Return AddInt32(conv1.ToInt32(Nothing), ToVBBool(conv2)) + + Case TypeCode.Boolean * TCMAX + TypeCode.Int32 + Return AddInt32(ToVBBool(conv1), conv2.ToInt32(Nothing)) + + Case TypeCode.Int16 * TCMAX + TypeCode.Byte, _ + TypeCode.Int16 * TCMAX + TypeCode.Int16, _ + TypeCode.Byte * TCMAX + TypeCode.Int16 + Return AddInt16(conv1.ToInt16(Nothing), conv2.ToInt16(Nothing)) + + Case TypeCode.Boolean * TCMAX + TypeCode.Byte, _ + TypeCode.Boolean * TCMAX + TypeCode.Int16 + Return AddInt16(CShort(ToVBBool(conv1)), conv2.ToInt16(Nothing)) + + Case TypeCode.Byte * TCMAX + TypeCode.Boolean, _ + TypeCode.Int16 * TCMAX + TypeCode.Boolean + Return AddInt16(conv1.ToInt16(Nothing), CShort(ToVBBool(conv2))) + + Case TypeCode.Boolean * TCMAX + TypeCode.Boolean + Return AddInt16(CShort(ToVBBool(conv1)), CShort(ToVBBool(conv2))) + + Case TypeCode.Byte * TCMAX + TypeCode.Byte + Return AddByte(conv1.ToByte(Nothing), conv2.ToByte(Nothing)) + + Case Else + + End Select + + Throw GetNoValidOperatorException(o1, o2) + + End Function + + + Private Shared Function AddString(ByVal conv1 As IConvertible, ByVal tc1 As TypeCode, ByVal conv2 As IConvertible, ByVal tc2 As TypeCode) As Object + Dim dbl1, dbl2 As Double + + If tc1 = TypeCode.String Then + dbl1 = DoubleType.FromString(conv1.ToString(Nothing)) + ElseIf tc1 = TypeCode.Boolean Then + dbl1 = ToVBBool(conv1) + Else + dbl1 = conv1.ToDouble(Nothing) + End If + + If tc2 = TypeCode.String Then + dbl2 = DoubleType.FromString(conv2.ToString(Nothing)) + ElseIf tc2 = TypeCode.Boolean Then + dbl2 = ToVBBool(conv2) + Else + dbl2 = conv2.ToDouble(Nothing) + End If + + Return dbl1 + dbl2 + End Function + + + Private Shared Function AddByte(ByVal i1 As Byte, ByVal i2 As Byte) As Object + Dim result As Short = CShort(i1) + CShort(i2) + + If result >= Byte.MinValue AndAlso result <= Byte.MaxValue Then + Return CByte(result) + Else + Return result + End If + End Function + + Private Shared Function AddInt16(ByVal i1 As Short, ByVal i2 As Short) As Object + Dim result As Integer = CInt(i1) + CInt(i2) + + If result >= Short.MinValue AndAlso result <= Short.MaxValue Then + Return CShort(result) + Else + Return result + End If + End Function + + Private Shared Function AddInt32(ByVal i1 As Integer, ByVal i2 As Integer) As Object + Dim result As Long = CLng(i1) + CLng(i2) + If result >= Integer.MinValue AndAlso result <= Integer.MaxValue Then + Return CInt(result) + Else + Return result + End If + End Function + + Private Shared Function AddInt64(ByVal i1 As Long, ByVal i2 As Long) As Object + Try + Return i1 + i2 + Catch e As OverflowException + Return CDec(i1) + CDec(i2) + End Try + End Function + + Private Shared Function AddSingle(ByVal f1 As Single, ByVal f2 As Single) As Object + Dim result As Double = CDbl(f1) + CDbl(f2) + If ((result <= Single.MaxValue AndAlso result >= Single.MinValue)) Then + Return CSng(result) + ElseIf Double.IsInfinity(result) AndAlso (Single.IsInfinity(f1) OrElse Single.IsInfinity(f2)) Then + Return CSng(result) + Else + Return result + End If + End Function + + Private Shared Function AddDouble(ByVal d1 As Double, ByVal d2 As Double) As Object + Return d1 + d2 + End Function + + Private Shared Function AddDecimal(ByVal conv1 As IConvertible, ByVal conv2 As IConvertible) As Object + + Dim d1, d2 As Decimal + + If Not conv1 Is Nothing Then + d1 = conv1.ToDecimal(Nothing) + End If + d2 = conv2.ToDecimal(Nothing) + Try + Return (d1 + d2) + Catch e As OverflowException + Return CDbl(d1) + CDbl(d2) + End Try + + End Function + + + Private Shared Function ToVBBool(ByVal conv As IConvertible) As Integer + If conv.ToBoolean(Nothing) Then + Return -1 + Else + Return 0 + End If + End Function + + Private Shared Function ToVBBoolConv(ByVal conv As IConvertible) As IConvertible + If conv.ToBoolean(Nothing) Then + Return -1 + Else + Return 0 + End If + End Function + + + + Public Shared Function SubObj(ByVal o1 As Object, ByVal o2 As Object) As Object + Dim conv1, conv2 As IConvertible + Dim tc1, tc2 As TypeCode + + conv1 = TryCast(o1, IConvertible) + + If conv1 Is Nothing Then + If o1 Is Nothing Then + tc1 = TypeCode.Empty + Else + tc1 = TypeCode.Object + End If + Else + tc1 = conv1.GetTypeCode() + End If + + + conv2 = TryCast(o2, IConvertible) + + If conv2 Is Nothing Then + If o2 Is Nothing Then + tc2 = TypeCode.Empty + Else + tc2 = TypeCode.Object + End If + Else + tc2 = conv2.GetTypeCode() + End If + + Select Case tc1 * TCMAX + tc2 + + Case TypeCode.Empty * TCMAX + TypeCode.Empty + Return 0I + + Case TypeCode.Empty * TCMAX + TypeCode.String + Return SubStringString(Nothing, conv2.ToString(Nothing)) + + Case TypeCode.String * TCMAX + TypeCode.Empty + Return SubStringString(conv1.ToString(Nothing), Nothing) + + Case TypeCode.Boolean * TCMAX + TypeCode.Empty, _ + TypeCode.Byte * TCMAX + TypeCode.Empty, _ + TypeCode.Int16 * TCMAX + TypeCode.Empty, _ + TypeCode.Int32 * TCMAX + TypeCode.Empty, _ + TypeCode.Int64 * TCMAX + TypeCode.Empty, _ + TypeCode.Single * TCMAX + TypeCode.Empty, _ + TypeCode.Double * TCMAX + TypeCode.Empty, _ + TypeCode.Decimal * TCMAX + TypeCode.Empty + Return o1 + + Case TypeCode.Empty * TCMAX + TypeCode.Boolean, _ + TypeCode.Empty * TCMAX + TypeCode.Byte, _ + TypeCode.Empty * TCMAX + TypeCode.Int16, _ + TypeCode.Empty * TCMAX + TypeCode.Int32, _ + TypeCode.Empty * TCMAX + TypeCode.Int64, _ + TypeCode.Empty * TCMAX + TypeCode.Single, _ + TypeCode.Empty * TCMAX + TypeCode.Double, _ + TypeCode.Empty * TCMAX + TypeCode.Decimal + Return InternalNegObj(o2, conv2, tc2) + + Case TypeCode.Decimal * TCMAX + TypeCode.Byte, _ + TypeCode.Decimal * TCMAX + TypeCode.Int16, _ + TypeCode.Decimal * TCMAX + TypeCode.Int32, _ + TypeCode.Decimal * TCMAX + TypeCode.Int64, _ + TypeCode.Decimal * TCMAX + TypeCode.Decimal, _ + TypeCode.Int16 * TCMAX + TypeCode.Decimal, _ + TypeCode.Byte * TCMAX + TypeCode.Decimal, _ + TypeCode.Int32 * TCMAX + TypeCode.Decimal, _ + TypeCode.Int64 * TCMAX + TypeCode.Decimal + Return SubDecimal(conv1, conv2) + + Case TypeCode.Boolean * TCMAX + TypeCode.Decimal + Return SubDecimal(ToVBBoolConv(conv1), conv2) + + Case TypeCode.Decimal * TCMAX + TypeCode.Boolean + Return SubDecimal(conv1, ToVBBoolConv(conv2)) + + Case TypeCode.String * TCMAX + TypeCode.Byte, _ + TypeCode.String * TCMAX + TypeCode.Int16, _ + TypeCode.String * TCMAX + TypeCode.Int32, _ + TypeCode.String * TCMAX + TypeCode.Int64, _ + TypeCode.String * TCMAX + TypeCode.Single, _ + TypeCode.String * TCMAX + TypeCode.Double, _ + TypeCode.String * TCMAX + TypeCode.Decimal, _ + TypeCode.Byte * TCMAX + TypeCode.String, _ + TypeCode.Int16 * TCMAX + TypeCode.String, _ + TypeCode.Int32 * TCMAX + TypeCode.String, _ + TypeCode.Int64 * TCMAX + TypeCode.String, _ + TypeCode.Single * TCMAX + TypeCode.String, _ + TypeCode.Double * TCMAX + TypeCode.String, _ + TypeCode.Decimal * TCMAX + TypeCode.String, _ + TypeCode.Boolean * TCMAX + TypeCode.String, _ + TypeCode.String * TCMAX + TypeCode.Boolean + Return SubString(conv1, tc1, conv2, tc2) + + + Case TypeCode.String * TCMAX + TypeCode.String + Return SubStringString(conv1.ToString(Nothing), conv2.ToString(Nothing)) + + Case TypeCode.Double * TCMAX + TypeCode.Byte, _ + TypeCode.Double * TCMAX + TypeCode.Int16, _ + TypeCode.Double * TCMAX + TypeCode.Int32, _ + TypeCode.Double * TCMAX + TypeCode.Int64, _ + TypeCode.Double * TCMAX + TypeCode.Single, _ + TypeCode.Double * TCMAX + TypeCode.Double, _ + TypeCode.Byte * TCMAX + TypeCode.Double, _ + TypeCode.Int16 * TCMAX + TypeCode.Double, _ + TypeCode.Int32 * TCMAX + TypeCode.Double, _ + TypeCode.Int64 * TCMAX + TypeCode.Double, _ + TypeCode.Single * TCMAX + TypeCode.Double, _ + TypeCode.Double * TCMAX + TypeCode.Decimal, _ + TypeCode.Decimal * TCMAX + TypeCode.Double + Return SubDouble(conv1.ToDouble(Nothing), conv2.ToDouble(Nothing)) + + Case TypeCode.Double * TCMAX + TypeCode.Boolean + Return SubDouble(conv1.ToDouble(Nothing), ToVBBool(conv2)) + + Case TypeCode.Boolean * TCMAX + TypeCode.Double + Return SubDouble(ToVBBool(conv1), conv2.ToDouble(Nothing)) + + Case TypeCode.Single * TCMAX + TypeCode.Byte, _ + TypeCode.Single * TCMAX + TypeCode.Int16, _ + TypeCode.Single * TCMAX + TypeCode.Int32, _ + TypeCode.Single * TCMAX + TypeCode.Int64, _ + TypeCode.Single * TCMAX + TypeCode.Single, _ + TypeCode.Byte * TCMAX + TypeCode.Single, _ + TypeCode.Int16 * TCMAX + TypeCode.Single, _ + TypeCode.Int32 * TCMAX + TypeCode.Single, _ + TypeCode.Int64 * TCMAX + TypeCode.Single, _ + TypeCode.Decimal * TCMAX + TypeCode.Single, _ + TypeCode.Single * TCMAX + TypeCode.Decimal + Return SubSingle(conv1.ToSingle(Nothing), conv2.ToSingle(Nothing)) + + Case TypeCode.Single * TCMAX + TypeCode.Boolean + Return SubSingle(conv1.ToSingle(Nothing), ToVBBool(conv2)) + + Case TypeCode.Boolean * TCMAX + TypeCode.Single + Return SubSingle(ToVBBool(conv1), conv2.ToSingle(Nothing)) + + Case TypeCode.Byte * TCMAX + TypeCode.Int64, _ + TypeCode.Int64 * TCMAX + TypeCode.Byte, _ + TypeCode.Int64 * TCMAX + TypeCode.Int16, _ + TypeCode.Int64 * TCMAX + TypeCode.Int32, _ + TypeCode.Int64 * TCMAX + TypeCode.Int64, _ + TypeCode.Int16 * TCMAX + TypeCode.Int64, _ + TypeCode.Int32 * TCMAX + TypeCode.Int64 + Return SubInt64(conv1.ToInt64(Nothing), conv2.ToInt64(Nothing)) + + Case TypeCode.Int64 * TCMAX + TypeCode.Boolean + Return SubInt64(conv1.ToInt64(Nothing), ToVBBool(conv2)) + + Case TypeCode.Boolean * TCMAX + TypeCode.Int64 + Return SubInt64(ToVBBool(conv1), conv2.ToInt64(Nothing)) + + Case TypeCode.Int16 * TCMAX + TypeCode.Int32, _ + TypeCode.Int32 * TCMAX + TypeCode.Int16, _ + TypeCode.Int32 * TCMAX + TypeCode.Int32, _ + TypeCode.Int32 * TCMAX + TypeCode.Byte, _ + TypeCode.Byte * TCMAX + TypeCode.Int32 + Return SubInt32(conv1.ToInt32(Nothing), conv2.ToInt32(Nothing)) + + Case TypeCode.Int32 * TCMAX + TypeCode.Boolean + Return SubInt32(conv1.ToInt32(Nothing), ToVBBool(conv2)) + + Case TypeCode.Boolean * TCMAX + TypeCode.Int32 + Return SubInt32(ToVBBool(conv1), conv2.ToInt32(Nothing)) + + Case TypeCode.Int16 * TCMAX + TypeCode.Byte, _ + TypeCode.Int16 * TCMAX + TypeCode.Int16, _ + TypeCode.Byte * TCMAX + TypeCode.Int16 + Return SubInt16(conv1.ToInt16(Nothing), conv2.ToInt16(Nothing)) + + Case TypeCode.Boolean * TCMAX + TypeCode.Byte, _ + TypeCode.Boolean * TCMAX + TypeCode.Int16 + Return SubInt16(CShort(ToVBBool(conv1)), conv2.ToInt16(Nothing)) + + Case TypeCode.Byte * TCMAX + TypeCode.Boolean, _ + TypeCode.Int16 * TCMAX + TypeCode.Boolean + Return SubInt16(conv1.ToInt16(Nothing), CShort(ToVBBool(conv2))) + + Case TypeCode.Boolean * TCMAX + TypeCode.Boolean + Return SubInt16(CShort(ToVBBool(conv1)), CShort(ToVBBool(conv2))) + + Case TypeCode.Byte * TCMAX + TypeCode.Byte + Return SubByte(conv1.ToByte(Nothing), conv2.ToByte(Nothing)) + + Case Else + + End Select + + Throw GetNoValidOperatorException(o1, o2) + + End Function + + + Private Shared Function SubString(ByVal conv1 As IConvertible, ByVal tc1 As TypeCode, ByVal conv2 As IConvertible, ByVal tc2 As TypeCode) As Object + Dim dbl1, dbl2 As Double + + If tc1 = TypeCode.String Then + dbl1 = DoubleType.FromString(conv1.ToString(Nothing)) + ElseIf tc1 = TypeCode.Boolean Then + dbl1 = ToVBBool(conv1) + Else + dbl1 = conv1.ToDouble(Nothing) + End If + + If tc2 = TypeCode.String Then + dbl2 = DoubleType.FromString(conv2.ToString(Nothing)) + ElseIf tc2 = TypeCode.Boolean Then + dbl2 = ToVBBool(conv2) + Else + dbl2 = conv2.ToDouble(Nothing) + End If + + Return dbl1 - dbl2 + End Function + + + Private Shared Function SubStringString(ByVal s1 As String, ByVal s2 As String) As Object + Dim dbl1, dbl2 As Double + + If Not s1 Is Nothing Then + dbl1 = DoubleType.FromString(s1) + End If + + If Not s2 Is Nothing Then + dbl2 = DoubleType.FromString(s2) + End If + + Return dbl1 - dbl2 + + End Function + + + Private Shared Function SubByte(ByVal i1 As Byte, ByVal i2 As Byte) As Object + Dim result As Short = CShort(i1) - CShort(i2) + + If result >= Byte.MinValue AndAlso result <= Byte.MaxValue Then + Return CByte(result) + Else + Return result + End If + End Function + + Private Shared Function SubInt16(ByVal i1 As Short, ByVal i2 As Short) As Object + Dim result As Integer = CInt(i1) - CInt(i2) + + If result >= Short.MinValue AndAlso result <= Short.MaxValue Then + Return CShort(result) + Else + Return result + End If + End Function + + Private Shared Function SubInt32(ByVal i1 As Integer, ByVal i2 As Integer) As Object + Dim result As Long = CLng(i1) - CLng(i2) + If result >= Integer.MinValue AndAlso result <= Integer.MaxValue Then + Return CInt(result) + Else + Return result + End If + End Function + + Private Shared Function SubInt64(ByVal i1 As Long, ByVal i2 As Long) As Object + Try + Return i1 - i2 + Catch ex As StackOverflowException + Throw ex + Catch ex As OutOfMemoryException + Throw ex + Catch ex As System.Threading.ThreadAbortException + Throw ex + Catch 'e As OverflowException + Return CDec(i1) - CDec(i2) + End Try + End Function + + Private Shared Function SubSingle(ByVal f1 As Single, ByVal f2 As Single) As Object + Dim result As Double = CDbl(f1) - CDbl(f2) + If ((result <= Single.MaxValue AndAlso result >= Single.MinValue)) Then + Return CSng(result) + ElseIf Double.IsInfinity(result) AndAlso (Single.IsInfinity(f1) OrElse Single.IsInfinity(f2)) Then + Return CSng(result) + Else + Return result + End If + End Function + + Private Shared Function SubDouble(ByVal d1 As Double, ByVal d2 As Double) As Object + Return d1 - d2 + End Function + + Private Shared Function SubDecimal(ByVal conv1 As IConvertible, ByVal conv2 As IConvertible) As Object + Dim d1, d2 As Decimal + d1 = conv1.ToDecimal(Nothing) + d2 = conv2.ToDecimal(Nothing) + Try + Return (d1 - d2) + Catch e As OverflowException + Return CDbl(d1) - CDbl(d2) + End Try + End Function + + + + Public Shared Function MulObj(ByVal o1 As Object, ByVal o2 As Object) As Object + Dim conv1, conv2 As IConvertible + Dim tc1, tc2 As TypeCode + + conv1 = TryCast(o1, IConvertible) + + If conv1 Is Nothing Then + If o1 Is Nothing Then + tc1 = TypeCode.Empty + Else + tc1 = TypeCode.Object + End If + Else + tc1 = conv1.GetTypeCode() + End If + + + conv2 = TryCast(o2, IConvertible) + + If conv2 Is Nothing Then + If o2 Is Nothing Then + tc2 = TypeCode.Empty + Else + tc2 = TypeCode.Object + End If + Else + tc2 = conv2.GetTypeCode() + End If + + Select Case tc1 * TCMAX + tc2 + + Case TypeCode.Empty * TCMAX + TypeCode.String, _ + TypeCode.String * TCMAX + TypeCode.Empty + Return CDbl(0) + + Case TypeCode.Byte * TCMAX + TypeCode.Empty, _ + TypeCode.Empty * TCMAX + TypeCode.Byte + Return CByte(0) + + Case TypeCode.Boolean * TCMAX + TypeCode.Empty, _ + TypeCode.Empty * TCMAX + TypeCode.Boolean, _ + TypeCode.Int16 * TCMAX + TypeCode.Empty, _ + TypeCode.Empty * TCMAX + TypeCode.Int16 + Return CShort(0) + + Case TypeCode.Empty * TCMAX + TypeCode.Empty, _ + TypeCode.Int32 * TCMAX + TypeCode.Empty, _ + TypeCode.Empty * TCMAX + TypeCode.Int32 + Return CInt(0) + + Case TypeCode.Int64 * TCMAX + TypeCode.Empty, _ + TypeCode.Empty * TCMAX + TypeCode.Int64 + Return CLng(0) + + Case TypeCode.Single * TCMAX + TypeCode.Empty, _ + TypeCode.Empty * TCMAX + TypeCode.Single + Return CSng(0) + + Case TypeCode.Double * TCMAX + TypeCode.Empty, _ + TypeCode.Empty * TCMAX + TypeCode.Double + Return CDbl(0) + + Case TypeCode.Decimal * TCMAX + TypeCode.Empty, _ + TypeCode.Empty * TCMAX + TypeCode.Decimal + Return CDec(0) + + Case TypeCode.Decimal * TCMAX + TypeCode.Byte, _ + TypeCode.Decimal * TCMAX + TypeCode.Int16, _ + TypeCode.Decimal * TCMAX + TypeCode.Int32, _ + TypeCode.Decimal * TCMAX + TypeCode.Int64, _ + TypeCode.Decimal * TCMAX + TypeCode.Decimal, _ + TypeCode.Int16 * TCMAX + TypeCode.Decimal, _ + TypeCode.Byte * TCMAX + TypeCode.Decimal, _ + TypeCode.Int32 * TCMAX + TypeCode.Decimal, _ + TypeCode.Int64 * TCMAX + TypeCode.Decimal + Return MulDecimal(conv1, conv2) + + Case TypeCode.Boolean * TCMAX + TypeCode.Decimal + Return MulDecimal(ToVBBoolConv(conv1), conv2) + + Case TypeCode.Decimal * TCMAX + TypeCode.Boolean + Return MulDecimal(conv1, ToVBBoolConv(conv2)) + + Case TypeCode.String * TCMAX + TypeCode.Byte, _ + TypeCode.String * TCMAX + TypeCode.Int16, _ + TypeCode.String * TCMAX + TypeCode.Int32, _ + TypeCode.String * TCMAX + TypeCode.Int64, _ + TypeCode.String * TCMAX + TypeCode.Single, _ + TypeCode.String * TCMAX + TypeCode.Double, _ + TypeCode.String * TCMAX + TypeCode.Decimal, _ + TypeCode.Byte * TCMAX + TypeCode.String, _ + TypeCode.Int16 * TCMAX + TypeCode.String, _ + TypeCode.Int32 * TCMAX + TypeCode.String, _ + TypeCode.Int64 * TCMAX + TypeCode.String, _ + TypeCode.Single * TCMAX + TypeCode.String, _ + TypeCode.Double * TCMAX + TypeCode.String, _ + TypeCode.Decimal * TCMAX + TypeCode.String, _ + TypeCode.Boolean * TCMAX + TypeCode.String, _ + TypeCode.String * TCMAX + TypeCode.Boolean + Return MulString(conv1, tc1, conv2, tc2) + + Case TypeCode.String * TCMAX + TypeCode.String + Return MulStringString(conv1.ToString(Nothing), conv2.ToString(Nothing)) + + Case TypeCode.Double * TCMAX + TypeCode.Byte, _ + TypeCode.Double * TCMAX + TypeCode.Int16, _ + TypeCode.Double * TCMAX + TypeCode.Int32, _ + TypeCode.Double * TCMAX + TypeCode.Int64, _ + TypeCode.Double * TCMAX + TypeCode.Decimal, _ + TypeCode.Double * TCMAX + TypeCode.Single, _ + TypeCode.Double * TCMAX + TypeCode.Double, _ + TypeCode.Byte * TCMAX + TypeCode.Double, _ + TypeCode.Int16 * TCMAX + TypeCode.Double, _ + TypeCode.Int32 * TCMAX + TypeCode.Double, _ + TypeCode.Int64 * TCMAX + TypeCode.Double, _ + TypeCode.Single * TCMAX + TypeCode.Double, _ + TypeCode.Decimal * TCMAX + TypeCode.Double + Return MulDouble(conv1.ToDouble(Nothing), conv2.ToDouble(Nothing)) + + Case TypeCode.Double * TCMAX + TypeCode.Boolean + Return MulDouble(conv1.ToDouble(Nothing), ToVBBool(conv2)) + + Case TypeCode.Boolean * TCMAX + TypeCode.Double + Return MulDouble(ToVBBool(conv1), conv2.ToDouble(Nothing)) + + Case TypeCode.Single * TCMAX + TypeCode.Byte, _ + TypeCode.Single * TCMAX + TypeCode.Int16, _ + TypeCode.Single * TCMAX + TypeCode.Int32, _ + TypeCode.Single * TCMAX + TypeCode.Int64, _ + TypeCode.Single * TCMAX + TypeCode.Single, _ + TypeCode.Byte * TCMAX + TypeCode.Single, _ + TypeCode.Int16 * TCMAX + TypeCode.Single, _ + TypeCode.Int32 * TCMAX + TypeCode.Single, _ + TypeCode.Int64 * TCMAX + TypeCode.Single, _ + TypeCode.Decimal * TCMAX + TypeCode.Single, _ + TypeCode.Single * TCMAX + TypeCode.Decimal + Return MulSingle(conv1.ToSingle(Nothing), conv2.ToSingle(Nothing)) + + Case TypeCode.Single * TCMAX + TypeCode.Boolean + Return MulSingle(conv1.ToSingle(Nothing), ToVBBool(conv2)) + + Case TypeCode.Boolean * TCMAX + TypeCode.Single + Return MulSingle(ToVBBool(conv1), conv2.ToSingle(Nothing)) + + Case TypeCode.Byte * TCMAX + TypeCode.Int64, _ + TypeCode.Int64 * TCMAX + TypeCode.Byte, _ + TypeCode.Int64 * TCMAX + TypeCode.Int16, _ + TypeCode.Int64 * TCMAX + TypeCode.Int32, _ + TypeCode.Int64 * TCMAX + TypeCode.Int64, _ + TypeCode.Int16 * TCMAX + TypeCode.Int64, _ + TypeCode.Int32 * TCMAX + TypeCode.Int64 + Return MulInt64(conv1.ToInt64(Nothing), conv2.ToInt64(Nothing)) + + Case TypeCode.Int64 * TCMAX + TypeCode.Boolean + Return MulInt64(conv1.ToInt64(Nothing), ToVBBool(conv2)) + + Case TypeCode.Boolean * TCMAX + TypeCode.Int64 + Return MulInt64(ToVBBool(conv1), conv2.ToInt64(Nothing)) + + Case TypeCode.Int16 * TCMAX + TypeCode.Int32, _ + TypeCode.Int32 * TCMAX + TypeCode.Int16, _ + TypeCode.Int32 * TCMAX + TypeCode.Int32, _ + TypeCode.Int32 * TCMAX + TypeCode.Byte, _ + TypeCode.Byte * TCMAX + TypeCode.Int32 + Return MulInt32(conv1.ToInt32(Nothing), conv2.ToInt32(Nothing)) + + Case TypeCode.Int32 * TCMAX + TypeCode.Boolean + Return MulInt32(conv1.ToInt32(Nothing), ToVBBool(conv2)) + + Case TypeCode.Boolean * TCMAX + TypeCode.Int32 + Return MulInt32(ToVBBool(conv1), conv2.ToInt32(Nothing)) + + Case TypeCode.Int16 * TCMAX + TypeCode.Byte, _ + TypeCode.Int16 * TCMAX + TypeCode.Int16, _ + TypeCode.Byte * TCMAX + TypeCode.Int16 + Return MulInt16(conv1.ToInt16(Nothing), conv2.ToInt16(Nothing)) + + Case TypeCode.Boolean * TCMAX + TypeCode.Byte, _ + TypeCode.Boolean * TCMAX + TypeCode.Int16 + Return MulInt16(CShort(ToVBBool(conv1)), conv2.ToInt16(Nothing)) + + Case TypeCode.Byte * TCMAX + TypeCode.Boolean, _ + TypeCode.Int16 * TCMAX + TypeCode.Boolean + Return MulInt16(conv1.ToInt16(Nothing), CShort(ToVBBool(conv2))) + + Case TypeCode.Boolean * TCMAX + TypeCode.Boolean + Return MulInt16(CShort(ToVBBool(conv1)), CShort(ToVBBool(conv2))) + + Case TypeCode.Byte * TCMAX + TypeCode.Byte + Return MulByte(conv1.ToByte(Nothing), conv2.ToByte(Nothing)) + + Case Else + + End Select + + Throw GetNoValidOperatorException(o1, o2) + + End Function + + Private Shared Function MulString(ByVal conv1 As IConvertible, ByVal tc1 As TypeCode, ByVal conv2 As IConvertible, ByVal tc2 As TypeCode) As Object + Dim dbl1, dbl2 As Double + + If tc1 = TypeCode.String Then + dbl1 = DoubleType.FromString(conv1.ToString(Nothing)) + ElseIf tc1 = TypeCode.Boolean Then + dbl1 = ToVBBool(conv1) + Else + dbl1 = conv1.ToDouble(Nothing) + End If + + If tc2 = TypeCode.String Then + dbl2 = DoubleType.FromString(conv2.ToString(Nothing)) + ElseIf tc2 = TypeCode.Boolean Then + dbl2 = ToVBBool(conv2) + Else + dbl2 = conv2.ToDouble(Nothing) + End If + + Return dbl1 * dbl2 + End Function + + + Private Shared Function MulStringString(ByVal s1 As String, ByVal s2 As String) As Object + Dim dbl1, dbl2 As Double + + If Not s1 Is Nothing Then + dbl1 = DoubleType.FromString(s1) + End If + + If Not s2 Is Nothing Then + dbl2 = DoubleType.FromString(s2) + End If + + Return dbl1 * dbl2 + + End Function + + Private Shared Function MulByte(ByVal i1 As Byte, ByVal i2 As Byte) As Object + Dim result As Integer = CInt(i1) * CInt(i2) + + If result >= Byte.MinValue AndAlso result <= Byte.MaxValue Then + Return CByte(result) + ElseIf result >= Int16.MinValue AndAlso result <= Int16.MaxValue Then + Return CShort(result) + Else + Return result + End If + End Function + + Private Shared Function MulInt16(ByVal i1 As Short, ByVal i2 As Short) As Object + Dim result As Integer = CInt(i1) * CInt(i2) + + If result >= Short.MinValue AndAlso result <= Short.MaxValue Then + Return CShort(result) + Else + Return result + End If + End Function + + Private Shared Function MulInt32(ByVal i1 As Integer, ByVal i2 As Integer) As Object + Dim result As Long = CLng(i1) * CLng(i2) + If result >= Integer.MinValue AndAlso result <= Integer.MaxValue Then + Return CInt(result) + Else + Return result + End If + End Function + + Private Shared Function MulInt64(ByVal i1 As Long, ByVal i2 As Long) As Object + Try + Return i1 * i2 + Catch ex1 As OverflowException + Try + Return CDec(i1) * CDec(i2) + Catch ex2 As OverflowException + Return CDbl(i1) * CDbl(i2) + End Try + End Try + End Function + + Private Shared Function MulSingle(ByVal f1 As Single, ByVal f2 As Single) As Object + Dim result As Double = CDbl(f1) * CDbl(f2) + If ((result <= Single.MaxValue AndAlso result >= Single.MinValue)) Then + Return CSng(result) + ElseIf Double.IsInfinity(result) AndAlso (Single.IsInfinity(f1) OrElse Single.IsInfinity(f2)) Then + Return CSng(result) + Else + Return result + End If + End Function + + Private Shared Function MulDouble(ByVal d1 As Double, ByVal d2 As Double) As Object + Return d1 * d2 + End Function + + Private Shared Function MulDecimal(ByVal conv1 As IConvertible, ByVal conv2 As IConvertible) As Object + Dim d1, d2 As Decimal + d1 = conv1.ToDecimal(Nothing) + d2 = conv2.ToDecimal(Nothing) + Try + Return (d1 * d2) + Catch e As OverflowException + Return CDbl(d1) * CDbl(d2) + End Try + End Function + + + + + Public Shared Function DivObj(ByVal o1 As Object, ByVal o2 As Object) As Object + + Dim conv1, conv2 As IConvertible + Dim tc1, tc2 As TypeCode + + conv1 = TryCast(o1, IConvertible) + + If conv1 Is Nothing Then + If o1 Is Nothing Then + tc1 = TypeCode.Empty + Else + tc1 = TypeCode.Object + End If + Else + tc1 = conv1.GetTypeCode() + End If + + + conv2 = TryCast(o2, IConvertible) + + If conv2 Is Nothing Then + If o2 Is Nothing Then + tc2 = TypeCode.Empty + Else + tc2 = TypeCode.Object + End If + Else + tc2 = conv2.GetTypeCode() + End If + + Select Case tc1 * TCMAX + tc2 + + 'STRING + Case TypeCode.Empty * TCMAX + TypeCode.String + Return DivString(conv1, tc1, conv2, tc2) + + Case TypeCode.String * TCMAX + TypeCode.Empty + Return DivString(conv1, tc1, conv2, tc2) + + Case TypeCode.String * TCMAX + TypeCode.Boolean + Return DivString(conv1, tc1, conv2, tc2) + + Case TypeCode.Boolean * TCMAX + TypeCode.String + Return DivString(conv1, tc1, conv2, tc2) + + Case TypeCode.String * TCMAX + TypeCode.Byte, _ + TypeCode.String * TCMAX + TypeCode.Int16, _ + TypeCode.String * TCMAX + TypeCode.Int32, _ + TypeCode.String * TCMAX + TypeCode.Int64, _ + TypeCode.String * TCMAX + TypeCode.Single, _ + TypeCode.String * TCMAX + TypeCode.Double, _ + TypeCode.String * TCMAX + TypeCode.Decimal + Return DivString(conv1, tc1, conv2, tc2) + + Case TypeCode.Byte * TCMAX + TypeCode.String, _ + TypeCode.Int16 * TCMAX + TypeCode.String, _ + TypeCode.Int32 * TCMAX + TypeCode.String, _ + TypeCode.Int64 * TCMAX + TypeCode.String, _ + TypeCode.Single * TCMAX + TypeCode.String, _ + TypeCode.Double * TCMAX + TypeCode.String, _ + TypeCode.Decimal * TCMAX + TypeCode.String + Return DivString(conv1, tc1, conv2, tc2) + + Case TypeCode.String * TCMAX + TypeCode.String + Return DivStringString(conv1.ToString(Nothing), conv2.ToString(Nothing)) + + + 'EMPTY + Case TypeCode.Empty * TCMAX + TypeCode.Empty + Return DivDouble(0, 0) + + Case TypeCode.Boolean * TCMAX + TypeCode.Empty + Return DivDouble(ToVBBool(conv1), 0) + + Case TypeCode.Byte * TCMAX + TypeCode.Empty, _ + TypeCode.Int16 * TCMAX + TypeCode.Empty, _ + TypeCode.Int32 * TCMAX + TypeCode.Empty, _ + TypeCode.Int64 * TCMAX + TypeCode.Empty, _ + TypeCode.Decimal * TCMAX + TypeCode.Empty, _ + TypeCode.Single * TCMAX + TypeCode.Empty, _ + TypeCode.Double * TCMAX + TypeCode.Empty + Return DivDouble(conv1.ToDouble(Nothing), 0) + + Case TypeCode.Empty * TCMAX + TypeCode.Boolean, _ + TypeCode.Empty * TCMAX + TypeCode.Byte, _ + TypeCode.Empty * TCMAX + TypeCode.Int16, _ + TypeCode.Empty * TCMAX + TypeCode.Int32, _ + TypeCode.Empty * TCMAX + TypeCode.Int64, _ + TypeCode.Empty * TCMAX + TypeCode.Decimal, _ + TypeCode.Empty * TCMAX + TypeCode.Single, _ + TypeCode.Empty * TCMAX + TypeCode.Double + Return DivDouble(0, conv2.ToDouble(Nothing)) + + + 'BOOLEAN + Case TypeCode.Boolean * TCMAX + TypeCode.Byte, _ + TypeCode.Boolean * TCMAX + TypeCode.Int16, _ + TypeCode.Boolean * TCMAX + TypeCode.Int32, _ + TypeCode.Boolean * TCMAX + TypeCode.Int64, _ + TypeCode.Boolean * TCMAX + TypeCode.Double + Return DivDouble(ToVBBool(conv1), conv2.ToDouble(Nothing)) + + Case TypeCode.Boolean * TCMAX + TypeCode.Decimal + Return DivDecimal(ToVBBoolConv(conv1), conv2.ToDecimal(Nothing)) + + Case TypeCode.Decimal * TCMAX + TypeCode.Boolean + Return DivDecimal(conv1, ToVBBoolConv(conv2)) + + Case TypeCode.Boolean * TCMAX + TypeCode.Boolean + Return DivDouble(ToVBBool(conv1), ToVBBool(conv2)) + + + Case TypeCode.Boolean * TCMAX + TypeCode.Single + Return DivSingle(ToVBBool(conv1), conv2.ToSingle(Nothing)) + + Case TypeCode.Single * TCMAX + TypeCode.Boolean + Return DivSingle(conv1.ToSingle(Nothing), ToVBBool(conv2)) + + Case TypeCode.Byte * TCMAX + TypeCode.Boolean, _ + TypeCode.Int16 * TCMAX + TypeCode.Boolean, _ + TypeCode.Int32 * TCMAX + TypeCode.Boolean, _ + TypeCode.Int64 * TCMAX + TypeCode.Boolean, _ + TypeCode.Double * TCMAX + TypeCode.Boolean + Return DivDouble(conv1.ToDouble(Nothing), ToVBBool(conv2)) + + 'DECIMAL + Case TypeCode.Decimal * TCMAX + TypeCode.Byte, _ + TypeCode.Decimal * TCMAX + TypeCode.Int16, _ + TypeCode.Decimal * TCMAX + TypeCode.Int32, _ + TypeCode.Decimal * TCMAX + TypeCode.Int64, _ + TypeCode.Decimal * TCMAX + TypeCode.Decimal, _ + TypeCode.Int16 * TCMAX + TypeCode.Decimal, _ + TypeCode.Byte * TCMAX + TypeCode.Decimal, _ + TypeCode.Int32 * TCMAX + TypeCode.Decimal, _ + TypeCode.Int64 * TCMAX + TypeCode.Decimal + Return DivDecimal(conv1, conv2) + + 'SINGLE + Case TypeCode.Decimal * TCMAX + TypeCode.Single, _ + TypeCode.Single * TCMAX + TypeCode.Byte, _ + TypeCode.Single * TCMAX + TypeCode.Int16, _ + TypeCode.Single * TCMAX + TypeCode.Int32, _ + TypeCode.Single * TCMAX + TypeCode.Int64, _ + TypeCode.Single * TCMAX + TypeCode.Single, _ + TypeCode.Byte * TCMAX + TypeCode.Single, _ + TypeCode.Int16 * TCMAX + TypeCode.Single, _ + TypeCode.Int32 * TCMAX + TypeCode.Single, _ + TypeCode.Int64 * TCMAX + TypeCode.Single, _ + TypeCode.Single * TCMAX + TypeCode.Decimal + Return DivSingle(conv1.ToSingle(Nothing), conv2.ToSingle(Nothing)) + + 'DOUBLE + Case TypeCode.Double * TCMAX + TypeCode.Byte, _ + TypeCode.Double * TCMAX + TypeCode.Int16, _ + TypeCode.Double * TCMAX + TypeCode.Int32, _ + TypeCode.Double * TCMAX + TypeCode.Int64, _ + TypeCode.Double * TCMAX + TypeCode.Single, _ + TypeCode.Double * TCMAX + TypeCode.Double, _ + TypeCode.Byte * TCMAX + TypeCode.Double, _ + TypeCode.Int16 * TCMAX + TypeCode.Double, _ + TypeCode.Int32 * TCMAX + TypeCode.Double, _ + TypeCode.Int64 * TCMAX + TypeCode.Double, _ + TypeCode.Single * TCMAX + TypeCode.Double, _ + TypeCode.Double * TCMAX + TypeCode.Decimal, _ + TypeCode.Decimal * TCMAX + TypeCode.Double, _ + TypeCode.Byte * TCMAX + TypeCode.Int64, _ + TypeCode.Byte * TCMAX + TypeCode.Int16, _ + TypeCode.Byte * TCMAX + TypeCode.Byte, _ + TypeCode.Int16 * TCMAX + TypeCode.Byte, _ + TypeCode.Int16 * TCMAX + TypeCode.Int16, _ + TypeCode.Int64 * TCMAX + TypeCode.Byte, _ + TypeCode.Int64 * TCMAX + TypeCode.Int16, _ + TypeCode.Int64 * TCMAX + TypeCode.Int32, _ + TypeCode.Int64 * TCMAX + TypeCode.Int64, _ + TypeCode.Int16 * TCMAX + TypeCode.Int64, _ + TypeCode.Int32 * TCMAX + TypeCode.Int64, _ + TypeCode.Int16 * TCMAX + TypeCode.Int32, _ + TypeCode.Int32 * TCMAX + TypeCode.Int16, _ + TypeCode.Int32 * TCMAX + TypeCode.Int32, _ + TypeCode.Int32 * TCMAX + TypeCode.Byte, _ + TypeCode.Byte * TCMAX + TypeCode.Int32 + Return DivDouble(conv1.ToDouble(Nothing), conv2.ToDouble(Nothing)) + + Case Else + + End Select + + Throw GetNoValidOperatorException(o1, o2) + + End Function + + + Private Shared Function DivString(ByVal conv1 As IConvertible, ByVal tc1 As TypeCode, ByVal conv2 As IConvertible, ByVal tc2 As TypeCode) As Object + Dim dbl1, dbl2 As Double + + If tc1 = TypeCode.String Then + dbl1 = DoubleType.FromString(conv1.ToString(Nothing)) + ElseIf tc1 = TypeCode.Boolean Then + dbl1 = ToVBBool(conv1) + Else + dbl1 = conv1.ToDouble(Nothing) + End If + + If tc2 = TypeCode.String Then + dbl2 = DoubleType.FromString(conv2.ToString(Nothing)) + ElseIf tc2 = TypeCode.Boolean Then + dbl2 = ToVBBool(conv2) + Else + dbl2 = conv2.ToDouble(Nothing) + End If + + Return dbl1 / dbl2 + End Function + + + Private Shared Function DivStringString(ByVal s1 As String, ByVal s2 As String) As Object + Dim dbl1, dbl2 As Double + + If Not s1 Is Nothing Then + dbl1 = DoubleType.FromString(s1) + End If + + If Not s2 Is Nothing Then + dbl2 = DoubleType.FromString(s2) + End If + + Return dbl1 / dbl2 + + End Function + + + Private Shared Function DivDouble(ByVal d1 As Double, ByVal d2 As Double) As Object + Return d1 / d2 + End Function + + Private Shared Function DivSingle(ByVal sng1 As Single, ByVal sng2 As Single) As Object + + Dim sng As Single = sng1 / sng2 + + If Single.IsInfinity(sng) Then + If Single.IsInfinity(sng1) OrElse Single.IsInfinity(sng2) Then + Return sng + End If + Return CDbl(sng1) / CDbl(sng2) + Else + Return sng + End If + + End Function + + Private Shared Function DivDecimal(ByVal conv1 As IConvertible, ByVal conv2 As IConvertible) As Object + + Dim d1, d2 As Decimal + + If Not conv1 Is Nothing Then + d1 = conv1.ToDecimal(Nothing) + End If + If Not conv2 Is Nothing Then + d2 = conv2.ToDecimal(Nothing) + End If + + Try + Return d1 / d2 + Catch e As OverflowException + Return CSng(d1) / CSng(d2) + End Try + + End Function + + + + + Public Shared Function PowObj(ByVal obj1 As Object, ByVal obj2 As Object) As Object + If obj1 Is Nothing AndAlso obj2 Is Nothing Then + Return 1.0R + End If + + Select Case GetWidestType(obj1, obj2) + + Case TypeCode.Boolean, _ + TypeCode.Byte, _ + TypeCode.Int16, _ + TypeCode.Int32, _ + TypeCode.Int64, _ + TypeCode.Decimal, _ + TypeCode.Single, _ + TypeCode.Double, _ + TypeCode.String + Return DoubleType.FromObject(obj1) ^ DoubleType.FromObject(obj2) + + Case TypeCode.Char + ' Fall through to error + + Case TypeCode.DateTime + ' Fall through to error + Case Else + ' Fall through to error + + End Select + + Throw GetNoValidOperatorException(obj1, obj2) + + End Function + + + + + Public Shared Function ModObj(ByVal o1 As Object, ByVal o2 As Object) As Object + Dim conv1, conv2 As IConvertible + Dim tc1, tc2 As TypeCode + + conv1 = TryCast(o1, IConvertible) + conv2 = TryCast(o2, IConvertible) + + If Not conv1 Is Nothing Then + tc1 = conv1.GetTypeCode() + Else + If o1 Is Nothing Then + tc1 = TypeCode.Empty + Else + tc1 = TypeCode.Object + End If + End If + + If Not conv2 Is Nothing Then + tc2 = conv2.GetTypeCode() + Else + conv2 = Nothing + If o2 Is Nothing Then + tc2 = TypeCode.Empty + Else + tc2 = TypeCode.Object + End If + End If + + Select Case tc1 * TCMAX + tc2 + + 'STRING + Case TypeCode.Empty * TCMAX + TypeCode.String + Return ModString(conv1, tc1, conv2, tc2) + + Case TypeCode.String * TCMAX + TypeCode.Empty + Return ModString(conv1, tc1, conv2, tc2) + + Case TypeCode.String * TCMAX + TypeCode.Byte, _ + TypeCode.String * TCMAX + TypeCode.Int16, _ + TypeCode.String * TCMAX + TypeCode.Int32, _ + TypeCode.String * TCMAX + TypeCode.Int64, _ + TypeCode.String * TCMAX + TypeCode.Single, _ + TypeCode.String * TCMAX + TypeCode.Double, _ + TypeCode.String * TCMAX + TypeCode.Decimal + Return ModString(conv1, tc1, conv2, tc2) + + Case TypeCode.Byte * TCMAX + TypeCode.String, _ + TypeCode.Int16 * TCMAX + TypeCode.String, _ + TypeCode.Int32 * TCMAX + TypeCode.String, _ + TypeCode.Int64 * TCMAX + TypeCode.String, _ + TypeCode.Single * TCMAX + TypeCode.String, _ + TypeCode.Double * TCMAX + TypeCode.String, _ + TypeCode.Decimal * TCMAX + TypeCode.String + Return ModString(conv1, tc1, conv2, tc2) + + Case TypeCode.String * TCMAX + TypeCode.String + Return ModStringString(conv1.ToString(Nothing), conv2.ToString(Nothing)) + + Case TypeCode.Boolean * TCMAX + TypeCode.String + Return ModString(conv1, tc1, conv2, tc2) + + Case TypeCode.String * TCMAX + TypeCode.Boolean + Return ModString(conv1, tc1, conv2, tc2) + + + 'EMPTY + Case TypeCode.Empty * TCMAX + TypeCode.Empty + Return ModInt32(0, 0) + + Case TypeCode.Byte * TCMAX + TypeCode.Empty + Return ModByte(conv1.ToByte(Nothing), 0) + + Case TypeCode.Boolean * TCMAX + TypeCode.Empty + Return ModInt16(CShort(ToVBBool(conv1)), 0) + + Case TypeCode.Int16 * TCMAX + TypeCode.Empty + Return ModInt16(conv1.ToInt16(Nothing), 0) + + Case TypeCode.Int32 * TCMAX + TypeCode.Empty + Return ModInt32(conv1.ToInt32(Nothing), 0) + + Case TypeCode.Int64 * TCMAX + TypeCode.Empty + Return ModInt64(conv1.ToInt64(Nothing), 0) + + Case TypeCode.Single * TCMAX + TypeCode.Empty + Return ModSingle(conv1.ToSingle(Nothing), 0) + + Case TypeCode.Double * TCMAX + TypeCode.Empty + Return ModDouble(conv1.ToDouble(Nothing), 0) + + Case TypeCode.Decimal * TCMAX + TypeCode.Empty + Return ModDecimal(conv1, Nothing) + + Case TypeCode.Empty * TCMAX + TypeCode.Boolean + Return ModInt16(0, CShort(ToVBBool(conv2))) + + Case TypeCode.Empty * TCMAX + TypeCode.Byte + Return ModByte(0, conv2.ToByte(Nothing)) + + Case TypeCode.Empty * TCMAX + TypeCode.Int16 + Return ModInt16(0, CShort(ToVBBool(conv2))) + + Case TypeCode.Empty * TCMAX + TypeCode.Int32 + Return ModInt32(0, conv2.ToInt32(Nothing)) + + Case TypeCode.Empty * TCMAX + TypeCode.Int64 + Return ModInt64(0, conv2.ToInt64(Nothing)) + + Case TypeCode.Empty * TCMAX + TypeCode.Single + Return ModSingle(0, conv2.ToSingle(Nothing)) + + Case TypeCode.Empty * TCMAX + TypeCode.Double + Return ModDouble(0, conv2.ToDouble(Nothing)) + + Case TypeCode.Empty * TCMAX + TypeCode.Decimal + Return ModDecimal(Nothing, conv2) + + + 'DECIMAL + Case TypeCode.Decimal * TCMAX + TypeCode.Byte, _ + TypeCode.Decimal * TCMAX + TypeCode.Int16, _ + TypeCode.Decimal * TCMAX + TypeCode.Int32, _ + TypeCode.Decimal * TCMAX + TypeCode.Int64, _ + TypeCode.Decimal * TCMAX + TypeCode.Decimal, _ + TypeCode.Int16 * TCMAX + TypeCode.Decimal, _ + TypeCode.Byte * TCMAX + TypeCode.Decimal, _ + TypeCode.Int32 * TCMAX + TypeCode.Decimal, _ + TypeCode.Int64 * TCMAX + TypeCode.Decimal + Return ModDecimal(conv1, conv2) + + Case TypeCode.Boolean * TCMAX + TypeCode.Decimal + Return ModDecimal(ToVBBoolConv(conv1), conv2) + + Case TypeCode.Decimal * TCMAX + TypeCode.Boolean + Return ModDecimal(conv1, ToVBBoolConv(conv2)) + + + 'DOUBLE + Case TypeCode.Double * TCMAX + TypeCode.Byte, _ + TypeCode.Double * TCMAX + TypeCode.Int16, _ + TypeCode.Double * TCMAX + TypeCode.Int32, _ + TypeCode.Double * TCMAX + TypeCode.Int64, _ + TypeCode.Double * TCMAX + TypeCode.Single, _ + TypeCode.Double * TCMAX + TypeCode.Double, _ + TypeCode.Byte * TCMAX + TypeCode.Double, _ + TypeCode.Int16 * TCMAX + TypeCode.Double, _ + TypeCode.Int32 * TCMAX + TypeCode.Double, _ + TypeCode.Int64 * TCMAX + TypeCode.Double, _ + TypeCode.Single * TCMAX + TypeCode.Double, _ + TypeCode.Double * TCMAX + TypeCode.Decimal, _ + TypeCode.Decimal * TCMAX + TypeCode.Double + Return ModDouble(conv1.ToDouble(Nothing), conv2.ToDouble(Nothing)) + + Case TypeCode.Double * TCMAX + TypeCode.Boolean + Return ModDouble(conv1.ToDouble(Nothing), ToVBBool(conv2)) + + Case TypeCode.Boolean * TCMAX + TypeCode.Double + Return ModDouble(ToVBBool(conv1), conv2.ToDouble(Nothing)) + + + 'SINGLE + Case TypeCode.Single * TCMAX + TypeCode.Byte, _ + TypeCode.Single * TCMAX + TypeCode.Int16, _ + TypeCode.Single * TCMAX + TypeCode.Int32, _ + TypeCode.Single * TCMAX + TypeCode.Int64, _ + TypeCode.Single * TCMAX + TypeCode.Single, _ + TypeCode.Byte * TCMAX + TypeCode.Single, _ + TypeCode.Int16 * TCMAX + TypeCode.Single, _ + TypeCode.Int32 * TCMAX + TypeCode.Single, _ + TypeCode.Int64 * TCMAX + TypeCode.Single, _ + TypeCode.Decimal * TCMAX + TypeCode.Single, _ + TypeCode.Single * TCMAX + TypeCode.Decimal + Return ModSingle(conv1.ToSingle(Nothing), conv2.ToSingle(Nothing)) + + Case TypeCode.Single * TCMAX + TypeCode.Boolean + Return ModSingle(conv1.ToSingle(Nothing), ToVBBool(conv2)) + + Case TypeCode.Boolean * TCMAX + TypeCode.Single + Return ModSingle(ToVBBool(conv1), conv2.ToSingle(Nothing)) + + + 'INT64 + Case TypeCode.Byte * TCMAX + TypeCode.Int64, _ + TypeCode.Int64 * TCMAX + TypeCode.Byte, _ + TypeCode.Int64 * TCMAX + TypeCode.Int16, _ + TypeCode.Int64 * TCMAX + TypeCode.Int32, _ + TypeCode.Int64 * TCMAX + TypeCode.Int64, _ + TypeCode.Int16 * TCMAX + TypeCode.Int64, _ + TypeCode.Int32 * TCMAX + TypeCode.Int64 + Return ModInt64(conv1.ToInt64(Nothing), conv2.ToInt64(Nothing)) + + Case TypeCode.Int64 * TCMAX + TypeCode.Boolean + Return ModInt64(conv1.ToInt64(Nothing), ToVBBool(conv2)) + + Case TypeCode.Boolean * TCMAX + TypeCode.Int64 + Return ModInt64(ToVBBool(conv1), conv2.ToInt64(Nothing)) + + + 'INT32 + Case TypeCode.Int16 * TCMAX + TypeCode.Int32, _ + TypeCode.Int32 * TCMAX + TypeCode.Int16, _ + TypeCode.Int32 * TCMAX + TypeCode.Int32, _ + TypeCode.Int32 * TCMAX + TypeCode.Byte, _ + TypeCode.Byte * TCMAX + TypeCode.Int32 + Return ModInt32(conv1.ToInt32(Nothing), conv2.ToInt32(Nothing)) + + Case TypeCode.Int32 * TCMAX + TypeCode.Boolean + Return ModInt32(conv1.ToInt32(Nothing), ToVBBool(conv2)) + + Case TypeCode.Boolean * TCMAX + TypeCode.Int32 + Return ModInt32(ToVBBool(conv1), conv2.ToInt32(Nothing)) + + + 'INT16 + Case TypeCode.Int16 * TCMAX + TypeCode.Byte, _ + TypeCode.Int16 * TCMAX + TypeCode.Int16, _ + TypeCode.Byte * TCMAX + TypeCode.Int16 + Return ModInt16(conv1.ToInt16(Nothing), conv2.ToInt16(Nothing)) + + Case TypeCode.Boolean * TCMAX + TypeCode.Byte, _ + TypeCode.Boolean * TCMAX + TypeCode.Int16 + Return ModInt16(CShort(ToVBBool(conv1)), conv2.ToInt16(Nothing)) + + Case TypeCode.Byte * TCMAX + TypeCode.Boolean, _ + TypeCode.Int16 * TCMAX + TypeCode.Boolean + Return ModInt16(conv1.ToInt16(Nothing), CShort(ToVBBool(conv2))) + + Case TypeCode.Boolean * TCMAX + TypeCode.Boolean + Return ModInt16(CShort(ToVBBool(conv1)), CShort(ToVBBool(conv2))) + + + 'BYTE + Case TypeCode.Byte * TCMAX + TypeCode.Byte + Return ModByte(conv1.ToByte(Nothing), conv2.ToByte(Nothing)) + + + Case Else + + End Select + + Throw GetNoValidOperatorException(o1, o2) + + End Function + + Private Shared Function ModString(ByVal conv1 As IConvertible, ByVal tc1 As TypeCode, ByVal conv2 As IConvertible, ByVal tc2 As TypeCode) As Object + Dim dbl1, dbl2 As Double + + If tc1 = TypeCode.String Then + dbl1 = DoubleType.FromString(conv1.ToString(Nothing)) + ElseIf tc1 = TypeCode.Boolean Then + dbl1 = ToVBBool(conv1) + Else + dbl1 = conv1.ToDouble(Nothing) + End If + + If tc2 = TypeCode.String Then + dbl2 = DoubleType.FromString(conv2.ToString(Nothing)) + ElseIf tc2 = TypeCode.Boolean Then + dbl2 = ToVBBool(conv2) + Else + dbl2 = conv2.ToDouble(Nothing) + End If + + Return dbl1 Mod dbl2 + End Function + + + Private Shared Function ModStringString(ByVal s1 As String, ByVal s2 As String) As Object + Dim dbl1, dbl2 As Double + + If Not s1 Is Nothing Then + dbl1 = DoubleType.FromString(s1) + End If + + If Not s2 Is Nothing Then + dbl2 = DoubleType.FromString(s2) + End If + + Return dbl1 Mod dbl2 + + End Function + + Private Shared Function ModByte(ByVal i1 As Byte, ByVal i2 As Byte) As Object + Return i1 Mod i2 + End Function + + Private Shared Function ModInt16(ByVal i1 As Short, ByVal i2 As Short) As Object + 'Do operation with Int64 to avoid OverflowException with Int16.MinValue and -1 + Dim result As Integer = CInt(i1) Mod CInt(i2) + + If result < Int16.MinValue OrElse result > Int16.MaxValue Then + Return result + Else + Return CShort(result) + End If + End Function + + Private Shared Function ModInt32(ByVal i1 As Integer, ByVal i2 As Integer) As Object + + 'Do operation with Int64 to avoid OverflowException with Int32.MinValue and -1 + Dim result As Long = CLng(i1) Mod CLng(i2) + + If result < Int32.MinValue OrElse result > Int32.MaxValue Then + Return result + Else + Return CInt(result) + End If + End Function + + Private Shared Function ModInt64(ByVal i1 As Long, ByVal i2 As Long) As Object + 'If i1 = Int64.MinValue and i2 = -1, then we get an overflow + Try + Return i1 Mod i2 + Catch ex As OverflowException + Dim DecimalResult As Decimal + DecimalResult = CDec(i1) Mod CDec(i2) + 'Overflow is not caused by remainder, so we will most likely still return Int64 + If DecimalResult < Int64.MinValue OrElse DecimalResult > Int64.MaxValue Then + Return DecimalResult + Else + Return CLng(DecimalResult) + End If + End Try + End Function + + Private Shared Function ModSingle(ByVal sng1 As Single, ByVal sng2 As Single) As Object + Return sng1 Mod sng2 + End Function + + Private Shared Function ModDouble(ByVal d1 As Double, ByVal d2 As Double) As Object + Return d1 Mod d2 + End Function + + Private Shared Function ModDecimal(ByVal conv1 As IConvertible, ByVal conv2 As IConvertible) As Object + Dim d1, d2 As Decimal + If Not conv1 Is Nothing Then + d1 = conv1.ToDecimal(Nothing) + End If + If Not conv2 Is Nothing Then + d2 = conv2.ToDecimal(Nothing) + End If + + Return (d1 Mod d2) + End Function + + + + Public Shared Function IDivObj(ByVal o1 As Object, ByVal o2 As Object) As Object + + Dim conv1, conv2 As IConvertible + Dim tc1, tc2 As TypeCode + + conv1 = TryCast(o1, IConvertible) + + If conv1 Is Nothing Then + If o1 Is Nothing Then + tc1 = TypeCode.Empty + Else + tc1 = TypeCode.Object + End If + Else + tc1 = conv1.GetTypeCode() + End If + + + conv2 = TryCast(o2, IConvertible) + + If conv2 Is Nothing Then + If o2 Is Nothing Then + tc2 = TypeCode.Empty + Else + tc2 = TypeCode.Object + End If + Else + tc2 = conv2.GetTypeCode() + End If + + Select Case tc1 * TCMAX + tc2 + + 'STRING + Case TypeCode.Empty * TCMAX + TypeCode.String + Return IDivideInt64(0, LongType.FromString(conv2.ToString(Nothing))) + + Case TypeCode.String * TCMAX + TypeCode.Empty + Return IDivideInt64(LongType.FromString(conv1.ToString(Nothing)), 0) + + Case TypeCode.Byte * TCMAX + TypeCode.String, _ + TypeCode.Int16 * TCMAX + TypeCode.String, _ + TypeCode.Int32 * TCMAX + TypeCode.String, _ + TypeCode.Int64 * TCMAX + TypeCode.String, _ + TypeCode.Single * TCMAX + TypeCode.String, _ + TypeCode.Double * TCMAX + TypeCode.String, _ + TypeCode.Decimal * TCMAX + TypeCode.String + Return IDivideString(conv1, tc1, conv2, tc2) + + Case TypeCode.String * TCMAX + TypeCode.String + Return IDivideStringString(conv1.ToString(Nothing), conv2.ToString(Nothing)) + + Case TypeCode.String * TCMAX + TypeCode.Boolean + Return IDivideInt64(LongType.FromString(conv1.ToString(Nothing)), ToVBBool(conv2)) + + Case TypeCode.String * TCMAX + TypeCode.Byte, _ + TypeCode.String * TCMAX + TypeCode.Int16, _ + TypeCode.String * TCMAX + TypeCode.Int32, _ + TypeCode.String * TCMAX + TypeCode.Int64, _ + TypeCode.String * TCMAX + TypeCode.Single, _ + TypeCode.String * TCMAX + TypeCode.Double, _ + TypeCode.String * TCMAX + TypeCode.Decimal + Return IDivideInt64(LongType.FromString(conv1.ToString(Nothing)), conv2.ToInt64(Nothing)) + + + 'EMPTY + Case TypeCode.Empty * TCMAX + TypeCode.Empty + Return IDivideInt32(0, 0) + + Case TypeCode.Boolean * TCMAX + TypeCode.Empty + Return IDivideInt16(CShort(ToVBBool(conv1)), 0) + + Case TypeCode.Byte * TCMAX + TypeCode.Empty + Return IDivideByte(conv1.ToByte(Nothing), 0) + + Case TypeCode.Int16 * TCMAX + TypeCode.Empty + Return IDivideInt16(conv1.ToInt16(Nothing), 0) + + Case TypeCode.Int32 * TCMAX + TypeCode.Empty + Return IDivideInt32(conv1.ToInt32(Nothing), 0) + + Case TypeCode.Int64 * TCMAX + TypeCode.Empty, _ + TypeCode.Decimal * TCMAX + TypeCode.Empty, _ + TypeCode.Single * TCMAX + TypeCode.Empty, _ + TypeCode.Double * TCMAX + TypeCode.Empty + Return IDivideInt64(conv1.ToInt64(Nothing), 0) + + Case TypeCode.Empty * TCMAX + TypeCode.Boolean + Return IDivideInt64(0, ToVBBool(conv2)) + + Case TypeCode.Empty * TCMAX + TypeCode.Byte + Return IDivideByte(0, conv2.ToByte(Nothing)) + + Case TypeCode.Empty * TCMAX + TypeCode.Int16 + Return IDivideInt16(0, conv2.ToInt16(Nothing)) + + Case TypeCode.Empty * TCMAX + TypeCode.Int32 + Return IDivideInt32(0, conv2.ToInt32(Nothing)) + + Case TypeCode.Empty * TCMAX + TypeCode.Int64, _ + TypeCode.Empty * TCMAX + TypeCode.Decimal, _ + TypeCode.Empty * TCMAX + TypeCode.Single, _ + TypeCode.Empty * TCMAX + TypeCode.Double + Return IDivideInt64(0, conv2.ToInt64(Nothing)) + + 'BOOLEAN + Case TypeCode.Boolean * TCMAX + TypeCode.Byte, _ + TypeCode.Boolean * TCMAX + TypeCode.Int16 + Return IDivideInt16(CShort(ToVBBool(conv1)), conv2.ToInt16(Nothing)) + + Case TypeCode.Boolean * TCMAX + TypeCode.Int32 + Return IDivideInt32(ToVBBool(conv1), conv2.ToInt32(Nothing)) + + Case TypeCode.Boolean * TCMAX + TypeCode.Int64, _ + TypeCode.Boolean * TCMAX + TypeCode.Decimal, _ + TypeCode.Boolean * TCMAX + TypeCode.Single, _ + TypeCode.Boolean * TCMAX + TypeCode.Double + + Return IDivideInt64(ToVBBool(conv1), conv2.ToInt64(Nothing)) + + Case TypeCode.Boolean * TCMAX + TypeCode.Boolean + Return IDivideInt16(CShort(ToVBBool(conv1)), CShort(ToVBBool(conv2))) + + Case TypeCode.Boolean * TCMAX + TypeCode.String + Return IDivideInt64(ToVBBool(conv1), LongType.FromString(conv2.ToString(Nothing))) + + Case TypeCode.Byte * TCMAX + TypeCode.Boolean, _ + TypeCode.Int16 * TCMAX + TypeCode.Boolean + Return IDivideInt16(conv1.ToInt16(Nothing), CShort(ToVBBool(conv2))) + + Case TypeCode.Int32 * TCMAX + TypeCode.Boolean + Return IDivideInt32(conv1.ToInt32(Nothing), ToVBBool(conv2)) + + Case TypeCode.Int64 * TCMAX + TypeCode.Boolean, _ + TypeCode.Decimal * TCMAX + TypeCode.Boolean, _ + TypeCode.Single * TCMAX + TypeCode.Boolean, _ + TypeCode.Double * TCMAX + TypeCode.Boolean + Return IDivideInt64(conv1.ToInt64(Nothing), ToVBBool(conv2)) + + Case TypeCode.Byte * TCMAX + TypeCode.Byte + Return IDivideByte(conv1.ToByte(Nothing), conv2.ToByte(Nothing)) + + Case TypeCode.Byte * TCMAX + TypeCode.Int16, _ + TypeCode.Int16 * TCMAX + TypeCode.Byte, _ + TypeCode.Int16 * TCMAX + TypeCode.Int16 + + Return IDivideInt16(conv1.ToInt16(Nothing), conv2.ToInt16(Nothing)) + + Case TypeCode.Int16 * TCMAX + TypeCode.Int32, _ + TypeCode.Int32 * TCMAX + TypeCode.Int16, _ + TypeCode.Int32 * TCMAX + TypeCode.Int32, _ + TypeCode.Int32 * TCMAX + TypeCode.Byte, _ + TypeCode.Byte * TCMAX + TypeCode.Int32 + Return IDivideInt32(conv1.ToInt32(Nothing), conv2.ToInt32(Nothing)) + + 'OTHERS + Case TypeCode.Double * TCMAX + TypeCode.Byte, _ + TypeCode.Double * TCMAX + TypeCode.Int16, _ + TypeCode.Double * TCMAX + TypeCode.Int32, _ + TypeCode.Double * TCMAX + TypeCode.Int64, _ + TypeCode.Double * TCMAX + TypeCode.Single, _ + TypeCode.Double * TCMAX + TypeCode.Double, _ + TypeCode.Byte * TCMAX + TypeCode.Double, _ + TypeCode.Int16 * TCMAX + TypeCode.Double, _ + TypeCode.Int32 * TCMAX + TypeCode.Double, _ + TypeCode.Int64 * TCMAX + TypeCode.Double, _ + TypeCode.Single * TCMAX + TypeCode.Double, _ + TypeCode.Double * TCMAX + TypeCode.Decimal, _ + TypeCode.Decimal * TCMAX + TypeCode.Double, _ + TypeCode.Single * TCMAX + TypeCode.Byte, _ + TypeCode.Single * TCMAX + TypeCode.Int16, _ + TypeCode.Single * TCMAX + TypeCode.Int32, _ + TypeCode.Single * TCMAX + TypeCode.Int64, _ + TypeCode.Single * TCMAX + TypeCode.Single, _ + TypeCode.Byte * TCMAX + TypeCode.Single, _ + TypeCode.Int16 * TCMAX + TypeCode.Single, _ + TypeCode.Int32 * TCMAX + TypeCode.Single, _ + TypeCode.Int64 * TCMAX + TypeCode.Single, _ + TypeCode.Decimal * TCMAX + TypeCode.Single, _ + TypeCode.Single * TCMAX + TypeCode.Decimal, _ + TypeCode.Byte * TCMAX + TypeCode.Int64, _ + TypeCode.Int64 * TCMAX + TypeCode.Byte, _ + TypeCode.Int64 * TCMAX + TypeCode.Int16, _ + TypeCode.Int64 * TCMAX + TypeCode.Int32, _ + TypeCode.Int64 * TCMAX + TypeCode.Int64, _ + TypeCode.Int16 * TCMAX + TypeCode.Int64, _ + TypeCode.Int32 * TCMAX + TypeCode.Int64, _ + TypeCode.Decimal * TCMAX + TypeCode.Byte, _ + TypeCode.Decimal * TCMAX + TypeCode.Int16, _ + TypeCode.Decimal * TCMAX + TypeCode.Int32, _ + TypeCode.Decimal * TCMAX + TypeCode.Int64, _ + TypeCode.Decimal * TCMAX + TypeCode.Decimal, _ + TypeCode.Int16 * TCMAX + TypeCode.Decimal, _ + TypeCode.Byte * TCMAX + TypeCode.Decimal, _ + TypeCode.Int32 * TCMAX + TypeCode.Decimal, _ + TypeCode.Int64 * TCMAX + TypeCode.Decimal + Return IDivideInt64(conv1.ToInt64(Nothing), conv2.ToInt64(Nothing)) + + Case Else + + End Select + + Throw GetNoValidOperatorException(o1, o2) + + End Function + + Private Shared Function IDivideString(ByVal conv1 As IConvertible, ByVal tc1 As TypeCode, ByVal conv2 As IConvertible, ByVal tc2 As TypeCode) As Object + Dim lng1, lng2 As Int64 + + If tc1 = TypeCode.String Then + Try + lng1 = LongType.FromString(conv1.ToString(Nothing)) + Catch ex As StackOverflowException + Throw ex + Catch ex As OutOfMemoryException + Throw ex + Catch ex As System.Threading.ThreadAbortException + Throw ex + Catch + Throw GetNoValidOperatorException(conv1, conv2) + End Try + ElseIf tc1 = TypeCode.Boolean Then + lng1 = ToVBBool(conv1) + Else + lng1 = conv1.ToInt64(Nothing) + End If + + If tc2 = TypeCode.String Then + Try + lng2 = LongType.FromString(conv2.ToString(Nothing)) + Catch ex As StackOverflowException + Throw ex + Catch ex As OutOfMemoryException + Throw ex + Catch ex As System.Threading.ThreadAbortException + Throw ex + Catch + Throw GetNoValidOperatorException(conv1, conv2) + End Try + ElseIf tc2 = TypeCode.Boolean Then + lng2 = ToVBBool(conv2) + Else + lng2 = conv2.ToInt64(Nothing) + End If + + Return lng1 \ lng2 + End Function + + Private Shared Function IDivideStringString(ByVal s1 As String, ByVal s2 As String) As Object + Dim lng1, lng2 As Int64 + + If Not s1 Is Nothing Then + lng1 = LongType.FromString(s1) + End If + + If Not s2 Is Nothing Then + lng2 = LongType.FromString(s2) + End If + + Return lng1 \ lng2 + + End Function + + Private Shared Function IDivideByte(ByVal d1 As Byte, ByVal d2 As Byte) As Object + Return d1 \ d2 + End Function + + Private Shared Function IDivideInt16(ByVal d1 As Int16, ByVal d2 As Int16) As Object + Return d1 \ d2 + End Function + + Private Shared Function IDivideInt32(ByVal d1 As Int32, ByVal d2 As Int32) As Object + Return d1 \ d2 + End Function + + Private Shared Function IDivideInt64(ByVal d1 As Int64, ByVal d2 As Int64) As Object + Return d1 \ d2 + End Function + + Public Shared Function ShiftLeftObj(ByVal o1 As Object, ByVal amount As Int32) As Object + + Dim conv1 As IConvertible + Dim tc1 As TypeCode + + conv1 = TryCast(o1, IConvertible) + + If conv1 Is Nothing Then + If o1 Is Nothing Then + tc1 = TypeCode.Empty + Else + tc1 = TypeCode.Object + End If + Else + tc1 = conv1.GetTypeCode() + End If + + Select Case tc1 + Case TypeCode.Empty + Return Nothing << amount + Case TypeCode.Boolean + Return CShort(conv1.ToBoolean(Nothing)) << amount + Case TypeCode.Byte + Return conv1.ToByte(Nothing) << amount + Case TypeCode.Int16 + Return conv1.ToInt16(Nothing) << amount + Case TypeCode.Int32 + Return conv1.ToInt32(Nothing) << amount + Case TypeCode.Int64, TypeCode.Single, TypeCode.Double, TypeCode.Decimal + Return conv1.ToInt64(Nothing) << amount + Case TypeCode.String + Return LongType.FromString(conv1.ToString(Nothing)) << amount + End Select + + Throw GetNoValidOperatorException(o1) + End Function + + + + Public Shared Function ShiftRightObj(ByVal o1 As Object, ByVal amount As Int32) As Object + + Dim conv1 As IConvertible + Dim tc1 As TypeCode + + conv1 = TryCast(o1, IConvertible) + + If conv1 Is Nothing Then + If o1 Is Nothing Then + tc1 = TypeCode.Empty + Else + tc1 = TypeCode.Object + End If + Else + tc1 = conv1.GetTypeCode() + End If + + Select Case tc1 + Case TypeCode.Empty + Return Nothing >> amount + Case TypeCode.Boolean + Return CShort(conv1.ToBoolean(Nothing)) >> amount + Case TypeCode.Byte + Return conv1.ToByte(Nothing) >> amount + Case TypeCode.Int16 + Return conv1.ToInt16(Nothing) >> amount + Case TypeCode.Int32 + Return conv1.ToInt32(Nothing) >> amount + Case TypeCode.Int64, TypeCode.Single, TypeCode.Double, TypeCode.Decimal + Return conv1.ToInt64(Nothing) >> amount + Case TypeCode.String + Return LongType.FromString(conv1.ToString(Nothing)) >> amount + End Select + + Throw GetNoValidOperatorException(o1) + End Function + + + Public Shared Function XorObj(ByVal obj1 As Object, ByVal obj2 As Object) As Object + + If obj1 Is Nothing AndAlso obj2 Is Nothing Then + Return False + End If + + Select Case GetWidestType(obj1, obj2) + + Case TypeCode.Boolean, _ + TypeCode.Byte, _ + TypeCode.Int16, _ + TypeCode.Int32, _ + TypeCode.Int64, _ + TypeCode.Decimal, _ + TypeCode.Single, _ + TypeCode.Double, _ + TypeCode.String + Return BooleanType.FromObject(obj1) Xor BooleanType.FromObject(obj2) + + Case TypeCode.Char + ' Fall through to error + + Case TypeCode.DateTime + ' Fall through to error + + Case Else + ' Fall through to error + + End Select + + Throw GetNoValidOperatorException(obj1, obj2) + + End Function + + + + Public Shared Function LikeObj(ByVal vLeft As Object, ByVal vRight As Object, ByVal CompareOption As CompareMethod) As Boolean + Return StrLike(StringType.FromObject(vLeft), StringType.FromObject(vRight), CompareOption) + End Function + + Public Shared Function StrCatObj(ByVal vLeft As Object, ByVal vRight As Object) As Object + Dim LeftIsNull As Boolean = TypeOf vLeft Is System.DBNull + Dim RightIsNull As Boolean = TypeOf vRight Is System.DBNull + + If LeftIsNull And RightIsNull Then + Return vLeft + ElseIf LeftIsNull And Not RightIsNull Then + vLeft = "" + ElseIf RightIsNull And Not LeftIsNull Then + vRight = "" + End If + + Return StringType.FromObject(vLeft) & StringType.FromObject(vRight) + End Function + + Friend Overloads Shared Function CTypeHelper(ByVal obj As Object, ByVal toType As TypeCode) As Object + + If obj Is Nothing Then + Return Nothing + End If + + Select Case toType + + Case TypeCode.Boolean + Return BooleanType.FromObject(obj) + + Case TypeCode.Byte + Return ByteType.FromObject(obj) + + Case TypeCode.Int16 + Return ShortType.FromObject(obj) + + Case TypeCode.Int32 + Return IntegerType.FromObject(obj) + + Case TypeCode.Int64 + Return LongType.FromObject(obj) + + Case TypeCode.Decimal + Return DecimalType.FromObject(obj) + + Case TypeCode.Single + Return SingleType.FromObject(obj) + + Case TypeCode.Double + Return DoubleType.FromObject(obj) + + Case TypeCode.String + Return StringType.FromObject(obj) + + Case TypeCode.Char + Return CharType.FromObject(obj) + + Case TypeCode.DateTime + Return DateType.FromObject(obj) + + Case Else + ' Fall through and throw exception + + End Select + + Throw New InvalidCastException(GetResourceString(ResID.InvalidCast_FromTo, VBFriendlyName(obj), VBFriendlyName(TypeFromTypeCode(toType)))) + + End Function + + Friend Overloads Shared Function CTypeHelper(ByVal obj As Object, ByVal toType As Type) As Object + Dim fromType As System.Type + Dim IsToByRef As Boolean + Dim Result As Object + + If obj Is Nothing Then + Return Nothing + End If + + If toType Is GetType(Object) Then + Return obj + End If + + fromType = obj.GetType() + + 'REVIEW: - Should we handle ByRef in this manner? + ' what happens when it's returned as non-byref? + If toType.IsByRef Then + toType = toType.GetElementType() + IsToByRef = True + End If + + If fromType.IsByRef Then + fromType = fromType.GetElementType() + End If + + If (fromType Is toType OrElse toType Is GetType(Object)) Then + If IsToByRef Then + 'Make sure we copy boxed primitives + Result = ObjectType.GetObjectValuePrimitive(obj) + GoTo CheckForEnumAndExit + Else + Return obj + End If + End If + 'END REVIEW + + Dim toTypeCode As TypeCode = Type.GetTypeCode(toType) + + If toTypeCode = TypeCode.Object Then + If toType Is GetType(Object) OrElse toType.IsInstanceOfType(obj) Then + Return obj + 'Char() typecode is object, so we need to test for it here + Else + Dim ObjString As String = TryCast(obj, String) + + If (ObjString IsNot Nothing) AndAlso (toType Is GetType(Char())) Then + Return CharArrayType.FromString(ObjString) + Else + Throw New InvalidCastException(GetResourceString(ResID.InvalidCast_FromTo, VBFriendlyName(fromType), VBFriendlyName(toType))) + End If + End If + Else + Result = CTypeHelper(obj, toTypeCode) + End If + +CheckForEnumAndExit: + If toType.IsEnum Then + Return System.Enum.ToObject(toType, Result) + End If + + Return Result + End Function + + Private Shared Function GetNoValidOperatorException(ByVal Operand As Object) As Exception + Return New InvalidCastException(GetResourceString(ResID.NoValidOperator_OneOperand, VBFriendlyName(Operand))) + End Function + + Private Shared Function GetNoValidOperatorException(ByVal Left As Object, ByVal Right As Object) As Exception + Const MAX_INSERTION_SIZE As Integer = 32 + + Dim Substitution1 As String + Dim Substitution2 As String + + If Left Is Nothing Then + Substitution1 = "'Nothing'" + Else + Dim LeftString As String = TryCast(Left, String) + + If LeftString IsNot Nothing Then + Substitution1 = _ + GetResourceString(ResID.NoValidOperator_StringType1, Strings.Left(LeftString, MAX_INSERTION_SIZE)) + Else + Substitution1 = GetResourceString(ResID.NoValidOperator_NonStringType1, VBFriendlyName(Left)) + End If + End If + + If Right Is Nothing Then + Substitution2 = "'Nothing'" + Else + Dim RightString As String = TryCast(Right, String) + + If RightString IsNot Nothing Then + Substitution2 = _ + GetResourceString(ResID.NoValidOperator_StringType1, Strings.Left(RightString, MAX_INSERTION_SIZE)) + Else + Substitution2 = GetResourceString(ResID.NoValidOperator_NonStringType1, VBFriendlyName(Right)) + End If + End If + + Return New InvalidCastException(GetResourceString(ResID.NoValidOperator_TwoOperands, Substitution1, Substitution2)) + End Function + + '** + '** Used when RuntimeHelpers.GetObjectValue has already been called + '** + '** This is used to prevent copying structures multiple times + '** + Public Shared Function GetObjectValuePrimitive(ByVal o As Object) As Object + + Dim iconv As IConvertible + + If o Is Nothing Then + Return Nothing + End If + + iconv = TryCast(o, IConvertible) + + If iconv Is Nothing Then + Return o + End If + + Select Case iconv.GetTypeCode() + + Case TypeCode.Char + Return iconv.ToChar(Nothing) + + Case TypeCode.String + Return o + + Case TypeCode.Boolean + Return iconv.ToBoolean(Nothing) + + Case TypeCode.Byte + Return iconv.ToByte(Nothing) + + Case TypeCode.SByte + Return iconv.ToSByte(Nothing) + + Case TypeCode.Int16 + Return iconv.ToInt16(Nothing) + + Case TypeCode.UInt16 + Return iconv.ToUInt16(Nothing) + + Case TypeCode.Int32 + Return iconv.ToInt32(Nothing) + + Case TypeCode.UInt32 + Return iconv.ToUInt32(Nothing) + + Case TypeCode.Int64 + Return iconv.ToInt64(Nothing) + + Case TypeCode.UInt64 + Return iconv.ToUInt64(Nothing) + + Case TypeCode.Single + Return iconv.ToSingle(Nothing) + + Case TypeCode.Double + Return iconv.ToDouble(Nothing) + + Case TypeCode.Decimal + Return iconv.ToDecimal(Nothing) + + Case TypeCode.DateTime + Return iconv.ToDateTime(Nothing) + + Case Else + Return o + + End Select + + End Function + + End Class + +#End Region + +End Namespace + diff --git a/Microsoft.VisualBasic/runtime/msvbalib/Helpers/OperatorResolution.vb b/Microsoft.VisualBasic/runtime/msvbalib/Helpers/OperatorResolution.vb new file mode 100644 index 000000000..39255b6fd --- /dev/null +++ b/Microsoft.VisualBasic/runtime/msvbalib/Helpers/OperatorResolution.vb @@ -0,0 +1,296 @@ +' Copyright (c) Microsoft Corporation. All rights reserved. + +Imports System +Imports System.Collections.Generic +Imports System.Diagnostics +Imports System.Dynamic +Imports System.Linq.Expressions +Imports System.Reflection + +Imports Microsoft.VisualBasic.CompilerServices.Symbols +Imports Microsoft.VisualBasic.CompilerServices.OverloadResolution +Imports Microsoft.VisualBasic.CompilerServices.Utils + +Namespace Microsoft.VisualBasic.CompilerServices + + Partial Public NotInheritable Class Operators + + Friend Shared Function CollectOperators( _ + ByVal Op As UserDefinedOperator, _ + ByVal Type1 As System.Type, _ + ByVal Type2 As System.Type, _ + ByRef FoundType1Operators As Boolean, _ + ByRef FoundType2Operators As Boolean) As List(Of Method) + + 'Given an operator kind and two types to scan, construct a list of operators by + 'collecting operators from both types. + ' + 'The second type can be NULL, in which case operators are collected from only the first + 'type. + + Dim SearchBothTypes As Boolean = Type2 IsNot Nothing + Dim Result As List(Of Method) + + If Not IsRootObjectType(Type1) AndAlso IsClassOrValueType(Type1) Then + + Dim Container As Container = New Container(Type1) + Dim Members As MemberInfo() = Container.LookupNamedMembers(OperatorCLSNames(Op)) + + Result = _ + CollectOverloadCandidates( _ + Members, _ + Nothing, _ + IIf(IsUnaryOperator(Op), 1, 2), _ + Nothing, _ + Nothing, _ + True, _ + Nothing, _ + Nothing, _ + Nothing, + Nothing) + + If Result.Count > 0 Then FoundType1Operators = True + + Else + Result = New List(Of Method) + End If + + If SearchBothTypes AndAlso Not IsRootObjectType(Type2) AndAlso IsClassOrValueType(Type2) Then + + Dim CommonAncestor As Type = Type1 + While CommonAncestor IsNot Nothing + If IsOrInheritsFrom(Type2, CommonAncestor) Then + Exit While + End If + CommonAncestor = CommonAncestor.BaseType + End While + + Dim Container As Container = New Container(Type2) + Dim Members As MemberInfo() = Container.LookupNamedMembers(OperatorCLSNames(Op)) + Dim SecondResult As List(Of Method) + + 'Collect operators up until the common ancestor because we don't want + 'duplicate operators in the result list. + SecondResult = _ + CollectOverloadCandidates( _ + Members, _ + Nothing, _ + IIf(IsUnaryOperator(Op), 1, 2), _ + Nothing, _ + Nothing, _ + True, _ + CommonAncestor, _ + Nothing, _ + Nothing, + Nothing) + + If SecondResult.Count > 0 Then FoundType2Operators = True + + 'Merge the second result into the main result. + Result.AddRange(SecondResult) + End If + + Return Result + + End Function + + Friend Shared Function ResolveUserDefinedOperator( _ + ByVal Op As UserDefinedOperator, _ + ByVal Arguments As Object(), _ + ByVal ReportErrors As Boolean) As Method + + 'Given an operation to perform with operands, select the appropriate + 'user-defined operator. If one exists, it will be supplied as an out parameter. This + 'function will generate compile errors if the resolution is ambiguous. + ' + 'Unary operators will have only one operand. + ' + 'To select the appropriate operator, first collect all applicable operators. If only one + 'exists, resolution is complete. If more than one exists, perform standard method overload + 'resolution to select the correct operator. If none exist, report an error. + ' + 'See the language specification for an in-depth discussion of the algorithm. + + Debug.Assert((IsBinaryOperator(Op) AndAlso Arguments.Length = 2) OrElse _ + (IsUnaryOperator(Op) AndAlso Arguments.Length = 1), _ + "second operand supplied for a unary operator?") + + 'The value Nothing is treated as the default value of the type of the other operand in a binary operator expression. + 'If one of the operands is Nothing, find the other operand's type now. In a unary operator expression, or if both + 'operands are Nothing in a binary operator expression, the type of operation is Integer. However, these cases + '(necessarily involving intrinsic types) should not reach this far. + + 'During normal overload resolution, Nothing matches any type. In the context of operator overload resolution, + 'Nothing must match only the type of the other operand. To do this, we introduce the notion of a typed Nothing. + 'We represent a typed Nothing with an instance of a special object which overload resolution uses to understand that + 'Nothing should match only one type. + + 'Make a copy of the arguments so that typed Nothings don't escape from this function. + Arguments = DirectCast(Arguments.Clone, Object()) + + Dim LeftType As Type + Dim RightType As Type = Nothing + + If Arguments(0) Is Nothing Then + Debug.Assert(Arguments.Length > 1, "unary op on Nothing unexpected here") + Debug.Assert(Arguments(1) IsNot Nothing, "binary op on Nothing operands unexpected here") + + RightType = Arguments(1).GetType + LeftType = RightType + Arguments(0) = New TypedNothing(LeftType) + Else + LeftType = Arguments(0).GetType + + If Arguments.Length > 1 Then + If Arguments(1) IsNot Nothing Then + RightType = Arguments(1).GetType + Else + RightType = LeftType + Arguments(1) = New TypedNothing(RightType) + End If + End If + End If + + 'First construct the list of operators we will consider. + Dim FoundLeftOperators As Boolean + Dim FoundRightOperators As Boolean + Dim Candidates As List(Of Method) = _ + CollectOperators( _ + Op, _ + LeftType, _ + RightType, _ + FoundLeftOperators, _ + FoundRightOperators) + + If Candidates.Count > 0 Then + 'There are operators available, so use standard method overload resolution + 'to choose the correct one. + + Dim Failure As ResolutionFailure + + Return _ + ResolveOverloadedCall( _ + OperatorNames(Op), _ + Candidates, _ + Arguments, _ + NoArgumentNames, _ + NoTypeArguments, _ + BindingFlags.InvokeMethod, _ + ReportErrors, _ + Failure) + End If + + Return Nothing + + End Function + + Friend Shared Function InvokeUserDefinedOperator( _ + ByVal OperatorMethod As Method, _ + ByVal ForceArgumentValidation As Boolean, _ + ByVal ParamArray Arguments As Object()) As Object + + Debug.Assert(OperatorMethod IsNot Nothing, "Operator can't be nothing at this point") + + 'Overload resolution will potentially select one method before validating arguments. + 'Validate those arguments now. + 'CONSIDER: move the overload list construction up and out of overload resolution and into this function. + If Not OperatorMethod.ArgumentsValidated OrElse ForceArgumentValidation Then + + If Not CanMatchArguments(OperatorMethod, Arguments, NoArgumentNames, NoTypeArguments, False, Nothing) Then + + Const ReportErrors As Boolean = True + If ReportErrors Then + Dim ErrorMessage As String = "" + Dim Errors As New List(Of String) + + Dim Result As Boolean = _ + CanMatchArguments(OperatorMethod, Arguments, NoArgumentNames, NoTypeArguments, False, Errors) + + Debug.Assert(Result = False AndAlso Errors.Count > 0, "expected this candidate to fail") + + For Each ErrorString As String In Errors + ErrorMessage &= vbCrLf & " " & ErrorString + Next + + ErrorMessage = GetResourceString(ResID.MatchArgumentFailure2, OperatorMethod.ToString, ErrorMessage) + 'We are missing a member which can match the arguments, so throw a missing member exception. + Throw New InvalidCastException(ErrorMessage) + End If + + Return Nothing + End If + + End If + + Dim BaseReference As Container = New Container(OperatorMethod.DeclaringType) + Return _ + BaseReference.InvokeMethod( _ + OperatorMethod, _ + Arguments, _ + Nothing, _ + BindingFlags.InvokeMethod) + End Function 'InvokeUserDefinedOperator + + Friend Shared Function InvokeUserDefinedOperator( _ + ByVal Op As UserDefinedOperator, _ + ByVal ParamArray Arguments As Object()) As Object + + If IDOUtils.TryCastToIDMOP(Arguments(0)) IsNot Nothing Then + Return IDOBinder.InvokeUserDefinedOperator(Op, Arguments) + Else + Return InvokeObjectUserDefinedOperator(Op, Arguments) + End If + End Function 'InvokeUserDefinedOperator + + _ + _ + Public Shared Function FallbackInvokeUserDefinedOperator( _ + ByVal vbOp As Object, _ + ByVal Arguments As Object()) As Object + + Return InvokeObjectUserDefinedOperator(CType(vbOp, UserDefinedOperator), Arguments) + End Function 'FallbackInvokeUserDefinedOperator + + Friend Shared Function InvokeObjectUserDefinedOperator( _ + ByVal Op As UserDefinedOperator, _ + ByVal Arguments As Object()) As Object + + Dim OperatorMethod As Method = ResolveUserDefinedOperator(Op, Arguments, True) + + If OperatorMethod IsNot Nothing Then + Return InvokeUserDefinedOperator(OperatorMethod, False, Arguments) + End If + + 'There are no results, so the operation is not defined for the operands. + If Arguments.Length > 1 Then + Throw GetNoValidOperatorException(Op, Arguments(0), Arguments(1)) + Else + Throw GetNoValidOperatorException(Op, Arguments(0)) + End If + End Function 'InvokeObjectUserDefinedOperator + + Friend Shared Function GetCallableUserDefinedOperator( _ + ByVal Op As UserDefinedOperator, _ + ByVal ParamArray Arguments As Object()) As Method + + Dim OperatorMethod As Method = ResolveUserDefinedOperator(Op, Arguments, False) + + If OperatorMethod IsNot Nothing Then + 'Overload resolution will potentially select one method before validating arguments. + 'Validate those arguments now. + If Not OperatorMethod.ArgumentsValidated Then + If Not CanMatchArguments(OperatorMethod, Arguments, NoArgumentNames, NoTypeArguments, False, Nothing) Then + Return Nothing + End If + End If + End If + + Return OperatorMethod + End Function + + End Class + +End Namespace + + + diff --git a/Microsoft.VisualBasic/runtime/msvbalib/Helpers/Operators.vb b/Microsoft.VisualBasic/runtime/msvbalib/Helpers/Operators.vb new file mode 100644 index 000000000..1d11ce70f --- /dev/null +++ b/Microsoft.VisualBasic/runtime/msvbalib/Helpers/Operators.vb @@ -0,0 +1,6912 @@ +' Copyright (c) Microsoft Corporation. All rights reserved. + +Option Strict On + +Imports System +Imports System.Diagnostics +Imports System.Globalization +Imports System.Collections.Generic +Imports System.Reflection + +Imports Microsoft.VisualBasic.CompilerServices.ExceptionUtils +Imports Microsoft.VisualBasic.CompilerServices.Symbols +Imports Microsoft.VisualBasic.CompilerServices.Utils + +Namespace Microsoft.VisualBasic.CompilerServices + +#If TELESTO Then + 'FIXME: + Public NotInheritable Class Operators +#Else + _ + Public NotInheritable Class Operators +#End If + + Friend Shared ReadOnly Boxed_ZeroDouble As Object = 0.0R + Friend Shared ReadOnly Boxed_ZeroSinge As Object = 0.0F + Friend Shared ReadOnly Boxed_ZeroDecimal As Object = CDec(0) + Friend Shared ReadOnly Boxed_ZeroLong As Object = 0L + Friend Shared ReadOnly Boxed_ZeroInteger As Object = 0I + Friend Shared ReadOnly Boxed_ZeroShort As Object = 0S + Friend Shared ReadOnly Boxed_ZeroULong As Object = 0UL + Friend Shared ReadOnly Boxed_ZeroUInteger As Object = 0UI + Friend Shared ReadOnly Boxed_ZeroUShort As Object = 0US + Friend Shared ReadOnly Boxed_ZeroSByte As Object = CSByte(0) + Friend Shared ReadOnly Boxed_ZeroByte As Object = CByte(0) + + Private Sub New() + End Sub + + Private Const TCMAX As Integer = TypeCode.String + 1 + + Private Shared Function ToVBBool(ByVal conv As IConvertible) As SByte + Return CSByte(conv.ToBoolean(Nothing)) + End Function + + Private Shared Function ToVBBoolConv(ByVal conv As IConvertible) As IConvertible + Return CSByte(conv.ToBoolean(Nothing)) + End Function + + 'This function determines the enum result type of And, Or, Xor operations. + 'If the type of Left and Right are the same enum type, then return that type, otherwise if + 'one is an enum and the other is Nothing, return that type, otherwise return Nothing. + Private Shared Function GetEnumResult(ByVal Left As Object, ByVal Right As Object) As Type + + Debug.Assert(Left Is Nothing OrElse Right Is Nothing OrElse CType(Left, IConvertible).GetTypeCode = CType(Right, IConvertible).GetTypeCode, _ + "Expected identical type codes for checking enum result") + + + If Left IsNot Nothing Then + + If TypeOf Left Is System.Enum Then + + If Right Is Nothing Then + Return Left.GetType + + ElseIf TypeOf Right Is System.Enum Then + Dim LeftType As Type = Left.GetType + If LeftType Is Right.GetType Then + Return LeftType + End If + + End If + + End If + + ElseIf TypeOf Right Is System.Enum Then + Return Right.GetType + + End If + + Return Nothing + + End Function + + Private Shared Function GetNoValidOperatorException(ByVal Op As UserDefinedOperator, ByVal Operand As Object) As Exception + Return New InvalidCastException(GetResourceString(ResID.UnaryOperand2, OperatorNames(Op), VBFriendlyName(Operand))) + End Function + + Private Shared Function GetNoValidOperatorException(ByVal Op As UserDefinedOperator, ByVal Left As Object, ByVal Right As Object) As Exception + Const MAX_INSERTION_SIZE As Integer = 32 + + Dim Substitution1 As String + Dim Substitution2 As String + + If Left Is Nothing Then + Substitution1 = "'Nothing'" + Else + Dim LeftString As String = TryCast(Left, String) + + If LeftString IsNot Nothing Then + Substitution1 = _ + GetResourceString(ResID.NoValidOperator_StringType1, Strings.Left(LeftString, MAX_INSERTION_SIZE)) + Else + Substitution1 = GetResourceString(ResID.NoValidOperator_NonStringType1, VBFriendlyName(Left)) + End If + End If + + If Right Is Nothing Then + Substitution2 = "'Nothing'" + Else + Dim RightString As String = TryCast(Right, String) + + If RightString IsNot Nothing Then + Substitution2 = _ + GetResourceString(ResID.NoValidOperator_StringType1, Strings.Left(RightString, MAX_INSERTION_SIZE)) + Else + Substitution2 = GetResourceString(ResID.NoValidOperator_NonStringType1, VBFriendlyName(Right)) + End If + End If + + Return New InvalidCastException(GetResourceString(ResID.BinaryOperands3, OperatorNames(Op), Substitution1, Substitution2)) + End Function + +#Region " Comparison Operators = <> < <= > >= " + + Private Enum CompareClass + Less = -1 + Equal = 0 + Greater = 1 + Unordered + UserDefined + Undefined + End Enum + + Public Shared Function CompareObjectEqual(ByVal Left As Object, ByVal Right As Object, ByVal TextCompare As Boolean) As Object + Dim Comparison As CompareClass = CompareObject2(Left, Right, TextCompare) + + Select Case Comparison + Case CompareClass.Unordered + Return False + Case CompareClass.UserDefined + Return InvokeUserDefinedOperator(UserDefinedOperator.Equal, Left, Right) + Case CompareClass.Undefined + Throw GetNoValidOperatorException(UserDefinedOperator.Equal, Left, Right) + Case Else + Debug.Assert(Comparison = CompareClass.Less OrElse _ + Comparison = CompareClass.Equal OrElse _ + Comparison = CompareClass.Greater) + Return Comparison = 0 + End Select + End Function + + Public Shared Function ConditionalCompareObjectEqual(ByVal Left As Object, ByVal Right As Object, ByVal TextCompare As Boolean) As Boolean + Dim Comparison As CompareClass = CompareObject2(Left, Right, TextCompare) + + Select Case Comparison + Case CompareClass.Unordered + Return False + Case CompareClass.UserDefined + Return CBool(InvokeUserDefinedOperator(UserDefinedOperator.Equal, Left, Right)) + Case CompareClass.Undefined + Throw GetNoValidOperatorException(UserDefinedOperator.Equal, Left, Right) + Case Else + Debug.Assert(Comparison = CompareClass.Less OrElse _ + Comparison = CompareClass.Equal OrElse _ + Comparison = CompareClass.Greater) + Return Comparison = 0 + End Select + End Function + + Public Shared Function CompareObjectNotEqual(ByVal Left As Object, ByVal Right As Object, ByVal TextCompare As Boolean) As Object + Dim Comparison As CompareClass = CompareObject2(Left, Right, TextCompare) + + Select Case Comparison + Case CompareClass.Unordered + Return True + Case CompareClass.UserDefined + Return InvokeUserDefinedOperator(UserDefinedOperator.NotEqual, Left, Right) + Case CompareClass.Undefined + Throw GetNoValidOperatorException(UserDefinedOperator.NotEqual, Left, Right) + Case Else + Debug.Assert(Comparison = CompareClass.Less OrElse _ + Comparison = CompareClass.Equal OrElse _ + Comparison = CompareClass.Greater) + Return Comparison <> 0 + End Select + End Function + + Public Shared Function ConditionalCompareObjectNotEqual(ByVal Left As Object, ByVal Right As Object, ByVal TextCompare As Boolean) As Boolean + Dim Comparison As CompareClass = CompareObject2(Left, Right, TextCompare) + + Select Case Comparison + Case CompareClass.Unordered + Return True + Case CompareClass.UserDefined + Return CBool(InvokeUserDefinedOperator(UserDefinedOperator.NotEqual, Left, Right)) + Case CompareClass.Undefined + Throw GetNoValidOperatorException(UserDefinedOperator.NotEqual, Left, Right) + Case Else + Debug.Assert(Comparison = CompareClass.Less OrElse _ + Comparison = CompareClass.Equal OrElse _ + Comparison = CompareClass.Greater) + Return Comparison <> 0 + End Select + End Function + + Public Shared Function CompareObjectLess(ByVal Left As Object, ByVal Right As Object, ByVal TextCompare As Boolean) As Object + Dim Comparison As CompareClass = CompareObject2(Left, Right, TextCompare) + + Select Case Comparison + Case CompareClass.Unordered + Return False + Case CompareClass.UserDefined + Return InvokeUserDefinedOperator(UserDefinedOperator.Less, Left, Right) + Case CompareClass.Undefined + Throw GetNoValidOperatorException(UserDefinedOperator.Less, Left, Right) + Case Else + Debug.Assert(Comparison = CompareClass.Less OrElse _ + Comparison = CompareClass.Equal OrElse _ + Comparison = CompareClass.Greater) + Return Comparison < 0 + End Select + End Function + + Public Shared Function ConditionalCompareObjectLess(ByVal Left As Object, ByVal Right As Object, ByVal TextCompare As Boolean) As Boolean + Dim Comparison As CompareClass = CompareObject2(Left, Right, TextCompare) + + Select Case Comparison + Case CompareClass.Unordered + Return False + Case CompareClass.UserDefined + Return CBool(InvokeUserDefinedOperator(UserDefinedOperator.Less, Left, Right)) + Case CompareClass.Undefined + Throw GetNoValidOperatorException(UserDefinedOperator.Less, Left, Right) + Case Else + Debug.Assert(Comparison = CompareClass.Less OrElse _ + Comparison = CompareClass.Equal OrElse _ + Comparison = CompareClass.Greater) + Return Comparison < 0 + End Select + End Function + + Public Shared Function CompareObjectLessEqual(ByVal Left As Object, ByVal Right As Object, ByVal TextCompare As Boolean) As Object + Dim Comparison As CompareClass = CompareObject2(Left, Right, TextCompare) + + Select Case Comparison + Case CompareClass.Unordered + Return False + Case CompareClass.UserDefined + Return InvokeUserDefinedOperator(UserDefinedOperator.LessEqual, Left, Right) + Case CompareClass.Undefined + Throw GetNoValidOperatorException(UserDefinedOperator.LessEqual, Left, Right) + Case Else + Debug.Assert(Comparison = CompareClass.Less OrElse _ + Comparison = CompareClass.Equal OrElse _ + Comparison = CompareClass.Greater) + Return Comparison <= 0 + End Select + End Function + + Public Shared Function ConditionalCompareObjectLessEqual(ByVal Left As Object, ByVal Right As Object, ByVal TextCompare As Boolean) As Boolean + Dim Comparison As CompareClass = CompareObject2(Left, Right, TextCompare) + + Select Case Comparison + Case CompareClass.Unordered + Return False + Case CompareClass.UserDefined + Return CBool(InvokeUserDefinedOperator(UserDefinedOperator.LessEqual, Left, Right)) + Case CompareClass.Undefined + Throw GetNoValidOperatorException(UserDefinedOperator.LessEqual, Left, Right) + Case Else + Debug.Assert(Comparison = CompareClass.Less OrElse _ + Comparison = CompareClass.Equal OrElse _ + Comparison = CompareClass.Greater) + Return Comparison <= 0 + End Select + End Function + + Public Shared Function CompareObjectGreaterEqual(ByVal Left As Object, ByVal Right As Object, ByVal TextCompare As Boolean) As Object + Dim Comparison As CompareClass = CompareObject2(Left, Right, TextCompare) + + Select Case Comparison + Case CompareClass.Unordered + Return False + Case CompareClass.UserDefined + Return InvokeUserDefinedOperator(UserDefinedOperator.GreaterEqual, Left, Right) + Case CompareClass.Undefined + Throw GetNoValidOperatorException(UserDefinedOperator.GreaterEqual, Left, Right) + Case Else + Debug.Assert(Comparison = CompareClass.Less OrElse _ + Comparison = CompareClass.Equal OrElse _ + Comparison = CompareClass.Greater) + Return Comparison >= 0 + End Select + End Function + + Public Shared Function ConditionalCompareObjectGreaterEqual(ByVal Left As Object, ByVal Right As Object, ByVal TextCompare As Boolean) As Boolean + Dim Comparison As CompareClass = CompareObject2(Left, Right, TextCompare) + + Select Case Comparison + Case CompareClass.Unordered + Return False + Case CompareClass.UserDefined + Return CBool(InvokeUserDefinedOperator(UserDefinedOperator.GreaterEqual, Left, Right)) + Case CompareClass.Undefined + Throw GetNoValidOperatorException(UserDefinedOperator.GreaterEqual, Left, Right) + Case Else + Debug.Assert(Comparison = CompareClass.Less OrElse _ + Comparison = CompareClass.Equal OrElse _ + Comparison = CompareClass.Greater) + Return Comparison >= 0 + End Select + End Function + + Public Shared Function CompareObjectGreater(ByVal Left As Object, ByVal Right As Object, ByVal TextCompare As Boolean) As Object + Dim Comparison As CompareClass = CompareObject2(Left, Right, TextCompare) + + Select Case Comparison + Case CompareClass.Unordered + Return False + Case CompareClass.UserDefined + Return InvokeUserDefinedOperator(UserDefinedOperator.Greater, Left, Right) + Case CompareClass.Undefined + Throw GetNoValidOperatorException(UserDefinedOperator.Greater, Left, Right) + Case Else + Debug.Assert(Comparison = CompareClass.Less OrElse _ + Comparison = CompareClass.Equal OrElse _ + Comparison = CompareClass.Greater) + Return Comparison > 0 + End Select + End Function + + Public Shared Function ConditionalCompareObjectGreater(ByVal Left As Object, ByVal Right As Object, ByVal TextCompare As Boolean) As Boolean + Dim Comparison As CompareClass = CompareObject2(Left, Right, TextCompare) + + Select Case Comparison + Case CompareClass.Unordered + Return False + Case CompareClass.UserDefined + Return CBool(InvokeUserDefinedOperator(UserDefinedOperator.Greater, Left, Right)) + Case CompareClass.Undefined + Throw GetNoValidOperatorException(UserDefinedOperator.Greater, Left, Right) + Case Else + Debug.Assert(Comparison = CompareClass.Less OrElse _ + Comparison = CompareClass.Equal OrElse _ + Comparison = CompareClass.Greater) + Return Comparison > 0 + End Select + End Function + + 'UNDONE UNDONE UNDONE: remove this function after the next toolset update that incorporates build 40213. + 'and rename CompareObject2 back to CompareObject, but keep it private. + Public Shared Function CompareObject(ByVal Left As Object, ByVal Right As Object, ByVal TextCompare As Boolean) As Integer + Dim Comparison As CompareClass = CompareObject2(Left, Right, TextCompare) + + Select Case Comparison + Case CompareClass.Unordered + Return 0 + Case CompareClass.UserDefined, _ + CompareClass.Undefined + Throw GetNoValidOperatorException(UserDefinedOperator.IsTrue, Left, Right) + Case Else + Return Comparison + End Select + End Function + + Private Shared Function CompareObject2(ByVal Left As Object, ByVal Right As Object, ByVal TextCompare As Boolean) As CompareClass + Dim conv1, conv2 As IConvertible + Dim tc1, tc2 As TypeCode + + conv1 = TryCast(Left, IConvertible) + If conv1 Is Nothing Then + If Left Is Nothing Then + tc1 = TypeCode.Empty + Else + tc1 = TypeCode.Object + End If + Else + tc1 = conv1.GetTypeCode() + End If + + conv2 = TryCast(Right, IConvertible) + If conv2 Is Nothing Then + If Right Is Nothing Then + tc2 = TypeCode.Empty + Else + tc2 = TypeCode.Object + End If + Else + tc2 = conv2.GetTypeCode() + End If + + 'Special cases for Char() + If tc1 = TypeCode.Object Then + Dim LeftCharArray As Char() = TryCast(left, Char()) + + If LeftCharArray IsNot Nothing Then + If tc2 = TypeCode.String OrElse tc2 = TypeCode.Empty OrElse ((tc2 = TypeCode.Object) AndAlso (TypeOf Right Is Char())) Then + 'Treat Char() as String for these cases + Left = CStr(LeftCharArray) + conv1 = CType(Left, IConvertible) + tc1 = TypeCode.String + End If + End If + End If + + If (tc2 = TypeCode.Object) Then + Dim RightCharArray As Char() = TryCast(right, Char()) + + If RightCharArray IsNot Nothing Then + If tc1 = TypeCode.String OrElse tc1 = TypeCode.Empty Then + Right = CStr(RightCharArray) + conv2 = DirectCast(Right, IConvertible) + tc2 = TypeCode.String + End If + End If + End If + + Select Case tc1 * TCMAX + tc2 'CONSIDER: overflow checking is not necessary for this calculation - perf improvement. + + Case TypeCode.Empty * TCMAX + TypeCode.Empty + Return 0 + + Case TypeCode.Empty * TCMAX + TypeCode.Boolean + Return CompareBoolean(Nothing, conv2.ToBoolean(Nothing)) + + Case TypeCode.Empty * TCMAX + TypeCode.SByte + Return CompareInt32(Nothing, conv2.ToSByte(Nothing)) + + Case TypeCode.Empty * TCMAX + TypeCode.Byte + Return CompareInt32(Nothing, conv2.ToByte(Nothing)) + + Case TypeCode.Empty * TCMAX + TypeCode.Int16 + Return CompareInt32(Nothing, conv2.ToInt16(Nothing)) + + Case TypeCode.Empty * TCMAX + TypeCode.UInt16 + Return CompareInt32(Nothing, conv2.ToUInt16(Nothing)) + + Case TypeCode.Empty * TCMAX + TypeCode.Int32 + Return CompareInt32(Nothing, conv2.ToInt32(Nothing)) + + Case TypeCode.Empty * TCMAX + TypeCode.UInt32 + Return CompareUInt32(Nothing, conv2.ToUInt32(Nothing)) + + Case TypeCode.Empty * TCMAX + TypeCode.Int64 + Return CompareInt64(Nothing, conv2.ToInt64(Nothing)) + + Case TypeCode.Empty * TCMAX + TypeCode.UInt64 + Return CompareUInt64(Nothing, conv2.ToUInt64(Nothing)) + + Case TypeCode.Empty * TCMAX + TypeCode.Decimal + Return CompareDecimal(0D, conv2) + + Case TypeCode.Empty * TCMAX + TypeCode.Single + Return CompareSingle(Nothing, conv2.ToSingle(Nothing)) + + Case TypeCode.Empty * TCMAX + TypeCode.Double + Return CompareDouble(Nothing, conv2.ToDouble(Nothing)) + + Case TypeCode.Empty * TCMAX + TypeCode.DateTime + Return CompareDate(Nothing, conv2.ToDateTime(Nothing)) + + Case TypeCode.Empty * TCMAX + TypeCode.Char + Return CompareChar(Nothing, conv2.ToChar(Nothing)) + + Case TypeCode.Empty * TCMAX + TypeCode.String + Return CType(CompareString(Nothing, conv2.ToString(Nothing), TextCompare), CompareClass) + + + Case TypeCode.Boolean * TCMAX + TypeCode.Empty + Return CompareBoolean(conv1.ToBoolean(Nothing), Nothing) + + Case TypeCode.Boolean * TCMAX + TypeCode.Boolean + Return CompareBoolean(conv1.ToBoolean(Nothing), conv2.ToBoolean(Nothing)) + + Case TypeCode.Boolean * TCMAX + TypeCode.SByte + Return CompareInt32(ToVBBool(conv1), conv2.ToSByte(Nothing)) + + Case TypeCode.Boolean * TCMAX + TypeCode.Byte, _ + TypeCode.Boolean * TCMAX + TypeCode.Int16 + Return CompareInt32(ToVBBool(conv1), conv2.ToInt16(Nothing)) + + Case TypeCode.Boolean * TCMAX + TypeCode.UInt16, _ + TypeCode.Boolean * TCMAX + TypeCode.Int32 + Return CompareInt32(ToVBBool(conv1), conv2.ToInt32(Nothing)) + + Case TypeCode.Boolean * TCMAX + TypeCode.UInt32, _ + TypeCode.Boolean * TCMAX + TypeCode.Int64 + Return CompareInt64(ToVBBool(conv1), conv2.ToInt64(Nothing)) + + Case TypeCode.Boolean * TCMAX + TypeCode.UInt64, _ + TypeCode.Boolean * TCMAX + TypeCode.Decimal + Return CompareDecimal(ToVBBoolConv(conv1), conv2) + + Case TypeCode.Boolean * TCMAX + TypeCode.Single + Return CompareSingle(ToVBBool(conv1), conv2.ToSingle(Nothing)) + + Case TypeCode.Boolean * TCMAX + TypeCode.Double + Return CompareDouble(ToVBBool(conv1), conv2.ToDouble(Nothing)) + + Case TypeCode.Boolean * TCMAX + TypeCode.String + Return CompareBoolean(conv1.ToBoolean(Nothing), CBool(conv2.ToString(Nothing))) + + + Case TypeCode.SByte * TCMAX + TypeCode.Empty + Return CompareInt32(conv1.ToSByte(Nothing), Nothing) + + Case TypeCode.SByte * TCMAX + TypeCode.Boolean + Return CompareInt32(conv1.ToSByte(Nothing), ToVBBool(conv2)) + + Case TypeCode.SByte * TCMAX + TypeCode.SByte + Return CompareInt32(conv1.ToSByte(Nothing), conv2.ToSByte(Nothing)) + + Case TypeCode.SByte * TCMAX + TypeCode.Byte, _ + TypeCode.SByte * TCMAX + TypeCode.Int16, _ + TypeCode.Byte * TCMAX + TypeCode.SByte, _ + TypeCode.Byte * TCMAX + TypeCode.Int16, _ + TypeCode.Int16 * TCMAX + TypeCode.SByte, _ + TypeCode.Int16 * TCMAX + TypeCode.Byte, _ + TypeCode.Int16 * TCMAX + TypeCode.Int16 + + Return CompareInt32(conv1.ToInt16(Nothing), conv2.ToInt16(Nothing)) + + Case TypeCode.SByte * TCMAX + TypeCode.UInt16, _ + TypeCode.SByte * TCMAX + TypeCode.Int32, _ + TypeCode.Byte * TCMAX + TypeCode.Int32, _ + TypeCode.Int16 * TCMAX + TypeCode.UInt16, _ + TypeCode.Int16 * TCMAX + TypeCode.Int32, _ + TypeCode.UInt16 * TCMAX + TypeCode.SByte, _ + TypeCode.UInt16 * TCMAX + TypeCode.Int16, _ + TypeCode.UInt16 * TCMAX + TypeCode.Int32, _ + TypeCode.Int32 * TCMAX + TypeCode.SByte, _ + TypeCode.Int32 * TCMAX + TypeCode.Byte, _ + TypeCode.Int32 * TCMAX + TypeCode.Int16, _ + TypeCode.Int32 * TCMAX + TypeCode.UInt16, _ + TypeCode.Int32 * TCMAX + TypeCode.Int32 + + Return CompareInt32(conv1.ToInt32(Nothing), conv2.ToInt32(Nothing)) + + Case TypeCode.SByte * TCMAX + TypeCode.UInt32, _ + TypeCode.SByte * TCMAX + TypeCode.Int64, _ + TypeCode.Byte * TCMAX + TypeCode.Int64, _ + TypeCode.Int16 * TCMAX + TypeCode.UInt32, _ + TypeCode.Int16 * TCMAX + TypeCode.Int64, _ + TypeCode.UInt16 * TCMAX + TypeCode.Int64, _ + TypeCode.Int32 * TCMAX + TypeCode.UInt32, _ + TypeCode.Int32 * TCMAX + TypeCode.Int64, _ + TypeCode.UInt32 * TCMAX + TypeCode.SByte, _ + TypeCode.UInt32 * TCMAX + TypeCode.Int16, _ + TypeCode.UInt32 * TCMAX + TypeCode.Int32, _ + TypeCode.UInt32 * TCMAX + TypeCode.Int64, _ + TypeCode.Int64 * TCMAX + TypeCode.SByte, _ + TypeCode.Int64 * TCMAX + TypeCode.Byte, _ + TypeCode.Int64 * TCMAX + TypeCode.Int16, _ + TypeCode.Int64 * TCMAX + TypeCode.UInt16, _ + TypeCode.Int64 * TCMAX + TypeCode.Int32, _ + TypeCode.Int64 * TCMAX + TypeCode.UInt32, _ + TypeCode.Int64 * TCMAX + TypeCode.Int64 + + Return CompareInt64(conv1.ToInt64(Nothing), conv2.ToInt64(Nothing)) + + Case TypeCode.SByte * TCMAX + TypeCode.UInt64, _ + TypeCode.SByte * TCMAX + TypeCode.Decimal, _ + TypeCode.Byte * TCMAX + TypeCode.Decimal, _ + TypeCode.Int16 * TCMAX + TypeCode.UInt64, _ + TypeCode.Int16 * TCMAX + TypeCode.Decimal, _ + TypeCode.UInt16 * TCMAX + TypeCode.Decimal, _ + TypeCode.Int32 * TCMAX + TypeCode.UInt64, _ + TypeCode.Int32 * TCMAX + TypeCode.Decimal, _ + TypeCode.UInt32 * TCMAX + TypeCode.Decimal, _ + TypeCode.Int64 * TCMAX + TypeCode.UInt64, _ + TypeCode.Int64 * TCMAX + TypeCode.Decimal, _ + TypeCode.UInt64 * TCMAX + TypeCode.SByte, _ + TypeCode.UInt64 * TCMAX + TypeCode.Int16, _ + TypeCode.UInt64 * TCMAX + TypeCode.Int32, _ + TypeCode.UInt64 * TCMAX + TypeCode.Int64, _ + TypeCode.UInt64 * TCMAX + TypeCode.Decimal, _ + TypeCode.Decimal * TCMAX + TypeCode.SByte, _ + TypeCode.Decimal * TCMAX + TypeCode.Byte, _ + TypeCode.Decimal * TCMAX + TypeCode.Int16, _ + TypeCode.Decimal * TCMAX + TypeCode.UInt16, _ + TypeCode.Decimal * TCMAX + TypeCode.Int32, _ + TypeCode.Decimal * TCMAX + TypeCode.UInt32, _ + TypeCode.Decimal * TCMAX + TypeCode.Int64, _ + TypeCode.Decimal * TCMAX + TypeCode.UInt64, _ + TypeCode.Decimal * TCMAX + TypeCode.Decimal + + Return CompareDecimal(conv1, conv2) + + Case TypeCode.SByte * TCMAX + TypeCode.Single, _ + TypeCode.Byte * TCMAX + TypeCode.Single, _ + TypeCode.Int16 * TCMAX + TypeCode.Single, _ + TypeCode.UInt16 * TCMAX + TypeCode.Single, _ + TypeCode.Int32 * TCMAX + TypeCode.Single, _ + TypeCode.UInt32 * TCMAX + TypeCode.Single, _ + TypeCode.Int64 * TCMAX + TypeCode.Single, _ + TypeCode.UInt64 * TCMAX + TypeCode.Single, _ + TypeCode.Decimal * TCMAX + TypeCode.Single, _ + TypeCode.Single * TCMAX + TypeCode.SByte, _ + TypeCode.Single * TCMAX + TypeCode.Byte, _ + TypeCode.Single * TCMAX + TypeCode.Int16, _ + TypeCode.Single * TCMAX + TypeCode.UInt16, _ + TypeCode.Single * TCMAX + TypeCode.Int32, _ + TypeCode.Single * TCMAX + TypeCode.UInt32, _ + TypeCode.Single * TCMAX + TypeCode.Int64, _ + TypeCode.Single * TCMAX + TypeCode.UInt64, _ + TypeCode.Single * TCMAX + TypeCode.Decimal, _ + TypeCode.Single * TCMAX + TypeCode.Single + + Return CompareSingle(conv1.ToSingle(Nothing), conv2.ToSingle(Nothing)) + + Case TypeCode.SByte * TCMAX + TypeCode.Double, _ + TypeCode.Byte * TCMAX + TypeCode.Double, _ + TypeCode.Int16 * TCMAX + TypeCode.Double, _ + TypeCode.UInt16 * TCMAX + TypeCode.Double, _ + TypeCode.Int32 * TCMAX + TypeCode.Double, _ + TypeCode.UInt32 * TCMAX + TypeCode.Double, _ + TypeCode.Int64 * TCMAX + TypeCode.Double, _ + TypeCode.UInt64 * TCMAX + TypeCode.Double, _ + TypeCode.Decimal * TCMAX + TypeCode.Double, _ + TypeCode.Single * TCMAX + TypeCode.Double, _ + TypeCode.Double * TCMAX + TypeCode.SByte, _ + TypeCode.Double * TCMAX + TypeCode.Byte, _ + TypeCode.Double * TCMAX + TypeCode.Int16, _ + TypeCode.Double * TCMAX + TypeCode.UInt16, _ + TypeCode.Double * TCMAX + TypeCode.Int32, _ + TypeCode.Double * TCMAX + TypeCode.UInt32, _ + TypeCode.Double * TCMAX + TypeCode.Int64, _ + TypeCode.Double * TCMAX + TypeCode.UInt64, _ + TypeCode.Double * TCMAX + TypeCode.Decimal, _ + TypeCode.Double * TCMAX + TypeCode.Single, _ + TypeCode.Double * TCMAX + TypeCode.Double + + Return CompareDouble(conv1.ToDouble(Nothing), conv2.ToDouble(Nothing)) + + Case TypeCode.SByte * TCMAX + TypeCode.String, _ + TypeCode.Byte * TCMAX + TypeCode.String, _ + TypeCode.Int16 * TCMAX + TypeCode.String, _ + TypeCode.UInt16 * TCMAX + TypeCode.String, _ + TypeCode.Int32 * TCMAX + TypeCode.String, _ + TypeCode.UInt32 * TCMAX + TypeCode.String, _ + TypeCode.Int64 * TCMAX + TypeCode.String, _ + TypeCode.UInt64 * TCMAX + TypeCode.String, _ + TypeCode.Decimal * TCMAX + TypeCode.String, _ + TypeCode.Single * TCMAX + TypeCode.String, _ + TypeCode.Double * TCMAX + TypeCode.String + + Return CompareDouble(conv1.ToDouble(Nothing), CDbl(conv2.ToString(Nothing))) + + Case TypeCode.Byte * TCMAX + TypeCode.Empty + Return CompareInt32(conv1.ToByte(Nothing), Nothing) + + Case TypeCode.Byte * TCMAX + TypeCode.Boolean + Return CompareInt32(conv1.ToInt16(Nothing), ToVBBool(conv2)) + + Case TypeCode.Byte * TCMAX + TypeCode.Byte + Return CompareInt32(conv1.ToByte(Nothing), conv2.ToByte(Nothing)) + + Case TypeCode.Byte * TCMAX + TypeCode.UInt16, _ + TypeCode.UInt16 * TCMAX + TypeCode.Byte, _ + TypeCode.UInt16 * TCMAX + TypeCode.UInt16 + + Return CompareInt32(conv1.ToUInt16(Nothing), conv2.ToUInt16(Nothing)) + + Case TypeCode.Byte * TCMAX + TypeCode.UInt32, _ + TypeCode.UInt16 * TCMAX + TypeCode.UInt32, _ + TypeCode.UInt32 * TCMAX + TypeCode.Byte, _ + TypeCode.UInt32 * TCMAX + TypeCode.UInt16, _ + TypeCode.UInt32 * TCMAX + TypeCode.UInt32 + + Return CompareUInt32(conv1.ToUInt32(Nothing), conv2.ToUInt32(Nothing)) + + Case TypeCode.Byte * TCMAX + TypeCode.UInt64, _ + TypeCode.UInt16 * TCMAX + TypeCode.UInt64, _ + TypeCode.UInt32 * TCMAX + TypeCode.UInt64, _ + TypeCode.UInt64 * TCMAX + TypeCode.Byte, _ + TypeCode.UInt64 * TCMAX + TypeCode.UInt16, _ + TypeCode.UInt64 * TCMAX + TypeCode.UInt32, _ + TypeCode.UInt64 * TCMAX + TypeCode.UInt64 + + Return CompareUInt64(conv1.ToUInt64(Nothing), conv2.ToUInt64(Nothing)) + + + Case TypeCode.Int16 * TCMAX + TypeCode.Empty + Return CompareInt32(conv1.ToInt16(Nothing), Nothing) + + Case TypeCode.Int16 * TCMAX + TypeCode.Boolean + Return CompareInt32(conv1.ToInt16(Nothing), ToVBBool(conv2)) + + + Case TypeCode.UInt16 * TCMAX + TypeCode.Empty + Return CompareInt32(conv1.ToUInt16(Nothing), Nothing) + + Case TypeCode.UInt16 * TCMAX + TypeCode.Boolean + Return CompareInt32(conv1.ToInt32(Nothing), ToVBBool(conv2)) + + + Case TypeCode.Int32 * TCMAX + TypeCode.Empty + Return CompareInt32(conv1.ToInt32(Nothing), Nothing) + + Case TypeCode.Int32 * TCMAX + TypeCode.Boolean + Return CompareInt32(conv1.ToInt32(Nothing), ToVBBool(conv2)) + + + Case TypeCode.UInt32 * TCMAX + TypeCode.Empty + Return CompareUInt32(conv1.ToUInt32(Nothing), Nothing) + + Case TypeCode.UInt32 * TCMAX + TypeCode.Boolean + Return CompareInt64(conv1.ToInt64(Nothing), ToVBBool(conv2)) + + + Case TypeCode.Int64 * TCMAX + TypeCode.Empty + Return CompareInt64(conv1.ToInt64(Nothing), Nothing) + + Case TypeCode.Int64 * TCMAX + TypeCode.Boolean + Return CompareInt64(conv1.ToInt64(Nothing), ToVBBool(conv2)) + + + Case TypeCode.UInt64 * TCMAX + TypeCode.Empty + Return CompareUInt64(conv1.ToUInt64(Nothing), Nothing) + + Case TypeCode.UInt64 * TCMAX + TypeCode.Boolean + Return CompareDecimal(conv1, ToVBBoolConv(conv2)) + + + Case TypeCode.Decimal * TCMAX + TypeCode.Empty + Return CompareDecimal(conv1, 0D) + + Case TypeCode.Decimal * TCMAX + TypeCode.Boolean + Return CompareDecimal(conv1, ToVBBoolConv(conv2)) + + + Case TypeCode.Single * TCMAX + TypeCode.Empty + Return CompareSingle(conv1.ToSingle(Nothing), Nothing) + + Case TypeCode.Single * TCMAX + TypeCode.Boolean + Return CompareSingle(conv1.ToSingle(Nothing), ToVBBool(conv2)) + + + Case TypeCode.Double * TCMAX + TypeCode.Empty + Return CompareDouble(conv1.ToDouble(Nothing), Nothing) + + Case TypeCode.Double * TCMAX + TypeCode.Boolean + Return CompareDouble(conv1.ToDouble(Nothing), ToVBBool(conv2)) + + + Case TypeCode.DateTime * TCMAX + TypeCode.Empty + Return CompareDate(conv1.ToDateTime(Nothing), Nothing) + + Case TypeCode.DateTime * TCMAX + TypeCode.DateTime + Return CompareDate(conv1.ToDateTime(Nothing), conv2.ToDateTime(Nothing)) + + Case TypeCode.DateTime * TCMAX + TypeCode.String + Return CompareDate(conv1.ToDateTime(Nothing), CDate(conv2.ToString(Nothing))) + + + Case TypeCode.Char * TCMAX + TypeCode.Empty + Return CompareChar(conv1.ToChar(Nothing), Nothing) + + Case TypeCode.Char * TCMAX + TypeCode.Char + Return CompareChar(conv1.ToChar(Nothing), conv2.ToChar(Nothing)) + + Case TypeCode.Char * TCMAX + TypeCode.String, _ + TypeCode.String * TCMAX + TypeCode.Char, _ + TypeCode.String * TCMAX + TypeCode.String + Return CType(CompareString(conv1.ToString(Nothing), conv2.ToString(Nothing), TextCompare), CompareClass) + + + Case TypeCode.String * TCMAX + TypeCode.Empty + Return CType(CompareString(conv1.ToString(Nothing), Nothing, TextCompare), CompareClass) + + Case TypeCode.String * TCMAX + TypeCode.Boolean + Return CompareBoolean(CBool(conv1.ToString(Nothing)), conv2.ToBoolean(Nothing)) + + Case TypeCode.String * TCMAX + TypeCode.SByte, _ + TypeCode.String * TCMAX + TypeCode.Byte, _ + TypeCode.String * TCMAX + TypeCode.Int16, _ + TypeCode.String * TCMAX + TypeCode.UInt16, _ + TypeCode.String * TCMAX + TypeCode.Int32, _ + TypeCode.String * TCMAX + TypeCode.UInt32, _ + TypeCode.String * TCMAX + TypeCode.Int64, _ + TypeCode.String * TCMAX + TypeCode.UInt64, _ + TypeCode.String * TCMAX + TypeCode.Decimal, _ + TypeCode.String * TCMAX + TypeCode.Single, _ + TypeCode.String * TCMAX + TypeCode.Double + + Return CompareDouble(CDbl(conv1.ToString(Nothing)), conv2.ToDouble(Nothing)) + + Case TypeCode.String * TCMAX + TypeCode.DateTime + Return CompareDate(CDate(conv1.ToString(Nothing)), conv2.ToDateTime(Nothing)) + + Case Else +#If 0 Then + Case TypeCode.Boolean * TCMAX + TypeCode.DateTime 'XX + Case TypeCode.Boolean * TCMAX + TypeCode.Char 'XX + Case TypeCode.SByte * TCMAX + TypeCode.DateTime 'XX + Case TypeCode.SByte * TCMAX + TypeCode.Char 'XX + Case TypeCode.Byte * TCMAX + TypeCode.DateTime 'XX + Case TypeCode.Byte * TCMAX + TypeCode.Char 'XX + Case TypeCode.Int16 * TCMAX + TypeCode.DateTime 'XX + Case TypeCode.Int16 * TCMAX + TypeCode.Char 'XX + Case TypeCode.UInt16 * TCMAX + TypeCode.DateTime 'XX + Case TypeCode.UInt16 * TCMAX + TypeCode.Char 'XX + Case TypeCode.Int32 * TCMAX + TypeCode.DateTime 'XX + Case TypeCode.Int32 * TCMAX + TypeCode.Char 'XX + Case TypeCode.UInt32 * TCMAX + TypeCode.DateTime 'XX + Case TypeCode.UInt32 * TCMAX + TypeCode.Char 'XX + Case TypeCode.Int64 * TCMAX + TypeCode.DateTime 'XX + Case TypeCode.Int64 * TCMAX + TypeCode.Char 'XX + Case TypeCode.UInt64 * TCMAX + TypeCode.DateTime 'XX + Case TypeCode.UInt64 * TCMAX + TypeCode.Char 'XX + Case TypeCode.Decimal * TCMAX + TypeCode.DateTime 'XX + Case TypeCode.Decimal * TCMAX + TypeCode.Char 'XX + Case TypeCode.Single * TCMAX + TypeCode.DateTime 'XX + Case TypeCode.Single * TCMAX + TypeCode.Char 'XX + Case TypeCode.Double * TCMAX + TypeCode.DateTime 'XX + Case TypeCode.Double * TCMAX + TypeCode.Char 'XX + Case TypeCode.DateTime * TCMAX + TypeCode.Boolean 'XX + Case TypeCode.DateTime * TCMAX + TypeCode.SByte 'XX + Case TypeCode.DateTime * TCMAX + TypeCode.Byte 'XX + Case TypeCode.DateTime * TCMAX + TypeCode.Int16 'XX + Case TypeCode.DateTime * TCMAX + TypeCode.UInt16 'XX + Case TypeCode.DateTime * TCMAX + TypeCode.Int32 'XX + Case TypeCode.DateTime * TCMAX + TypeCode.UInt32 'XX + Case TypeCode.DateTime * TCMAX + TypeCode.Int64 'XX + Case TypeCode.DateTime * TCMAX + TypeCode.UInt64 'XX + Case TypeCode.DateTime * TCMAX + TypeCode.Decimal 'XX + Case TypeCode.DateTime * TCMAX + TypeCode.Single 'XX + Case TypeCode.DateTime * TCMAX + TypeCode.Double 'XX + Case TypeCode.DateTime * TCMAX + TypeCode.Char 'XX + Case TypeCode.Char * TCMAX + TypeCode.Boolean 'XX + Case TypeCode.Char * TCMAX + TypeCode.SByte 'XX + Case TypeCode.Char * TCMAX + TypeCode.Byte 'XX + Case TypeCode.Char * TCMAX + TypeCode.Int16 'XX + Case TypeCode.Char * TCMAX + TypeCode.UInt16 'XX + Case TypeCode.Char * TCMAX + TypeCode.Int32 'XX + Case TypeCode.Char * TCMAX + TypeCode.UInt32 'XX + Case TypeCode.Char * TCMAX + TypeCode.Int64 'XX + Case TypeCode.Char * TCMAX + TypeCode.UInt64 'XX + Case TypeCode.Char * TCMAX + TypeCode.Decimal 'XX + Case TypeCode.Char * TCMAX + TypeCode.Single 'XX + Case TypeCode.Char * TCMAX + TypeCode.Double 'XX + Case TypeCode.Char * TCMAX + TypeCode.DateTime 'XX +#End If + + End Select + + If tc1 = TypeCode.Object OrElse tc2 = TypeCode.Object Then + Return CompareClass.UserDefined + End If + + Return CompareClass.Undefined + + End Function + + Private Shared Function CompareBoolean(ByVal Left As Boolean, ByVal Right As Boolean) As CompareClass + If Left = Right Then Return CompareClass.Equal + If Left > Right Then Return CompareClass.Greater + Return CompareClass.Less + End Function + + Private Shared Function CompareInt32(ByVal Left As Int32, ByVal Right As Int32) As CompareClass + If Left = Right Then Return CompareClass.Equal + If Left > Right Then Return CompareClass.Greater + Return CompareClass.Less + End Function + + Private Shared Function CompareUInt32(ByVal Left As UInt32, ByVal Right As UInt32) As CompareClass + If Left = Right Then Return CompareClass.Equal + If Left > Right Then Return CompareClass.Greater + Return CompareClass.Less + End Function + + Private Shared Function CompareInt64(ByVal Left As Int64, ByVal Right As Int64) As CompareClass + If Left = Right Then Return CompareClass.Equal + If Left > Right Then Return CompareClass.Greater + Return CompareClass.Less + End Function + + Private Shared Function CompareUInt64(ByVal Left As UInt64, ByVal Right As UInt64) As CompareClass + If Left = Right Then Return CompareClass.Equal + If Left > Right Then Return CompareClass.Greater + Return CompareClass.Less + End Function + + 'This function takes IConvertible because the JIT does not behave properly with Decimal temps + 'REVIEW VSW#395742: does the JIT now behave properly? + Private Shared Function CompareDecimal(ByVal Left As IConvertible, ByVal Right As IConvertible) As CompareClass + Dim Result As Integer = System.Decimal.Compare(Left.ToDecimal(Nothing), Right.ToDecimal(Nothing)) + + 'Normalize the result. + If Result = 0 Then + Return CompareClass.Equal + ElseIf Result > 0 Then + Return CompareClass.Greater + Else + Return CompareClass.Less + End If + End Function + + Private Shared Function CompareSingle(ByVal Left As Single, ByVal Right As Single) As CompareClass + If Left = Right Then Return CompareClass.Equal + If Left < Right Then Return CompareClass.Less + If Left > Right Then Return CompareClass.Greater + Return CompareClass.Unordered + End Function + + Private Shared Function CompareDouble(ByVal Left As Double, ByVal Right As Double) As CompareClass + If Left = Right Then Return CompareClass.Equal + If Left < Right Then Return CompareClass.Less + If Left > Right Then Return CompareClass.Greater + Return CompareClass.Unordered + End Function + + Private Shared Function CompareDate(ByVal Left As Date, ByVal Right As Date) As CompareClass + Dim Result As Integer = System.DateTime.Compare(Left, Right) + + 'Normalize the result. + If Result = 0 Then + Return CompareClass.Equal + ElseIf Result > 0 Then + Return CompareClass.Greater + Else + Return CompareClass.Less + End If + End Function + + Private Shared Function CompareChar(ByVal Left As Char, ByVal Right As Char) As CompareClass + If Left = Right Then Return CompareClass.Equal + If Left > Right Then Return CompareClass.Greater + Return CompareClass.Less + End Function + + 'String comparisons occur often enough that maybe the TextCompare should be broken out into two members that the compiler statically selects + Public Shared Function CompareString(ByVal Left As String, ByVal Right As String, ByVal TextCompare As Boolean) As Integer + If Left Is Right Then + Return CompareClass.Equal + End If + + If Left Is Nothing Then + If Right.Length() = 0 Then + Return CompareClass.Equal + End If + + Return CompareClass.Less + End If + + If Right Is Nothing Then + If Left.Length() = 0 Then + Return CompareClass.Equal + End If + + Return CompareClass.Greater + End If + + Dim Result As Integer + + If TextCompare Then + Result = GetCultureInfo().CompareInfo.Compare(Left, Right, OptionCompareTextFlags) + Else + Result = System.String.CompareOrdinal(Left, Right) + End If + + 'Normalize the result. + If Result = 0 Then + Return CompareClass.Equal + ElseIf Result > 0 Then + Return CompareClass.Greater + Else + Return CompareClass.Less + End If + End Function + +#End Region + +#Region " Operator Unary Plus + " + + Public Shared Function PlusObject(ByVal Operand As Object) As Object + + If Operand Is Nothing Then + Return Boxed_ZeroInteger + End If + + Dim conv As IConvertible + Dim typ As TypeCode + + conv = TryCast(Operand, IConvertible) + + If conv Is Nothing Then + If Operand Is Nothing Then + typ = TypeCode.Empty + Else + typ = TypeCode.Object + End If + Else + typ = conv.GetTypeCode() + End If + + + Select Case typ + + Case TypeCode.Empty + Return Boxed_ZeroInteger + + Case TypeCode.Boolean + Return CShort(conv.ToBoolean(Nothing)) + + Case TypeCode.SByte + Return conv.ToSByte(Nothing) + + Case TypeCode.Byte + Return conv.ToByte(Nothing) + + Case TypeCode.Int16 + Return conv.ToInt16(Nothing) + + Case TypeCode.UInt16 + Return conv.ToUInt16(Nothing) + + Case TypeCode.Int32 + Return conv.ToInt32(Nothing) + + Case TypeCode.UInt32 + Return conv.ToUInt32(Nothing) + + Case TypeCode.Int64 + Return conv.ToInt64(Nothing) + + Case TypeCode.UInt64 + Return conv.ToUInt64(Nothing) + + Case TypeCode.Decimal, _ + TypeCode.Single, _ + TypeCode.Double + Return Operand + + Case TypeCode.DateTime, _ + TypeCode.Char + ' Fall through to error + + Case TypeCode.String + Return CDbl(conv.ToString(Nothing)) + + Case TypeCode.Object + Return InvokeUserDefinedOperator(UserDefinedOperator.UnaryPlus, Operand) + + Case Else + ' Fall through to error + End Select + + Throw GetNoValidOperatorException(UserDefinedOperator.UnaryPlus, Operand) + End Function + +#End Region + +#Region " Operator Negate - " + + Public Shared Function NegateObject(ByVal Operand As Object) As Object + + Dim conv As IConvertible + Dim tc As TypeCode + + conv = TryCast(Operand, IConvertible) + + If conv Is Nothing Then + If Operand Is Nothing Then + tc = TypeCode.Empty + Else + tc = TypeCode.Object + End If + Else + tc = conv.GetTypeCode() + End If + + + Select Case tc + + Case TypeCode.Empty + Return Boxed_ZeroInteger + + Case TypeCode.Boolean + If TypeOf Operand Is Boolean Then + Return NegateBoolean(DirectCast(Operand, Boolean)) + Else + Return NegateBoolean(conv.ToBoolean(Nothing)) + End If + + Case TypeCode.SByte + If TypeOf Operand Is SByte Then + Return NegateSByte(DirectCast(Operand, SByte)) + Else + Return NegateSByte(conv.ToSByte(Nothing)) + End If + + Case TypeCode.Byte + If TypeOf Operand Is Byte Then + Return NegateByte(DirectCast(Operand, Byte)) + Else + Return NegateByte(conv.ToByte(Nothing)) + End If + + Case TypeCode.Int16 + If TypeOf Operand Is Int16 Then + Return NegateInt16(DirectCast(Operand, Int16)) + Else + Return NegateInt16(conv.ToInt16(Nothing)) + End If + + Case TypeCode.UInt16 + If TypeOf Operand Is UInt16 Then + Return NegateUInt16(DirectCast(Operand, UInt16)) + Else + Return NegateUInt16(conv.ToUInt16(Nothing)) + End If + + Case TypeCode.Int32 + If TypeOf Operand Is Int32 Then + Return NegateInt32(DirectCast(Operand, Int32)) + Else + Return NegateInt32(conv.ToInt32(Nothing)) + End If + + Case TypeCode.UInt32 + If TypeOf Operand Is UInt32 Then + Return NegateUInt32(DirectCast(Operand, UInt32)) + Else + Return NegateUInt32(conv.ToUInt32(Nothing)) + End If + + Case TypeCode.Int64 + If TypeOf Operand Is Int64 Then + Return NegateInt64(DirectCast(Operand, Int64)) + Else + Return NegateInt64(conv.ToInt64(Nothing)) + End If + + Case TypeCode.UInt64 + If TypeOf Operand Is UInt64 Then + Return NegateUInt64(DirectCast(Operand, UInt64)) + Else + Return NegateUInt64(conv.ToUInt64(Nothing)) + End If + + Case TypeCode.Decimal + If TypeOf Operand Is Decimal Then + Return NegateDecimal(DirectCast(Operand, Decimal)) + Else + Return NegateDecimal(conv.ToDecimal(Nothing)) + End If + + Case TypeCode.Single + If TypeOf Operand Is Single Then + Return NegateSingle(DirectCast(Operand, Single)) + Else + Return NegateSingle(conv.ToSingle(Nothing)) + End If + + Case TypeCode.Double + If TypeOf Operand Is Double Then + Return NegateDouble(DirectCast(Operand, Double)) + Else + Return NegateDouble(conv.ToDouble(Nothing)) + End If + + Case TypeCode.DateTime, _ + TypeCode.Char + 'Fall through to error. + + Case TypeCode.String + Dim StringOperand As String = TryCast(Operand, String) + + If StringOperand IsNot Nothing Then + Return NegateString(StringOperand) + Else + Return NegateString(conv.ToString(Nothing)) + End If + + Case TypeCode.Object + Return InvokeUserDefinedOperator(UserDefinedOperator.Negate, Operand) + + Case Else + 'Fall through to error. + + End Select + + Throw GetNoValidOperatorException(UserDefinedOperator.Negate, Operand) + + End Function + + Private Shared Function NegateBoolean(ByVal Operand As Boolean) As Object + Return -CShort(Operand) + End Function + + Private Shared Function NegateSByte(ByVal Operand As SByte) As Object + If Operand = SByte.MinValue Then + Return -CShort(SByte.MinValue) + End If + Return -Operand + End Function + + Private Shared Function NegateByte(ByVal Operand As Byte) As Object + Return -CShort(Operand) + End Function + + Private Shared Function NegateInt16(ByVal Operand As Int16) As Object + If Operand = Int16.MinValue Then + Return -CInt(Int16.MinValue) + End If + Return -Operand + End Function + + Private Shared Function NegateUInt16(ByVal Operand As UInt16) As Object + Return -CInt(Operand) + End Function + + Private Shared Function NegateInt32(ByVal Operand As Int32) As Object + If Operand = Int32.MinValue Then + Return -CLng(Int32.MinValue) + End If + Return -Operand + End Function + + Private Shared Function NegateUInt32(ByVal Operand As UInt32) As Object + Return -CLng(Operand) + End Function + + Private Shared Function NegateInt64(ByVal Operand As Int64) As Object + If Operand = Int64.MinValue Then + Return -CDec(Int64.MinValue) + End If + Return -Operand + End Function + + Private Shared Function NegateUInt64(ByVal Operand As UInt64) As Object + Return -CDec(Operand) + End Function + + Private Shared Function NegateDecimal(ByVal Operand As Decimal) As Object + 'Using try/catch instead of check with MinValue since the overflow case should be very rare + 'and a compare would be a big cost for the normal case. + Try + Return -Operand + Catch ex As OverflowException + Return -CDbl(Operand) + End Try + End Function + + Private Shared Function NegateSingle(ByVal Operand As Single) As Object + Return -Operand + End Function + + Private Shared Function NegateDouble(ByVal Operand As Double) As Object + Return -Operand + End Function + + Private Shared Function NegateString(ByVal Operand As String) As Object + Return -CDbl(Operand) + End Function + +#End Region + +#Region " Operator Not " + + 'UNDONE UNSIGNED - is it faster to do it the old way, with local vars of the specific types that get returned? + Public Shared Function NotObject(ByVal Operand As Object) As Object + + Dim conv As IConvertible + Dim tc As TypeCode + + conv = TryCast(Operand, IConvertible) + + If conv Is Nothing Then + If Operand Is Nothing Then + tc = TypeCode.Empty + Else + tc = TypeCode.Object + End If + Else + tc = conv.GetTypeCode() + End If + + + Select Case tc + + Case TypeCode.Empty + Return Not 0I + + Case TypeCode.Boolean + Return NotBoolean(conv.ToBoolean(Nothing)) + + Case TypeCode.SByte + Return NotSByte(conv.ToSByte(Nothing), Operand.GetType()) + + Case TypeCode.Byte + Return NotByte(conv.ToByte(Nothing), Operand.GetType()) + + Case TypeCode.Int16 + Return NotInt16(conv.ToInt16(Nothing), Operand.GetType()) + + Case TypeCode.UInt16 + Return NotUInt16(conv.ToUInt16(Nothing), Operand.GetType()) + + Case TypeCode.Int32 + Return NotInt32(conv.ToInt32(Nothing), Operand.GetType()) + + Case TypeCode.UInt32 + Return NotUInt32(conv.ToUInt32(Nothing), Operand.GetType()) + + Case TypeCode.Int64 + Return NotInt64(conv.ToInt64(Nothing), Operand.GetType()) + + Case TypeCode.UInt64 + Return NotUInt64(conv.ToUInt64(Nothing), Operand.GetType()) + + Case TypeCode.Decimal, _ + TypeCode.Single, _ + TypeCode.Double + Return NotInt64(conv.ToInt64(Nothing)) + + Case TypeCode.DateTime, _ + TypeCode.Char + 'Fall through to error. + + Case TypeCode.String + Return NotInt64(CLng(conv.ToString(Nothing))) + + Case TypeCode.Object + Return InvokeUserDefinedOperator(UserDefinedOperator.Not, Operand) + + Case Else + 'Fall through to error. + + End Select + + Throw GetNoValidOperatorException(UserDefinedOperator.Not, Operand) + + End Function + + Private Shared Function NotBoolean(ByVal Operand As Boolean) As Object + Return Not Operand + End Function + + Private Shared Function NotSByte(ByVal Operand As SByte, ByVal OperandType As Type) As Object + Dim Result As SByte = Not Operand + + If OperandType.IsEnum Then + Return System.Enum.ToObject(OperandType, Result) + End If + Return Result + End Function + + Private Shared Function NotByte(ByVal Operand As Byte, ByVal OperandType As Type) As Object + Dim Result As Byte = Not Operand + + If OperandType.IsEnum Then + Return System.Enum.ToObject(OperandType, Result) + End If + Return Result + End Function + + Private Shared Function NotInt16(ByVal Operand As Int16, ByVal OperandType As Type) As Object + Dim Result As Int16 = Not Operand + + If OperandType.IsEnum Then + Return System.Enum.ToObject(OperandType, Result) + End If + Return Result + End Function + + Private Shared Function NotUInt16(ByVal Operand As UInt16, ByVal OperandType As Type) As Object + Dim Result As UInt16 = Not Operand + + If OperandType.IsEnum Then + Return System.Enum.ToObject(OperandType, Result) + End If + Return Result + End Function + + Private Shared Function NotInt32(ByVal Operand As Int32, ByVal OperandType As Type) As Object + Dim Result As Int32 = Not Operand + + If OperandType.IsEnum Then + Return System.Enum.ToObject(OperandType, Result) + End If + Return Result + End Function + + Private Shared Function NotUInt32(ByVal Operand As UInt32, ByVal OperandType As Type) As Object + Dim Result As UInt32 = Not Operand + + If OperandType.IsEnum Then + Return System.Enum.ToObject(OperandType, Result) + End If + Return Result + End Function + + Private Shared Function NotInt64(ByVal Operand As Int64) As Object + Return Not Operand + End Function + + Private Shared Function NotInt64(ByVal Operand As Int64, ByVal OperandType As Type) As Object + Dim Result As Int64 = Not Operand + + If OperandType.IsEnum Then + Return System.Enum.ToObject(OperandType, Result) + End If + Return Result + End Function + + Private Shared Function NotUInt64(ByVal Operand As UInt64, ByVal OperandType As Type) As Object + Dim Result As UInt64 = Not Operand + + If OperandType.IsEnum Then + Return System.Enum.ToObject(OperandType, Result) + End If + Return Result + End Function + +#End Region + +#Region " Operator And " + + Public Shared Function AndObject(ByVal Left As Object, ByVal Right As Object) As Object + + Dim conv1, conv2 As IConvertible + Dim tc1, tc2 As TypeCode + + conv1 = TryCast(Left, IConvertible) + + If conv1 Is Nothing Then + If Left Is Nothing Then + tc1 = TypeCode.Empty + Else + tc1 = TypeCode.Object + End If + Else + tc1 = conv1.GetTypeCode() + End If + + + conv2 = TryCast(Right, IConvertible) + + If conv2 Is Nothing Then + If Right Is Nothing Then + tc2 = TypeCode.Empty + Else + tc2 = TypeCode.Object + End If + Else + tc2 = conv2.GetTypeCode() + End If + + + Select Case tc1 * TCMAX + tc2 'CONSIDER: overflow checking is not necessary for this calculation - perf improvement. + + Case TypeCode.Empty * TCMAX + TypeCode.Empty + Return Boxed_ZeroInteger + + Case TypeCode.Empty * TCMAX + TypeCode.Boolean, _ + TypeCode.Boolean * TCMAX + TypeCode.Empty + Return False + + Case TypeCode.Empty * TCMAX + TypeCode.SByte, _ + TypeCode.SByte * TCMAX + TypeCode.Empty + Return AndSByte(CSByte(0), CSByte(0), GetEnumResult(Left, Right)) + + Case TypeCode.Empty * TCMAX + TypeCode.Byte, _ + TypeCode.Byte * TCMAX + TypeCode.Empty + Return AndByte(CByte(0), CByte(0), GetEnumResult(Left, Right)) + + Case TypeCode.Empty * TCMAX + TypeCode.Int16, _ + TypeCode.Int16 * TCMAX + TypeCode.Empty + Return AndInt16(0S, 0S, GetEnumResult(Left, Right)) + + Case TypeCode.Empty * TCMAX + TypeCode.UInt16, _ + TypeCode.UInt16 * TCMAX + TypeCode.Empty + Return AndUInt16(0US, 0US, GetEnumResult(Left, Right)) + + Case TypeCode.Empty * TCMAX + TypeCode.Int32, _ + TypeCode.Int32 * TCMAX + TypeCode.Empty + Return AndInt32(0I, 0I, GetEnumResult(Left, Right)) + + Case TypeCode.Empty * TCMAX + TypeCode.UInt32, _ + TypeCode.UInt32 * TCMAX + TypeCode.Empty + Return AndUInt32(0UI, 0UI, GetEnumResult(Left, Right)) + + Case TypeCode.Empty * TCMAX + TypeCode.Int64, _ + TypeCode.Int64 * TCMAX + TypeCode.Empty + Return AndInt64(0L, 0L, GetEnumResult(Left, Right)) + + Case TypeCode.Empty * TCMAX + TypeCode.UInt64, _ + TypeCode.UInt64 * TCMAX + TypeCode.Empty + Return AndUInt64(0UL, 0UL, GetEnumResult(Left, Right)) + + Case TypeCode.Empty * TCMAX + TypeCode.Decimal, _ + TypeCode.Empty * TCMAX + TypeCode.Single, _ + TypeCode.Empty * TCMAX + TypeCode.Double + Return AndInt64(Nothing, conv2.ToInt64(Nothing)) + + Case TypeCode.Empty * TCMAX + TypeCode.String + Return AndInt64(Nothing, CLng(conv2.ToString(Nothing))) + + + Case TypeCode.Boolean * TCMAX + TypeCode.Boolean + Return AndBoolean(conv1.ToBoolean(Nothing), conv2.ToBoolean(Nothing)) + + Case TypeCode.Boolean * TCMAX + TypeCode.SByte + Return AndSByte(ToVBBool(conv1), conv2.ToSByte(Nothing)) + + Case TypeCode.Boolean * TCMAX + TypeCode.Byte, _ + TypeCode.Boolean * TCMAX + TypeCode.Int16 + Return AndInt16(ToVBBool(conv1), conv2.ToInt16(Nothing)) + + Case TypeCode.Boolean * TCMAX + TypeCode.UInt16, _ + TypeCode.Boolean * TCMAX + TypeCode.Int32 + Return AndInt32(ToVBBool(conv1), conv2.ToInt32(Nothing)) + + Case TypeCode.Boolean * TCMAX + TypeCode.UInt32, _ + TypeCode.Boolean * TCMAX + TypeCode.Int64, _ + TypeCode.Boolean * TCMAX + TypeCode.UInt64, _ + TypeCode.Boolean * TCMAX + TypeCode.Decimal, _ + TypeCode.Boolean * TCMAX + TypeCode.Single, _ + TypeCode.Boolean * TCMAX + TypeCode.Double + + Return AndInt64(ToVBBool(conv1), conv2.ToInt64(Nothing)) 'UNDONE: what about error messages on the overflow? not very useful coming from iconvertible code. + + Case TypeCode.Boolean * TCMAX + TypeCode.String + Return AndBoolean(conv1.ToBoolean(Nothing), CBool(conv2.ToString(Nothing))) + + + Case TypeCode.SByte * TCMAX + TypeCode.Boolean + Return AndSByte(conv1.ToSByte(Nothing), ToVBBool(conv2)) + + Case TypeCode.SByte * TCMAX + TypeCode.SByte + Return AndSByte(conv1.ToSByte(Nothing), conv2.ToSByte(Nothing), GetEnumResult(Left, Right)) + + Case TypeCode.SByte * TCMAX + TypeCode.Byte, _ + TypeCode.SByte * TCMAX + TypeCode.Int16, _ + TypeCode.Byte * TCMAX + TypeCode.SByte, _ + TypeCode.Byte * TCMAX + TypeCode.Int16, _ + TypeCode.Int16 * TCMAX + TypeCode.SByte, _ + TypeCode.Int16 * TCMAX + TypeCode.Byte + + Return AndInt16(conv1.ToInt16(Nothing), conv2.ToInt16(Nothing)) + + Case TypeCode.SByte * TCMAX + TypeCode.UInt16, _ + TypeCode.SByte * TCMAX + TypeCode.Int32, _ + TypeCode.Byte * TCMAX + TypeCode.Int32, _ + TypeCode.Int16 * TCMAX + TypeCode.UInt16, _ + TypeCode.Int16 * TCMAX + TypeCode.Int32, _ + TypeCode.UInt16 * TCMAX + TypeCode.SByte, _ + TypeCode.UInt16 * TCMAX + TypeCode.Int16, _ + TypeCode.UInt16 * TCMAX + TypeCode.Int32, _ + TypeCode.Int32 * TCMAX + TypeCode.SByte, _ + TypeCode.Int32 * TCMAX + TypeCode.Byte, _ + TypeCode.Int32 * TCMAX + TypeCode.Int16, _ + TypeCode.Int32 * TCMAX + TypeCode.UInt16 + + Return AndInt32(conv1.ToInt32(Nothing), conv2.ToInt32(Nothing)) + + Case TypeCode.SByte * TCMAX + TypeCode.UInt32, _ + TypeCode.SByte * TCMAX + TypeCode.Int64, _ + TypeCode.SByte * TCMAX + TypeCode.UInt64, _ + TypeCode.SByte * TCMAX + TypeCode.Decimal, _ + TypeCode.SByte * TCMAX + TypeCode.Single, _ + TypeCode.SByte * TCMAX + TypeCode.Double, _ + TypeCode.Byte * TCMAX + TypeCode.Int64, _ + TypeCode.Byte * TCMAX + TypeCode.Decimal, _ + TypeCode.Byte * TCMAX + TypeCode.Single, _ + TypeCode.Byte * TCMAX + TypeCode.Double, _ + TypeCode.Int16 * TCMAX + TypeCode.UInt32, _ + TypeCode.Int16 * TCMAX + TypeCode.Int64, _ + TypeCode.Int16 * TCMAX + TypeCode.UInt64, _ + TypeCode.Int16 * TCMAX + TypeCode.Decimal, _ + TypeCode.Int16 * TCMAX + TypeCode.Single, _ + TypeCode.Int16 * TCMAX + TypeCode.Double, _ + TypeCode.UInt16 * TCMAX + TypeCode.Int64, _ + TypeCode.UInt16 * TCMAX + TypeCode.Decimal, _ + TypeCode.UInt16 * TCMAX + TypeCode.Single, _ + TypeCode.UInt16 * TCMAX + TypeCode.Double, _ + TypeCode.Int32 * TCMAX + TypeCode.UInt32, _ + TypeCode.Int32 * TCMAX + TypeCode.Int64, _ + TypeCode.Int32 * TCMAX + TypeCode.UInt64, _ + TypeCode.Int32 * TCMAX + TypeCode.Decimal, _ + TypeCode.Int32 * TCMAX + TypeCode.Single, _ + TypeCode.Int32 * TCMAX + TypeCode.Double, _ + TypeCode.UInt32 * TCMAX + TypeCode.SByte, _ + TypeCode.UInt32 * TCMAX + TypeCode.Int16, _ + TypeCode.UInt32 * TCMAX + TypeCode.Int32, _ + TypeCode.UInt32 * TCMAX + TypeCode.Int64, _ + TypeCode.UInt32 * TCMAX + TypeCode.Decimal, _ + TypeCode.UInt32 * TCMAX + TypeCode.Single, _ + TypeCode.UInt32 * TCMAX + TypeCode.Double, _ + TypeCode.Int64 * TCMAX + TypeCode.SByte, _ + TypeCode.Int64 * TCMAX + TypeCode.Byte, _ + TypeCode.Int64 * TCMAX + TypeCode.Int16, _ + TypeCode.Int64 * TCMAX + TypeCode.UInt16, _ + TypeCode.Int64 * TCMAX + TypeCode.Int32, _ + TypeCode.Int64 * TCMAX + TypeCode.UInt32, _ + TypeCode.Int64 * TCMAX + TypeCode.UInt64, _ + TypeCode.Int64 * TCMAX + TypeCode.Decimal, _ + TypeCode.Int64 * TCMAX + TypeCode.Single, _ + TypeCode.Int64 * TCMAX + TypeCode.Double, _ + TypeCode.UInt64 * TCMAX + TypeCode.SByte, _ + TypeCode.UInt64 * TCMAX + TypeCode.Int16, _ + TypeCode.UInt64 * TCMAX + TypeCode.Int32, _ + TypeCode.UInt64 * TCMAX + TypeCode.Int64, _ + TypeCode.UInt64 * TCMAX + TypeCode.Decimal, _ + TypeCode.UInt64 * TCMAX + TypeCode.Single, _ + TypeCode.UInt64 * TCMAX + TypeCode.Double, _ + TypeCode.Decimal * TCMAX + TypeCode.SByte, _ + TypeCode.Decimal * TCMAX + TypeCode.Byte, _ + TypeCode.Decimal * TCMAX + TypeCode.Int16, _ + TypeCode.Decimal * TCMAX + TypeCode.UInt16, _ + TypeCode.Decimal * TCMAX + TypeCode.Int32, _ + TypeCode.Decimal * TCMAX + TypeCode.UInt32, _ + TypeCode.Decimal * TCMAX + TypeCode.Int64, _ + TypeCode.Decimal * TCMAX + TypeCode.UInt64, _ + TypeCode.Decimal * TCMAX + TypeCode.Decimal, _ + TypeCode.Decimal * TCMAX + TypeCode.Single, _ + TypeCode.Decimal * TCMAX + TypeCode.Double, _ + TypeCode.Single * TCMAX + TypeCode.SByte, _ + TypeCode.Single * TCMAX + TypeCode.Byte, _ + TypeCode.Single * TCMAX + TypeCode.Int16, _ + TypeCode.Single * TCMAX + TypeCode.UInt16, _ + TypeCode.Single * TCMAX + TypeCode.Int32, _ + TypeCode.Single * TCMAX + TypeCode.UInt32, _ + TypeCode.Single * TCMAX + TypeCode.Int64, _ + TypeCode.Single * TCMAX + TypeCode.UInt64, _ + TypeCode.Single * TCMAX + TypeCode.Decimal, _ + TypeCode.Single * TCMAX + TypeCode.Single, _ + TypeCode.Single * TCMAX + TypeCode.Double, _ + TypeCode.Double * TCMAX + TypeCode.SByte, _ + TypeCode.Double * TCMAX + TypeCode.Byte, _ + TypeCode.Double * TCMAX + TypeCode.Int16, _ + TypeCode.Double * TCMAX + TypeCode.UInt16, _ + TypeCode.Double * TCMAX + TypeCode.Int32, _ + TypeCode.Double * TCMAX + TypeCode.UInt32, _ + TypeCode.Double * TCMAX + TypeCode.Int64, _ + TypeCode.Double * TCMAX + TypeCode.UInt64, _ + TypeCode.Double * TCMAX + TypeCode.Decimal, _ + TypeCode.Double * TCMAX + TypeCode.Single, _ + TypeCode.Double * TCMAX + TypeCode.Double + + Return AndInt64(conv1.ToInt64(Nothing), conv2.ToInt64(Nothing)) 'UNDONE: what about error messages on the overflow? not very useful coming from iconvertible code. + + Case TypeCode.SByte * TCMAX + TypeCode.String, _ + TypeCode.Byte * TCMAX + TypeCode.String, _ + TypeCode.Int16 * TCMAX + TypeCode.String, _ + TypeCode.UInt16 * TCMAX + TypeCode.String, _ + TypeCode.Int32 * TCMAX + TypeCode.String, _ + TypeCode.UInt32 * TCMAX + TypeCode.String, _ + TypeCode.Int64 * TCMAX + TypeCode.String, _ + TypeCode.UInt64 * TCMAX + TypeCode.String, _ + TypeCode.Decimal * TCMAX + TypeCode.String, _ + TypeCode.Single * TCMAX + TypeCode.String, _ + TypeCode.Double * TCMAX + TypeCode.String + + Return AndInt64(conv1.ToInt64(Nothing), CLng(conv2.ToString(Nothing))) + + + Case TypeCode.Byte * TCMAX + TypeCode.Boolean, _ + TypeCode.Int16 * TCMAX + TypeCode.Boolean + Return AndInt16(conv1.ToInt16(Nothing), ToVBBool(conv2)) + + Case TypeCode.Byte * TCMAX + TypeCode.Byte + Return AndByte(conv1.ToByte(Nothing), conv2.ToByte(Nothing), GetEnumResult(Left, Right)) + + Case TypeCode.Byte * TCMAX + TypeCode.UInt16, _ + TypeCode.UInt16 * TCMAX + TypeCode.Byte + Return AndUInt16(conv1.ToUInt16(Nothing), conv2.ToUInt16(Nothing)) + + Case TypeCode.Byte * TCMAX + TypeCode.UInt32, _ + TypeCode.UInt16 * TCMAX + TypeCode.UInt32, _ + TypeCode.UInt32 * TCMAX + TypeCode.Byte, _ + TypeCode.UInt32 * TCMAX + TypeCode.UInt16 + + Return AndUInt32(conv1.ToUInt32(Nothing), conv2.ToUInt32(Nothing)) + + Case TypeCode.Byte * TCMAX + TypeCode.UInt64, _ + TypeCode.UInt16 * TCMAX + TypeCode.UInt64, _ + TypeCode.UInt32 * TCMAX + TypeCode.UInt64, _ + TypeCode.UInt64 * TCMAX + TypeCode.Byte, _ + TypeCode.UInt64 * TCMAX + TypeCode.UInt16, _ + TypeCode.UInt64 * TCMAX + TypeCode.UInt32 + + Return AndUInt64(conv1.ToUInt64(Nothing), conv2.ToUInt64(Nothing)) + + + Case TypeCode.Int16 * TCMAX + TypeCode.Int16 + Return AndInt16(conv1.ToInt16(Nothing), conv2.ToInt16(Nothing), GetEnumResult(Left, Right)) + + + Case TypeCode.UInt16 * TCMAX + TypeCode.Boolean, _ + TypeCode.Int32 * TCMAX + TypeCode.Boolean + Return AndInt32(conv1.ToInt32(Nothing), ToVBBool(conv2)) + + Case TypeCode.UInt16 * TCMAX + TypeCode.UInt16 + Return AndUInt16(conv1.ToUInt16(Nothing), conv2.ToUInt16(Nothing), GetEnumResult(Left, Right)) + + + Case TypeCode.Int32 * TCMAX + TypeCode.Int32 + Return AndInt32(conv1.ToInt32(Nothing), conv2.ToInt32(Nothing), GetEnumResult(Left, Right)) + + + Case TypeCode.UInt32 * TCMAX + TypeCode.Boolean, _ + TypeCode.Int64 * TCMAX + TypeCode.Boolean, _ + TypeCode.UInt64 * TCMAX + TypeCode.Boolean, _ + TypeCode.Decimal * TCMAX + TypeCode.Boolean, _ + TypeCode.Single * TCMAX + TypeCode.Boolean, _ + TypeCode.Double * TCMAX + TypeCode.Boolean + + Return AndInt64(conv1.ToInt64(Nothing), ToVBBool(conv2)) + + Case TypeCode.UInt32 * TCMAX + TypeCode.UInt32 + Return AndUInt32(conv1.ToUInt32(Nothing), conv2.ToUInt32(Nothing), GetEnumResult(Left, Right)) + + + Case TypeCode.Int64 * TCMAX + TypeCode.Int64 + Return AndInt64(conv1.ToInt64(Nothing), conv2.ToInt64(Nothing), GetEnumResult(Left, Right)) 'UNDONE: what about error messages on the overflow? not very useful coming from iconvertible code. + + + Case TypeCode.UInt64 * TCMAX + TypeCode.UInt64 + Return AndUInt64(conv1.ToUInt64(Nothing), conv2.ToUInt64(Nothing), GetEnumResult(Left, Right)) + + + Case TypeCode.Decimal * TCMAX + TypeCode.Empty, _ + TypeCode.Single * TCMAX + TypeCode.Empty, _ + TypeCode.Double * TCMAX + TypeCode.Empty + Return AndInt64(conv1.ToInt64(Nothing), Nothing) + + + Case TypeCode.String * TCMAX + TypeCode.Empty + Return AndInt64(CLng(conv1.ToString(Nothing)), Nothing) + + Case TypeCode.String * TCMAX + TypeCode.Boolean + Return AndBoolean(CBool(conv1.ToString(Nothing)), conv2.ToBoolean(Nothing)) + + Case TypeCode.String * TCMAX + TypeCode.SByte, _ + TypeCode.String * TCMAX + TypeCode.Byte, _ + TypeCode.String * TCMAX + TypeCode.Int16, _ + TypeCode.String * TCMAX + TypeCode.UInt16, _ + TypeCode.String * TCMAX + TypeCode.Int32, _ + TypeCode.String * TCMAX + TypeCode.UInt32, _ + TypeCode.String * TCMAX + TypeCode.Int64, _ + TypeCode.String * TCMAX + TypeCode.UInt64, _ + TypeCode.String * TCMAX + TypeCode.Decimal, _ + TypeCode.String * TCMAX + TypeCode.Single, _ + TypeCode.String * TCMAX + TypeCode.Double + + Return AndInt64(CLng(conv1.ToString(Nothing)), conv2.ToInt64(Nothing)) + + Case TypeCode.String * TCMAX + TypeCode.String + Return AndInt64(CLng(conv1.ToString(Nothing)), CLng(conv2.ToString(Nothing))) + +#If 0 Then + 'ERROR CASES + Case TypeCode.Empty * TCMAX + TypeCode.DateTime 'XX + Case TypeCode.Empty * TCMAX + TypeCode.Char 'XX + Case TypeCode.Boolean * TCMAX + TypeCode.DateTime 'XX + Case TypeCode.Boolean * TCMAX + TypeCode.Char 'XX + Case TypeCode.SByte * TCMAX + TypeCode.DateTime 'XX + Case TypeCode.SByte * TCMAX + TypeCode.Char 'XX + Case TypeCode.Byte * TCMAX + TypeCode.DateTime 'XX + Case TypeCode.Byte * TCMAX + TypeCode.Char 'XX + Case TypeCode.Int16 * TCMAX + TypeCode.DateTime 'XX + Case TypeCode.Int16 * TCMAX + TypeCode.Char 'XX + Case TypeCode.UInt16 * TCMAX + TypeCode.DateTime 'XX + Case TypeCode.UInt16 * TCMAX + TypeCode.Char 'XX + Case TypeCode.Int32 * TCMAX + TypeCode.DateTime 'XX + Case TypeCode.Int32 * TCMAX + TypeCode.Char 'XX + Case TypeCode.UInt32 * TCMAX + TypeCode.DateTime 'XX + Case TypeCode.UInt32 * TCMAX + TypeCode.Char 'XX + Case TypeCode.Int64 * TCMAX + TypeCode.DateTime 'XX + Case TypeCode.Int64 * TCMAX + TypeCode.Char 'XX + Case TypeCode.UInt64 * TCMAX + TypeCode.DateTime 'XX + Case TypeCode.UInt64 * TCMAX + TypeCode.Char 'XX + Case TypeCode.Decimal * TCMAX + TypeCode.DateTime 'XX + Case TypeCode.Decimal * TCMAX + TypeCode.Char 'XX + Case TypeCode.Single * TCMAX + TypeCode.DateTime 'XX + Case TypeCode.Single * TCMAX + TypeCode.Char 'XX + Case TypeCode.Double * TCMAX + TypeCode.DateTime 'XX + Case TypeCode.Double * TCMAX + TypeCode.Char 'XX + Case TypeCode.DateTime * TCMAX + TypeCode.Empty 'XX + Case TypeCode.DateTime * TCMAX + TypeCode.Boolean 'XX + Case TypeCode.DateTime * TCMAX + TypeCode.SByte 'XX + Case TypeCode.DateTime * TCMAX + TypeCode.Byte 'XX + Case TypeCode.DateTime * TCMAX + TypeCode.Int16 'XX + Case TypeCode.DateTime * TCMAX + TypeCode.UInt16 'XX + Case TypeCode.DateTime * TCMAX + TypeCode.Int32 'XX + Case TypeCode.DateTime * TCMAX + TypeCode.UInt32 'XX + Case TypeCode.DateTime * TCMAX + TypeCode.Int64 'XX + Case TypeCode.DateTime * TCMAX + TypeCode.UInt64 'XX + Case TypeCode.DateTime * TCMAX + TypeCode.Decimal 'XX + Case TypeCode.DateTime * TCMAX + TypeCode.Single 'XX + Case TypeCode.DateTime * TCMAX + TypeCode.Double 'XX + Case TypeCode.DateTime * TCMAX + TypeCode.DateTime 'XX + Case TypeCode.DateTime * TCMAX + TypeCode.Char 'XX + Case TypeCode.DateTime * TCMAX + TypeCode.String 'XX + Case TypeCode.Char * TCMAX + TypeCode.Empty 'XX + Case TypeCode.Char * TCMAX + TypeCode.Boolean 'XX + Case TypeCode.Char * TCMAX + TypeCode.SByte 'XX + Case TypeCode.Char * TCMAX + TypeCode.Byte 'XX + Case TypeCode.Char * TCMAX + TypeCode.Int16 'XX + Case TypeCode.Char * TCMAX + TypeCode.UInt16 'XX + Case TypeCode.Char * TCMAX + TypeCode.Int32 'XX + Case TypeCode.Char * TCMAX + TypeCode.UInt32 'XX + Case TypeCode.Char * TCMAX + TypeCode.Int64 'XX + Case TypeCode.Char * TCMAX + TypeCode.UInt64 'XX + Case TypeCode.Char * TCMAX + TypeCode.Decimal 'XX + Case TypeCode.Char * TCMAX + TypeCode.Single 'XX + Case TypeCode.Char * TCMAX + TypeCode.Double 'XX + Case TypeCode.Char * TCMAX + TypeCode.DateTime 'XX + Case TypeCode.Char * TCMAX + TypeCode.Char 'XX + Case TypeCode.Char * TCMAX + TypeCode.String 'XX + Case TypeCode.String * TCMAX + TypeCode.DateTime 'XX + Case TypeCode.String * TCMAX + TypeCode.Char 'XX +#End If + End Select + + If tc1 = TypeCode.Object OrElse tc2 = TypeCode.Object Then + Return InvokeUserDefinedOperator(UserDefinedOperator.And, Left, Right) + End If + + Throw GetNoValidOperatorException(UserDefinedOperator.And, Left, Right) + + End Function + + Private Shared Function AndBoolean(ByVal Left As Boolean, ByVal Right As Boolean) As Object + Return Left And Right + End Function + + Private Shared Function AndSByte(ByVal Left As SByte, ByVal Right As SByte, Optional ByVal EnumType As Type = Nothing) As Object + Dim Result As SByte = Left And Right + + If EnumType IsNot Nothing Then Return System.Enum.ToObject(EnumType, Result) + Return Result + End Function + + Private Shared Function AndByte(ByVal Left As Byte, ByVal Right As Byte, Optional ByVal EnumType As Type = Nothing) As Object + Dim Result As Byte = Left And Right + + If EnumType IsNot Nothing Then Return System.Enum.ToObject(EnumType, Result) + Return Result + End Function + + Private Shared Function AndInt16(ByVal Left As Int16, ByVal Right As Int16, Optional ByVal EnumType As Type = Nothing) As Object + Dim Result As Int16 = Left And Right + + If EnumType IsNot Nothing Then Return System.Enum.ToObject(EnumType, Result) + Return Result + End Function + + Private Shared Function AndUInt16(ByVal Left As UInt16, ByVal Right As UInt16, Optional ByVal EnumType As Type = Nothing) As Object + Dim Result As UInt16 = Left And Right + + If EnumType IsNot Nothing Then Return System.Enum.ToObject(EnumType, Result) + Return Result + End Function + + Private Shared Function AndInt32(ByVal Left As Int32, ByVal Right As Int32, Optional ByVal EnumType As Type = Nothing) As Object + Dim Result As Int32 = Left And Right + + If EnumType IsNot Nothing Then Return System.Enum.ToObject(EnumType, Result) + Return Result + End Function + + Private Shared Function AndUInt32(ByVal Left As UInt32, ByVal Right As UInt32, Optional ByVal EnumType As Type = Nothing) As Object + Dim Result As UInt32 = Left And Right + + If EnumType IsNot Nothing Then Return System.Enum.ToObject(EnumType, Result) + Return Result + End Function + + Private Shared Function AndInt64(ByVal Left As Int64, ByVal Right As Int64, Optional ByVal EnumType As Type = Nothing) As Object + Dim Result As Int64 = Left And Right + + If EnumType IsNot Nothing Then Return System.Enum.ToObject(EnumType, Result) + Return Result + End Function + + Private Shared Function AndUInt64(ByVal Left As UInt64, ByVal Right As UInt64, Optional ByVal EnumType As Type = Nothing) As Object + Dim Result As UInt64 = Left And Right + + If EnumType IsNot Nothing Then Return System.Enum.ToObject(EnumType, Result) + Return Result + End Function + +#End Region + +#Region " Operator Or " + + Public Shared Function OrObject(ByVal Left As Object, ByVal Right As Object) As Object + + Dim conv1, conv2 As IConvertible + Dim tc1, tc2 As TypeCode + + conv1 = TryCast(Left, IConvertible) + + If conv1 Is Nothing Then + If Left Is Nothing Then + tc1 = TypeCode.Empty + Else + tc1 = TypeCode.Object + End If + Else + tc1 = conv1.GetTypeCode() + End If + + + conv2 = TryCast(Right, IConvertible) + + If conv2 Is Nothing Then + If Right Is Nothing Then + tc2 = TypeCode.Empty + Else + tc2 = TypeCode.Object + End If + Else + tc2 = conv2.GetTypeCode() + End If + + + Select Case tc1 * TCMAX + tc2 'CONSIDER: overflow checking is not necessary for this calculation - perf improvement. + + Case TypeCode.Empty * TCMAX + TypeCode.Empty + Return Boxed_ZeroInteger + + Case TypeCode.Empty * TCMAX + TypeCode.Boolean + Return OrBoolean(Nothing, conv2.ToBoolean(Nothing)) + + Case TypeCode.Empty * TCMAX + TypeCode.SByte, _ + TypeCode.Empty * TCMAX + TypeCode.Byte, _ + TypeCode.Empty * TCMAX + TypeCode.Int16, _ + TypeCode.Empty * TCMAX + TypeCode.UInt16, _ + TypeCode.Empty * TCMAX + TypeCode.Int32, _ + TypeCode.Empty * TCMAX + TypeCode.UInt32, _ + TypeCode.Empty * TCMAX + TypeCode.Int64, _ + TypeCode.Empty * TCMAX + TypeCode.UInt64 + + Return Right + + Case TypeCode.Empty * TCMAX + TypeCode.Decimal, _ + TypeCode.Empty * TCMAX + TypeCode.Single, _ + TypeCode.Empty * TCMAX + TypeCode.Double + Return OrInt64(Nothing, conv2.ToInt64(Nothing)) + + Case TypeCode.Empty * TCMAX + TypeCode.String + Return OrInt64(Nothing, CLng(conv2.ToString(Nothing))) + + + Case TypeCode.Boolean * TCMAX + TypeCode.Empty + Return OrBoolean(conv1.ToBoolean(Nothing), Nothing) + + Case TypeCode.Boolean * TCMAX + TypeCode.Boolean + Return OrBoolean(conv1.ToBoolean(Nothing), conv2.ToBoolean(Nothing)) + + Case TypeCode.Boolean * TCMAX + TypeCode.SByte + Return OrSByte(ToVBBool(conv1), conv2.ToSByte(Nothing)) + + Case TypeCode.Boolean * TCMAX + TypeCode.Byte, _ + TypeCode.Boolean * TCMAX + TypeCode.Int16 + Return OrInt16(ToVBBool(conv1), conv2.ToInt16(Nothing)) + + Case TypeCode.Boolean * TCMAX + TypeCode.UInt16, _ + TypeCode.Boolean * TCMAX + TypeCode.Int32 + Return OrInt32(ToVBBool(conv1), conv2.ToInt32(Nothing)) + + Case TypeCode.Boolean * TCMAX + TypeCode.UInt32, _ + TypeCode.Boolean * TCMAX + TypeCode.Int64, _ + TypeCode.Boolean * TCMAX + TypeCode.UInt64, _ + TypeCode.Boolean * TCMAX + TypeCode.Decimal, _ + TypeCode.Boolean * TCMAX + TypeCode.Single, _ + TypeCode.Boolean * TCMAX + TypeCode.Double + + Return OrInt64(ToVBBool(conv1), conv2.ToInt64(Nothing)) 'UNDONE: what about error messages on the overflow? not very useful coming from iconvertible code. + + Case TypeCode.Boolean * TCMAX + TypeCode.String + Return OrBoolean(conv1.ToBoolean(Nothing), CBool(conv2.ToString(Nothing))) + + + Case TypeCode.SByte * TCMAX + TypeCode.Empty, _ + TypeCode.Byte * TCMAX + TypeCode.Empty, _ + TypeCode.Int16 * TCMAX + TypeCode.Empty, _ + TypeCode.UInt16 * TCMAX + TypeCode.Empty, _ + TypeCode.Int32 * TCMAX + TypeCode.Empty, _ + TypeCode.UInt32 * TCMAX + TypeCode.Empty, _ + TypeCode.Int64 * TCMAX + TypeCode.Empty, _ + TypeCode.UInt64 * TCMAX + TypeCode.Empty + + Return Left + + Case TypeCode.SByte * TCMAX + TypeCode.Boolean + Return OrSByte(conv1.ToSByte(Nothing), ToVBBool(conv2)) + + Case TypeCode.SByte * TCMAX + TypeCode.SByte + Return OrSByte(conv1.ToSByte(Nothing), conv2.ToSByte(Nothing), GetEnumResult(Left, Right)) + + Case TypeCode.SByte * TCMAX + TypeCode.Byte, _ + TypeCode.SByte * TCMAX + TypeCode.Int16, _ + TypeCode.Byte * TCMAX + TypeCode.SByte, _ + TypeCode.Byte * TCMAX + TypeCode.Int16, _ + TypeCode.Int16 * TCMAX + TypeCode.SByte, _ + TypeCode.Int16 * TCMAX + TypeCode.Byte + + Return OrInt16(conv1.ToInt16(Nothing), conv2.ToInt16(Nothing)) + + Case TypeCode.SByte * TCMAX + TypeCode.UInt16, _ + TypeCode.SByte * TCMAX + TypeCode.Int32, _ + TypeCode.Byte * TCMAX + TypeCode.Int32, _ + TypeCode.Int16 * TCMAX + TypeCode.UInt16, _ + TypeCode.Int16 * TCMAX + TypeCode.Int32, _ + TypeCode.UInt16 * TCMAX + TypeCode.SByte, _ + TypeCode.UInt16 * TCMAX + TypeCode.Int16, _ + TypeCode.UInt16 * TCMAX + TypeCode.Int32, _ + TypeCode.Int32 * TCMAX + TypeCode.SByte, _ + TypeCode.Int32 * TCMAX + TypeCode.Byte, _ + TypeCode.Int32 * TCMAX + TypeCode.Int16, _ + TypeCode.Int32 * TCMAX + TypeCode.UInt16 + + Return OrInt32(conv1.ToInt32(Nothing), conv2.ToInt32(Nothing)) + + Case TypeCode.SByte * TCMAX + TypeCode.UInt32, _ + TypeCode.SByte * TCMAX + TypeCode.Int64, _ + TypeCode.SByte * TCMAX + TypeCode.UInt64, _ + TypeCode.SByte * TCMAX + TypeCode.Decimal, _ + TypeCode.SByte * TCMAX + TypeCode.Single, _ + TypeCode.SByte * TCMAX + TypeCode.Double, _ + TypeCode.Byte * TCMAX + TypeCode.Int64, _ + TypeCode.Byte * TCMAX + TypeCode.Decimal, _ + TypeCode.Byte * TCMAX + TypeCode.Single, _ + TypeCode.Byte * TCMAX + TypeCode.Double, _ + TypeCode.Int16 * TCMAX + TypeCode.UInt32, _ + TypeCode.Int16 * TCMAX + TypeCode.Int64, _ + TypeCode.Int16 * TCMAX + TypeCode.UInt64, _ + TypeCode.Int16 * TCMAX + TypeCode.Decimal, _ + TypeCode.Int16 * TCMAX + TypeCode.Single, _ + TypeCode.Int16 * TCMAX + TypeCode.Double, _ + TypeCode.UInt16 * TCMAX + TypeCode.Int64, _ + TypeCode.UInt16 * TCMAX + TypeCode.Decimal, _ + TypeCode.UInt16 * TCMAX + TypeCode.Single, _ + TypeCode.UInt16 * TCMAX + TypeCode.Double, _ + TypeCode.Int32 * TCMAX + TypeCode.UInt32, _ + TypeCode.Int32 * TCMAX + TypeCode.Int64, _ + TypeCode.Int32 * TCMAX + TypeCode.UInt64, _ + TypeCode.Int32 * TCMAX + TypeCode.Decimal, _ + TypeCode.Int32 * TCMAX + TypeCode.Single, _ + TypeCode.Int32 * TCMAX + TypeCode.Double, _ + TypeCode.UInt32 * TCMAX + TypeCode.SByte, _ + TypeCode.UInt32 * TCMAX + TypeCode.Int16, _ + TypeCode.UInt32 * TCMAX + TypeCode.Int32, _ + TypeCode.UInt32 * TCMAX + TypeCode.Int64, _ + TypeCode.UInt32 * TCMAX + TypeCode.Decimal, _ + TypeCode.UInt32 * TCMAX + TypeCode.Single, _ + TypeCode.UInt32 * TCMAX + TypeCode.Double, _ + TypeCode.Int64 * TCMAX + TypeCode.SByte, _ + TypeCode.Int64 * TCMAX + TypeCode.Byte, _ + TypeCode.Int64 * TCMAX + TypeCode.Int16, _ + TypeCode.Int64 * TCMAX + TypeCode.UInt16, _ + TypeCode.Int64 * TCMAX + TypeCode.Int32, _ + TypeCode.Int64 * TCMAX + TypeCode.UInt32, _ + TypeCode.Int64 * TCMAX + TypeCode.UInt64, _ + TypeCode.Int64 * TCMAX + TypeCode.Decimal, _ + TypeCode.Int64 * TCMAX + TypeCode.Single, _ + TypeCode.Int64 * TCMAX + TypeCode.Double, _ + TypeCode.UInt64 * TCMAX + TypeCode.SByte, _ + TypeCode.UInt64 * TCMAX + TypeCode.Int16, _ + TypeCode.UInt64 * TCMAX + TypeCode.Int32, _ + TypeCode.UInt64 * TCMAX + TypeCode.Int64, _ + TypeCode.UInt64 * TCMAX + TypeCode.Decimal, _ + TypeCode.UInt64 * TCMAX + TypeCode.Single, _ + TypeCode.UInt64 * TCMAX + TypeCode.Double, _ + TypeCode.Decimal * TCMAX + TypeCode.SByte, _ + TypeCode.Decimal * TCMAX + TypeCode.Byte, _ + TypeCode.Decimal * TCMAX + TypeCode.Int16, _ + TypeCode.Decimal * TCMAX + TypeCode.UInt16, _ + TypeCode.Decimal * TCMAX + TypeCode.Int32, _ + TypeCode.Decimal * TCMAX + TypeCode.UInt32, _ + TypeCode.Decimal * TCMAX + TypeCode.Int64, _ + TypeCode.Decimal * TCMAX + TypeCode.UInt64, _ + TypeCode.Decimal * TCMAX + TypeCode.Decimal, _ + TypeCode.Decimal * TCMAX + TypeCode.Single, _ + TypeCode.Decimal * TCMAX + TypeCode.Double, _ + TypeCode.Single * TCMAX + TypeCode.SByte, _ + TypeCode.Single * TCMAX + TypeCode.Byte, _ + TypeCode.Single * TCMAX + TypeCode.Int16, _ + TypeCode.Single * TCMAX + TypeCode.UInt16, _ + TypeCode.Single * TCMAX + TypeCode.Int32, _ + TypeCode.Single * TCMAX + TypeCode.UInt32, _ + TypeCode.Single * TCMAX + TypeCode.Int64, _ + TypeCode.Single * TCMAX + TypeCode.UInt64, _ + TypeCode.Single * TCMAX + TypeCode.Decimal, _ + TypeCode.Single * TCMAX + TypeCode.Single, _ + TypeCode.Single * TCMAX + TypeCode.Double, _ + TypeCode.Double * TCMAX + TypeCode.SByte, _ + TypeCode.Double * TCMAX + TypeCode.Byte, _ + TypeCode.Double * TCMAX + TypeCode.Int16, _ + TypeCode.Double * TCMAX + TypeCode.UInt16, _ + TypeCode.Double * TCMAX + TypeCode.Int32, _ + TypeCode.Double * TCMAX + TypeCode.UInt32, _ + TypeCode.Double * TCMAX + TypeCode.Int64, _ + TypeCode.Double * TCMAX + TypeCode.UInt64, _ + TypeCode.Double * TCMAX + TypeCode.Decimal, _ + TypeCode.Double * TCMAX + TypeCode.Single, _ + TypeCode.Double * TCMAX + TypeCode.Double + + Return OrInt64(conv1.ToInt64(Nothing), conv2.ToInt64(Nothing)) 'UNDONE: what about error messages on the overflow? not very useful coming from iconvertible code. + + Case TypeCode.SByte * TCMAX + TypeCode.String, _ + TypeCode.Byte * TCMAX + TypeCode.String, _ + TypeCode.Int16 * TCMAX + TypeCode.String, _ + TypeCode.UInt16 * TCMAX + TypeCode.String, _ + TypeCode.Int32 * TCMAX + TypeCode.String, _ + TypeCode.UInt32 * TCMAX + TypeCode.String, _ + TypeCode.Int64 * TCMAX + TypeCode.String, _ + TypeCode.UInt64 * TCMAX + TypeCode.String, _ + TypeCode.Decimal * TCMAX + TypeCode.String, _ + TypeCode.Single * TCMAX + TypeCode.String, _ + TypeCode.Double * TCMAX + TypeCode.String + + Return OrInt64(conv1.ToInt64(Nothing), CLng(conv2.ToString(Nothing))) + + + Case TypeCode.Byte * TCMAX + TypeCode.Boolean, _ + TypeCode.Int16 * TCMAX + TypeCode.Boolean + Return OrInt16(conv1.ToInt16(Nothing), ToVBBool(conv2)) + + Case TypeCode.Byte * TCMAX + TypeCode.Byte + Return OrByte(conv1.ToByte(Nothing), conv2.ToByte(Nothing), GetEnumResult(Left, Right)) + + Case TypeCode.Byte * TCMAX + TypeCode.UInt16, _ + TypeCode.UInt16 * TCMAX + TypeCode.Byte + Return OrUInt16(conv1.ToUInt16(Nothing), conv2.ToUInt16(Nothing)) + + Case TypeCode.Byte * TCMAX + TypeCode.UInt32, _ + TypeCode.UInt16 * TCMAX + TypeCode.UInt32, _ + TypeCode.UInt32 * TCMAX + TypeCode.Byte, _ + TypeCode.UInt32 * TCMAX + TypeCode.UInt16 + + Return OrUInt32(conv1.ToUInt32(Nothing), conv2.ToUInt32(Nothing)) + + Case TypeCode.Byte * TCMAX + TypeCode.UInt64, _ + TypeCode.UInt16 * TCMAX + TypeCode.UInt64, _ + TypeCode.UInt32 * TCMAX + TypeCode.UInt64, _ + TypeCode.UInt64 * TCMAX + TypeCode.Byte, _ + TypeCode.UInt64 * TCMAX + TypeCode.UInt16, _ + TypeCode.UInt64 * TCMAX + TypeCode.UInt32 + + Return OrUInt64(conv1.ToUInt64(Nothing), conv2.ToUInt64(Nothing)) + + + Case TypeCode.Int16 * TCMAX + TypeCode.Int16 + Return OrInt16(conv1.ToInt16(Nothing), conv2.ToInt16(Nothing), GetEnumResult(Left, Right)) + + + Case TypeCode.UInt16 * TCMAX + TypeCode.Boolean, _ + TypeCode.Int32 * TCMAX + TypeCode.Boolean + Return OrInt32(conv1.ToInt32(Nothing), ToVBBool(conv2)) + + Case TypeCode.UInt16 * TCMAX + TypeCode.UInt16 + Return OrUInt16(conv1.ToUInt16(Nothing), conv2.ToUInt16(Nothing), GetEnumResult(Left, Right)) + + + Case TypeCode.Int32 * TCMAX + TypeCode.Int32 + Return OrInt32(conv1.ToInt32(Nothing), conv2.ToInt32(Nothing), GetEnumResult(Left, Right)) + + + Case TypeCode.UInt32 * TCMAX + TypeCode.Boolean, _ + TypeCode.Int64 * TCMAX + TypeCode.Boolean, _ + TypeCode.UInt64 * TCMAX + TypeCode.Boolean, _ + TypeCode.Decimal * TCMAX + TypeCode.Boolean, _ + TypeCode.Single * TCMAX + TypeCode.Boolean, _ + TypeCode.Double * TCMAX + TypeCode.Boolean + + Return OrInt64(conv1.ToInt64(Nothing), ToVBBool(conv2)) + + Case TypeCode.UInt32 * TCMAX + TypeCode.UInt32 + Return OrUInt32(conv1.ToUInt32(Nothing), conv2.ToUInt32(Nothing), GetEnumResult(Left, Right)) + + + Case TypeCode.Int64 * TCMAX + TypeCode.Int64 + Return OrInt64(conv1.ToInt64(Nothing), conv2.ToInt64(Nothing), GetEnumResult(Left, Right)) 'UNDONE: what about error messages on the overflow? not very useful coming from iconvertible code. + + + Case TypeCode.UInt64 * TCMAX + TypeCode.UInt64 + Return OrUInt64(conv1.ToUInt64(Nothing), conv2.ToUInt64(Nothing), GetEnumResult(Left, Right)) + + + Case TypeCode.Decimal * TCMAX + TypeCode.Empty, _ + TypeCode.Single * TCMAX + TypeCode.Empty, _ + TypeCode.Double * TCMAX + TypeCode.Empty + Return OrInt64(conv1.ToInt64(Nothing), Nothing) + + + Case TypeCode.String * TCMAX + TypeCode.Empty + Return OrInt64(CLng(conv1.ToString(Nothing)), Nothing) + + Case TypeCode.String * TCMAX + TypeCode.Boolean + Return OrBoolean(CBool(conv1.ToString(Nothing)), conv2.ToBoolean(Nothing)) + + Case TypeCode.String * TCMAX + TypeCode.SByte, _ + TypeCode.String * TCMAX + TypeCode.Byte, _ + TypeCode.String * TCMAX + TypeCode.Int16, _ + TypeCode.String * TCMAX + TypeCode.UInt16, _ + TypeCode.String * TCMAX + TypeCode.Int32, _ + TypeCode.String * TCMAX + TypeCode.UInt32, _ + TypeCode.String * TCMAX + TypeCode.Int64, _ + TypeCode.String * TCMAX + TypeCode.UInt64, _ + TypeCode.String * TCMAX + TypeCode.Decimal, _ + TypeCode.String * TCMAX + TypeCode.Single, _ + TypeCode.String * TCMAX + TypeCode.Double + + Return OrInt64(CLng(conv1.ToString(Nothing)), conv2.ToInt64(Nothing)) + + Case TypeCode.String * TCMAX + TypeCode.String + Return OrInt64(CLng(conv1.ToString(Nothing)), CLng(conv2.ToString(Nothing))) + +#If 0 Then + 'ERROR CASES + Case TypeCode.Empty * TCMAX + TypeCode.DateTime 'XX + Case TypeCode.Empty * TCMAX + TypeCode.Char 'XX + Case TypeCode.Boolean * TCMAX + TypeCode.DateTime 'XX + Case TypeCode.Boolean * TCMAX + TypeCode.Char 'XX + Case TypeCode.SByte * TCMAX + TypeCode.DateTime 'XX + Case TypeCode.SByte * TCMAX + TypeCode.Char 'XX + Case TypeCode.Byte * TCMAX + TypeCode.DateTime 'XX + Case TypeCode.Byte * TCMAX + TypeCode.Char 'XX + Case TypeCode.Int16 * TCMAX + TypeCode.DateTime 'XX + Case TypeCode.Int16 * TCMAX + TypeCode.Char 'XX + Case TypeCode.UInt16 * TCMAX + TypeCode.DateTime 'XX + Case TypeCode.UInt16 * TCMAX + TypeCode.Char 'XX + Case TypeCode.Int32 * TCMAX + TypeCode.DateTime 'XX + Case TypeCode.Int32 * TCMAX + TypeCode.Char 'XX + Case TypeCode.UInt32 * TCMAX + TypeCode.DateTime 'XX + Case TypeCode.UInt32 * TCMAX + TypeCode.Char 'XX + Case TypeCode.Int64 * TCMAX + TypeCode.DateTime 'XX + Case TypeCode.Int64 * TCMAX + TypeCode.Char 'XX + Case TypeCode.UInt64 * TCMAX + TypeCode.DateTime 'XX + Case TypeCode.UInt64 * TCMAX + TypeCode.Char 'XX + Case TypeCode.Decimal * TCMAX + TypeCode.DateTime 'XX + Case TypeCode.Decimal * TCMAX + TypeCode.Char 'XX + Case TypeCode.Single * TCMAX + TypeCode.DateTime 'XX + Case TypeCode.Single * TCMAX + TypeCode.Char 'XX + Case TypeCode.Double * TCMAX + TypeCode.DateTime 'XX + Case TypeCode.Double * TCMAX + TypeCode.Char 'XX + Case TypeCode.DateTime * TCMAX + TypeCode.Empty 'XX + Case TypeCode.DateTime * TCMAX + TypeCode.Boolean 'XX + Case TypeCode.DateTime * TCMAX + TypeCode.SByte 'XX + Case TypeCode.DateTime * TCMAX + TypeCode.Byte 'XX + Case TypeCode.DateTime * TCMAX + TypeCode.Int16 'XX + Case TypeCode.DateTime * TCMAX + TypeCode.UInt16 'XX + Case TypeCode.DateTime * TCMAX + TypeCode.Int32 'XX + Case TypeCode.DateTime * TCMAX + TypeCode.UInt32 'XX + Case TypeCode.DateTime * TCMAX + TypeCode.Int64 'XX + Case TypeCode.DateTime * TCMAX + TypeCode.UInt64 'XX + Case TypeCode.DateTime * TCMAX + TypeCode.Decimal 'XX + Case TypeCode.DateTime * TCMAX + TypeCode.Single 'XX + Case TypeCode.DateTime * TCMAX + TypeCode.Double 'XX + Case TypeCode.DateTime * TCMAX + TypeCode.DateTime 'XX + Case TypeCode.DateTime * TCMAX + TypeCode.Char 'XX + Case TypeCode.DateTime * TCMAX + TypeCode.String 'XX + Case TypeCode.Char * TCMAX + TypeCode.Empty 'XX + Case TypeCode.Char * TCMAX + TypeCode.Boolean 'XX + Case TypeCode.Char * TCMAX + TypeCode.SByte 'XX + Case TypeCode.Char * TCMAX + TypeCode.Byte 'XX + Case TypeCode.Char * TCMAX + TypeCode.Int16 'XX + Case TypeCode.Char * TCMAX + TypeCode.UInt16 'XX + Case TypeCode.Char * TCMAX + TypeCode.Int32 'XX + Case TypeCode.Char * TCMAX + TypeCode.UInt32 'XX + Case TypeCode.Char * TCMAX + TypeCode.Int64 'XX + Case TypeCode.Char * TCMAX + TypeCode.UInt64 'XX + Case TypeCode.Char * TCMAX + TypeCode.Decimal 'XX + Case TypeCode.Char * TCMAX + TypeCode.Single 'XX + Case TypeCode.Char * TCMAX + TypeCode.Double 'XX + Case TypeCode.Char * TCMAX + TypeCode.DateTime 'XX + Case TypeCode.Char * TCMAX + TypeCode.Char 'XX + Case TypeCode.Char * TCMAX + TypeCode.String 'XX + Case TypeCode.String * TCMAX + TypeCode.DateTime 'XX + Case TypeCode.String * TCMAX + TypeCode.Char 'XX +#End If + End Select + + If tc1 = TypeCode.Object OrElse tc2 = TypeCode.Object Then + Return InvokeUserDefinedOperator(UserDefinedOperator.Or, Left, Right) + End If + + Throw GetNoValidOperatorException(UserDefinedOperator.Or, Left, Right) + + End Function + + Private Shared Function OrBoolean(ByVal Left As Boolean, ByVal Right As Boolean) As Object + Return Left Or Right + End Function + + Private Shared Function OrSByte(ByVal Left As SByte, ByVal Right As SByte, Optional ByVal EnumType As Type = Nothing) As Object + Dim Result As SByte = Left Or Right + + If EnumType IsNot Nothing Then Return System.Enum.ToObject(EnumType, Result) + Return Result + End Function + + Private Shared Function OrByte(ByVal Left As Byte, ByVal Right As Byte, Optional ByVal EnumType As Type = Nothing) As Object + Dim Result As Byte = Left Or Right + + If EnumType IsNot Nothing Then Return System.Enum.ToObject(EnumType, Result) + Return Result + End Function + + Private Shared Function OrInt16(ByVal Left As Int16, ByVal Right As Int16, Optional ByVal EnumType As Type = Nothing) As Object + Dim Result As Int16 = Left Or Right + + If EnumType IsNot Nothing Then Return System.Enum.ToObject(EnumType, Result) + Return Result + End Function + + Private Shared Function OrUInt16(ByVal Left As UInt16, ByVal Right As UInt16, Optional ByVal EnumType As Type = Nothing) As Object + Dim Result As UInt16 = Left Or Right + + If EnumType IsNot Nothing Then Return System.Enum.ToObject(EnumType, Result) + Return Result + End Function + + Private Shared Function OrInt32(ByVal Left As Int32, ByVal Right As Int32, Optional ByVal EnumType As Type = Nothing) As Object + Dim Result As Int32 = Left Or Right + + If EnumType IsNot Nothing Then Return System.Enum.ToObject(EnumType, Result) + Return Result + End Function + + Private Shared Function OrUInt32(ByVal Left As UInt32, ByVal Right As UInt32, Optional ByVal EnumType As Type = Nothing) As Object + Dim Result As UInt32 = Left Or Right + + If EnumType IsNot Nothing Then Return System.Enum.ToObject(EnumType, Result) + Return Result + End Function + + Private Shared Function OrInt64(ByVal Left As Int64, ByVal Right As Int64, Optional ByVal EnumType As Type = Nothing) As Object + Dim Result As Int64 = Left Or Right + + If EnumType IsNot Nothing Then Return System.Enum.ToObject(EnumType, Result) + Return Result + End Function + + Private Shared Function OrUInt64(ByVal Left As UInt64, ByVal Right As UInt64, Optional ByVal EnumType As Type = Nothing) As Object + Dim Result As UInt64 = Left Or Right + + If EnumType IsNot Nothing Then Return System.Enum.ToObject(EnumType, Result) + Return Result + End Function + +#End Region + +#Region " Operator Xor " + + Public Shared Function XorObject(ByVal Left As Object, ByVal Right As Object) As Object + + Dim conv1, conv2 As IConvertible + Dim tc1, tc2 As TypeCode + + conv1 = TryCast(Left, IConvertible) + + If conv1 Is Nothing Then + If Left Is Nothing Then + tc1 = TypeCode.Empty + Else + tc1 = TypeCode.Object + End If + Else + tc1 = conv1.GetTypeCode() + End If + + + conv2 = TryCast(Right, IConvertible) + + If conv2 Is Nothing Then + If Right Is Nothing Then + tc2 = TypeCode.Empty + Else + tc2 = TypeCode.Object + End If + Else + tc2 = conv2.GetTypeCode() + End If + + + Select Case tc1 * TCMAX + tc2 'CONSIDER: overflow checking is not necessary for this calculation - perf improvement. + + Case TypeCode.Empty * TCMAX + TypeCode.Empty + Return Boxed_ZeroInteger + + Case TypeCode.Empty * TCMAX + TypeCode.Boolean + Return XorBoolean(Nothing, conv2.ToBoolean(Nothing)) + + Case TypeCode.Empty * TCMAX + TypeCode.SByte + Return XorSByte(Nothing, conv2.ToSByte(Nothing), GetEnumResult(Left, Right)) + + Case TypeCode.Empty * TCMAX + TypeCode.Byte + Return XorByte(Nothing, conv2.ToByte(Nothing), GetEnumResult(Left, Right)) + + Case TypeCode.Empty * TCMAX + TypeCode.Int16 + Return XorInt16(Nothing, conv2.ToInt16(Nothing), GetEnumResult(Left, Right)) + + Case TypeCode.Empty * TCMAX + TypeCode.UInt16 + Return XorUInt16(Nothing, conv2.ToUInt16(Nothing), GetEnumResult(Left, Right)) + + Case TypeCode.Empty * TCMAX + TypeCode.Int32 + Return XorInt32(Nothing, conv2.ToInt32(Nothing), GetEnumResult(Left, Right)) + + Case TypeCode.Empty * TCMAX + TypeCode.UInt32 + Return XorUInt32(Nothing, conv2.ToUInt32(Nothing), GetEnumResult(Left, Right)) + + Case TypeCode.Empty * TCMAX + TypeCode.Int64 + Return XorInt64(Nothing, conv2.ToInt64(Nothing), GetEnumResult(Left, Right)) + + Case TypeCode.Empty * TCMAX + TypeCode.UInt64 + Return XorUInt64(Nothing, conv2.ToUInt64(Nothing), GetEnumResult(Left, Right)) + + Case TypeCode.Empty * TCMAX + TypeCode.Decimal, _ + TypeCode.Empty * TCMAX + TypeCode.Single, _ + TypeCode.Empty * TCMAX + TypeCode.Double + Return XorInt64(Nothing, conv2.ToInt64(Nothing)) + + Case TypeCode.Empty * TCMAX + TypeCode.String + Return XorInt64(Nothing, CLng(conv2.ToString(Nothing))) + + + Case TypeCode.Boolean * TCMAX + TypeCode.Empty + Return XorBoolean(conv1.ToBoolean(Nothing), Nothing) + + Case TypeCode.Boolean * TCMAX + TypeCode.Boolean + Return XorBoolean(conv1.ToBoolean(Nothing), conv2.ToBoolean(Nothing)) + + Case TypeCode.Boolean * TCMAX + TypeCode.SByte + Return XorSByte(ToVBBool(conv1), conv2.ToSByte(Nothing)) + + Case TypeCode.Boolean * TCMAX + TypeCode.Byte, _ + TypeCode.Boolean * TCMAX + TypeCode.Int16 + Return XorInt16(ToVBBool(conv1), conv2.ToInt16(Nothing)) + + Case TypeCode.Boolean * TCMAX + TypeCode.UInt16, _ + TypeCode.Boolean * TCMAX + TypeCode.Int32 + Return XorInt32(ToVBBool(conv1), conv2.ToInt32(Nothing)) + + Case TypeCode.Boolean * TCMAX + TypeCode.UInt32, _ + TypeCode.Boolean * TCMAX + TypeCode.Int64, _ + TypeCode.Boolean * TCMAX + TypeCode.UInt64, _ + TypeCode.Boolean * TCMAX + TypeCode.Decimal, _ + TypeCode.Boolean * TCMAX + TypeCode.Single, _ + TypeCode.Boolean * TCMAX + TypeCode.Double + + Return XorInt64(ToVBBool(conv1), conv2.ToInt64(Nothing)) 'UNDONE: what about error messages on the overflow? not very useful coming from iconvertible code. + + Case TypeCode.Boolean * TCMAX + TypeCode.String + Return XorBoolean(conv1.ToBoolean(Nothing), CBool(conv2.ToString(Nothing))) + + + Case TypeCode.SByte * TCMAX + TypeCode.Empty + Return XorSByte(conv1.ToSByte(Nothing), Nothing, GetEnumResult(Left, Right)) + + Case TypeCode.SByte * TCMAX + TypeCode.Boolean + Return XorSByte(conv1.ToSByte(Nothing), ToVBBool(conv2)) + + Case TypeCode.SByte * TCMAX + TypeCode.SByte + Return XorSByte(conv1.ToSByte(Nothing), conv2.ToSByte(Nothing), GetEnumResult(Left, Right)) + + Case TypeCode.SByte * TCMAX + TypeCode.Byte, _ + TypeCode.SByte * TCMAX + TypeCode.Int16, _ + TypeCode.Byte * TCMAX + TypeCode.SByte, _ + TypeCode.Byte * TCMAX + TypeCode.Int16, _ + TypeCode.Int16 * TCMAX + TypeCode.SByte, _ + TypeCode.Int16 * TCMAX + TypeCode.Byte + + Return XorInt16(conv1.ToInt16(Nothing), conv2.ToInt16(Nothing)) + + Case TypeCode.SByte * TCMAX + TypeCode.UInt16, _ + TypeCode.SByte * TCMAX + TypeCode.Int32, _ + TypeCode.Byte * TCMAX + TypeCode.Int32, _ + TypeCode.Int16 * TCMAX + TypeCode.UInt16, _ + TypeCode.Int16 * TCMAX + TypeCode.Int32, _ + TypeCode.UInt16 * TCMAX + TypeCode.SByte, _ + TypeCode.UInt16 * TCMAX + TypeCode.Int16, _ + TypeCode.UInt16 * TCMAX + TypeCode.Int32, _ + TypeCode.Int32 * TCMAX + TypeCode.SByte, _ + TypeCode.Int32 * TCMAX + TypeCode.Byte, _ + TypeCode.Int32 * TCMAX + TypeCode.Int16, _ + TypeCode.Int32 * TCMAX + TypeCode.UInt16 + + Return XorInt32(conv1.ToInt32(Nothing), conv2.ToInt32(Nothing)) + + Case TypeCode.SByte * TCMAX + TypeCode.UInt32, _ + TypeCode.SByte * TCMAX + TypeCode.Int64, _ + TypeCode.SByte * TCMAX + TypeCode.UInt64, _ + TypeCode.SByte * TCMAX + TypeCode.Decimal, _ + TypeCode.SByte * TCMAX + TypeCode.Single, _ + TypeCode.SByte * TCMAX + TypeCode.Double, _ + TypeCode.Byte * TCMAX + TypeCode.Int64, _ + TypeCode.Byte * TCMAX + TypeCode.Decimal, _ + TypeCode.Byte * TCMAX + TypeCode.Single, _ + TypeCode.Byte * TCMAX + TypeCode.Double, _ + TypeCode.Int16 * TCMAX + TypeCode.UInt32, _ + TypeCode.Int16 * TCMAX + TypeCode.Int64, _ + TypeCode.Int16 * TCMAX + TypeCode.UInt64, _ + TypeCode.Int16 * TCMAX + TypeCode.Decimal, _ + TypeCode.Int16 * TCMAX + TypeCode.Single, _ + TypeCode.Int16 * TCMAX + TypeCode.Double, _ + TypeCode.UInt16 * TCMAX + TypeCode.Int64, _ + TypeCode.UInt16 * TCMAX + TypeCode.Decimal, _ + TypeCode.UInt16 * TCMAX + TypeCode.Single, _ + TypeCode.UInt16 * TCMAX + TypeCode.Double, _ + TypeCode.Int32 * TCMAX + TypeCode.UInt32, _ + TypeCode.Int32 * TCMAX + TypeCode.Int64, _ + TypeCode.Int32 * TCMAX + TypeCode.UInt64, _ + TypeCode.Int32 * TCMAX + TypeCode.Decimal, _ + TypeCode.Int32 * TCMAX + TypeCode.Single, _ + TypeCode.Int32 * TCMAX + TypeCode.Double, _ + TypeCode.UInt32 * TCMAX + TypeCode.SByte, _ + TypeCode.UInt32 * TCMAX + TypeCode.Int16, _ + TypeCode.UInt32 * TCMAX + TypeCode.Int32, _ + TypeCode.UInt32 * TCMAX + TypeCode.Int64, _ + TypeCode.UInt32 * TCMAX + TypeCode.Decimal, _ + TypeCode.UInt32 * TCMAX + TypeCode.Single, _ + TypeCode.UInt32 * TCMAX + TypeCode.Double, _ + TypeCode.Int64 * TCMAX + TypeCode.SByte, _ + TypeCode.Int64 * TCMAX + TypeCode.Byte, _ + TypeCode.Int64 * TCMAX + TypeCode.Int16, _ + TypeCode.Int64 * TCMAX + TypeCode.UInt16, _ + TypeCode.Int64 * TCMAX + TypeCode.Int32, _ + TypeCode.Int64 * TCMAX + TypeCode.UInt32, _ + TypeCode.Int64 * TCMAX + TypeCode.UInt64, _ + TypeCode.Int64 * TCMAX + TypeCode.Decimal, _ + TypeCode.Int64 * TCMAX + TypeCode.Single, _ + TypeCode.Int64 * TCMAX + TypeCode.Double, _ + TypeCode.UInt64 * TCMAX + TypeCode.SByte, _ + TypeCode.UInt64 * TCMAX + TypeCode.Int16, _ + TypeCode.UInt64 * TCMAX + TypeCode.Int32, _ + TypeCode.UInt64 * TCMAX + TypeCode.Int64, _ + TypeCode.UInt64 * TCMAX + TypeCode.Decimal, _ + TypeCode.UInt64 * TCMAX + TypeCode.Single, _ + TypeCode.UInt64 * TCMAX + TypeCode.Double, _ + TypeCode.Decimal * TCMAX + TypeCode.SByte, _ + TypeCode.Decimal * TCMAX + TypeCode.Byte, _ + TypeCode.Decimal * TCMAX + TypeCode.Int16, _ + TypeCode.Decimal * TCMAX + TypeCode.UInt16, _ + TypeCode.Decimal * TCMAX + TypeCode.Int32, _ + TypeCode.Decimal * TCMAX + TypeCode.UInt32, _ + TypeCode.Decimal * TCMAX + TypeCode.Int64, _ + TypeCode.Decimal * TCMAX + TypeCode.UInt64, _ + TypeCode.Decimal * TCMAX + TypeCode.Decimal, _ + TypeCode.Decimal * TCMAX + TypeCode.Single, _ + TypeCode.Decimal * TCMAX + TypeCode.Double, _ + TypeCode.Single * TCMAX + TypeCode.SByte, _ + TypeCode.Single * TCMAX + TypeCode.Byte, _ + TypeCode.Single * TCMAX + TypeCode.Int16, _ + TypeCode.Single * TCMAX + TypeCode.UInt16, _ + TypeCode.Single * TCMAX + TypeCode.Int32, _ + TypeCode.Single * TCMAX + TypeCode.UInt32, _ + TypeCode.Single * TCMAX + TypeCode.Int64, _ + TypeCode.Single * TCMAX + TypeCode.UInt64, _ + TypeCode.Single * TCMAX + TypeCode.Decimal, _ + TypeCode.Single * TCMAX + TypeCode.Single, _ + TypeCode.Single * TCMAX + TypeCode.Double, _ + TypeCode.Double * TCMAX + TypeCode.SByte, _ + TypeCode.Double * TCMAX + TypeCode.Byte, _ + TypeCode.Double * TCMAX + TypeCode.Int16, _ + TypeCode.Double * TCMAX + TypeCode.UInt16, _ + TypeCode.Double * TCMAX + TypeCode.Int32, _ + TypeCode.Double * TCMAX + TypeCode.UInt32, _ + TypeCode.Double * TCMAX + TypeCode.Int64, _ + TypeCode.Double * TCMAX + TypeCode.UInt64, _ + TypeCode.Double * TCMAX + TypeCode.Decimal, _ + TypeCode.Double * TCMAX + TypeCode.Single, _ + TypeCode.Double * TCMAX + TypeCode.Double + + Return XorInt64(conv1.ToInt64(Nothing), conv2.ToInt64(Nothing)) 'UNDONE: what about error messages on the overflow? not very useful coming from iconvertible code. + + Case TypeCode.SByte * TCMAX + TypeCode.String, _ + TypeCode.Byte * TCMAX + TypeCode.String, _ + TypeCode.Int16 * TCMAX + TypeCode.String, _ + TypeCode.UInt16 * TCMAX + TypeCode.String, _ + TypeCode.Int32 * TCMAX + TypeCode.String, _ + TypeCode.UInt32 * TCMAX + TypeCode.String, _ + TypeCode.Int64 * TCMAX + TypeCode.String, _ + TypeCode.UInt64 * TCMAX + TypeCode.String, _ + TypeCode.Decimal * TCMAX + TypeCode.String, _ + TypeCode.Single * TCMAX + TypeCode.String, _ + TypeCode.Double * TCMAX + TypeCode.String + + Return XorInt64(conv1.ToInt64(Nothing), CLng(conv2.ToString(Nothing))) + + + Case TypeCode.Byte * TCMAX + TypeCode.Empty + Return XorByte(conv1.ToByte(Nothing), Nothing, GetEnumResult(Left, Right)) + + Case TypeCode.Byte * TCMAX + TypeCode.Boolean, _ + TypeCode.Int16 * TCMAX + TypeCode.Boolean + Return XorInt16(conv1.ToInt16(Nothing), ToVBBool(conv2)) + + Case TypeCode.Byte * TCMAX + TypeCode.Byte + Return XorByte(conv1.ToByte(Nothing), conv2.ToByte(Nothing), GetEnumResult(Left, Right)) + + Case TypeCode.Byte * TCMAX + TypeCode.UInt16, _ + TypeCode.UInt16 * TCMAX + TypeCode.Byte + Return XorUInt16(conv1.ToUInt16(Nothing), conv2.ToUInt16(Nothing)) + + Case TypeCode.Byte * TCMAX + TypeCode.UInt32, _ + TypeCode.UInt16 * TCMAX + TypeCode.UInt32, _ + TypeCode.UInt32 * TCMAX + TypeCode.Byte, _ + TypeCode.UInt32 * TCMAX + TypeCode.UInt16 + + Return XorUInt32(conv1.ToUInt32(Nothing), conv2.ToUInt32(Nothing)) + + Case TypeCode.Byte * TCMAX + TypeCode.UInt64, _ + TypeCode.UInt16 * TCMAX + TypeCode.UInt64, _ + TypeCode.UInt32 * TCMAX + TypeCode.UInt64, _ + TypeCode.UInt64 * TCMAX + TypeCode.Byte, _ + TypeCode.UInt64 * TCMAX + TypeCode.UInt16, _ + TypeCode.UInt64 * TCMAX + TypeCode.UInt32 + + Return XorUInt64(conv1.ToUInt64(Nothing), conv2.ToUInt64(Nothing)) + + + Case TypeCode.Int16 * TCMAX + TypeCode.Empty + Return XorInt16(conv1.ToInt16(Nothing), Nothing, GetEnumResult(Left, Right)) + + Case TypeCode.Int16 * TCMAX + TypeCode.Int16 + Return XorInt16(conv1.ToInt16(Nothing), conv2.ToInt16(Nothing), GetEnumResult(Left, Right)) + + + Case TypeCode.UInt16 * TCMAX + TypeCode.Empty + Return XorUInt16(conv1.ToUInt16(Nothing), Nothing, GetEnumResult(Left, Right)) + + Case TypeCode.UInt16 * TCMAX + TypeCode.Boolean, _ + TypeCode.Int32 * TCMAX + TypeCode.Boolean + Return XorInt32(conv1.ToInt32(Nothing), ToVBBool(conv2)) + + Case TypeCode.UInt16 * TCMAX + TypeCode.UInt16 + Return XorUInt16(conv1.ToUInt16(Nothing), conv2.ToUInt16(Nothing), GetEnumResult(Left, Right)) + + + Case TypeCode.Int32 * TCMAX + TypeCode.Empty + Return XorInt32(conv1.ToInt32(Nothing), Nothing, GetEnumResult(Left, Right)) + + Case TypeCode.Int32 * TCMAX + TypeCode.Int32 + Return XorInt32(conv1.ToInt32(Nothing), conv2.ToInt32(Nothing), GetEnumResult(Left, Right)) + + + Case TypeCode.UInt32 * TCMAX + TypeCode.Empty + Return XorUInt32(conv1.ToUInt32(Nothing), Nothing, GetEnumResult(Left, Right)) + + Case TypeCode.UInt32 * TCMAX + TypeCode.Boolean, _ + TypeCode.Int64 * TCMAX + TypeCode.Boolean, _ + TypeCode.UInt64 * TCMAX + TypeCode.Boolean, _ + TypeCode.Decimal * TCMAX + TypeCode.Boolean, _ + TypeCode.Single * TCMAX + TypeCode.Boolean, _ + TypeCode.Double * TCMAX + TypeCode.Boolean + + Return XorInt64(conv1.ToInt64(Nothing), ToVBBool(conv2)) + + Case TypeCode.UInt32 * TCMAX + TypeCode.UInt32 + Return XorUInt32(conv1.ToUInt32(Nothing), conv2.ToUInt32(Nothing), GetEnumResult(Left, Right)) + + + Case TypeCode.Int64 * TCMAX + TypeCode.Empty + Return XorInt64(conv1.ToInt64(Nothing), Nothing, GetEnumResult(Left, Right)) + + Case TypeCode.Int64 * TCMAX + TypeCode.Int64 + Return XorInt64(conv1.ToInt64(Nothing), conv2.ToInt64(Nothing), GetEnumResult(Left, Right)) 'UNDONE: what about error messages on the overflow? not very useful coming from iconvertible code. + + + Case TypeCode.UInt64 * TCMAX + TypeCode.Empty + Return XorUInt64(conv1.ToUInt64(Nothing), Nothing, GetEnumResult(Left, Right)) + + Case TypeCode.UInt64 * TCMAX + TypeCode.UInt64 + Return XorUInt64(conv1.ToUInt64(Nothing), conv2.ToUInt64(Nothing), GetEnumResult(Left, Right)) + + + Case TypeCode.Decimal * TCMAX + TypeCode.Empty, _ + TypeCode.Single * TCMAX + TypeCode.Empty, _ + TypeCode.Double * TCMAX + TypeCode.Empty + Return XorInt64(conv1.ToInt64(Nothing), Nothing) + + + Case TypeCode.String * TCMAX + TypeCode.Empty + Return XorInt64(CLng(conv1.ToString(Nothing)), Nothing) + + Case TypeCode.String * TCMAX + TypeCode.Boolean + Return XorBoolean(CBool(conv1.ToString(Nothing)), conv2.ToBoolean(Nothing)) + + Case TypeCode.String * TCMAX + TypeCode.SByte, _ + TypeCode.String * TCMAX + TypeCode.Byte, _ + TypeCode.String * TCMAX + TypeCode.Int16, _ + TypeCode.String * TCMAX + TypeCode.UInt16, _ + TypeCode.String * TCMAX + TypeCode.Int32, _ + TypeCode.String * TCMAX + TypeCode.UInt32, _ + TypeCode.String * TCMAX + TypeCode.Int64, _ + TypeCode.String * TCMAX + TypeCode.UInt64, _ + TypeCode.String * TCMAX + TypeCode.Decimal, _ + TypeCode.String * TCMAX + TypeCode.Single, _ + TypeCode.String * TCMAX + TypeCode.Double + + Return XorInt64(CLng(conv1.ToString(Nothing)), conv2.ToInt64(Nothing)) + + Case TypeCode.String * TCMAX + TypeCode.String + Return XorInt64(CLng(conv1.ToString(Nothing)), CLng(conv2.ToString(Nothing))) + +#If 0 Then + 'ERROR CASES + Case TypeCode.Empty * TCMAX + TypeCode.DateTime 'XX + Case TypeCode.Empty * TCMAX + TypeCode.Char 'XX + Case TypeCode.Boolean * TCMAX + TypeCode.DateTime 'XX + Case TypeCode.Boolean * TCMAX + TypeCode.Char 'XX + Case TypeCode.SByte * TCMAX + TypeCode.DateTime 'XX + Case TypeCode.SByte * TCMAX + TypeCode.Char 'XX + Case TypeCode.Byte * TCMAX + TypeCode.DateTime 'XX + Case TypeCode.Byte * TCMAX + TypeCode.Char 'XX + Case TypeCode.Int16 * TCMAX + TypeCode.DateTime 'XX + Case TypeCode.Int16 * TCMAX + TypeCode.Char 'XX + Case TypeCode.UInt16 * TCMAX + TypeCode.DateTime 'XX + Case TypeCode.UInt16 * TCMAX + TypeCode.Char 'XX + Case TypeCode.Int32 * TCMAX + TypeCode.DateTime 'XX + Case TypeCode.Int32 * TCMAX + TypeCode.Char 'XX + Case TypeCode.UInt32 * TCMAX + TypeCode.DateTime 'XX + Case TypeCode.UInt32 * TCMAX + TypeCode.Char 'XX + Case TypeCode.Int64 * TCMAX + TypeCode.DateTime 'XX + Case TypeCode.Int64 * TCMAX + TypeCode.Char 'XX + Case TypeCode.UInt64 * TCMAX + TypeCode.DateTime 'XX + Case TypeCode.UInt64 * TCMAX + TypeCode.Char 'XX + Case TypeCode.Decimal * TCMAX + TypeCode.DateTime 'XX + Case TypeCode.Decimal * TCMAX + TypeCode.Char 'XX + Case TypeCode.Single * TCMAX + TypeCode.DateTime 'XX + Case TypeCode.Single * TCMAX + TypeCode.Char 'XX + Case TypeCode.Double * TCMAX + TypeCode.DateTime 'XX + Case TypeCode.Double * TCMAX + TypeCode.Char 'XX + Case TypeCode.DateTime * TCMAX + TypeCode.Empty 'XX + Case TypeCode.DateTime * TCMAX + TypeCode.Boolean 'XX + Case TypeCode.DateTime * TCMAX + TypeCode.SByte 'XX + Case TypeCode.DateTime * TCMAX + TypeCode.Byte 'XX + Case TypeCode.DateTime * TCMAX + TypeCode.Int16 'XX + Case TypeCode.DateTime * TCMAX + TypeCode.UInt16 'XX + Case TypeCode.DateTime * TCMAX + TypeCode.Int32 'XX + Case TypeCode.DateTime * TCMAX + TypeCode.UInt32 'XX + Case TypeCode.DateTime * TCMAX + TypeCode.Int64 'XX + Case TypeCode.DateTime * TCMAX + TypeCode.UInt64 'XX + Case TypeCode.DateTime * TCMAX + TypeCode.Decimal 'XX + Case TypeCode.DateTime * TCMAX + TypeCode.Single 'XX + Case TypeCode.DateTime * TCMAX + TypeCode.Double 'XX + Case TypeCode.DateTime * TCMAX + TypeCode.DateTime 'XX + Case TypeCode.DateTime * TCMAX + TypeCode.Char 'XX + Case TypeCode.DateTime * TCMAX + TypeCode.String 'XX + Case TypeCode.Char * TCMAX + TypeCode.Empty 'XX + Case TypeCode.Char * TCMAX + TypeCode.Boolean 'XX + Case TypeCode.Char * TCMAX + TypeCode.SByte 'XX + Case TypeCode.Char * TCMAX + TypeCode.Byte 'XX + Case TypeCode.Char * TCMAX + TypeCode.Int16 'XX + Case TypeCode.Char * TCMAX + TypeCode.UInt16 'XX + Case TypeCode.Char * TCMAX + TypeCode.Int32 'XX + Case TypeCode.Char * TCMAX + TypeCode.UInt32 'XX + Case TypeCode.Char * TCMAX + TypeCode.Int64 'XX + Case TypeCode.Char * TCMAX + TypeCode.UInt64 'XX + Case TypeCode.Char * TCMAX + TypeCode.Decimal 'XX + Case TypeCode.Char * TCMAX + TypeCode.Single 'XX + Case TypeCode.Char * TCMAX + TypeCode.Double 'XX + Case TypeCode.Char * TCMAX + TypeCode.DateTime 'XX + Case TypeCode.Char * TCMAX + TypeCode.Char 'XX + Case TypeCode.Char * TCMAX + TypeCode.String 'XX + Case TypeCode.String * TCMAX + TypeCode.DateTime 'XX + Case TypeCode.String * TCMAX + TypeCode.Char 'XX +#End If + End Select + + If tc1 = TypeCode.Object OrElse tc2 = TypeCode.Object Then + Return InvokeUserDefinedOperator(UserDefinedOperator.Xor, Left, Right) + End If + + Throw GetNoValidOperatorException(UserDefinedOperator.Xor, Left, Right) + + End Function + + Private Shared Function XorBoolean(ByVal Left As Boolean, ByVal Right As Boolean) As Object + Return Left Xor Right + End Function + + Private Shared Function XorSByte(ByVal Left As SByte, ByVal Right As SByte, Optional ByVal EnumType As Type = Nothing) As Object + Dim Result As SByte = Left Xor Right + + If EnumType IsNot Nothing Then Return System.Enum.ToObject(EnumType, Result) + Return Result + End Function + + Private Shared Function XorByte(ByVal Left As Byte, ByVal Right As Byte, Optional ByVal EnumType As Type = Nothing) As Object + Dim Result As Byte = Left Xor Right + + If EnumType IsNot Nothing Then Return System.Enum.ToObject(EnumType, Result) + Return Result + End Function + + Private Shared Function XorInt16(ByVal Left As Int16, ByVal Right As Int16, Optional ByVal EnumType As Type = Nothing) As Object + Dim Result As Int16 = Left Xor Right + + If EnumType IsNot Nothing Then Return System.Enum.ToObject(EnumType, Result) + Return Result + End Function + + Private Shared Function XorUInt16(ByVal Left As UInt16, ByVal Right As UInt16, Optional ByVal EnumType As Type = Nothing) As Object + Dim Result As UInt16 = Left Xor Right + + If EnumType IsNot Nothing Then Return System.Enum.ToObject(EnumType, Result) + Return Result + End Function + + Private Shared Function XorInt32(ByVal Left As Int32, ByVal Right As Int32, Optional ByVal EnumType As Type = Nothing) As Object + Dim Result As Int32 = Left Xor Right + + If EnumType IsNot Nothing Then Return System.Enum.ToObject(EnumType, Result) + Return Result + End Function + + Private Shared Function XorUInt32(ByVal Left As UInt32, ByVal Right As UInt32, Optional ByVal EnumType As Type = Nothing) As Object + Dim Result As UInt32 = Left Xor Right + + If EnumType IsNot Nothing Then Return System.Enum.ToObject(EnumType, Result) + Return Result + End Function + + Private Shared Function XorInt64(ByVal Left As Int64, ByVal Right As Int64, Optional ByVal EnumType As Type = Nothing) As Object + Dim Result As Int64 = Left Xor Right + + If EnumType IsNot Nothing Then Return System.Enum.ToObject(EnumType, Result) + Return Result + End Function + + Private Shared Function XorUInt64(ByVal Left As UInt64, ByVal Right As UInt64, Optional ByVal EnumType As Type = Nothing) As Object + Dim Result As UInt64 = Left Xor Right + + If EnumType IsNot Nothing Then Return System.Enum.ToObject(EnumType, Result) + Return Result + End Function + +#End Region + +#Region " Operator Plus + " + + Public Shared Function AddObject(ByVal Left As Object, ByVal Right As Object) As Object + Dim conv1, conv2 As IConvertible + Dim tc1, tc2 As TypeCode + + conv1 = TryCast(Left, IConvertible) + + If conv1 Is Nothing Then + If Left Is Nothing Then + tc1 = TypeCode.Empty + Else + tc1 = TypeCode.Object + End If + Else + tc1 = conv1.GetTypeCode() + End If + + + conv2 = TryCast(Right, IConvertible) + + If conv2 Is Nothing Then + If Right Is Nothing Then + tc2 = TypeCode.Empty + Else + tc2 = TypeCode.Object + End If + Else + tc2 = conv2.GetTypeCode() + End If + + 'Special cases for Char() + If tc1 = TypeCode.Object Then + Dim LeftCharArray As Char() = TryCast(Left, Char()) + + If LeftCharArray IsNot Nothing Then + If tc2 = TypeCode.String OrElse tc2 = TypeCode.Empty OrElse ((tc2 = TypeCode.Object) AndAlso (TypeOf Right Is Char())) Then + 'Treat Char() as String for these cases + Left = CStr(LeftCharArray) + conv1 = CType(Left, IConvertible) + tc1 = TypeCode.String + End If + End If + End If + + If (tc2 = TypeCode.Object) Then + Dim RightCharArray As Char() = TryCast(Right, Char()) + + If RightCharArray IsNot Nothing Then + If tc1 = TypeCode.String OrElse tc1 = TypeCode.Empty Then + Right = CStr(RightCharArray) + conv2 = DirectCast(Right, IConvertible) + tc2 = TypeCode.String + End If + End If + End If + + + 'UNDONE : CONVERSION FROM SINGLE AND DOUBLE MUST DO ROUNDING!! + Select Case tc1 * TCMAX + tc2 + + Case TypeCode.Empty * TCMAX + TypeCode.Empty + Return Boxed_ZeroInteger + + Case TypeCode.Empty * TCMAX + TypeCode.Boolean + Return AddInt16(Nothing, ToVBBool(conv2)) + + Case TypeCode.Empty * TCMAX + TypeCode.SByte + Return conv2.ToSByte(Nothing) + + Case TypeCode.Empty * TCMAX + TypeCode.Byte + Return conv2.ToByte(Nothing) + + Case TypeCode.Empty * TCMAX + TypeCode.Int16 + Return conv2.ToInt16(Nothing) + + Case TypeCode.Empty * TCMAX + TypeCode.UInt16 + Return conv2.ToUInt16(Nothing) + + Case TypeCode.Empty * TCMAX + TypeCode.Int32 + Return conv2.ToInt32(Nothing) + + Case TypeCode.Empty * TCMAX + TypeCode.UInt32 + Return conv2.ToUInt32(Nothing) + + Case TypeCode.Empty * TCMAX + TypeCode.Int64 + Return conv2.ToInt64(Nothing) + + Case TypeCode.Empty * TCMAX + TypeCode.UInt64 + Return conv2.ToUInt64(Nothing) + + Case TypeCode.Empty * TCMAX + TypeCode.Decimal, _ + TypeCode.Empty * TCMAX + TypeCode.Single, _ + TypeCode.Empty * TCMAX + TypeCode.Double, _ + TypeCode.Empty * TCMAX + TypeCode.String, _ + TypeCode.DBNull * TCMAX + TypeCode.String + + Return Right + + Case TypeCode.Empty * TCMAX + TypeCode.DateTime + Return AddString(CStr(CDate(Nothing)), CStr(conv2.ToDateTime(Nothing))) + + Case TypeCode.Empty * TCMAX + TypeCode.Char + Return AddString(ControlChars.NullChar, conv2.ToString(Nothing)) + + Case TypeCode.Boolean * TCMAX + TypeCode.Empty + Return AddInt16(ToVBBool(conv1), Nothing) + + Case TypeCode.Boolean * TCMAX + TypeCode.Boolean + Return AddInt16(ToVBBool(conv1), ToVBBool(conv2)) + + Case TypeCode.Boolean * TCMAX + TypeCode.SByte + Return AddSByte(ToVBBool(conv1), conv2.ToSByte(Nothing)) + + Case TypeCode.Boolean * TCMAX + TypeCode.Byte, _ + TypeCode.Boolean * TCMAX + TypeCode.Int16 + Return AddInt16(ToVBBool(conv1), conv2.ToInt16(Nothing)) + + Case TypeCode.Boolean * TCMAX + TypeCode.UInt16, _ + TypeCode.Boolean * TCMAX + TypeCode.Int32 + Return AddInt32(ToVBBool(conv1), conv2.ToInt32(Nothing)) + + Case TypeCode.Boolean * TCMAX + TypeCode.UInt32, _ + TypeCode.Boolean * TCMAX + TypeCode.Int64 + Return AddInt64(ToVBBool(conv1), conv2.ToInt64(Nothing)) + + Case TypeCode.Boolean * TCMAX + TypeCode.UInt64, _ + TypeCode.Boolean * TCMAX + TypeCode.Decimal + Return AddDecimal(ToVBBoolConv(conv1), conv2.ToDecimal(Nothing)) + + Case TypeCode.Boolean * TCMAX + TypeCode.Single + Return AddSingle(ToVBBool(conv1), conv2.ToSingle(Nothing)) + + Case TypeCode.Boolean * TCMAX + TypeCode.Double + Return AddDouble(ToVBBool(conv1), conv2.ToDouble(Nothing)) + + Case TypeCode.Boolean * TCMAX + TypeCode.String + Return AddDouble(ToVBBool(conv1), CDbl(conv2.ToString(Nothing))) + + + Case TypeCode.SByte * TCMAX + TypeCode.Empty + Return conv1.ToSByte(Nothing) + + Case TypeCode.SByte * TCMAX + TypeCode.Boolean + Return AddSByte(conv1.ToSByte(Nothing), ToVBBool(conv2)) + + Case TypeCode.SByte * TCMAX + TypeCode.SByte + Return AddSByte(conv1.ToSByte(Nothing), conv2.ToSByte(Nothing)) + + Case TypeCode.SByte * TCMAX + TypeCode.Byte, _ + TypeCode.SByte * TCMAX + TypeCode.Int16, _ + TypeCode.Byte * TCMAX + TypeCode.SByte, _ + TypeCode.Byte * TCMAX + TypeCode.Int16, _ + TypeCode.Int16 * TCMAX + TypeCode.SByte, _ + TypeCode.Int16 * TCMAX + TypeCode.Byte, _ + TypeCode.Int16 * TCMAX + TypeCode.Int16 + + Return AddInt16(conv1.ToInt16(Nothing), conv2.ToInt16(Nothing)) + + Case TypeCode.SByte * TCMAX + TypeCode.UInt16, _ + TypeCode.SByte * TCMAX + TypeCode.Int32, _ + TypeCode.Byte * TCMAX + TypeCode.Int32, _ + TypeCode.Int16 * TCMAX + TypeCode.UInt16, _ + TypeCode.Int16 * TCMAX + TypeCode.Int32, _ + TypeCode.UInt16 * TCMAX + TypeCode.SByte, _ + TypeCode.UInt16 * TCMAX + TypeCode.Int16, _ + TypeCode.UInt16 * TCMAX + TypeCode.Int32, _ + TypeCode.Int32 * TCMAX + TypeCode.SByte, _ + TypeCode.Int32 * TCMAX + TypeCode.Byte, _ + TypeCode.Int32 * TCMAX + TypeCode.Int16, _ + TypeCode.Int32 * TCMAX + TypeCode.UInt16, _ + TypeCode.Int32 * TCMAX + TypeCode.Int32 + + Return AddInt32(conv1.ToInt32(Nothing), conv2.ToInt32(Nothing)) + + Case TypeCode.SByte * TCMAX + TypeCode.UInt32, _ + TypeCode.SByte * TCMAX + TypeCode.Int64, _ + TypeCode.Byte * TCMAX + TypeCode.Int64, _ + TypeCode.Int16 * TCMAX + TypeCode.UInt32, _ + TypeCode.Int16 * TCMAX + TypeCode.Int64, _ + TypeCode.UInt16 * TCMAX + TypeCode.Int64, _ + TypeCode.Int32 * TCMAX + TypeCode.UInt32, _ + TypeCode.Int32 * TCMAX + TypeCode.Int64, _ + TypeCode.UInt32 * TCMAX + TypeCode.SByte, _ + TypeCode.UInt32 * TCMAX + TypeCode.Int16, _ + TypeCode.UInt32 * TCMAX + TypeCode.Int32, _ + TypeCode.UInt32 * TCMAX + TypeCode.Int64, _ + TypeCode.Int64 * TCMAX + TypeCode.SByte, _ + TypeCode.Int64 * TCMAX + TypeCode.Byte, _ + TypeCode.Int64 * TCMAX + TypeCode.Int16, _ + TypeCode.Int64 * TCMAX + TypeCode.UInt16, _ + TypeCode.Int64 * TCMAX + TypeCode.Int32, _ + TypeCode.Int64 * TCMAX + TypeCode.UInt32, _ + TypeCode.Int64 * TCMAX + TypeCode.Int64 + + Return AddInt64(conv1.ToInt64(Nothing), conv2.ToInt64(Nothing)) + + Case TypeCode.SByte * TCMAX + TypeCode.UInt64, _ + TypeCode.SByte * TCMAX + TypeCode.Decimal, _ + TypeCode.Byte * TCMAX + TypeCode.Decimal, _ + TypeCode.Int16 * TCMAX + TypeCode.UInt64, _ + TypeCode.Int16 * TCMAX + TypeCode.Decimal, _ + TypeCode.UInt16 * TCMAX + TypeCode.Decimal, _ + TypeCode.Int32 * TCMAX + TypeCode.UInt64, _ + TypeCode.Int32 * TCMAX + TypeCode.Decimal, _ + TypeCode.UInt32 * TCMAX + TypeCode.Decimal, _ + TypeCode.Int64 * TCMAX + TypeCode.UInt64, _ + TypeCode.Int64 * TCMAX + TypeCode.Decimal, _ + TypeCode.UInt64 * TCMAX + TypeCode.SByte, _ + TypeCode.UInt64 * TCMAX + TypeCode.Int16, _ + TypeCode.UInt64 * TCMAX + TypeCode.Int32, _ + TypeCode.UInt64 * TCMAX + TypeCode.Int64, _ + TypeCode.UInt64 * TCMAX + TypeCode.Decimal, _ + TypeCode.Decimal * TCMAX + TypeCode.SByte, _ + TypeCode.Decimal * TCMAX + TypeCode.Byte, _ + TypeCode.Decimal * TCMAX + TypeCode.Int16, _ + TypeCode.Decimal * TCMAX + TypeCode.UInt16, _ + TypeCode.Decimal * TCMAX + TypeCode.Int32, _ + TypeCode.Decimal * TCMAX + TypeCode.UInt32, _ + TypeCode.Decimal * TCMAX + TypeCode.Int64, _ + TypeCode.Decimal * TCMAX + TypeCode.UInt64, _ + TypeCode.Decimal * TCMAX + TypeCode.Decimal + + Return AddDecimal(conv1, conv2) + + Case TypeCode.SByte * TCMAX + TypeCode.Single, _ + TypeCode.Byte * TCMAX + TypeCode.Single, _ + TypeCode.Int16 * TCMAX + TypeCode.Single, _ + TypeCode.UInt16 * TCMAX + TypeCode.Single, _ + TypeCode.Int32 * TCMAX + TypeCode.Single, _ + TypeCode.UInt32 * TCMAX + TypeCode.Single, _ + TypeCode.Int64 * TCMAX + TypeCode.Single, _ + TypeCode.UInt64 * TCMAX + TypeCode.Single, _ + TypeCode.Decimal * TCMAX + TypeCode.Single, _ + TypeCode.Single * TCMAX + TypeCode.SByte, _ + TypeCode.Single * TCMAX + TypeCode.Byte, _ + TypeCode.Single * TCMAX + TypeCode.Int16, _ + TypeCode.Single * TCMAX + TypeCode.UInt16, _ + TypeCode.Single * TCMAX + TypeCode.Int32, _ + TypeCode.Single * TCMAX + TypeCode.UInt32, _ + TypeCode.Single * TCMAX + TypeCode.Int64, _ + TypeCode.Single * TCMAX + TypeCode.UInt64, _ + TypeCode.Single * TCMAX + TypeCode.Decimal, _ + TypeCode.Single * TCMAX + TypeCode.Single + + Return AddSingle(conv1.ToSingle(Nothing), conv2.ToSingle(Nothing)) + + Case TypeCode.SByte * TCMAX + TypeCode.Double, _ + TypeCode.Byte * TCMAX + TypeCode.Double, _ + TypeCode.Int16 * TCMAX + TypeCode.Double, _ + TypeCode.UInt16 * TCMAX + TypeCode.Double, _ + TypeCode.Int32 * TCMAX + TypeCode.Double, _ + TypeCode.UInt32 * TCMAX + TypeCode.Double, _ + TypeCode.Int64 * TCMAX + TypeCode.Double, _ + TypeCode.UInt64 * TCMAX + TypeCode.Double, _ + TypeCode.Decimal * TCMAX + TypeCode.Double, _ + TypeCode.Single * TCMAX + TypeCode.Double, _ + TypeCode.Double * TCMAX + TypeCode.SByte, _ + TypeCode.Double * TCMAX + TypeCode.Byte, _ + TypeCode.Double * TCMAX + TypeCode.Int16, _ + TypeCode.Double * TCMAX + TypeCode.UInt16, _ + TypeCode.Double * TCMAX + TypeCode.Int32, _ + TypeCode.Double * TCMAX + TypeCode.UInt32, _ + TypeCode.Double * TCMAX + TypeCode.Int64, _ + TypeCode.Double * TCMAX + TypeCode.UInt64, _ + TypeCode.Double * TCMAX + TypeCode.Decimal, _ + TypeCode.Double * TCMAX + TypeCode.Single, _ + TypeCode.Double * TCMAX + TypeCode.Double + + Return AddDouble(conv1.ToDouble(Nothing), conv2.ToDouble(Nothing)) + + Case TypeCode.SByte * TCMAX + TypeCode.String, _ + TypeCode.Byte * TCMAX + TypeCode.String, _ + TypeCode.Int16 * TCMAX + TypeCode.String, _ + TypeCode.UInt16 * TCMAX + TypeCode.String, _ + TypeCode.Int32 * TCMAX + TypeCode.String, _ + TypeCode.UInt32 * TCMAX + TypeCode.String, _ + TypeCode.Int64 * TCMAX + TypeCode.String, _ + TypeCode.UInt64 * TCMAX + TypeCode.String, _ + TypeCode.Decimal * TCMAX + TypeCode.String, _ + TypeCode.Single * TCMAX + TypeCode.String, _ + TypeCode.Double * TCMAX + TypeCode.String + + Return AddDouble(conv1.ToDouble(Nothing), CDbl(conv2.ToString(Nothing))) + + + Case TypeCode.Byte * TCMAX + TypeCode.Empty + Return conv1.ToByte(Nothing) + + Case TypeCode.Byte * TCMAX + TypeCode.Boolean, _ + TypeCode.Int16 * TCMAX + TypeCode.Boolean + Return AddInt16(conv1.ToInt16(Nothing), ToVBBool(conv2)) + + Case TypeCode.Byte * TCMAX + TypeCode.Byte + Return AddByte(conv1.ToByte(Nothing), conv2.ToByte(Nothing)) + + Case TypeCode.Byte * TCMAX + TypeCode.UInt16, _ + TypeCode.UInt16 * TCMAX + TypeCode.Byte, _ + TypeCode.UInt16 * TCMAX + TypeCode.UInt16 + Return AddUInt16(conv1.ToUInt16(Nothing), conv2.ToUInt16(Nothing)) + + Case TypeCode.Byte * TCMAX + TypeCode.UInt32, _ + TypeCode.UInt16 * TCMAX + TypeCode.UInt32, _ + TypeCode.UInt32 * TCMAX + TypeCode.Byte, _ + TypeCode.UInt32 * TCMAX + TypeCode.UInt16, _ + TypeCode.UInt32 * TCMAX + TypeCode.UInt32 + Return AddUInt32(conv1.ToUInt32(Nothing), conv2.ToUInt32(Nothing)) + + Case TypeCode.Byte * TCMAX + TypeCode.UInt64, _ + TypeCode.UInt16 * TCMAX + TypeCode.UInt64, _ + TypeCode.UInt32 * TCMAX + TypeCode.UInt64, _ + TypeCode.UInt64 * TCMAX + TypeCode.Byte, _ + TypeCode.UInt64 * TCMAX + TypeCode.UInt16, _ + TypeCode.UInt64 * TCMAX + TypeCode.UInt32, _ + TypeCode.UInt64 * TCMAX + TypeCode.UInt64 + + Return AddUInt64(conv1.ToUInt64(Nothing), conv2.ToUInt64(Nothing)) + + + Case TypeCode.Int16 * TCMAX + TypeCode.Empty + Return conv1.ToInt16(Nothing) + + + Case TypeCode.UInt16 * TCMAX + TypeCode.Empty + Return conv1.ToUInt16(Nothing) + + Case TypeCode.UInt16 * TCMAX + TypeCode.Boolean, _ + TypeCode.Int32 * TCMAX + TypeCode.Boolean + Return AddInt32(conv1.ToInt32(Nothing), ToVBBool(conv2)) + + + Case TypeCode.Int32 * TCMAX + TypeCode.Empty + Return conv1.ToInt32(Nothing) + + + Case TypeCode.UInt32 * TCMAX + TypeCode.Empty + Return conv1.ToUInt32(Nothing) + + Case TypeCode.UInt32 * TCMAX + TypeCode.Boolean, _ + TypeCode.Int64 * TCMAX + TypeCode.Boolean + Return AddInt64(conv1.ToInt64(Nothing), ToVBBool(conv2)) + + + Case TypeCode.Int64 * TCMAX + TypeCode.Empty + Return conv1.ToInt64(Nothing) + + + Case TypeCode.UInt64 * TCMAX + TypeCode.Empty + Return conv1.ToUInt64(Nothing) + + Case TypeCode.UInt64 * TCMAX + TypeCode.Boolean, _ + TypeCode.Decimal * TCMAX + TypeCode.Boolean + Return AddDecimal(conv1, ToVBBoolConv(conv2)) + + + Case TypeCode.Decimal * TCMAX + TypeCode.Empty, _ + TypeCode.Single * TCMAX + TypeCode.Empty, _ + TypeCode.Double * TCMAX + TypeCode.Empty, _ + TypeCode.String * TCMAX + TypeCode.Empty, _ + TypeCode.String * TCMAX + TypeCode.DBNull + + Return Left + + + Case TypeCode.Single * TCMAX + TypeCode.Boolean + Return AddSingle(conv1.ToSingle(Nothing), ToVBBool(conv2)) + + + Case TypeCode.Double * TCMAX + TypeCode.Boolean + Return AddDouble(conv1.ToDouble(Nothing), ToVBBool(conv2)) + + + Case TypeCode.DateTime * TCMAX + TypeCode.Empty + Return AddString(CStr(conv1.ToDateTime(Nothing)), CStr(CDate(Nothing))) + + Case TypeCode.DateTime * TCMAX + TypeCode.DateTime + Return AddString(CStr(conv1.ToDateTime(Nothing)), CStr(conv2.ToDateTime(Nothing))) + + Case TypeCode.DateTime * TCMAX + TypeCode.String + Return AddString(CStr(conv1.ToDateTime(Nothing)), conv2.ToString(Nothing)) + + + Case TypeCode.Char * TCMAX + TypeCode.Empty + Return AddString(conv1.ToString(Nothing), ControlChars.NullChar) + + Case TypeCode.Char * TCMAX + TypeCode.Char, _ + TypeCode.Char * TCMAX + TypeCode.String, _ + TypeCode.String * TCMAX + TypeCode.Char + Return AddString(conv1.ToString(Nothing), conv2.ToString(Nothing)) + + + Case TypeCode.String * TCMAX + TypeCode.Boolean + Return AddDouble(CDbl(conv1.ToString(Nothing)), ToVBBool(conv2)) + + Case TypeCode.String * TCMAX + TypeCode.SByte, _ + TypeCode.String * TCMAX + TypeCode.Byte, _ + TypeCode.String * TCMAX + TypeCode.Int16, _ + TypeCode.String * TCMAX + TypeCode.UInt16, _ + TypeCode.String * TCMAX + TypeCode.Int32, _ + TypeCode.String * TCMAX + TypeCode.UInt32, _ + TypeCode.String * TCMAX + TypeCode.Int64, _ + TypeCode.String * TCMAX + TypeCode.UInt64, _ + TypeCode.String * TCMAX + TypeCode.Decimal, _ + TypeCode.String * TCMAX + TypeCode.Single, _ + TypeCode.String * TCMAX + TypeCode.Double + + Return AddDouble(CDbl(conv1.ToString(Nothing)), conv2.ToDouble(Nothing)) + + Case TypeCode.String * TCMAX + TypeCode.DateTime + Return AddString(conv1.ToString(Nothing), CStr(conv2.ToDateTime(Nothing))) + + Case TypeCode.String * TCMAX + TypeCode.String + Return AddString(conv1.ToString(Nothing), conv2.ToString(Nothing)) + + +#If 0 Then + Case TypeCode.Boolean * TCMAX + TypeCode.DateTime 'XX + Case TypeCode.Boolean * TCMAX + TypeCode.Char 'XX + Case TypeCode.SByte * TCMAX + TypeCode.DateTime 'XX + Case TypeCode.SByte * TCMAX + TypeCode.Char 'XX + Case TypeCode.Byte * TCMAX + TypeCode.DateTime 'XX + Case TypeCode.Byte * TCMAX + TypeCode.Char 'XX + Case TypeCode.Int16 * TCMAX + TypeCode.DateTime 'XX + Case TypeCode.Int16 * TCMAX + TypeCode.Char 'XX + Case TypeCode.UInt16 * TCMAX + TypeCode.DateTime 'XX + Case TypeCode.UInt16 * TCMAX + TypeCode.Char 'XX + Case TypeCode.Int32 * TCMAX + TypeCode.DateTime 'XX + Case TypeCode.Int32 * TCMAX + TypeCode.Char 'XX + Case TypeCode.UInt32 * TCMAX + TypeCode.DateTime 'XX + Case TypeCode.UInt32 * TCMAX + TypeCode.Char 'XX + Case TypeCode.Int64 * TCMAX + TypeCode.DateTime 'XX + Case TypeCode.Int64 * TCMAX + TypeCode.Char 'XX + Case TypeCode.UInt64 * TCMAX + TypeCode.DateTime 'XX + Case TypeCode.UInt64 * TCMAX + TypeCode.Char 'XX + Case TypeCode.Decimal * TCMAX + TypeCode.DateTime 'XX + Case TypeCode.Decimal * TCMAX + TypeCode.Char 'XX + Case TypeCode.Single * TCMAX + TypeCode.DateTime 'XX + Case TypeCode.Single * TCMAX + TypeCode.Char 'XX + Case TypeCode.Double * TCMAX + TypeCode.DateTime 'XX + Case TypeCode.Double * TCMAX + TypeCode.Char 'XX + Case TypeCode.DateTime * TCMAX + TypeCode.Boolean 'XX + Case TypeCode.DateTime * TCMAX + TypeCode.SByte 'XX + Case TypeCode.DateTime * TCMAX + TypeCode.Byte 'XX + Case TypeCode.DateTime * TCMAX + TypeCode.Int16 'XX + Case TypeCode.DateTime * TCMAX + TypeCode.UInt16 'XX + Case TypeCode.DateTime * TCMAX + TypeCode.Int32 'XX + Case TypeCode.DateTime * TCMAX + TypeCode.UInt32 'XX + Case TypeCode.DateTime * TCMAX + TypeCode.Int64 'XX + Case TypeCode.DateTime * TCMAX + TypeCode.UInt64 'XX + Case TypeCode.DateTime * TCMAX + TypeCode.Decimal 'XX + Case TypeCode.DateTime * TCMAX + TypeCode.Single 'XX + Case TypeCode.DateTime * TCMAX + TypeCode.Double 'XX + Case TypeCode.DateTime * TCMAX + TypeCode.Char 'XX + Case TypeCode.Char * TCMAX + TypeCode.Boolean 'XX + Case TypeCode.Char * TCMAX + TypeCode.SByte 'XX + Case TypeCode.Char * TCMAX + TypeCode.Byte 'XX + Case TypeCode.Char * TCMAX + TypeCode.Int16 'XX + Case TypeCode.Char * TCMAX + TypeCode.UInt16 'XX + Case TypeCode.Char * TCMAX + TypeCode.Int32 'XX + Case TypeCode.Char * TCMAX + TypeCode.UInt32 'XX + Case TypeCode.Char * TCMAX + TypeCode.Int64 'XX + Case TypeCode.Char * TCMAX + TypeCode.UInt64 'XX + Case TypeCode.Char * TCMAX + TypeCode.Decimal 'XX + Case TypeCode.Char * TCMAX + TypeCode.Single 'XX + Case TypeCode.Char * TCMAX + TypeCode.Double 'XX + Case TypeCode.Char * TCMAX + TypeCode.DateTime 'XX +#End If + + Case Else + + End Select + + If tc1 = TypeCode.Object OrElse tc2 = TypeCode.Object Then + Return InvokeUserDefinedOperator(UserDefinedOperator.Plus, Left, Right) + End If + + Throw GetNoValidOperatorException(UserDefinedOperator.Plus, Left, Right) + + End Function + + Private Shared Function AddByte(ByVal Left As Byte, ByVal Right As Byte) As Object + 'Range of possible values: [0, 510] + Dim Result As Int16 = CShort(Left) + CShort(Right) + + If Result > Byte.MaxValue Then + Return Result + Else + Return CByte(Result) 'REVIEW VSW#395757: overflow checking can be turned off here, and other similar places, since we've already checked for overflow. + End If + End Function + + Private Shared Function AddSByte(ByVal Left As SByte, ByVal Right As SByte) As Object + 'Range of possible values: [-256, 254] + Dim Result As Int16 = CShort(Left) + CShort(Right) + + If Result > SByte.MaxValue OrElse Result < SByte.MinValue Then + Return Result + Else + Return CSByte(Result) 'REVIEW VSW#395757: overflow checking can be turned off here, and other similar places, since we've already checked for overflow. + End If + End Function + + Private Shared Function AddInt16(ByVal Left As Int16, ByVal Right As Int16) As Object + 'Range of possible values: [-65536, 65534] + Dim Result As Int32 = CInt(Left) + CInt(Right) + + If Result > Int16.MaxValue OrElse Result < Int16.MinValue Then + Return Result + Else + Return CShort(Result) 'REVIEW VSW#395757: overflow checking can be turned off here, and other similar places, since we've already checked for overflow. + End If + End Function + + Private Shared Function AddUInt16(ByVal Left As UInt16, ByVal Right As UInt16) As Object + 'Range of possible values: [0, 131070] + Dim Result As Int32 = CInt(Left) + CInt(Right) + + If Result > UInt16.MaxValue Then + Return Result + Else + Return CUShort(Result) 'REVIEW VSW#395757: overflow checking can be turned off here, and other similar places, since we've already checked for overflow. + End If + End Function + + Private Shared Function AddInt32(ByVal Left As Int32, ByVal Right As Int32) As Object + 'Range of possible values: [-4294967296, 4294967294] + Dim Result As Int64 = CLng(Left) + CLng(Right) + + If Result > Int32.MaxValue OrElse Result < Int32.MinValue Then + Return Result + Else + Return CInt(Result) + End If + End Function + + Private Shared Function AddUInt32(ByVal Left As UInt32, ByVal Right As UInt32) As Object + 'Range of possible values: [0, 8589934590] + Dim Result As Int64 = CLng(Left) + CLng(Right) + + If Result > UInt32.MaxValue Then + Return Result + Else + Return CUInt(Result) + End If + End Function + + Private Shared Function AddInt64(ByVal Left As Int64, ByVal Right As Int64) As Object + 'Range of possible values: [-18446744073709551616, 18446744073709551614] + Try + Return Left + Right + Catch e As OverflowException + Return CDec(Left) + CDec(Right) + End Try + +#If 0 Then 'REVIEW VSW#395757: which implementation is better? If only we could turn off overflow checking on a per-block basis, then + 'this function could be rewritten to check the roundtrip and not have to do unecessary decimal addition. + Dim Result As Decimal = CDec(Left) + CDec(Right) + + If Result > Int64.MaxValue OrElse Result < Int64.MinValue Then + Return Result + Else + Return CLng(Result) + End If +#End If 'REVIEW VSW#395757: which implementation is better? If only we could turn off overflow checking on a per-block basis, then + End Function + + Private Shared Function AddUInt64(ByVal Left As UInt64, ByVal Right As UInt64) As Object + 'Range of possible values: [0, 36893488147419103230] + Try + Return Left + Right + Catch e As OverflowException + Return CDec(Left) + CDec(Right) + End Try + +#If 0 Then 'REVIEW VSW#395757: which implementation is better? If only we could turn off overflow checking on a per-block basis, then + 'this function could be rewritten to check the roundtrip and not have to do unecessary decimal addition. + Dim Result As Decimal = CDec(Left) + CDec(Right) + + If Result > UInt64.MaxValue Then + Return Result + Else + Return CULng(Result) + End If +#End If 'REVIEW VSW#395757: which implementation is better? If only we could turn off overflow checking on a per-block basis, then + End Function + + Private Shared Function AddDecimal(ByVal Left As IConvertible, ByVal Right As IConvertible) As Object + 'REVIWE VSW#395758: there must be a better way to do this. If not, ask for one. + Dim LeftValue As Decimal = Left.ToDecimal(Nothing) + Dim RightValue As Decimal = Right.ToDecimal(Nothing) + + Try + Return LeftValue + RightValue + Catch ex As OverflowException + Return CDbl(LeftValue) + CDbl(RightValue) + End Try + End Function + + Private Shared Function AddSingle(ByVal Left As Single, ByVal Right As Single) As Object + Dim Result As Double = CDbl(Left) + CDbl(Right) + + If ((Result <= Single.MaxValue AndAlso Result >= Single.MinValue)) Then + Return CSng(Result) + ElseIf Double.IsInfinity(Result) AndAlso (Single.IsInfinity(Left) OrElse Single.IsInfinity(Right)) Then + Return CSng(Result) + Else + Return Result + End If + End Function + + Private Shared Function AddDouble(ByVal Left As Double, ByVal Right As Double) As Object + Return Left + Right + End Function + + Private Shared Function AddString(ByVal Left As String, ByVal Right As String) As Object + Return Left & Right + End Function + +#End Region + +#Region " Operator Minus - " + + Public Shared Function SubtractObject(ByVal Left As Object, ByVal Right As Object) As Object + Dim conv1, conv2 As IConvertible + Dim tc1, tc2 As TypeCode + + conv1 = TryCast(Left, IConvertible) + + If conv1 Is Nothing Then + If Left Is Nothing Then + tc1 = TypeCode.Empty + Else + tc1 = TypeCode.Object + End If + Else + tc1 = conv1.GetTypeCode() + End If + + + conv2 = TryCast(Right, IConvertible) + + If conv2 Is Nothing Then + If Right Is Nothing Then + tc2 = TypeCode.Empty + Else + tc2 = TypeCode.Object + End If + Else + tc2 = conv2.GetTypeCode() + End If + + + Select Case tc1 * TCMAX + tc2 + + Case TypeCode.Empty * TCMAX + TypeCode.Empty + Return Boxed_ZeroInteger + + Case TypeCode.Empty * TCMAX + TypeCode.Boolean + Return SubtractInt16(Nothing, ToVBBool(conv2)) + + Case TypeCode.Empty * TCMAX + TypeCode.SByte + Return SubtractSByte(Nothing, conv2.ToSByte(Nothing)) + + Case TypeCode.Empty * TCMAX + TypeCode.Byte + Return SubtractByte(Nothing, conv2.ToByte(Nothing)) + + Case TypeCode.Empty * TCMAX + TypeCode.Int16 + Return SubtractInt16(Nothing, conv2.ToInt16(Nothing)) + + Case TypeCode.Empty * TCMAX + TypeCode.UInt16 + Return SubtractUInt16(Nothing, conv2.ToUInt16(Nothing)) + + Case TypeCode.Empty * TCMAX + TypeCode.Int32 + Return SubtractInt32(Nothing, conv2.ToInt32(Nothing)) + + Case TypeCode.Empty * TCMAX + TypeCode.UInt32 + Return SubtractUInt32(Nothing, conv2.ToUInt32(Nothing)) + + Case TypeCode.Empty * TCMAX + TypeCode.Int64 + Return SubtractInt64(Nothing, conv2.ToInt64(Nothing)) + + Case TypeCode.Empty * TCMAX + TypeCode.UInt64 + Return SubtractUInt64(Nothing, conv2.ToUInt64(Nothing)) + + Case TypeCode.Empty * TCMAX + TypeCode.Decimal + Return SubtractDecimal(0D, conv2) + + Case TypeCode.Empty * TCMAX + TypeCode.Single + Return SubtractSingle(Nothing, conv2.ToSingle(Nothing)) + + Case TypeCode.Empty * TCMAX + TypeCode.Double + Return SubtractDouble(Nothing, conv2.ToDouble(Nothing)) + + Case TypeCode.Empty * TCMAX + TypeCode.String + Return SubtractDouble(Nothing, CDbl(conv2.ToString(Nothing))) + + + Case TypeCode.Boolean * TCMAX + TypeCode.Empty + Return SubtractInt16(ToVBBool(conv1), Nothing) + + Case TypeCode.Boolean * TCMAX + TypeCode.Boolean + Return SubtractInt16(ToVBBool(conv1), ToVBBool(conv2)) + + Case TypeCode.Boolean * TCMAX + TypeCode.SByte + Return SubtractSByte(ToVBBool(conv1), conv2.ToSByte(Nothing)) + + Case TypeCode.Boolean * TCMAX + TypeCode.Byte, _ + TypeCode.Boolean * TCMAX + TypeCode.Int16 + Return SubtractInt16(ToVBBool(conv1), conv2.ToInt16(Nothing)) + + Case TypeCode.Boolean * TCMAX + TypeCode.UInt16, _ + TypeCode.Boolean * TCMAX + TypeCode.Int32 + Return SubtractInt32(ToVBBool(conv1), conv2.ToInt32(Nothing)) + + Case TypeCode.Boolean * TCMAX + TypeCode.UInt32, _ + TypeCode.Boolean * TCMAX + TypeCode.Int64 + Return SubtractInt64(ToVBBool(conv1), conv2.ToInt64(Nothing)) + + Case TypeCode.Boolean * TCMAX + TypeCode.UInt64, _ + TypeCode.Boolean * TCMAX + TypeCode.Decimal + Return SubtractDecimal(ToVBBoolConv(conv1), conv2.ToDecimal(Nothing)) + + Case TypeCode.Boolean * TCMAX + TypeCode.Single + Return SubtractSingle(ToVBBool(conv1), conv2.ToSingle(Nothing)) + + Case TypeCode.Boolean * TCMAX + TypeCode.Double + Return SubtractDouble(ToVBBool(conv1), conv2.ToDouble(Nothing)) + + Case TypeCode.Boolean * TCMAX + TypeCode.String + Return SubtractDouble(ToVBBool(conv1), CDbl(conv2.ToString(Nothing))) + + + Case TypeCode.SByte * TCMAX + TypeCode.Empty + Return conv1.ToSByte(Nothing) + + Case TypeCode.SByte * TCMAX + TypeCode.Boolean + Return SubtractSByte(conv1.ToSByte(Nothing), ToVBBool(conv2)) + + Case TypeCode.SByte * TCMAX + TypeCode.SByte + Return SubtractSByte(conv1.ToSByte(Nothing), conv2.ToSByte(Nothing)) + + Case TypeCode.SByte * TCMAX + TypeCode.Byte, _ + TypeCode.SByte * TCMAX + TypeCode.Int16, _ + TypeCode.Byte * TCMAX + TypeCode.SByte, _ + TypeCode.Byte * TCMAX + TypeCode.Int16, _ + TypeCode.Int16 * TCMAX + TypeCode.SByte, _ + TypeCode.Int16 * TCMAX + TypeCode.Byte, _ + TypeCode.Int16 * TCMAX + TypeCode.Int16 + + Return SubtractInt16(conv1.ToInt16(Nothing), conv2.ToInt16(Nothing)) + + Case TypeCode.SByte * TCMAX + TypeCode.UInt16, _ + TypeCode.SByte * TCMAX + TypeCode.Int32, _ + TypeCode.Byte * TCMAX + TypeCode.Int32, _ + TypeCode.Int16 * TCMAX + TypeCode.UInt16, _ + TypeCode.Int16 * TCMAX + TypeCode.Int32, _ + TypeCode.UInt16 * TCMAX + TypeCode.SByte, _ + TypeCode.UInt16 * TCMAX + TypeCode.Int16, _ + TypeCode.UInt16 * TCMAX + TypeCode.Int32, _ + TypeCode.Int32 * TCMAX + TypeCode.SByte, _ + TypeCode.Int32 * TCMAX + TypeCode.Byte, _ + TypeCode.Int32 * TCMAX + TypeCode.Int16, _ + TypeCode.Int32 * TCMAX + TypeCode.UInt16, _ + TypeCode.Int32 * TCMAX + TypeCode.Int32 + + Return SubtractInt32(conv1.ToInt32(Nothing), conv2.ToInt32(Nothing)) + + Case TypeCode.SByte * TCMAX + TypeCode.UInt32, _ + TypeCode.SByte * TCMAX + TypeCode.Int64, _ + TypeCode.Byte * TCMAX + TypeCode.Int64, _ + TypeCode.Int16 * TCMAX + TypeCode.UInt32, _ + TypeCode.Int16 * TCMAX + TypeCode.Int64, _ + TypeCode.UInt16 * TCMAX + TypeCode.Int64, _ + TypeCode.Int32 * TCMAX + TypeCode.UInt32, _ + TypeCode.Int32 * TCMAX + TypeCode.Int64, _ + TypeCode.UInt32 * TCMAX + TypeCode.SByte, _ + TypeCode.UInt32 * TCMAX + TypeCode.Int16, _ + TypeCode.UInt32 * TCMAX + TypeCode.Int32, _ + TypeCode.UInt32 * TCMAX + TypeCode.Int64, _ + TypeCode.Int64 * TCMAX + TypeCode.SByte, _ + TypeCode.Int64 * TCMAX + TypeCode.Byte, _ + TypeCode.Int64 * TCMAX + TypeCode.Int16, _ + TypeCode.Int64 * TCMAX + TypeCode.UInt16, _ + TypeCode.Int64 * TCMAX + TypeCode.Int32, _ + TypeCode.Int64 * TCMAX + TypeCode.UInt32, _ + TypeCode.Int64 * TCMAX + TypeCode.Int64 + + Return SubtractInt64(conv1.ToInt64(Nothing), conv2.ToInt64(Nothing)) + + Case TypeCode.SByte * TCMAX + TypeCode.UInt64, _ + TypeCode.SByte * TCMAX + TypeCode.Decimal, _ + TypeCode.Byte * TCMAX + TypeCode.Decimal, _ + TypeCode.Int16 * TCMAX + TypeCode.UInt64, _ + TypeCode.Int16 * TCMAX + TypeCode.Decimal, _ + TypeCode.UInt16 * TCMAX + TypeCode.Decimal, _ + TypeCode.Int32 * TCMAX + TypeCode.UInt64, _ + TypeCode.Int32 * TCMAX + TypeCode.Decimal, _ + TypeCode.UInt32 * TCMAX + TypeCode.Decimal, _ + TypeCode.Int64 * TCMAX + TypeCode.UInt64, _ + TypeCode.Int64 * TCMAX + TypeCode.Decimal, _ + TypeCode.UInt64 * TCMAX + TypeCode.SByte, _ + TypeCode.UInt64 * TCMAX + TypeCode.Int16, _ + TypeCode.UInt64 * TCMAX + TypeCode.Int32, _ + TypeCode.UInt64 * TCMAX + TypeCode.Int64, _ + TypeCode.UInt64 * TCMAX + TypeCode.Decimal, _ + TypeCode.Decimal * TCMAX + TypeCode.SByte, _ + TypeCode.Decimal * TCMAX + TypeCode.Byte, _ + TypeCode.Decimal * TCMAX + TypeCode.Int16, _ + TypeCode.Decimal * TCMAX + TypeCode.UInt16, _ + TypeCode.Decimal * TCMAX + TypeCode.Int32, _ + TypeCode.Decimal * TCMAX + TypeCode.UInt32, _ + TypeCode.Decimal * TCMAX + TypeCode.Int64, _ + TypeCode.Decimal * TCMAX + TypeCode.UInt64, _ + TypeCode.Decimal * TCMAX + TypeCode.Decimal + + Return SubtractDecimal(conv1, conv2) + + Case TypeCode.SByte * TCMAX + TypeCode.Single, _ + TypeCode.Byte * TCMAX + TypeCode.Single, _ + TypeCode.Int16 * TCMAX + TypeCode.Single, _ + TypeCode.UInt16 * TCMAX + TypeCode.Single, _ + TypeCode.Int32 * TCMAX + TypeCode.Single, _ + TypeCode.UInt32 * TCMAX + TypeCode.Single, _ + TypeCode.Int64 * TCMAX + TypeCode.Single, _ + TypeCode.UInt64 * TCMAX + TypeCode.Single, _ + TypeCode.Decimal * TCMAX + TypeCode.Single, _ + TypeCode.Single * TCMAX + TypeCode.SByte, _ + TypeCode.Single * TCMAX + TypeCode.Byte, _ + TypeCode.Single * TCMAX + TypeCode.Int16, _ + TypeCode.Single * TCMAX + TypeCode.UInt16, _ + TypeCode.Single * TCMAX + TypeCode.Int32, _ + TypeCode.Single * TCMAX + TypeCode.UInt32, _ + TypeCode.Single * TCMAX + TypeCode.Int64, _ + TypeCode.Single * TCMAX + TypeCode.UInt64, _ + TypeCode.Single * TCMAX + TypeCode.Decimal, _ + TypeCode.Single * TCMAX + TypeCode.Single + + Return SubtractSingle(conv1.ToSingle(Nothing), conv2.ToSingle(Nothing)) + + Case TypeCode.SByte * TCMAX + TypeCode.Double, _ + TypeCode.Byte * TCMAX + TypeCode.Double, _ + TypeCode.Int16 * TCMAX + TypeCode.Double, _ + TypeCode.UInt16 * TCMAX + TypeCode.Double, _ + TypeCode.Int32 * TCMAX + TypeCode.Double, _ + TypeCode.UInt32 * TCMAX + TypeCode.Double, _ + TypeCode.Int64 * TCMAX + TypeCode.Double, _ + TypeCode.UInt64 * TCMAX + TypeCode.Double, _ + TypeCode.Decimal * TCMAX + TypeCode.Double, _ + TypeCode.Single * TCMAX + TypeCode.Double, _ + TypeCode.Double * TCMAX + TypeCode.SByte, _ + TypeCode.Double * TCMAX + TypeCode.Byte, _ + TypeCode.Double * TCMAX + TypeCode.Int16, _ + TypeCode.Double * TCMAX + TypeCode.UInt16, _ + TypeCode.Double * TCMAX + TypeCode.Int32, _ + TypeCode.Double * TCMAX + TypeCode.UInt32, _ + TypeCode.Double * TCMAX + TypeCode.Int64, _ + TypeCode.Double * TCMAX + TypeCode.UInt64, _ + TypeCode.Double * TCMAX + TypeCode.Decimal, _ + TypeCode.Double * TCMAX + TypeCode.Single, _ + TypeCode.Double * TCMAX + TypeCode.Double + + Return SubtractDouble(conv1.ToDouble(Nothing), conv2.ToDouble(Nothing)) + + Case TypeCode.SByte * TCMAX + TypeCode.String, _ + TypeCode.Byte * TCMAX + TypeCode.String, _ + TypeCode.Int16 * TCMAX + TypeCode.String, _ + TypeCode.UInt16 * TCMAX + TypeCode.String, _ + TypeCode.Int32 * TCMAX + TypeCode.String, _ + TypeCode.UInt32 * TCMAX + TypeCode.String, _ + TypeCode.Int64 * TCMAX + TypeCode.String, _ + TypeCode.UInt64 * TCMAX + TypeCode.String, _ + TypeCode.Decimal * TCMAX + TypeCode.String, _ + TypeCode.Single * TCMAX + TypeCode.String, _ + TypeCode.Double * TCMAX + TypeCode.String + + Return SubtractDouble(conv1.ToDouble(Nothing), CDbl(conv2.ToString(Nothing))) + + + Case TypeCode.Byte * TCMAX + TypeCode.Empty + Return conv1.ToByte(Nothing) + + Case TypeCode.Byte * TCMAX + TypeCode.Boolean, _ + TypeCode.Int16 * TCMAX + TypeCode.Boolean + Return SubtractInt16(conv1.ToInt16(Nothing), ToVBBool(conv2)) + + Case TypeCode.Byte * TCMAX + TypeCode.Byte + Return SubtractByte(conv1.ToByte(Nothing), conv2.ToByte(Nothing)) + + Case TypeCode.Byte * TCMAX + TypeCode.UInt16, _ + TypeCode.UInt16 * TCMAX + TypeCode.Byte, _ + TypeCode.UInt16 * TCMAX + TypeCode.UInt16 + Return SubtractUInt16(conv1.ToUInt16(Nothing), conv2.ToUInt16(Nothing)) + + Case TypeCode.Byte * TCMAX + TypeCode.UInt32, _ + TypeCode.UInt16 * TCMAX + TypeCode.UInt32, _ + TypeCode.UInt32 * TCMAX + TypeCode.Byte, _ + TypeCode.UInt32 * TCMAX + TypeCode.UInt16, _ + TypeCode.UInt32 * TCMAX + TypeCode.UInt32 + Return SubtractUInt32(conv1.ToUInt32(Nothing), conv2.ToUInt32(Nothing)) + + Case TypeCode.Byte * TCMAX + TypeCode.UInt64, _ + TypeCode.UInt16 * TCMAX + TypeCode.UInt64, _ + TypeCode.UInt32 * TCMAX + TypeCode.UInt64, _ + TypeCode.UInt64 * TCMAX + TypeCode.Byte, _ + TypeCode.UInt64 * TCMAX + TypeCode.UInt16, _ + TypeCode.UInt64 * TCMAX + TypeCode.UInt32, _ + TypeCode.UInt64 * TCMAX + TypeCode.UInt64 + + Return SubtractUInt64(conv1.ToUInt64(Nothing), conv2.ToUInt64(Nothing)) + + + Case TypeCode.Int16 * TCMAX + TypeCode.Empty + Return conv1.ToInt16(Nothing) + + + Case TypeCode.UInt16 * TCMAX + TypeCode.Empty + Return conv1.ToUInt16(Nothing) + + Case TypeCode.UInt16 * TCMAX + TypeCode.Boolean, _ + TypeCode.Int32 * TCMAX + TypeCode.Boolean + Return SubtractInt32(conv1.ToInt32(Nothing), ToVBBool(conv2)) + + + Case TypeCode.Int32 * TCMAX + TypeCode.Empty + Return conv1.ToInt32(Nothing) + + + Case TypeCode.UInt32 * TCMAX + TypeCode.Empty + Return conv1.ToUInt32(Nothing) + + Case TypeCode.UInt32 * TCMAX + TypeCode.Boolean, _ + TypeCode.Int64 * TCMAX + TypeCode.Boolean + Return SubtractInt64(conv1.ToInt64(Nothing), ToVBBool(conv2)) + + + Case TypeCode.Int64 * TCMAX + TypeCode.Empty + Return conv1.ToInt64(Nothing) + + + Case TypeCode.UInt64 * TCMAX + TypeCode.Empty + Return conv1.ToUInt64(Nothing) + + Case TypeCode.UInt64 * TCMAX + TypeCode.Boolean, _ + TypeCode.Decimal * TCMAX + TypeCode.Boolean + Return SubtractDecimal(conv1, ToVBBoolConv(conv2)) + + + Case TypeCode.Decimal * TCMAX + TypeCode.Empty, _ + TypeCode.Single * TCMAX + TypeCode.Empty, _ + TypeCode.Double * TCMAX + TypeCode.Empty + Return Left + + + Case TypeCode.Single * TCMAX + TypeCode.Boolean + Return SubtractSingle(conv1.ToSingle(Nothing), ToVBBool(conv2)) + + + Case TypeCode.Double * TCMAX + TypeCode.Boolean + Return SubtractDouble(conv1.ToDouble(Nothing), ToVBBool(conv2)) + + + Case TypeCode.String * TCMAX + TypeCode.Empty + Return CDbl(conv1.ToString(Nothing)) + + Case TypeCode.String * TCMAX + TypeCode.Boolean + Return SubtractDouble(CDbl(conv1.ToString(Nothing)), ToVBBool(conv2)) + + Case TypeCode.String * TCMAX + TypeCode.SByte, _ + TypeCode.String * TCMAX + TypeCode.Byte, _ + TypeCode.String * TCMAX + TypeCode.Int16, _ + TypeCode.String * TCMAX + TypeCode.UInt16, _ + TypeCode.String * TCMAX + TypeCode.Int32, _ + TypeCode.String * TCMAX + TypeCode.UInt32, _ + TypeCode.String * TCMAX + TypeCode.Int64, _ + TypeCode.String * TCMAX + TypeCode.UInt64, _ + TypeCode.String * TCMAX + TypeCode.Decimal, _ + TypeCode.String * TCMAX + TypeCode.Single, _ + TypeCode.String * TCMAX + TypeCode.Double + + Return SubtractDouble(CDbl(conv1.ToString(Nothing)), conv2.ToDouble(Nothing)) + + Case TypeCode.String * TCMAX + TypeCode.String + Return SubtractDouble(CDbl(conv1.ToString(Nothing)), CDbl(conv2.ToString(Nothing))) + + +#If 0 Then + Case TypeCode.Empty * TCMAX + TypeCode.Char 'XX + Case TypeCode.Boolean * TCMAX + TypeCode.DateTime 'XX + Case TypeCode.Boolean * TCMAX + TypeCode.Char 'XX + Case TypeCode.SByte * TCMAX + TypeCode.DateTime 'XX + Case TypeCode.SByte * TCMAX + TypeCode.Char 'XX + Case TypeCode.Byte * TCMAX + TypeCode.DateTime 'XX + Case TypeCode.Byte * TCMAX + TypeCode.Char 'XX + Case TypeCode.Int16 * TCMAX + TypeCode.DateTime 'XX + Case TypeCode.Int16 * TCMAX + TypeCode.Char 'XX + Case TypeCode.UInt16 * TCMAX + TypeCode.DateTime 'XX + Case TypeCode.UInt16 * TCMAX + TypeCode.Char 'XX + Case TypeCode.Int32 * TCMAX + TypeCode.DateTime 'XX + Case TypeCode.Int32 * TCMAX + TypeCode.Char 'XX + Case TypeCode.UInt32 * TCMAX + TypeCode.DateTime 'XX + Case TypeCode.UInt32 * TCMAX + TypeCode.Char 'XX + Case TypeCode.Int64 * TCMAX + TypeCode.DateTime 'XX + Case TypeCode.Int64 * TCMAX + TypeCode.Char 'XX + Case TypeCode.UInt64 * TCMAX + TypeCode.DateTime 'XX + Case TypeCode.UInt64 * TCMAX + TypeCode.Char 'XX + Case TypeCode.Decimal * TCMAX + TypeCode.DateTime 'XX + Case TypeCode.Decimal * TCMAX + TypeCode.Char 'XX + Case TypeCode.Single * TCMAX + TypeCode.DateTime 'XX + Case TypeCode.Single * TCMAX + TypeCode.Char 'XX + Case TypeCode.Double * TCMAX + TypeCode.DateTime 'XX + Case TypeCode.Double * TCMAX + TypeCode.Char 'XX + Case TypeCode.DateTime * TCMAX + TypeCode.Boolean 'XX + Case TypeCode.DateTime * TCMAX + TypeCode.SByte 'XX + Case TypeCode.DateTime * TCMAX + TypeCode.Byte 'XX + Case TypeCode.DateTime * TCMAX + TypeCode.Int16 'XX + Case TypeCode.DateTime * TCMAX + TypeCode.UInt16 'XX + Case TypeCode.DateTime * TCMAX + TypeCode.Int32 'XX + Case TypeCode.DateTime * TCMAX + TypeCode.UInt32 'XX + Case TypeCode.DateTime * TCMAX + TypeCode.Int64 'XX + Case TypeCode.DateTime * TCMAX + TypeCode.UInt64 'XX + Case TypeCode.DateTime * TCMAX + TypeCode.Decimal 'XX + Case TypeCode.DateTime * TCMAX + TypeCode.Single 'XX + Case TypeCode.DateTime * TCMAX + TypeCode.Double 'XX + Case TypeCode.DateTime * TCMAX + TypeCode.Char 'XX + Case TypeCode.DateTime * TCMAX + TypeCode.String 'XX + Case TypeCode.Char * TCMAX + TypeCode.Empty 'XX + Case TypeCode.Char * TCMAX + TypeCode.Boolean 'XX + Case TypeCode.Char * TCMAX + TypeCode.SByte 'XX + Case TypeCode.Char * TCMAX + TypeCode.Byte 'XX + Case TypeCode.Char * TCMAX + TypeCode.Int16 'XX + Case TypeCode.Char * TCMAX + TypeCode.UInt16 'XX + Case TypeCode.Char * TCMAX + TypeCode.Int32 'XX + Case TypeCode.Char * TCMAX + TypeCode.UInt32 'XX + Case TypeCode.Char * TCMAX + TypeCode.Int64 'XX + Case TypeCode.Char * TCMAX + TypeCode.UInt64 'XX + Case TypeCode.Char * TCMAX + TypeCode.Decimal 'XX + Case TypeCode.Char * TCMAX + TypeCode.Single 'XX + Case TypeCode.Char * TCMAX + TypeCode.Double 'XX + Case TypeCode.Char * TCMAX + TypeCode.DateTime 'XX + Case TypeCode.Char * TCMAX + TypeCode.Char 'XX + Case TypeCode.Char * TCMAX + TypeCode.String 'XX + Case TypeCode.String * TCMAX + TypeCode.DateTime 'XX + Case TypeCode.String * TCMAX + TypeCode.Char 'XX +#End If + + Case Else + + End Select + + If tc1 = TypeCode.Object OrElse tc2 = TypeCode.Object OrElse _ + (tc1 = TypeCode.DateTime AndAlso tc2 = TypeCode.DateTime) OrElse _ + (tc1 = TypeCode.DateTime AndAlso tc2 = TypeCode.Empty) OrElse _ + (tc1 = TypeCode.Empty AndAlso tc2 = TypeCode.DateTime) Then + + Return InvokeUserDefinedOperator(UserDefinedOperator.Minus, Left, Right) + End If + + Throw GetNoValidOperatorException(UserDefinedOperator.Minus, Left, Right) + + End Function + + Private Shared Function SubtractByte(ByVal Left As Byte, ByVal Right As Byte) As Object + 'Range of possible values: [-255, 255] + Dim Result As Int16 = CShort(Left) - CShort(Right) + + If Result < Byte.MinValue Then + Return Result + Else + Return CByte(Result) 'REVIEW VSW#395757: overflow checking can be turned off here, and other similar places, since we've already checked for overflow. + End If + End Function + + Private Shared Function SubtractSByte(ByVal Left As SByte, ByVal Right As SByte) As Object + 'Range of possible values: [-255, 255] + Dim Result As Int16 = CShort(Left) - CShort(Right) + + If Result < SByte.MinValue OrElse Result > SByte.MaxValue Then + Return Result + Else + Return CSByte(Result) + End If + End Function + + Private Shared Function SubtractInt16(ByVal Left As Int16, ByVal Right As Int16) As Object + 'Range of possible values: [-65535, 65535] + Dim Result As Int32 = CInt(Left) - CInt(Right) + + If Result < Int16.MinValue OrElse Result > Int16.MaxValue Then + Return Result + Else + Return CShort(Result) + End If + End Function + + Private Shared Function SubtractUInt16(ByVal Left As UInt16, ByVal Right As UInt16) As Object + 'Range of possible values: [-65535, 65535] + Dim Result As Int32 = CInt(Left) - CInt(Right) + + If Result < UInt16.MinValue Then + Return Result + Else + Return CUShort(Result) + End If + End Function + + Private Shared Function SubtractInt32(ByVal Left As Int32, ByVal Right As Int32) As Object + 'Range of possible values: [-4294967295, 4294967295] + Dim Result As Int64 = CLng(Left) - CLng(Right) + + If Result < Int32.MinValue OrElse Result > Int32.MaxValue Then + Return Result + Else + Return CInt(Result) + End If + End Function + + Private Shared Function SubtractUInt32(ByVal Left As UInt32, ByVal Right As UInt32) As Object + 'Range of possible values: [-4294967295, 4294967295] + Dim Result As Int64 = CLng(Left) - CLng(Right) + + If Result < UInt32.MinValue Then + Return Result + Else + Return CUInt(Result) + End If + End Function + + Private Shared Function SubtractInt64(ByVal Left As Int64, ByVal Right As Int64) As Object + 'Range of possible values: [-18446744073709551615, 18446744073709551615] + Try + Return Left - Right + Catch ex As OverflowException + Return CDec(Left) - CDec(Right) + End Try + +#If 0 Then 'REVIEW VSW#395757: which implementation is better? If only we could turn off overflow checking on a per-block basis, then + 'this function could be rewritten to check the roundtrip and not have to do unecessary decimal subtraction. + Dim Result As Decimal = CDec(Left) - CDec(Right) + + If Result < Int64.MinValue OrElse Result > Int64.MaxValue Then + Return Result + Else + Return CLng(Result) + End If +#End If 'REVIEW VSW#395757: which implementation is better? If only we could turn off overflow checking on a per-block basis, then + End Function + + Private Shared Function SubtractUInt64(ByVal Left As UInt64, ByVal Right As UInt64) As Object + 'Range of possible values: [-18446744073709551615, 18446744073709551615] + Try + Return Left - Right + Catch ex As OverflowException + Return CDec(Left) - CDec(Right) + End Try + +#If 0 Then 'REVIEW VSW#395757: which implementation is better? If only we could turn off overflow checking on a per-block basis, then + 'this function could be rewritten to check the roundtrip and not have to do unecessary decimal subtraction. + Dim Result As Decimal = CDec(Left) - CDec(Right) + + If Result < UInt64.MinValue Then + Return Result + Else + Return CULng(Result) + End If +#End If 'REVIEW VSW#395757: which implementation is better? If only we could turn off overflow checking on a per-block basis, then + End Function + + Private Shared Function SubtractDecimal(ByVal Left As IConvertible, ByVal Right As IConvertible) As Object + 'REVIEW VSW#395758: there must be a better way to do this. If not, ask for one. + Dim LeftValue As Decimal = Left.ToDecimal(Nothing) + Dim RightValue As Decimal = Right.ToDecimal(Nothing) + + Try + Return LeftValue - RightValue + Catch ex As OverflowException + Return CDbl(LeftValue) - CDbl(RightValue) + End Try + End Function + + Private Shared Function SubtractSingle(ByVal Left As Single, ByVal Right As Single) As Object + Dim Result As Double = CDbl(Left) - CDbl(Right) + + If ((Result <= Single.MaxValue AndAlso Result >= Single.MinValue)) Then + Return CSng(Result) + ElseIf Double.IsInfinity(Result) AndAlso (Single.IsInfinity(Left) OrElse Single.IsInfinity(Right)) Then + Return CSng(Result) + Else + Return Result + End If + End Function + + Private Shared Function SubtractDouble(ByVal Left As Double, ByVal Right As Double) As Object + Return Left - Right + End Function + +#End Region + +#Region " Operator Multiply * " + + Public Shared Function MultiplyObject(ByVal Left As Object, ByVal Right As Object) As Object + Dim conv1, conv2 As IConvertible + Dim tc1, tc2 As TypeCode + + + conv1 = TryCast(Left, IConvertible) + + If conv1 Is Nothing Then + If Left Is Nothing Then + tc1 = TypeCode.Empty + Else + tc1 = TypeCode.Object + End If + Else + tc1 = conv1.GetTypeCode() + End If + + + + conv2 = TryCast(Right, IConvertible) + + If conv2 Is Nothing Then + If Right Is Nothing Then + tc2 = TypeCode.Empty + Else + tc2 = TypeCode.Object + End If + Else + tc2 = conv2.GetTypeCode() + End If + + 'REVIEW VSW#395755: make shared members that represent pre-boxed zero values for each type. + + Select Case tc1 * TCMAX + tc2 + + Case TypeCode.Empty * TCMAX + TypeCode.Empty, _ + TypeCode.Empty * TCMAX + TypeCode.Int32, _ + TypeCode.Int32 * TCMAX + TypeCode.Empty + Return Boxed_ZeroInteger + + Case TypeCode.Empty * TCMAX + TypeCode.Boolean, _ + TypeCode.Boolean * TCMAX + TypeCode.Empty, _ + TypeCode.Empty * TCMAX + TypeCode.Int16, _ + TypeCode.Int16 * TCMAX + TypeCode.Empty + Return Boxed_ZeroShort + + Case TypeCode.Empty * TCMAX + TypeCode.SByte, _ + TypeCode.SByte * TCMAX + TypeCode.Empty + Return Boxed_ZeroSByte + + Case TypeCode.Empty * TCMAX + TypeCode.Byte, _ + TypeCode.Byte * TCMAX + TypeCode.Empty + Return Boxed_ZeroByte + + Case TypeCode.Empty * TCMAX + TypeCode.UInt16, _ + TypeCode.UInt16 * TCMAX + TypeCode.Empty + Return Boxed_ZeroUShort + + Case TypeCode.Empty * TCMAX + TypeCode.UInt32, _ + TypeCode.UInt32 * TCMAX + TypeCode.Empty + Return Boxed_ZeroUInteger + + Case TypeCode.Empty * TCMAX + TypeCode.Int64, _ + TypeCode.Int64 * TCMAX + TypeCode.Empty + Return Boxed_ZeroLong + + Case TypeCode.Empty * TCMAX + TypeCode.UInt64, _ + TypeCode.UInt64 * TCMAX + TypeCode.Empty + Return Boxed_ZeroULong + + Case TypeCode.Empty * TCMAX + TypeCode.Decimal, _ + TypeCode.Decimal * TCMAX + TypeCode.Empty + Return Boxed_ZeroDecimal + + Case TypeCode.Empty * TCMAX + TypeCode.Single, _ + TypeCode.Single * TCMAX + TypeCode.Empty + Return Boxed_ZeroSinge + + Case TypeCode.Empty * TCMAX + TypeCode.Double, _ + TypeCode.Double * TCMAX + TypeCode.Empty + Return Boxed_ZeroDouble + + Case TypeCode.Empty * TCMAX + TypeCode.String + Return MultiplyDouble(Nothing, CDbl(conv2.ToString(Nothing))) + + + Case TypeCode.Boolean * TCMAX + TypeCode.Boolean + Return MultiplyInt16(ToVBBool(conv1), ToVBBool(conv2)) + + Case TypeCode.Boolean * TCMAX + TypeCode.SByte + Return MultiplySByte(ToVBBool(conv1), conv2.ToSByte(Nothing)) + + Case TypeCode.Boolean * TCMAX + TypeCode.Byte, _ + TypeCode.Boolean * TCMAX + TypeCode.Int16 + Return MultiplyInt16(ToVBBool(conv1), conv2.ToInt16(Nothing)) + + Case TypeCode.Boolean * TCMAX + TypeCode.UInt16, _ + TypeCode.Boolean * TCMAX + TypeCode.Int32 + Return MultiplyInt32(ToVBBool(conv1), conv2.ToInt32(Nothing)) + + Case TypeCode.Boolean * TCMAX + TypeCode.UInt32, _ + TypeCode.Boolean * TCMAX + TypeCode.Int64 + Return MultiplyInt64(ToVBBool(conv1), conv2.ToInt64(Nothing)) + + Case TypeCode.Boolean * TCMAX + TypeCode.UInt64, _ + TypeCode.Boolean * TCMAX + TypeCode.Decimal + Return MultiplyDecimal(ToVBBoolConv(conv1), conv2.ToDecimal(Nothing)) + + Case TypeCode.Boolean * TCMAX + TypeCode.Single + Return MultiplySingle(ToVBBool(conv1), conv2.ToSingle(Nothing)) + + Case TypeCode.Boolean * TCMAX + TypeCode.Double + Return MultiplyDouble(ToVBBool(conv1), conv2.ToDouble(Nothing)) + + Case TypeCode.Boolean * TCMAX + TypeCode.String + Return MultiplyDouble(ToVBBool(conv1), CDbl(conv2.ToString(Nothing))) + + + Case TypeCode.SByte * TCMAX + TypeCode.Boolean + Return MultiplySByte(conv1.ToSByte(Nothing), ToVBBool(conv2)) + + Case TypeCode.SByte * TCMAX + TypeCode.SByte + Return MultiplySByte(conv1.ToSByte(Nothing), conv2.ToSByte(Nothing)) + + Case TypeCode.SByte * TCMAX + TypeCode.Byte, _ + TypeCode.SByte * TCMAX + TypeCode.Int16, _ + TypeCode.Byte * TCMAX + TypeCode.SByte, _ + TypeCode.Byte * TCMAX + TypeCode.Int16, _ + TypeCode.Int16 * TCMAX + TypeCode.SByte, _ + TypeCode.Int16 * TCMAX + TypeCode.Byte, _ + TypeCode.Int16 * TCMAX + TypeCode.Int16 + + Return MultiplyInt16(conv1.ToInt16(Nothing), conv2.ToInt16(Nothing)) + + Case TypeCode.SByte * TCMAX + TypeCode.UInt16, _ + TypeCode.SByte * TCMAX + TypeCode.Int32, _ + TypeCode.Byte * TCMAX + TypeCode.Int32, _ + TypeCode.Int16 * TCMAX + TypeCode.UInt16, _ + TypeCode.Int16 * TCMAX + TypeCode.Int32, _ + TypeCode.UInt16 * TCMAX + TypeCode.SByte, _ + TypeCode.UInt16 * TCMAX + TypeCode.Int16, _ + TypeCode.UInt16 * TCMAX + TypeCode.Int32, _ + TypeCode.Int32 * TCMAX + TypeCode.SByte, _ + TypeCode.Int32 * TCMAX + TypeCode.Byte, _ + TypeCode.Int32 * TCMAX + TypeCode.Int16, _ + TypeCode.Int32 * TCMAX + TypeCode.UInt16, _ + TypeCode.Int32 * TCMAX + TypeCode.Int32 + + Return MultiplyInt32(conv1.ToInt32(Nothing), conv2.ToInt32(Nothing)) + + Case TypeCode.SByte * TCMAX + TypeCode.UInt32, _ + TypeCode.SByte * TCMAX + TypeCode.Int64, _ + TypeCode.Byte * TCMAX + TypeCode.Int64, _ + TypeCode.Int16 * TCMAX + TypeCode.UInt32, _ + TypeCode.Int16 * TCMAX + TypeCode.Int64, _ + TypeCode.UInt16 * TCMAX + TypeCode.Int64, _ + TypeCode.Int32 * TCMAX + TypeCode.UInt32, _ + TypeCode.Int32 * TCMAX + TypeCode.Int64, _ + TypeCode.UInt32 * TCMAX + TypeCode.SByte, _ + TypeCode.UInt32 * TCMAX + TypeCode.Int16, _ + TypeCode.UInt32 * TCMAX + TypeCode.Int32, _ + TypeCode.UInt32 * TCMAX + TypeCode.Int64, _ + TypeCode.Int64 * TCMAX + TypeCode.SByte, _ + TypeCode.Int64 * TCMAX + TypeCode.Byte, _ + TypeCode.Int64 * TCMAX + TypeCode.Int16, _ + TypeCode.Int64 * TCMAX + TypeCode.UInt16, _ + TypeCode.Int64 * TCMAX + TypeCode.Int32, _ + TypeCode.Int64 * TCMAX + TypeCode.UInt32, _ + TypeCode.Int64 * TCMAX + TypeCode.Int64 + + Return MultiplyInt64(conv1.ToInt64(Nothing), conv2.ToInt64(Nothing)) + + Case TypeCode.SByte * TCMAX + TypeCode.UInt64, _ + TypeCode.SByte * TCMAX + TypeCode.Decimal, _ + TypeCode.Byte * TCMAX + TypeCode.Decimal, _ + TypeCode.Int16 * TCMAX + TypeCode.UInt64, _ + TypeCode.Int16 * TCMAX + TypeCode.Decimal, _ + TypeCode.UInt16 * TCMAX + TypeCode.Decimal, _ + TypeCode.Int32 * TCMAX + TypeCode.UInt64, _ + TypeCode.Int32 * TCMAX + TypeCode.Decimal, _ + TypeCode.UInt32 * TCMAX + TypeCode.Decimal, _ + TypeCode.Int64 * TCMAX + TypeCode.UInt64, _ + TypeCode.Int64 * TCMAX + TypeCode.Decimal, _ + TypeCode.UInt64 * TCMAX + TypeCode.SByte, _ + TypeCode.UInt64 * TCMAX + TypeCode.Int16, _ + TypeCode.UInt64 * TCMAX + TypeCode.Int32, _ + TypeCode.UInt64 * TCMAX + TypeCode.Int64, _ + TypeCode.UInt64 * TCMAX + TypeCode.Decimal, _ + TypeCode.Decimal * TCMAX + TypeCode.SByte, _ + TypeCode.Decimal * TCMAX + TypeCode.Byte, _ + TypeCode.Decimal * TCMAX + TypeCode.Int16, _ + TypeCode.Decimal * TCMAX + TypeCode.UInt16, _ + TypeCode.Decimal * TCMAX + TypeCode.Int32, _ + TypeCode.Decimal * TCMAX + TypeCode.UInt32, _ + TypeCode.Decimal * TCMAX + TypeCode.Int64, _ + TypeCode.Decimal * TCMAX + TypeCode.UInt64, _ + TypeCode.Decimal * TCMAX + TypeCode.Decimal + + Return MultiplyDecimal(conv1, conv2) + + Case TypeCode.SByte * TCMAX + TypeCode.Single, _ + TypeCode.Byte * TCMAX + TypeCode.Single, _ + TypeCode.Int16 * TCMAX + TypeCode.Single, _ + TypeCode.UInt16 * TCMAX + TypeCode.Single, _ + TypeCode.Int32 * TCMAX + TypeCode.Single, _ + TypeCode.UInt32 * TCMAX + TypeCode.Single, _ + TypeCode.Int64 * TCMAX + TypeCode.Single, _ + TypeCode.UInt64 * TCMAX + TypeCode.Single, _ + TypeCode.Decimal * TCMAX + TypeCode.Single, _ + TypeCode.Single * TCMAX + TypeCode.SByte, _ + TypeCode.Single * TCMAX + TypeCode.Byte, _ + TypeCode.Single * TCMAX + TypeCode.Int16, _ + TypeCode.Single * TCMAX + TypeCode.UInt16, _ + TypeCode.Single * TCMAX + TypeCode.Int32, _ + TypeCode.Single * TCMAX + TypeCode.UInt32, _ + TypeCode.Single * TCMAX + TypeCode.Int64, _ + TypeCode.Single * TCMAX + TypeCode.UInt64, _ + TypeCode.Single * TCMAX + TypeCode.Decimal, _ + TypeCode.Single * TCMAX + TypeCode.Single + + Return MultiplySingle(conv1.ToSingle(Nothing), conv2.ToSingle(Nothing)) + + Case TypeCode.SByte * TCMAX + TypeCode.Double, _ + TypeCode.Byte * TCMAX + TypeCode.Double, _ + TypeCode.Int16 * TCMAX + TypeCode.Double, _ + TypeCode.UInt16 * TCMAX + TypeCode.Double, _ + TypeCode.Int32 * TCMAX + TypeCode.Double, _ + TypeCode.UInt32 * TCMAX + TypeCode.Double, _ + TypeCode.Int64 * TCMAX + TypeCode.Double, _ + TypeCode.UInt64 * TCMAX + TypeCode.Double, _ + TypeCode.Decimal * TCMAX + TypeCode.Double, _ + TypeCode.Single * TCMAX + TypeCode.Double, _ + TypeCode.Double * TCMAX + TypeCode.SByte, _ + TypeCode.Double * TCMAX + TypeCode.Byte, _ + TypeCode.Double * TCMAX + TypeCode.Int16, _ + TypeCode.Double * TCMAX + TypeCode.UInt16, _ + TypeCode.Double * TCMAX + TypeCode.Int32, _ + TypeCode.Double * TCMAX + TypeCode.UInt32, _ + TypeCode.Double * TCMAX + TypeCode.Int64, _ + TypeCode.Double * TCMAX + TypeCode.UInt64, _ + TypeCode.Double * TCMAX + TypeCode.Decimal, _ + TypeCode.Double * TCMAX + TypeCode.Single, _ + TypeCode.Double * TCMAX + TypeCode.Double + + Return MultiplyDouble(conv1.ToDouble(Nothing), conv2.ToDouble(Nothing)) + + Case TypeCode.SByte * TCMAX + TypeCode.String, _ + TypeCode.Byte * TCMAX + TypeCode.String, _ + TypeCode.Int16 * TCMAX + TypeCode.String, _ + TypeCode.UInt16 * TCMAX + TypeCode.String, _ + TypeCode.Int32 * TCMAX + TypeCode.String, _ + TypeCode.UInt32 * TCMAX + TypeCode.String, _ + TypeCode.Int64 * TCMAX + TypeCode.String, _ + TypeCode.UInt64 * TCMAX + TypeCode.String, _ + TypeCode.Decimal * TCMAX + TypeCode.String, _ + TypeCode.Single * TCMAX + TypeCode.String, _ + TypeCode.Double * TCMAX + TypeCode.String + + Return MultiplyDouble(conv1.ToDouble(Nothing), CDbl(conv2.ToString(Nothing))) + + + Case TypeCode.Byte * TCMAX + TypeCode.Boolean, _ + TypeCode.Int16 * TCMAX + TypeCode.Boolean + Return MultiplyInt16(conv1.ToInt16(Nothing), ToVBBool(conv2)) + + Case TypeCode.Byte * TCMAX + TypeCode.Byte + Return MultiplyByte(conv1.ToByte(Nothing), conv2.ToByte(Nothing)) + + Case TypeCode.Byte * TCMAX + TypeCode.UInt16, _ + TypeCode.UInt16 * TCMAX + TypeCode.Byte, _ + TypeCode.UInt16 * TCMAX + TypeCode.UInt16 + Return MultiplyUInt16(conv1.ToUInt16(Nothing), conv2.ToUInt16(Nothing)) + + Case TypeCode.Byte * TCMAX + TypeCode.UInt32, _ + TypeCode.UInt16 * TCMAX + TypeCode.UInt32, _ + TypeCode.UInt32 * TCMAX + TypeCode.Byte, _ + TypeCode.UInt32 * TCMAX + TypeCode.UInt16, _ + TypeCode.UInt32 * TCMAX + TypeCode.UInt32 + Return MultiplyUInt32(conv1.ToUInt32(Nothing), conv2.ToUInt32(Nothing)) + + Case TypeCode.Byte * TCMAX + TypeCode.UInt64, _ + TypeCode.UInt16 * TCMAX + TypeCode.UInt64, _ + TypeCode.UInt32 * TCMAX + TypeCode.UInt64, _ + TypeCode.UInt64 * TCMAX + TypeCode.Byte, _ + TypeCode.UInt64 * TCMAX + TypeCode.UInt16, _ + TypeCode.UInt64 * TCMAX + TypeCode.UInt32, _ + TypeCode.UInt64 * TCMAX + TypeCode.UInt64 + + Return MultiplyUInt64(conv1.ToUInt64(Nothing), conv2.ToUInt64(Nothing)) + + + Case TypeCode.UInt16 * TCMAX + TypeCode.Boolean, _ + TypeCode.Int32 * TCMAX + TypeCode.Boolean + Return MultiplyInt32(conv1.ToInt32(Nothing), ToVBBool(conv2)) + + + Case TypeCode.UInt32 * TCMAX + TypeCode.Boolean, _ + TypeCode.Int64 * TCMAX + TypeCode.Boolean + Return MultiplyInt64(conv1.ToInt64(Nothing), ToVBBool(conv2)) + + + Case TypeCode.UInt64 * TCMAX + TypeCode.Boolean, _ + TypeCode.Decimal * TCMAX + TypeCode.Boolean + Return MultiplyDecimal(conv1, ToVBBoolConv(conv2)) + + + Case TypeCode.Single * TCMAX + TypeCode.Boolean + Return MultiplySingle(conv1.ToSingle(Nothing), ToVBBool(conv2)) + + + Case TypeCode.Double * TCMAX + TypeCode.Boolean + Return MultiplyDouble(conv1.ToDouble(Nothing), ToVBBool(conv2)) + + + Case TypeCode.String * TCMAX + TypeCode.Empty + Return MultiplyDouble(CDbl(conv1.ToString(Nothing)), Nothing) + + Case TypeCode.String * TCMAX + TypeCode.Boolean + Return MultiplyDouble(CDbl(conv1.ToString(Nothing)), ToVBBool(conv2)) + + Case TypeCode.String * TCMAX + TypeCode.SByte, _ + TypeCode.String * TCMAX + TypeCode.Byte, _ + TypeCode.String * TCMAX + TypeCode.Int16, _ + TypeCode.String * TCMAX + TypeCode.UInt16, _ + TypeCode.String * TCMAX + TypeCode.Int32, _ + TypeCode.String * TCMAX + TypeCode.UInt32, _ + TypeCode.String * TCMAX + TypeCode.Int64, _ + TypeCode.String * TCMAX + TypeCode.UInt64, _ + TypeCode.String * TCMAX + TypeCode.Decimal, _ + TypeCode.String * TCMAX + TypeCode.Single, _ + TypeCode.String * TCMAX + TypeCode.Double + + Return MultiplyDouble(CDbl(conv1.ToString(Nothing)), conv2.ToDouble(Nothing)) + + Case TypeCode.String * TCMAX + TypeCode.String + Return MultiplyDouble(CDbl(conv1.ToString(Nothing)), CDbl(conv2.ToString(Nothing))) + + +#If 0 Then + Case TypeCode.Empty * TCMAX + TypeCode.DateTime 'XX + Case TypeCode.Empty * TCMAX + TypeCode.Char 'XX + Case TypeCode.Boolean * TCMAX + TypeCode.DateTime 'XX + Case TypeCode.Boolean * TCMAX + TypeCode.Char 'XX + Case TypeCode.SByte * TCMAX + TypeCode.DateTime 'XX + Case TypeCode.SByte * TCMAX + TypeCode.Char 'XX + Case TypeCode.Byte * TCMAX + TypeCode.DateTime 'XX + Case TypeCode.Byte * TCMAX + TypeCode.Char 'XX + Case TypeCode.Int16 * TCMAX + TypeCode.DateTime 'XX + Case TypeCode.Int16 * TCMAX + TypeCode.Char 'XX + Case TypeCode.UInt16 * TCMAX + TypeCode.DateTime 'XX + Case TypeCode.UInt16 * TCMAX + TypeCode.Char 'XX + Case TypeCode.Int32 * TCMAX + TypeCode.DateTime 'XX + Case TypeCode.Int32 * TCMAX + TypeCode.Char 'XX + Case TypeCode.UInt32 * TCMAX + TypeCode.DateTime 'XX + Case TypeCode.UInt32 * TCMAX + TypeCode.Char 'XX + Case TypeCode.Int64 * TCMAX + TypeCode.DateTime 'XX + Case TypeCode.Int64 * TCMAX + TypeCode.Char 'XX + Case TypeCode.UInt64 * TCMAX + TypeCode.DateTime 'XX + Case TypeCode.UInt64 * TCMAX + TypeCode.Char 'XX + Case TypeCode.Decimal * TCMAX + TypeCode.DateTime 'XX + Case TypeCode.Decimal * TCMAX + TypeCode.Char 'XX + Case TypeCode.Single * TCMAX + TypeCode.DateTime 'XX + Case TypeCode.Single * TCMAX + TypeCode.Char 'XX + Case TypeCode.Double * TCMAX + TypeCode.DateTime 'XX + Case TypeCode.Double * TCMAX + TypeCode.Char 'XX + Case TypeCode.DateTime * TCMAX + TypeCode.Empty 'XX + Case TypeCode.DateTime * TCMAX + TypeCode.Boolean 'XX + Case TypeCode.DateTime * TCMAX + TypeCode.SByte 'XX + Case TypeCode.DateTime * TCMAX + TypeCode.Byte 'XX + Case TypeCode.DateTime * TCMAX + TypeCode.Int16 'XX + Case TypeCode.DateTime * TCMAX + TypeCode.UInt16 'XX + Case TypeCode.DateTime * TCMAX + TypeCode.Int32 'XX + Case TypeCode.DateTime * TCMAX + TypeCode.UInt32 'XX + Case TypeCode.DateTime * TCMAX + TypeCode.Int64 'XX + Case TypeCode.DateTime * TCMAX + TypeCode.UInt64 'XX + Case TypeCode.DateTime * TCMAX + TypeCode.Decimal 'XX + Case TypeCode.DateTime * TCMAX + TypeCode.Single 'XX + Case TypeCode.DateTime * TCMAX + TypeCode.Double 'XX + Case TypeCode.DateTime * TCMAX + TypeCode.DateTime 'XX + Case TypeCode.DateTime * TCMAX + TypeCode.Char 'XX + Case TypeCode.DateTime * TCMAX + TypeCode.String 'XX + Case TypeCode.Char * TCMAX + TypeCode.Empty 'XX + Case TypeCode.Char * TCMAX + TypeCode.Boolean 'XX + Case TypeCode.Char * TCMAX + TypeCode.SByte 'XX + Case TypeCode.Char * TCMAX + TypeCode.Byte 'XX + Case TypeCode.Char * TCMAX + TypeCode.Int16 'XX + Case TypeCode.Char * TCMAX + TypeCode.UInt16 'XX + Case TypeCode.Char * TCMAX + TypeCode.Int32 'XX + Case TypeCode.Char * TCMAX + TypeCode.UInt32 'XX + Case TypeCode.Char * TCMAX + TypeCode.Int64 'XX + Case TypeCode.Char * TCMAX + TypeCode.UInt64 'XX + Case TypeCode.Char * TCMAX + TypeCode.Decimal 'XX + Case TypeCode.Char * TCMAX + TypeCode.Single 'XX + Case TypeCode.Char * TCMAX + TypeCode.Double 'XX + Case TypeCode.Char * TCMAX + TypeCode.DateTime 'XX + Case TypeCode.Char * TCMAX + TypeCode.Char 'XX + case TypeCode.Char * TCMAX + TypeCode.String 'XX + Case TypeCode.String * TCMAX + TypeCode.DateTime 'XX + case TypeCode.String * TCMAX + TypeCode.Char 'XX + +#End If + + Case Else + + End Select + + If tc1 = TypeCode.Object OrElse tc2 = TypeCode.Object Then + Return InvokeUserDefinedOperator(UserDefinedOperator.Multiply, Left, Right) + End If + + Throw GetNoValidOperatorException(UserDefinedOperator.Multiply, Left, Right) + + End Function + + Private Shared Function MultiplyByte(ByVal Left As Byte, ByVal Right As Byte) As Object + 'Range of possible values: [0, 65025] + Dim Result As Int32 = CInt(Left) * CInt(Right) + + If Result > Byte.MaxValue Then + If Result > Int16.MaxValue Then + Return Result + Else + Return CShort(Result) 'REVIEW VSW#395757: overflow checking can be turned off here, and other similar places, since we've already checked for overflow. + End If + Else + Return CByte(Result) + End If + End Function + + Private Shared Function MultiplySByte(ByVal Left As SByte, ByVal Right As SByte) As Object + 'Range of possible values: [-16256 ,16384] + Dim Result As Int16 = CShort(Left) * CShort(Right) + + If Result > SByte.MaxValue OrElse Result < SByte.MinValue Then + Return Result + Else + Return CSByte(Result) + End If + End Function + + Private Shared Function MultiplyInt16(ByVal Left As Int16, ByVal Right As Int16) As Object + 'Range of possible values: [-1073709056, 1073741824] + Dim Result As Int32 = CInt(Left) * CInt(Right) + + If Result > Int16.MaxValue OrElse Result < Int16.MinValue Then + Return Result + Else + Return CShort(Result) + End If + End Function + + Private Shared Function MultiplyUInt16(ByVal Left As UInt16, ByVal Right As UInt16) As Object + 'Range of possible values: [0, 4294836225] + Dim Result As Int64 = CLng(Left) * CLng(Right) + + If Result > UInt16.MaxValue Then + If Result > Int32.MaxValue Then + Return Result + Else + Return CInt(Result) + End If + Else + Return CUShort(Result) + End If + End Function + + Private Shared Function MultiplyInt32(ByVal Left As Int32, ByVal Right As Int32) As Object + 'Range of possible values: [-4611686016279904256, 4611686018427387904] + Dim Result As Int64 = CLng(Left) * CLng(Right) + + If Result > Int32.MaxValue OrElse Result < Int32.MinValue Then + Return Result + Else + Return CInt(Result) + End If + End Function + + Private Shared Function MultiplyUInt32(ByVal Left As UInt32, ByVal Right As UInt32) As Object + 'Range of possible values: [0, 18446744065119617025] + Dim Result As UInt64 = CULng(Left) * CULng(Right) + + If Result > UInt32.MaxValue Then + If Result > Int64.MaxValue Then + Return CDec(Result) + Else + Return CLng(Result) + End If + Else + Return CUInt(Result) + End If + End Function + + Private Shared Function MultiplyInt64(ByVal Left As Int64, ByVal Right As Int64) As Object + 'CONSIDER VSW#395757: isn't there a better way to do this? + Try + Return Left * Right + Catch ex As OverflowException + End Try + + Try + Return CDec(Left) * CDec(Right) + Catch ex As OverflowException + Return CDbl(Left) * CDbl(Right) + End Try + End Function + + Private Shared Function MultiplyUInt64(ByVal Left As UInt64, ByVal Right As UInt64) As Object + ''CONSIDER VSW#395757: isn't there a better way to do this? + Try + Return Left * Right + Catch ex As OverflowException + End Try + + Try + Return CDec(Left) * CDec(Right) + Catch ex As OverflowException + Return CDbl(Left) * CDbl(Right) + End Try + End Function + + Private Shared Function MultiplyDecimal(ByVal Left As IConvertible, ByVal Right As IConvertible) As Object + 'REVIEW VSW#395758: there must be a better way to do this. If not, ask for one. + Dim LeftValue As Decimal = Left.ToDecimal(Nothing) + Dim RightValue As Decimal = Right.ToDecimal(Nothing) + + Try + Return LeftValue * RightValue + Catch ex As OverflowException + 'Converting to Double is inconsistent with Division, where we convert to Single. + Return CDbl(LeftValue) * CDbl(RightValue) + End Try + End Function + + Private Shared Function MultiplySingle(ByVal Left As Single, ByVal Right As Single) As Object + Dim Result As Double = CDbl(Left) * CDbl(Right) + + If ((Result <= Single.MaxValue AndAlso Result >= Single.MinValue)) Then + Return CSng(Result) + ElseIf Double.IsInfinity(Result) AndAlso (Single.IsInfinity(Left) OrElse Single.IsInfinity(Right)) Then + Return CSng(Result) + Else + Return Result + End If + End Function + + Private Shared Function MultiplyDouble(ByVal Left As Double, ByVal Right As Double) As Object + Return Left * Right + End Function + +#End Region + +#Region " Operator Divide / " + + Public Shared Function DivideObject(ByVal Left As Object, ByVal Right As Object) As Object + + Dim conv1, conv2 As IConvertible + Dim tc1, tc2 As TypeCode + + + conv1 = TryCast(Left, IConvertible) + + If conv1 Is Nothing Then + If Left Is Nothing Then + tc1 = TypeCode.Empty + Else + tc1 = TypeCode.Object + End If + Else + tc1 = conv1.GetTypeCode() + End If + + + conv2 = TryCast(Right, IConvertible) + + If conv2 Is Nothing Then + If Right Is Nothing Then + tc2 = TypeCode.Empty + Else + tc2 = TypeCode.Object + End If + Else + tc2 = conv2.GetTypeCode() + End If + + + Select Case tc1 * TCMAX + tc2 + + Case TypeCode.Empty * TCMAX + TypeCode.Empty + Return DivideDouble(Nothing, Nothing) + + Case TypeCode.Empty * TCMAX + TypeCode.Boolean + Return DivideDouble(Nothing, ToVBBool(conv2)) + + Case TypeCode.Empty * TCMAX + TypeCode.SByte, _ + TypeCode.Empty * TCMAX + TypeCode.Byte, _ + TypeCode.Empty * TCMAX + TypeCode.Int16, _ + TypeCode.Empty * TCMAX + TypeCode.UInt16, _ + TypeCode.Empty * TCMAX + TypeCode.Int32, _ + TypeCode.Empty * TCMAX + TypeCode.UInt32, _ + TypeCode.Empty * TCMAX + TypeCode.Int64, _ + TypeCode.Empty * TCMAX + TypeCode.UInt64, _ + TypeCode.Empty * TCMAX + TypeCode.Double + Return DivideDouble(Nothing, conv2.ToDouble(Nothing)) + + Case TypeCode.Empty * TCMAX + TypeCode.Decimal + Return DivideDecimal(0D, conv2) + + Case TypeCode.Empty * TCMAX + TypeCode.Single + Return DivideSingle(Nothing, conv2.ToSingle(Nothing)) + + Case TypeCode.Empty * TCMAX + TypeCode.String + Return DivideDouble(Nothing, CDbl(conv2.ToString(Nothing))) + + + Case TypeCode.Boolean * TCMAX + TypeCode.Empty + Return DivideDouble(ToVBBool(conv1), Nothing) + + Case TypeCode.Boolean * TCMAX + TypeCode.Boolean + Return DivideDouble(ToVBBool(conv1), ToVBBool(conv2)) + + Case TypeCode.Boolean * TCMAX + TypeCode.SByte, _ + TypeCode.Boolean * TCMAX + TypeCode.Byte, _ + TypeCode.Boolean * TCMAX + TypeCode.Int16, _ + TypeCode.Boolean * TCMAX + TypeCode.UInt16, _ + TypeCode.Boolean * TCMAX + TypeCode.Int32, _ + TypeCode.Boolean * TCMAX + TypeCode.UInt32, _ + TypeCode.Boolean * TCMAX + TypeCode.Int64, _ + TypeCode.Boolean * TCMAX + TypeCode.UInt64, _ + TypeCode.Boolean * TCMAX + TypeCode.Double + Return DivideDouble(ToVBBool(conv1), conv2.ToDouble(Nothing)) + + Case TypeCode.Boolean * TCMAX + TypeCode.Decimal + Return DivideDecimal(ToVBBoolConv(conv1), conv2) + + Case TypeCode.Boolean * TCMAX + TypeCode.Single + Return DivideSingle(ToVBBool(conv1), conv2.ToSingle(Nothing)) + + Case TypeCode.Boolean * TCMAX + TypeCode.String + Return DivideDouble(ToVBBool(conv1), CDbl(conv2.ToString(Nothing))) + + + Case TypeCode.SByte * TCMAX + TypeCode.Empty, _ + TypeCode.Byte * TCMAX + TypeCode.Empty, _ + TypeCode.Int16 * TCMAX + TypeCode.Empty, _ + TypeCode.UInt16 * TCMAX + TypeCode.Empty, _ + TypeCode.Int32 * TCMAX + TypeCode.Empty, _ + TypeCode.UInt32 * TCMAX + TypeCode.Empty, _ + TypeCode.Int64 * TCMAX + TypeCode.Empty, _ + TypeCode.UInt64 * TCMAX + TypeCode.Empty, _ + TypeCode.Double * TCMAX + TypeCode.Empty + Return DivideDouble(conv1.ToDouble(Nothing), Nothing) + + Case TypeCode.SByte * TCMAX + TypeCode.Boolean, _ + TypeCode.Byte * TCMAX + TypeCode.Boolean, _ + TypeCode.Int16 * TCMAX + TypeCode.Boolean, _ + TypeCode.UInt16 * TCMAX + TypeCode.Boolean, _ + TypeCode.Int32 * TCMAX + TypeCode.Boolean, _ + TypeCode.UInt32 * TCMAX + TypeCode.Boolean, _ + TypeCode.Int64 * TCMAX + TypeCode.Boolean, _ + TypeCode.UInt64 * TCMAX + TypeCode.Boolean, _ + TypeCode.Double * TCMAX + TypeCode.Boolean + Return DivideDouble(conv1.ToDouble(Nothing), ToVBBool(conv2)) + + Case TypeCode.SByte * TCMAX + TypeCode.SByte, _ + TypeCode.SByte * TCMAX + TypeCode.Byte, _ + TypeCode.SByte * TCMAX + TypeCode.Int16, _ + TypeCode.SByte * TCMAX + TypeCode.UInt16, _ + TypeCode.SByte * TCMAX + TypeCode.Int32, _ + TypeCode.SByte * TCMAX + TypeCode.UInt32, _ + TypeCode.SByte * TCMAX + TypeCode.Int64, _ + TypeCode.SByte * TCMAX + TypeCode.UInt64, _ + TypeCode.SByte * TCMAX + TypeCode.Double, _ + TypeCode.Byte * TCMAX + TypeCode.SByte, _ + TypeCode.Byte * TCMAX + TypeCode.Byte, _ + TypeCode.Byte * TCMAX + TypeCode.Int16, _ + TypeCode.Byte * TCMAX + TypeCode.UInt16, _ + TypeCode.Byte * TCMAX + TypeCode.Int32, _ + TypeCode.Byte * TCMAX + TypeCode.UInt32, _ + TypeCode.Byte * TCMAX + TypeCode.Int64, _ + TypeCode.Byte * TCMAX + TypeCode.UInt64, _ + TypeCode.Byte * TCMAX + TypeCode.Double, _ + TypeCode.Int16 * TCMAX + TypeCode.SByte, _ + TypeCode.Int16 * TCMAX + TypeCode.Byte, _ + TypeCode.Int16 * TCMAX + TypeCode.Int16, _ + TypeCode.Int16 * TCMAX + TypeCode.UInt16, _ + TypeCode.Int16 * TCMAX + TypeCode.Int32, _ + TypeCode.Int16 * TCMAX + TypeCode.UInt32, _ + TypeCode.Int16 * TCMAX + TypeCode.Int64, _ + TypeCode.Int16 * TCMAX + TypeCode.UInt64, _ + TypeCode.Int16 * TCMAX + TypeCode.Double, _ + TypeCode.UInt16 * TCMAX + TypeCode.SByte, _ + TypeCode.UInt16 * TCMAX + TypeCode.Byte, _ + TypeCode.UInt16 * TCMAX + TypeCode.Int16, _ + TypeCode.UInt16 * TCMAX + TypeCode.UInt16, _ + TypeCode.UInt16 * TCMAX + TypeCode.Int32, _ + TypeCode.UInt16 * TCMAX + TypeCode.UInt32, _ + TypeCode.UInt16 * TCMAX + TypeCode.Int64, _ + TypeCode.UInt16 * TCMAX + TypeCode.UInt64, _ + TypeCode.UInt16 * TCMAX + TypeCode.Double, _ + TypeCode.Int32 * TCMAX + TypeCode.SByte, _ + TypeCode.Int32 * TCMAX + TypeCode.Byte, _ + TypeCode.Int32 * TCMAX + TypeCode.Int16, _ + TypeCode.Int32 * TCMAX + TypeCode.UInt16, _ + TypeCode.Int32 * TCMAX + TypeCode.Int32, _ + TypeCode.Int32 * TCMAX + TypeCode.UInt32, _ + TypeCode.Int32 * TCMAX + TypeCode.Int64, _ + TypeCode.Int32 * TCMAX + TypeCode.UInt64, _ + TypeCode.Int32 * TCMAX + TypeCode.Double, _ + TypeCode.UInt32 * TCMAX + TypeCode.SByte, _ + TypeCode.UInt32 * TCMAX + TypeCode.Byte, _ + TypeCode.UInt32 * TCMAX + TypeCode.Int16, _ + TypeCode.UInt32 * TCMAX + TypeCode.UInt16, _ + TypeCode.UInt32 * TCMAX + TypeCode.Int32, _ + TypeCode.UInt32 * TCMAX + TypeCode.UInt32, _ + TypeCode.UInt32 * TCMAX + TypeCode.Int64, _ + TypeCode.UInt32 * TCMAX + TypeCode.UInt64, _ + TypeCode.UInt32 * TCMAX + TypeCode.Double, _ + TypeCode.Int64 * TCMAX + TypeCode.SByte, _ + TypeCode.Int64 * TCMAX + TypeCode.Byte, _ + TypeCode.Int64 * TCMAX + TypeCode.Int16, _ + TypeCode.Int64 * TCMAX + TypeCode.UInt16, _ + TypeCode.Int64 * TCMAX + TypeCode.Int32, _ + TypeCode.Int64 * TCMAX + TypeCode.UInt32, _ + TypeCode.Int64 * TCMAX + TypeCode.Int64, _ + TypeCode.Int64 * TCMAX + TypeCode.UInt64, _ + TypeCode.Int64 * TCMAX + TypeCode.Double, _ + TypeCode.UInt64 * TCMAX + TypeCode.SByte, _ + TypeCode.UInt64 * TCMAX + TypeCode.Byte, _ + TypeCode.UInt64 * TCMAX + TypeCode.Int16, _ + TypeCode.UInt64 * TCMAX + TypeCode.UInt16, _ + TypeCode.UInt64 * TCMAX + TypeCode.Int32, _ + TypeCode.UInt64 * TCMAX + TypeCode.UInt32, _ + TypeCode.UInt64 * TCMAX + TypeCode.Int64, _ + TypeCode.UInt64 * TCMAX + TypeCode.UInt64, _ + TypeCode.UInt64 * TCMAX + TypeCode.Double, _ + TypeCode.Decimal * TCMAX + TypeCode.Double, _ + TypeCode.Single * TCMAX + TypeCode.Double, _ + TypeCode.Double * TCMAX + TypeCode.SByte, _ + TypeCode.Double * TCMAX + TypeCode.Byte, _ + TypeCode.Double * TCMAX + TypeCode.Int16, _ + TypeCode.Double * TCMAX + TypeCode.UInt16, _ + TypeCode.Double * TCMAX + TypeCode.Int32, _ + TypeCode.Double * TCMAX + TypeCode.UInt32, _ + TypeCode.Double * TCMAX + TypeCode.Int64, _ + TypeCode.Double * TCMAX + TypeCode.UInt64, _ + TypeCode.Double * TCMAX + TypeCode.Decimal, _ + TypeCode.Double * TCMAX + TypeCode.Single, _ + TypeCode.Double * TCMAX + TypeCode.Double + Return DivideDouble(conv1.ToDouble(Nothing), conv2.ToDouble(Nothing)) + + Case TypeCode.SByte * TCMAX + TypeCode.Decimal, _ + TypeCode.Byte * TCMAX + TypeCode.Decimal, _ + TypeCode.Int16 * TCMAX + TypeCode.Decimal, _ + TypeCode.UInt16 * TCMAX + TypeCode.Decimal, _ + TypeCode.Int32 * TCMAX + TypeCode.Decimal, _ + TypeCode.UInt32 * TCMAX + TypeCode.Decimal, _ + TypeCode.Int64 * TCMAX + TypeCode.Decimal, _ + TypeCode.UInt64 * TCMAX + TypeCode.Decimal, _ + TypeCode.Decimal * TCMAX + TypeCode.SByte, _ + TypeCode.Decimal * TCMAX + TypeCode.Byte, _ + TypeCode.Decimal * TCMAX + TypeCode.Int16, _ + TypeCode.Decimal * TCMAX + TypeCode.UInt16, _ + TypeCode.Decimal * TCMAX + TypeCode.Int32, _ + TypeCode.Decimal * TCMAX + TypeCode.UInt32, _ + TypeCode.Decimal * TCMAX + TypeCode.Int64, _ + TypeCode.Decimal * TCMAX + TypeCode.UInt64, _ + TypeCode.Decimal * TCMAX + TypeCode.Decimal + Return DivideDecimal(conv1, conv2) + + Case TypeCode.SByte * TCMAX + TypeCode.Single, _ + TypeCode.Byte * TCMAX + TypeCode.Single, _ + TypeCode.Int16 * TCMAX + TypeCode.Single, _ + TypeCode.UInt16 * TCMAX + TypeCode.Single, _ + TypeCode.Int32 * TCMAX + TypeCode.Single, _ + TypeCode.UInt32 * TCMAX + TypeCode.Single, _ + TypeCode.Int64 * TCMAX + TypeCode.Single, _ + TypeCode.UInt64 * TCMAX + TypeCode.Single, _ + TypeCode.Decimal * TCMAX + TypeCode.Single, _ + TypeCode.Single * TCMAX + TypeCode.SByte, _ + TypeCode.Single * TCMAX + TypeCode.Byte, _ + TypeCode.Single * TCMAX + TypeCode.Int16, _ + TypeCode.Single * TCMAX + TypeCode.UInt16, _ + TypeCode.Single * TCMAX + TypeCode.Int32, _ + TypeCode.Single * TCMAX + TypeCode.UInt32, _ + TypeCode.Single * TCMAX + TypeCode.Int64, _ + TypeCode.Single * TCMAX + TypeCode.UInt64, _ + TypeCode.Single * TCMAX + TypeCode.Decimal, _ + TypeCode.Single * TCMAX + TypeCode.Single + Return DivideSingle(conv1.ToSingle(Nothing), conv2.ToSingle(Nothing)) + + Case TypeCode.SByte * TCMAX + TypeCode.String, _ + TypeCode.Byte * TCMAX + TypeCode.String, _ + TypeCode.Int16 * TCMAX + TypeCode.String, _ + TypeCode.UInt16 * TCMAX + TypeCode.String, _ + TypeCode.Int32 * TCMAX + TypeCode.String, _ + TypeCode.UInt32 * TCMAX + TypeCode.String, _ + TypeCode.Int64 * TCMAX + TypeCode.String, _ + TypeCode.UInt64 * TCMAX + TypeCode.String, _ + TypeCode.Decimal * TCMAX + TypeCode.String, _ + TypeCode.Single * TCMAX + TypeCode.String, _ + TypeCode.Double * TCMAX + TypeCode.String + Return DivideDouble(conv1.ToDouble(Nothing), CDbl(conv2.ToString(Nothing))) + + + Case TypeCode.Decimal * TCMAX + TypeCode.Empty + Return DivideDecimal(conv1, 0D) + + Case TypeCode.Decimal * TCMAX + TypeCode.Boolean + Return DivideDecimal(conv1, ToVBBoolConv(conv2)) + + + Case TypeCode.Single * TCMAX + TypeCode.Empty + Return DivideSingle(conv1.ToSingle(Nothing), Nothing) + + Case TypeCode.Single * TCMAX + TypeCode.Boolean + Return DivideSingle(conv1.ToSingle(Nothing), ToVBBool(conv2)) + + + Case TypeCode.String * TCMAX + TypeCode.Empty + Return DivideDouble(CDbl(conv1.ToString(Nothing)), Nothing) + + Case TypeCode.String * TCMAX + TypeCode.Boolean + Return DivideDouble(CDbl(conv1.ToString(Nothing)), ToVBBool(conv2)) + + Case TypeCode.String * TCMAX + TypeCode.SByte, _ + TypeCode.String * TCMAX + TypeCode.Byte, _ + TypeCode.String * TCMAX + TypeCode.Int16, _ + TypeCode.String * TCMAX + TypeCode.UInt16, _ + TypeCode.String * TCMAX + TypeCode.Int32, _ + TypeCode.String * TCMAX + TypeCode.UInt32, _ + TypeCode.String * TCMAX + TypeCode.Int64, _ + TypeCode.String * TCMAX + TypeCode.UInt64, _ + TypeCode.String * TCMAX + TypeCode.Decimal, _ + TypeCode.String * TCMAX + TypeCode.Single, _ + TypeCode.String * TCMAX + TypeCode.Double + Return DivideDouble(CDbl(conv1.ToString(Nothing)), conv2.ToDouble(Nothing)) + + Case TypeCode.String * TCMAX + TypeCode.String + Return DivideDouble(CDbl(conv1.ToString(Nothing)), CDbl(conv2.ToString(Nothing))) +#If 0 Then + Case TypeCode.Empty * TCMAX + TypeCode.DateTime 'XX + Case TypeCode.Empty * TCMAX + TypeCode.Char 'XX + Case TypeCode.Boolean * TCMAX + TypeCode.DateTime + Case TypeCode.Boolean * TCMAX + TypeCode.Char + Case TypeCode.SByte * TCMAX + TypeCode.DateTime + Case TypeCode.SByte * TCMAX + TypeCode.Char + Case TypeCode.Byte * TCMAX + TypeCode.DateTime + Case TypeCode.Byte * TCMAX + TypeCode.Char + Case TypeCode.Int16 * TCMAX + TypeCode.DateTime + Case TypeCode.Int16 * TCMAX + TypeCode.Char + Case TypeCode.UInt16 * TCMAX + TypeCode.DateTime + Case TypeCode.UInt16 * TCMAX + TypeCode.Char + Case TypeCode.Int32 * TCMAX + TypeCode.DateTime + Case TypeCode.Int32 * TCMAX + TypeCode.Char + Case TypeCode.UInt32 * TCMAX + TypeCode.DateTime + Case TypeCode.UInt32 * TCMAX + TypeCode.Char + Case TypeCode.Int64 * TCMAX + TypeCode.DateTime + Case TypeCode.Int64 * TCMAX + TypeCode.Char + Case TypeCode.UInt64 * TCMAX + TypeCode.DateTime + Case TypeCode.UInt64 * TCMAX + TypeCode.Char + Case TypeCode.Decimal * TCMAX + TypeCode.DateTime + Case TypeCode.Decimal * TCMAX + TypeCode.Char + Case TypeCode.Single * TCMAX + TypeCode.DateTime + Case TypeCode.Single * TCMAX + TypeCode.Char + Case TypeCode.Double * TCMAX + TypeCode.DateTime + Case TypeCode.Double * TCMAX + TypeCode.Char + Case TypeCode.DateTime * TCMAX + TypeCode.Empty + Case TypeCode.DateTime * TCMAX + TypeCode.Boolean + Case TypeCode.DateTime * TCMAX + TypeCode.SByte + Case TypeCode.DateTime * TCMAX + TypeCode.Byte + Case TypeCode.DateTime * TCMAX + TypeCode.Int16 + Case TypeCode.DateTime * TCMAX + TypeCode.UInt16 + Case TypeCode.DateTime * TCMAX + TypeCode.Int32 + Case TypeCode.DateTime * TCMAX + TypeCode.UInt32 + Case TypeCode.DateTime * TCMAX + TypeCode.Int64 + Case TypeCode.DateTime * TCMAX + TypeCode.UInt64 + Case TypeCode.DateTime * TCMAX + TypeCode.Decimal + Case TypeCode.DateTime * TCMAX + TypeCode.Single + Case TypeCode.DateTime * TCMAX + TypeCode.Double + Case TypeCode.DateTime * TCMAX + TypeCode.DateTime + Case TypeCode.DateTime * TCMAX + TypeCode.Char + Case TypeCode.DateTime * TCMAX + TypeCode.String + Case TypeCode.Char * TCMAX + TypeCode.Empty + Case TypeCode.Char * TCMAX + TypeCode.Boolean + Case TypeCode.Char * TCMAX + TypeCode.SByte + Case TypeCode.Char * TCMAX + TypeCode.Byte + Case TypeCode.Char * TCMAX + TypeCode.Int16 + Case TypeCode.Char * TCMAX + TypeCode.UInt16 + Case TypeCode.Char * TCMAX + TypeCode.Int32 + Case TypeCode.Char * TCMAX + TypeCode.UInt32 + Case TypeCode.Char * TCMAX + TypeCode.Int64 + Case TypeCode.Char * TCMAX + TypeCode.UInt64 + Case TypeCode.Char * TCMAX + TypeCode.Decimal + Case TypeCode.Char * TCMAX + TypeCode.Single + Case TypeCode.Char * TCMAX + TypeCode.Double + Case TypeCode.Char * TCMAX + TypeCode.DateTime + Case TypeCode.Char * TCMAX + TypeCode.Char + Case TypeCode.Char * TCMAX + TypeCode.String + Case TypeCode.String * TCMAX + TypeCode.DateTime + Case TypeCode.String * TCMAX + TypeCode.Char +#End If + + Case Else + + End Select + + If tc1 = TypeCode.Object OrElse tc2 = TypeCode.Object Then + Return InvokeUserDefinedOperator(UserDefinedOperator.Divide, Left, Right) + End If + + Throw GetNoValidOperatorException(UserDefinedOperator.Divide, Left, Right) + + End Function + + Private Shared Function DivideDecimal(ByVal Left As IConvertible, ByVal Right As IConvertible) As Object + Dim LeftValue As Decimal = Left.ToDecimal(Nothing) + Dim RightValue As Decimal = Right.ToDecimal(Nothing) + + Try + Return LeftValue / RightValue + Catch ex As OverflowException + 'Converting to Single is inconsistent with Multiplication, where we convert to Double. + Return CSng(LeftValue) / CSng(RightValue) + End Try + End Function + + Private Shared Function DivideSingle(ByVal Left As Single, ByVal Right As Single) As Object + 'REVIEW VSW#395759: think about this function. Is it correct? + Dim Result As Single = Left / Right + + If Single.IsInfinity(Result) Then + If Single.IsInfinity(Left) OrElse Single.IsInfinity(Right) Then + Return Result + End If + Return CDbl(Left) / CDbl(Right) + Else + Return Result + End If + +#If 0 Then + 'REVIEW VSW# 395759: wouldn't this be a better implementation, performance wise? it does change the semantics, though. + If ((Result <= Single.MaxValue AndAlso Result >= Single.MinValue)) Then + Return CSng(Result) + ElseIf Double.IsInfinity(Result) AndAlso (Single.IsInfinity(Left) OrElse Single.IsInfinity(Right)) Then + Return CSng(Result) + Else + Return Result + End If +#End If + End Function + + Private Shared Function DivideDouble(ByVal Left As Double, ByVal Right As Double) As Object + Return Left / Right + End Function + +#End Region + +#Region " Operator Power ^ " + + Public Shared Function ExponentObject(ByVal Left As Object, ByVal Right As Object) As Object + + Dim conv1, conv2 As IConvertible + Dim tc1, tc2 As TypeCode + + Dim LeftValue As Double + Dim RightValue As Double + + + conv1 = TryCast(Left, IConvertible) + + If conv1 Is Nothing Then + If Left Is Nothing Then + tc1 = TypeCode.Empty + Else + tc1 = TypeCode.Object + End If + Else + tc1 = conv1.GetTypeCode() + End If + + + conv2 = TryCast(Right, IConvertible) + + If conv2 Is Nothing Then + If Right Is Nothing Then + tc2 = TypeCode.Empty + Else + tc2 = TypeCode.Object + End If + Else + tc2 = conv2.GetTypeCode() + End If + + + Select Case tc1 + Case TypeCode.Empty + LeftValue = 0.0R + + Case TypeCode.Boolean + LeftValue = ToVBBool(conv1) + + Case TypeCode.SByte, _ + TypeCode.Byte, _ + TypeCode.Int16, _ + TypeCode.UInt16, _ + TypeCode.Int32, _ + TypeCode.UInt32, _ + TypeCode.Int64, _ + TypeCode.UInt64, _ + TypeCode.Decimal, _ + TypeCode.Single, _ + TypeCode.Double + LeftValue = conv1.ToDouble(Nothing) + + Case TypeCode.String + LeftValue = CDbl(conv1.ToString(Nothing)) + + Case TypeCode.Object + Return InvokeUserDefinedOperator(UserDefinedOperator.Power, Left, Right) + + Case Else + 'DateTime + 'Char + Throw GetNoValidOperatorException(UserDefinedOperator.Power, Left, Right) + End Select + + Select Case tc2 + Case TypeCode.Empty + RightValue = 0.0R + + Case TypeCode.Boolean + RightValue = ToVBBool(conv2) + + Case TypeCode.SByte, _ + TypeCode.Byte, _ + TypeCode.Int16, _ + TypeCode.UInt16, _ + TypeCode.Int32, _ + TypeCode.UInt32, _ + TypeCode.Int64, _ + TypeCode.UInt64, _ + TypeCode.Decimal, _ + TypeCode.Single, _ + TypeCode.Double + RightValue = conv2.ToDouble(Nothing) + + Case TypeCode.String + RightValue = CDbl(conv2.ToString(Nothing)) + + Case TypeCode.Object + Return InvokeUserDefinedOperator(UserDefinedOperator.Power, Left, Right) + + Case Else + 'DateTime + 'Char + Throw GetNoValidOperatorException(UserDefinedOperator.Power, Left, Right) + End Select + + Return LeftValue ^ RightValue + + End Function + +#End Region + +#Region " Operator Mod " + + Public Shared Function ModObject(ByVal Left As Object, ByVal Right As Object) As Object + Dim conv1, conv2 As IConvertible + Dim tc1, tc2 As TypeCode + + + conv1 = TryCast(Left, IConvertible) + + If conv1 Is Nothing Then + If Left Is Nothing Then + tc1 = TypeCode.Empty + Else + tc1 = TypeCode.Object + End If + Else + tc1 = conv1.GetTypeCode() + End If + + + conv2 = TryCast(Right, IConvertible) + + If conv2 Is Nothing Then + If Right Is Nothing Then + tc2 = TypeCode.Empty + Else + tc2 = TypeCode.Object + End If + Else + tc2 = conv2.GetTypeCode() + End If + + 'REVIEW VSW#395755: make shared members that represent pre-boxed zero values for each type. + + Select Case tc1 * TCMAX + tc2 + + Case TypeCode.Empty * TCMAX + TypeCode.Empty + Return ModInt32(Nothing, Nothing) + + Case TypeCode.Empty * TCMAX + TypeCode.Boolean + Return ModInt16(Nothing, ToVBBool(conv2)) + + Case TypeCode.Empty * TCMAX + TypeCode.SByte + Return ModSByte(Nothing, conv2.ToSByte(Nothing)) + + Case TypeCode.Empty * TCMAX + TypeCode.Byte + Return ModByte(Nothing, conv2.ToByte(Nothing)) + + Case TypeCode.Empty * TCMAX + TypeCode.Int16 + Return ModInt16(Nothing, conv2.ToInt16(Nothing)) + + Case TypeCode.Empty * TCMAX + TypeCode.UInt16 + Return ModUInt16(Nothing, conv2.ToUInt16(Nothing)) + + Case TypeCode.Empty * TCMAX + TypeCode.Int32 + Return ModInt32(Nothing, conv2.ToInt32(Nothing)) + + Case TypeCode.Empty * TCMAX + TypeCode.UInt32 + Return ModUInt32(Nothing, conv2.ToUInt32(Nothing)) + + Case TypeCode.Empty * TCMAX + TypeCode.Int64 + Return ModInt64(Nothing, conv2.ToInt64(Nothing)) + + Case TypeCode.Empty * TCMAX + TypeCode.UInt64 + Return ModUInt64(Nothing, conv2.ToUInt64(Nothing)) + + Case TypeCode.Empty * TCMAX + TypeCode.Decimal + Return ModDecimal(0D, conv2.ToDecimal(Nothing)) + + Case TypeCode.Empty * TCMAX + TypeCode.Single + Return ModSingle(Nothing, conv2.ToSingle(Nothing)) + + Case TypeCode.Empty * TCMAX + TypeCode.Double + Return ModDouble(Nothing, conv2.ToDouble(Nothing)) + + Case TypeCode.Empty * TCMAX + TypeCode.String + Return ModDouble(Nothing, CDbl(conv2.ToString(Nothing))) + + + Case TypeCode.Boolean * TCMAX + TypeCode.Empty + Return ModInt16(ToVBBool(conv1), Nothing) + + Case TypeCode.Boolean * TCMAX + TypeCode.Boolean + Return ModInt16(ToVBBool(conv1), ToVBBool(conv2)) + + Case TypeCode.Boolean * TCMAX + TypeCode.SByte + Return ModSByte(ToVBBool(conv1), conv2.ToSByte(Nothing)) + + Case TypeCode.Boolean * TCMAX + TypeCode.Byte, _ + TypeCode.Boolean * TCMAX + TypeCode.Int16 + Return ModInt16(ToVBBool(conv1), conv2.ToInt16(Nothing)) + + Case TypeCode.Boolean * TCMAX + TypeCode.UInt16, _ + TypeCode.Boolean * TCMAX + TypeCode.Int32 + Return ModInt32(ToVBBool(conv1), conv2.ToInt32(Nothing)) + + Case TypeCode.Boolean * TCMAX + TypeCode.UInt32, _ + TypeCode.Boolean * TCMAX + TypeCode.Int64 + Return ModInt64(ToVBBool(conv1), conv2.ToInt64(Nothing)) + + Case TypeCode.Boolean * TCMAX + TypeCode.UInt64, _ + TypeCode.Boolean * TCMAX + TypeCode.Decimal + Return ModDecimal(ToVBBoolConv(conv1), conv2.ToDecimal(Nothing)) + + Case TypeCode.Boolean * TCMAX + TypeCode.Single + Return ModSingle(ToVBBool(conv1), conv2.ToSingle(Nothing)) + + Case TypeCode.Boolean * TCMAX + TypeCode.Double + Return ModDouble(ToVBBool(conv1), conv2.ToDouble(Nothing)) + + Case TypeCode.Boolean * TCMAX + TypeCode.String + Return ModDouble(ToVBBool(conv1), CDbl(conv2.ToString(Nothing))) + + + Case TypeCode.SByte * TCMAX + TypeCode.Empty + Return ModSByte(conv1.ToSByte(Nothing), Nothing) + + Case TypeCode.SByte * TCMAX + TypeCode.Boolean + Return ModSByte(conv1.ToSByte(Nothing), ToVBBool(conv2)) + + Case TypeCode.SByte * TCMAX + TypeCode.SByte + Return ModSByte(conv1.ToSByte(Nothing), conv2.ToSByte(Nothing)) + + Case TypeCode.SByte * TCMAX + TypeCode.Byte, _ + TypeCode.SByte * TCMAX + TypeCode.Int16, _ + TypeCode.Byte * TCMAX + TypeCode.SByte, _ + TypeCode.Byte * TCMAX + TypeCode.Int16, _ + TypeCode.Int16 * TCMAX + TypeCode.SByte, _ + TypeCode.Int16 * TCMAX + TypeCode.Byte, _ + TypeCode.Int16 * TCMAX + TypeCode.Int16 + + Return ModInt16(conv1.ToInt16(Nothing), conv2.ToInt16(Nothing)) + + Case TypeCode.SByte * TCMAX + TypeCode.UInt16, _ + TypeCode.SByte * TCMAX + TypeCode.Int32, _ + TypeCode.Byte * TCMAX + TypeCode.Int32, _ + TypeCode.Int16 * TCMAX + TypeCode.UInt16, _ + TypeCode.Int16 * TCMAX + TypeCode.Int32, _ + TypeCode.UInt16 * TCMAX + TypeCode.SByte, _ + TypeCode.UInt16 * TCMAX + TypeCode.Int16, _ + TypeCode.UInt16 * TCMAX + TypeCode.Int32, _ + TypeCode.Int32 * TCMAX + TypeCode.SByte, _ + TypeCode.Int32 * TCMAX + TypeCode.Byte, _ + TypeCode.Int32 * TCMAX + TypeCode.Int16, _ + TypeCode.Int32 * TCMAX + TypeCode.UInt16, _ + TypeCode.Int32 * TCMAX + TypeCode.Int32 + + Return ModInt32(conv1.ToInt32(Nothing), conv2.ToInt32(Nothing)) + + Case TypeCode.SByte * TCMAX + TypeCode.UInt32, _ + TypeCode.SByte * TCMAX + TypeCode.Int64, _ + TypeCode.Byte * TCMAX + TypeCode.Int64, _ + TypeCode.Int16 * TCMAX + TypeCode.UInt32, _ + TypeCode.Int16 * TCMAX + TypeCode.Int64, _ + TypeCode.UInt16 * TCMAX + TypeCode.Int64, _ + TypeCode.Int32 * TCMAX + TypeCode.UInt32, _ + TypeCode.Int32 * TCMAX + TypeCode.Int64, _ + TypeCode.UInt32 * TCMAX + TypeCode.SByte, _ + TypeCode.UInt32 * TCMAX + TypeCode.Int16, _ + TypeCode.UInt32 * TCMAX + TypeCode.Int32, _ + TypeCode.UInt32 * TCMAX + TypeCode.Int64, _ + TypeCode.Int64 * TCMAX + TypeCode.SByte, _ + TypeCode.Int64 * TCMAX + TypeCode.Byte, _ + TypeCode.Int64 * TCMAX + TypeCode.Int16, _ + TypeCode.Int64 * TCMAX + TypeCode.UInt16, _ + TypeCode.Int64 * TCMAX + TypeCode.Int32, _ + TypeCode.Int64 * TCMAX + TypeCode.UInt32, _ + TypeCode.Int64 * TCMAX + TypeCode.Int64 + + Return ModInt64(conv1.ToInt64(Nothing), conv2.ToInt64(Nothing)) + + Case TypeCode.SByte * TCMAX + TypeCode.UInt64, _ + TypeCode.SByte * TCMAX + TypeCode.Decimal, _ + TypeCode.Byte * TCMAX + TypeCode.Decimal, _ + TypeCode.Int16 * TCMAX + TypeCode.UInt64, _ + TypeCode.Int16 * TCMAX + TypeCode.Decimal, _ + TypeCode.UInt16 * TCMAX + TypeCode.Decimal, _ + TypeCode.Int32 * TCMAX + TypeCode.UInt64, _ + TypeCode.Int32 * TCMAX + TypeCode.Decimal, _ + TypeCode.UInt32 * TCMAX + TypeCode.Decimal, _ + TypeCode.Int64 * TCMAX + TypeCode.UInt64, _ + TypeCode.Int64 * TCMAX + TypeCode.Decimal, _ + TypeCode.UInt64 * TCMAX + TypeCode.SByte, _ + TypeCode.UInt64 * TCMAX + TypeCode.Int16, _ + TypeCode.UInt64 * TCMAX + TypeCode.Int32, _ + TypeCode.UInt64 * TCMAX + TypeCode.Int64, _ + TypeCode.UInt64 * TCMAX + TypeCode.Decimal, _ + TypeCode.Decimal * TCMAX + TypeCode.SByte, _ + TypeCode.Decimal * TCMAX + TypeCode.Byte, _ + TypeCode.Decimal * TCMAX + TypeCode.Int16, _ + TypeCode.Decimal * TCMAX + TypeCode.UInt16, _ + TypeCode.Decimal * TCMAX + TypeCode.Int32, _ + TypeCode.Decimal * TCMAX + TypeCode.UInt32, _ + TypeCode.Decimal * TCMAX + TypeCode.Int64, _ + TypeCode.Decimal * TCMAX + TypeCode.UInt64, _ + TypeCode.Decimal * TCMAX + TypeCode.Decimal + + Return ModDecimal(conv1, conv2) + + Case TypeCode.SByte * TCMAX + TypeCode.Single, _ + TypeCode.Byte * TCMAX + TypeCode.Single, _ + TypeCode.Int16 * TCMAX + TypeCode.Single, _ + TypeCode.UInt16 * TCMAX + TypeCode.Single, _ + TypeCode.Int32 * TCMAX + TypeCode.Single, _ + TypeCode.UInt32 * TCMAX + TypeCode.Single, _ + TypeCode.Int64 * TCMAX + TypeCode.Single, _ + TypeCode.UInt64 * TCMAX + TypeCode.Single, _ + TypeCode.Decimal * TCMAX + TypeCode.Single, _ + TypeCode.Single * TCMAX + TypeCode.SByte, _ + TypeCode.Single * TCMAX + TypeCode.Byte, _ + TypeCode.Single * TCMAX + TypeCode.Int16, _ + TypeCode.Single * TCMAX + TypeCode.UInt16, _ + TypeCode.Single * TCMAX + TypeCode.Int32, _ + TypeCode.Single * TCMAX + TypeCode.UInt32, _ + TypeCode.Single * TCMAX + TypeCode.Int64, _ + TypeCode.Single * TCMAX + TypeCode.UInt64, _ + TypeCode.Single * TCMAX + TypeCode.Decimal, _ + TypeCode.Single * TCMAX + TypeCode.Single + + Return ModSingle(conv1.ToSingle(Nothing), conv2.ToSingle(Nothing)) + + Case TypeCode.SByte * TCMAX + TypeCode.Double, _ + TypeCode.Byte * TCMAX + TypeCode.Double, _ + TypeCode.Int16 * TCMAX + TypeCode.Double, _ + TypeCode.UInt16 * TCMAX + TypeCode.Double, _ + TypeCode.Int32 * TCMAX + TypeCode.Double, _ + TypeCode.UInt32 * TCMAX + TypeCode.Double, _ + TypeCode.Int64 * TCMAX + TypeCode.Double, _ + TypeCode.UInt64 * TCMAX + TypeCode.Double, _ + TypeCode.Decimal * TCMAX + TypeCode.Double, _ + TypeCode.Single * TCMAX + TypeCode.Double, _ + TypeCode.Double * TCMAX + TypeCode.SByte, _ + TypeCode.Double * TCMAX + TypeCode.Byte, _ + TypeCode.Double * TCMAX + TypeCode.Int16, _ + TypeCode.Double * TCMAX + TypeCode.UInt16, _ + TypeCode.Double * TCMAX + TypeCode.Int32, _ + TypeCode.Double * TCMAX + TypeCode.UInt32, _ + TypeCode.Double * TCMAX + TypeCode.Int64, _ + TypeCode.Double * TCMAX + TypeCode.UInt64, _ + TypeCode.Double * TCMAX + TypeCode.Decimal, _ + TypeCode.Double * TCMAX + TypeCode.Single, _ + TypeCode.Double * TCMAX + TypeCode.Double + + Return ModDouble(conv1.ToDouble(Nothing), conv2.ToDouble(Nothing)) + + Case TypeCode.SByte * TCMAX + TypeCode.String, _ + TypeCode.Byte * TCMAX + TypeCode.String, _ + TypeCode.Int16 * TCMAX + TypeCode.String, _ + TypeCode.UInt16 * TCMAX + TypeCode.String, _ + TypeCode.Int32 * TCMAX + TypeCode.String, _ + TypeCode.UInt32 * TCMAX + TypeCode.String, _ + TypeCode.Int64 * TCMAX + TypeCode.String, _ + TypeCode.UInt64 * TCMAX + TypeCode.String, _ + TypeCode.Decimal * TCMAX + TypeCode.String, _ + TypeCode.Single * TCMAX + TypeCode.String, _ + TypeCode.Double * TCMAX + TypeCode.String + + Return ModDouble(conv1.ToDouble(Nothing), CDbl(conv2.ToString(Nothing))) + + + Case TypeCode.Byte * TCMAX + TypeCode.Empty + Return ModByte(conv1.ToByte(Nothing), Nothing) + + Case TypeCode.Byte * TCMAX + TypeCode.Boolean, _ + TypeCode.Int16 * TCMAX + TypeCode.Boolean + Return ModInt16(conv1.ToInt16(Nothing), ToVBBool(conv2)) + + Case TypeCode.Byte * TCMAX + TypeCode.Byte + Return ModByte(conv1.ToByte(Nothing), conv2.ToByte(Nothing)) + + Case TypeCode.Byte * TCMAX + TypeCode.UInt16, _ + TypeCode.UInt16 * TCMAX + TypeCode.Byte, _ + TypeCode.UInt16 * TCMAX + TypeCode.UInt16 + Return ModUInt16(conv1.ToUInt16(Nothing), conv2.ToUInt16(Nothing)) + + Case TypeCode.Byte * TCMAX + TypeCode.UInt32, _ + TypeCode.UInt16 * TCMAX + TypeCode.UInt32, _ + TypeCode.UInt32 * TCMAX + TypeCode.Byte, _ + TypeCode.UInt32 * TCMAX + TypeCode.UInt16, _ + TypeCode.UInt32 * TCMAX + TypeCode.UInt32 + Return ModUInt32(conv1.ToUInt32(Nothing), conv2.ToUInt32(Nothing)) + + Case TypeCode.Byte * TCMAX + TypeCode.UInt64, _ + TypeCode.UInt16 * TCMAX + TypeCode.UInt64, _ + TypeCode.UInt32 * TCMAX + TypeCode.UInt64, _ + TypeCode.UInt64 * TCMAX + TypeCode.Byte, _ + TypeCode.UInt64 * TCMAX + TypeCode.UInt16, _ + TypeCode.UInt64 * TCMAX + TypeCode.UInt32, _ + TypeCode.UInt64 * TCMAX + TypeCode.UInt64 + + Return ModUInt64(conv1.ToUInt64(Nothing), conv2.ToUInt64(Nothing)) + + + Case TypeCode.Int16 * TCMAX + TypeCode.Empty + Return ModInt16(conv1.ToInt16(Nothing), Nothing) + + + Case TypeCode.UInt16 * TCMAX + TypeCode.Empty + Return ModUInt16(conv1.ToUInt16(Nothing), Nothing) + + Case TypeCode.UInt16 * TCMAX + TypeCode.Boolean, _ + TypeCode.Int32 * TCMAX + TypeCode.Boolean + Return ModInt32(conv1.ToInt32(Nothing), ToVBBool(conv2)) + + + Case TypeCode.Int32 * TCMAX + TypeCode.Empty + Return ModInt32(conv1.ToInt32(Nothing), Nothing) + + + Case TypeCode.UInt32 * TCMAX + TypeCode.Empty + Return ModUInt32(conv1.ToUInt32(Nothing), Nothing) + + Case TypeCode.UInt32 * TCMAX + TypeCode.Boolean, _ + TypeCode.Int64 * TCMAX + TypeCode.Boolean + Return ModInt64(conv1.ToInt64(Nothing), ToVBBool(conv2)) + + + Case TypeCode.Int64 * TCMAX + TypeCode.Empty + Return ModInt64(conv1.ToInt64(Nothing), Nothing) + + + Case TypeCode.UInt64 * TCMAX + TypeCode.Empty + Return ModUInt64(conv1.ToUInt64(Nothing), Nothing) + + Case TypeCode.UInt64 * TCMAX + TypeCode.Boolean, _ + TypeCode.Decimal * TCMAX + TypeCode.Boolean + Return ModDecimal(conv1, ToVBBoolConv(conv2)) + + + Case TypeCode.Decimal * TCMAX + TypeCode.Empty + Return ModDecimal(conv1, 0D) + + + Case TypeCode.Single * TCMAX + TypeCode.Empty + Return ModSingle(conv1.ToSingle(Nothing), Nothing) + + Case TypeCode.Single * TCMAX + TypeCode.Boolean + Return ModSingle(conv1.ToSingle(Nothing), ToVBBool(conv2)) + + + Case TypeCode.Double * TCMAX + TypeCode.Empty + Return ModDouble(conv1.ToDouble(Nothing), Nothing) + + Case TypeCode.Double * TCMAX + TypeCode.Boolean + Return ModDouble(conv1.ToDouble(Nothing), ToVBBool(conv2)) + + + Case TypeCode.String * TCMAX + TypeCode.Empty + Return ModDouble(CDbl(conv1.ToString(Nothing)), Nothing) + + Case TypeCode.String * TCMAX + TypeCode.Boolean + Return ModDouble(CDbl(conv1.ToString(Nothing)), ToVBBool(conv2)) + + Case TypeCode.String * TCMAX + TypeCode.SByte, _ + TypeCode.String * TCMAX + TypeCode.Byte, _ + TypeCode.String * TCMAX + TypeCode.Int16, _ + TypeCode.String * TCMAX + TypeCode.UInt16, _ + TypeCode.String * TCMAX + TypeCode.Int32, _ + TypeCode.String * TCMAX + TypeCode.UInt32, _ + TypeCode.String * TCMAX + TypeCode.Int64, _ + TypeCode.String * TCMAX + TypeCode.UInt64, _ + TypeCode.String * TCMAX + TypeCode.Decimal, _ + TypeCode.String * TCMAX + TypeCode.Single, _ + TypeCode.String * TCMAX + TypeCode.Double + + Return ModDouble(CDbl(conv1.ToString(Nothing)), conv2.ToDouble(Nothing)) + + Case TypeCode.String * TCMAX + TypeCode.String + Return ModDouble(CDbl(conv1.ToString(Nothing)), CDbl(conv2.ToString(Nothing))) + + +#If 0 Then + Case TypeCode.Empty * TCMAX + TypeCode.DateTime 'XX + Case TypeCode.Empty * TCMAX + TypeCode.Char 'XX + Case TypeCode.Boolean * TCMAX + TypeCode.DateTime 'XX + Case TypeCode.Boolean * TCMAX + TypeCode.Char 'XX + Case TypeCode.SByte * TCMAX + TypeCode.DateTime 'XX + Case TypeCode.SByte * TCMAX + TypeCode.Char 'XX + Case TypeCode.Byte * TCMAX + TypeCode.DateTime 'XX + Case TypeCode.Byte * TCMAX + TypeCode.Char 'XX + Case TypeCode.Int16 * TCMAX + TypeCode.DateTime 'XX + Case TypeCode.Int16 * TCMAX + TypeCode.Char 'XX + Case TypeCode.UInt16 * TCMAX + TypeCode.DateTime 'XX + Case TypeCode.UInt16 * TCMAX + TypeCode.Char 'XX + Case TypeCode.Int32 * TCMAX + TypeCode.DateTime 'XX + Case TypeCode.Int32 * TCMAX + TypeCode.Char 'XX + Case TypeCode.UInt32 * TCMAX + TypeCode.DateTime 'XX + Case TypeCode.UInt32 * TCMAX + TypeCode.Char 'XX + Case TypeCode.Int64 * TCMAX + TypeCode.DateTime 'XX + Case TypeCode.Int64 * TCMAX + TypeCode.Char 'XX + Case TypeCode.UInt64 * TCMAX + TypeCode.DateTime 'XX + Case TypeCode.UInt64 * TCMAX + TypeCode.Char 'XX + Case TypeCode.Decimal * TCMAX + TypeCode.DateTime 'XX + Case TypeCode.Decimal * TCMAX + TypeCode.Char 'XX + Case TypeCode.Single * TCMAX + TypeCode.DateTime 'XX + Case TypeCode.Single * TCMAX + TypeCode.Char 'XX + Case TypeCode.Double * TCMAX + TypeCode.DateTime 'XX + Case TypeCode.Double * TCMAX + TypeCode.Char 'XX + Case TypeCode.DateTime * TCMAX + TypeCode.Empty 'XX + Case TypeCode.DateTime * TCMAX + TypeCode.Boolean 'XX + Case TypeCode.DateTime * TCMAX + TypeCode.SByte 'XX + Case TypeCode.DateTime * TCMAX + TypeCode.Byte 'XX + Case TypeCode.DateTime * TCMAX + TypeCode.Int16 'XX + Case TypeCode.DateTime * TCMAX + TypeCode.UInt16 'XX + Case TypeCode.DateTime * TCMAX + TypeCode.Int32 'XX + Case TypeCode.DateTime * TCMAX + TypeCode.UInt32 'XX + Case TypeCode.DateTime * TCMAX + TypeCode.Int64 'XX + Case TypeCode.DateTime * TCMAX + TypeCode.UInt64 'XX + Case TypeCode.DateTime * TCMAX + TypeCode.Decimal 'XX + Case TypeCode.DateTime * TCMAX + TypeCode.Single 'XX + Case TypeCode.DateTime * TCMAX + TypeCode.Double 'XX + Case TypeCode.DateTime * TCMAX + TypeCode.DateTime 'XX + Case TypeCode.DateTime * TCMAX + TypeCode.Char 'XX + Case TypeCode.DateTime * TCMAX + TypeCode.String 'XX + Case TypeCode.Char * TCMAX + TypeCode.Empty 'XX + Case TypeCode.Char * TCMAX + TypeCode.Boolean 'XX + Case TypeCode.Char * TCMAX + TypeCode.SByte 'XX + Case TypeCode.Char * TCMAX + TypeCode.Byte 'XX + Case TypeCode.Char * TCMAX + TypeCode.Int16 'XX + Case TypeCode.Char * TCMAX + TypeCode.UInt16 'XX + Case TypeCode.Char * TCMAX + TypeCode.Int32 'XX + Case TypeCode.Char * TCMAX + TypeCode.UInt32 'XX + Case TypeCode.Char * TCMAX + TypeCode.Int64 'XX + Case TypeCode.Char * TCMAX + TypeCode.UInt64 'XX + Case TypeCode.Char * TCMAX + TypeCode.Decimal 'XX + Case TypeCode.Char * TCMAX + TypeCode.Single 'XX + Case TypeCode.Char * TCMAX + TypeCode.Double 'XX + Case TypeCode.Char * TCMAX + TypeCode.DateTime 'XX + Case TypeCode.Char * TCMAX + TypeCode.Char 'XX + case TypeCode.Char * TCMAX + TypeCode.String 'XX + Case TypeCode.String * TCMAX + TypeCode.DateTime 'XX + case TypeCode.String * TCMAX + TypeCode.Char 'XX +#End If + Case Else + + End Select + + If tc1 = TypeCode.Object OrElse tc2 = TypeCode.Object Then + Return InvokeUserDefinedOperator(UserDefinedOperator.Modulus, Left, Right) + End If + + Throw GetNoValidOperatorException(UserDefinedOperator.Modulus, Left, Right) + + End Function + + Private Shared Function ModSByte(ByVal Left As SByte, ByVal Right As SByte) As Object + Return Left Mod Right + End Function + + Private Shared Function ModByte(ByVal Left As Byte, ByVal Right As Byte) As Object + Return Left Mod Right + End Function + + Private Shared Function ModInt16(ByVal Left As Int16, ByVal Right As Int16) As Object + 'REVIEW VSW#395763: is it really necessary to consider promotion to Integer for Short Mod Short? + Dim Result As Integer = CInt(Left) Mod CInt(Right) + + If Result < Int16.MinValue OrElse Result > Int16.MaxValue Then + Return Result + Else + Return CShort(Result) 'REVIEW VSW#395757: overflow checking not needed here. + End If + End Function + + Private Shared Function ModUInt16(ByVal Left As UInt16, ByVal Right As UInt16) As Object + Return Left Mod Right + End Function + + Private Shared Function ModInt32(ByVal Left As Integer, ByVal Right As Integer) As Object + 'Do operation with Int64 to avoid OverflowException with Int32.MinValue and -1 + Dim result As Long = CLng(Left) Mod CLng(Right) + + If result < Int32.MinValue OrElse result > Int32.MaxValue Then + Return result + Else + Return CInt(result) 'REVIEW VSW#395757: overflow checking not needed here. + End If + End Function + + Private Shared Function ModUInt32(ByVal Left As UInt32, ByVal Right As UInt32) As Object + Return Left Mod Right + End Function + + Private Shared Function ModInt64(ByVal Left As Int64, ByVal Right As Int64) As Object + + If Left = Int64.MinValue AndAlso Right = -1 Then + Return 0L + Else + Return Left Mod Right + End If + +#If 0 Then + 'OLD IMPLEMENTATION + 'If i1 = Int64.MinValue and i2 = -1, then we get an overflow + Try + Return i1 Mod i2 + Catch ex As OverflowException + Dim DecimalResult As Decimal + DecimalResult = CDec(i1) Mod CDec(i2) + 'Overflow is not caused by remainder, so we will most likely still return Int64 + If DecimalResult < Int64.MinValue OrElse DecimalResult > Int64.MaxValue Then + Return DecimalResult + Else + Return CLng(DecimalResult) + End If + End Try +#End If + End Function + + Private Shared Function ModUInt64(ByVal Left As UInt64, ByVal Right As UInt64) As Object + Return Left Mod Right + End Function + + Private Shared Function ModDecimal(ByVal Left As IConvertible, ByVal Right As IConvertible) As Object + Dim LeftValue As Decimal = Left.ToDecimal(Nothing) + Dim RightValue As Decimal = Right.ToDecimal(Nothing) + + Return LeftValue Mod RightValue + End Function + + Private Shared Function ModSingle(ByVal Left As Single, ByVal Right As Single) As Object + Return Left Mod Right + End Function + + Private Shared Function ModDouble(ByVal Left As Double, ByVal Right As Double) As Object + Return Left Mod Right + End Function + +#End Region + +#Region " Operator Integral Divide \ " + + Public Shared Function IntDivideObject(ByVal Left As Object, ByVal Right As Object) As Object + + Dim conv1, conv2 As IConvertible + Dim tc1, tc2 As TypeCode + + + conv1 = TryCast(Left, IConvertible) + + If conv1 Is Nothing Then + If Left Is Nothing Then + tc1 = TypeCode.Empty + Else + tc1 = TypeCode.Object + End If + Else + tc1 = conv1.GetTypeCode() + End If + + + conv2 = TryCast(Right, IConvertible) + + If conv2 Is Nothing Then + If Right Is Nothing Then + tc2 = TypeCode.Empty + Else + tc2 = TypeCode.Object + End If + Else + tc2 = conv2.GetTypeCode() + End If + + + Select Case tc1 * TCMAX + tc2 'CONSIDER: overflow checking is not necessary for this calculation - perf improvement. + + Case TypeCode.Empty * TCMAX + TypeCode.Empty + Return IntDivideInt32(Nothing, Nothing) + + Case TypeCode.Empty * TCMAX + TypeCode.Boolean + Return IntDivideInt16(Nothing, ToVBBool(conv2)) + + Case TypeCode.Empty * TCMAX + TypeCode.SByte + Return IntDivideSByte(Nothing, conv2.ToSByte(Nothing)) + + Case TypeCode.Empty * TCMAX + TypeCode.Byte + Return IntDivideByte(Nothing, conv2.ToByte(Nothing)) + + Case TypeCode.Empty * TCMAX + TypeCode.Int16 + Return IntDivideInt16(Nothing, conv2.ToInt16(Nothing)) + + Case TypeCode.Empty * TCMAX + TypeCode.UInt16 + Return IntDivideUInt16(Nothing, conv2.ToUInt16(Nothing)) + + Case TypeCode.Empty * TCMAX + TypeCode.Int32 + Return IntDivideInt32(Nothing, conv2.ToInt32(Nothing)) + + Case TypeCode.Empty * TCMAX + TypeCode.UInt32 + Return IntDivideUInt32(Nothing, conv2.ToUInt32(Nothing)) + + Case TypeCode.Empty * TCMAX + TypeCode.Int64 + Return IntDivideInt64(Nothing, conv2.ToInt64(Nothing)) + + Case TypeCode.Empty * TCMAX + TypeCode.UInt64 + Return IntDivideUInt64(Nothing, conv2.ToUInt64(Nothing)) + + Case TypeCode.Empty * TCMAX + TypeCode.Decimal, _ + TypeCode.Empty * TCMAX + TypeCode.Single, _ + TypeCode.Empty * TCMAX + TypeCode.Double + Return IntDivideInt64(Nothing, conv2.ToInt64(Nothing)) + + Case TypeCode.Empty * TCMAX + TypeCode.String + Return IntDivideInt64(Nothing, CLng(conv2.ToString(Nothing))) + + + Case TypeCode.Boolean * TCMAX + TypeCode.Empty + Return IntDivideInt16(ToVBBool(conv1), Nothing) + + Case TypeCode.Boolean * TCMAX + TypeCode.Boolean + Return IntDivideInt16(ToVBBool(conv1), ToVBBool(conv2)) + + Case TypeCode.Boolean * TCMAX + TypeCode.SByte + Return IntDivideSByte(ToVBBool(conv1), conv2.ToSByte(Nothing)) + + Case TypeCode.Boolean * TCMAX + TypeCode.Byte, _ + TypeCode.Boolean * TCMAX + TypeCode.Int16 + Return IntDivideInt16(ToVBBool(conv1), conv2.ToInt16(Nothing)) + + Case TypeCode.Boolean * TCMAX + TypeCode.UInt16, _ + TypeCode.Boolean * TCMAX + TypeCode.Int32 + Return IntDivideInt32(ToVBBool(conv1), conv2.ToInt32(Nothing)) + + Case TypeCode.Boolean * TCMAX + TypeCode.UInt32, _ + TypeCode.Boolean * TCMAX + TypeCode.Int64, _ + TypeCode.Boolean * TCMAX + TypeCode.UInt64, _ + TypeCode.Boolean * TCMAX + TypeCode.Decimal, _ + TypeCode.Boolean * TCMAX + TypeCode.Single, _ + TypeCode.Boolean * TCMAX + TypeCode.Double + + Return IntDivideInt64(ToVBBool(conv1), conv2.ToInt64(Nothing)) 'UNDONE: what about error messages on the overflow? not very useful coming from iconvertible code. + + Case TypeCode.Boolean * TCMAX + TypeCode.String + Return IntDivideInt64(ToVBBool(conv1), CLng(conv2.ToString(Nothing))) + + + Case TypeCode.SByte * TCMAX + TypeCode.Empty + Return IntDivideSByte(conv1.ToSByte(Nothing), Nothing) + + Case TypeCode.SByte * TCMAX + TypeCode.Boolean + Return IntDivideSByte(conv1.ToSByte(Nothing), ToVBBool(conv2)) + + Case TypeCode.SByte * TCMAX + TypeCode.SByte + Return IntDivideSByte(conv1.ToSByte(Nothing), conv2.ToSByte(Nothing)) + + Case TypeCode.SByte * TCMAX + TypeCode.Byte, _ + TypeCode.SByte * TCMAX + TypeCode.Int16, _ + TypeCode.Byte * TCMAX + TypeCode.SByte, _ + TypeCode.Byte * TCMAX + TypeCode.Int16, _ + TypeCode.Int16 * TCMAX + TypeCode.SByte, _ + TypeCode.Int16 * TCMAX + TypeCode.Byte, _ + TypeCode.Int16 * TCMAX + TypeCode.Int16 + + Return IntDivideInt16(conv1.ToInt16(Nothing), conv2.ToInt16(Nothing)) + + Case TypeCode.SByte * TCMAX + TypeCode.UInt16, _ + TypeCode.SByte * TCMAX + TypeCode.Int32, _ + TypeCode.Byte * TCMAX + TypeCode.Int32, _ + TypeCode.Int16 * TCMAX + TypeCode.UInt16, _ + TypeCode.Int16 * TCMAX + TypeCode.Int32, _ + TypeCode.UInt16 * TCMAX + TypeCode.SByte, _ + TypeCode.UInt16 * TCMAX + TypeCode.Int16, _ + TypeCode.UInt16 * TCMAX + TypeCode.Int32, _ + TypeCode.Int32 * TCMAX + TypeCode.SByte, _ + TypeCode.Int32 * TCMAX + TypeCode.Byte, _ + TypeCode.Int32 * TCMAX + TypeCode.Int16, _ + TypeCode.Int32 * TCMAX + TypeCode.UInt16, _ + TypeCode.Int32 * TCMAX + TypeCode.Int32 + + Return IntDivideInt32(conv1.ToInt32(Nothing), conv2.ToInt32(Nothing)) + + Case TypeCode.SByte * TCMAX + TypeCode.UInt32, _ + TypeCode.SByte * TCMAX + TypeCode.Int64, _ + TypeCode.SByte * TCMAX + TypeCode.UInt64, _ + TypeCode.SByte * TCMAX + TypeCode.Decimal, _ + TypeCode.SByte * TCMAX + TypeCode.Single, _ + TypeCode.SByte * TCMAX + TypeCode.Double, _ + TypeCode.Byte * TCMAX + TypeCode.Int64, _ + TypeCode.Byte * TCMAX + TypeCode.Decimal, _ + TypeCode.Byte * TCMAX + TypeCode.Single, _ + TypeCode.Byte * TCMAX + TypeCode.Double, _ + TypeCode.Int16 * TCMAX + TypeCode.UInt32, _ + TypeCode.Int16 * TCMAX + TypeCode.Int64, _ + TypeCode.Int16 * TCMAX + TypeCode.UInt64, _ + TypeCode.Int16 * TCMAX + TypeCode.Decimal, _ + TypeCode.Int16 * TCMAX + TypeCode.Single, _ + TypeCode.Int16 * TCMAX + TypeCode.Double, _ + TypeCode.UInt16 * TCMAX + TypeCode.Int64, _ + TypeCode.UInt16 * TCMAX + TypeCode.Decimal, _ + TypeCode.UInt16 * TCMAX + TypeCode.Single, _ + TypeCode.UInt16 * TCMAX + TypeCode.Double, _ + TypeCode.Int32 * TCMAX + TypeCode.UInt32, _ + TypeCode.Int32 * TCMAX + TypeCode.Int64, _ + TypeCode.Int32 * TCMAX + TypeCode.UInt64, _ + TypeCode.Int32 * TCMAX + TypeCode.Decimal, _ + TypeCode.Int32 * TCMAX + TypeCode.Single, _ + TypeCode.Int32 * TCMAX + TypeCode.Double, _ + TypeCode.UInt32 * TCMAX + TypeCode.SByte, _ + TypeCode.UInt32 * TCMAX + TypeCode.Int16, _ + TypeCode.UInt32 * TCMAX + TypeCode.Int32, _ + TypeCode.UInt32 * TCMAX + TypeCode.Int64, _ + TypeCode.UInt32 * TCMAX + TypeCode.Decimal, _ + TypeCode.UInt32 * TCMAX + TypeCode.Single, _ + TypeCode.UInt32 * TCMAX + TypeCode.Double, _ + TypeCode.Int64 * TCMAX + TypeCode.SByte, _ + TypeCode.Int64 * TCMAX + TypeCode.Byte, _ + TypeCode.Int64 * TCMAX + TypeCode.Int16, _ + TypeCode.Int64 * TCMAX + TypeCode.UInt16, _ + TypeCode.Int64 * TCMAX + TypeCode.Int32, _ + TypeCode.Int64 * TCMAX + TypeCode.UInt32, _ + TypeCode.Int64 * TCMAX + TypeCode.Int64, _ + TypeCode.Int64 * TCMAX + TypeCode.UInt64, _ + TypeCode.Int64 * TCMAX + TypeCode.Decimal, _ + TypeCode.Int64 * TCMAX + TypeCode.Single, _ + TypeCode.Int64 * TCMAX + TypeCode.Double, _ + TypeCode.UInt64 * TCMAX + TypeCode.SByte, _ + TypeCode.UInt64 * TCMAX + TypeCode.Int16, _ + TypeCode.UInt64 * TCMAX + TypeCode.Int32, _ + TypeCode.UInt64 * TCMAX + TypeCode.Int64, _ + TypeCode.UInt64 * TCMAX + TypeCode.Decimal, _ + TypeCode.UInt64 * TCMAX + TypeCode.Single, _ + TypeCode.UInt64 * TCMAX + TypeCode.Double, _ + TypeCode.Decimal * TCMAX + TypeCode.SByte, _ + TypeCode.Decimal * TCMAX + TypeCode.Byte, _ + TypeCode.Decimal * TCMAX + TypeCode.Int16, _ + TypeCode.Decimal * TCMAX + TypeCode.UInt16, _ + TypeCode.Decimal * TCMAX + TypeCode.Int32, _ + TypeCode.Decimal * TCMAX + TypeCode.UInt32, _ + TypeCode.Decimal * TCMAX + TypeCode.Int64, _ + TypeCode.Decimal * TCMAX + TypeCode.UInt64, _ + TypeCode.Decimal * TCMAX + TypeCode.Decimal, _ + TypeCode.Decimal * TCMAX + TypeCode.Single, _ + TypeCode.Decimal * TCMAX + TypeCode.Double, _ + TypeCode.Single * TCMAX + TypeCode.SByte, _ + TypeCode.Single * TCMAX + TypeCode.Byte, _ + TypeCode.Single * TCMAX + TypeCode.Int16, _ + TypeCode.Single * TCMAX + TypeCode.UInt16, _ + TypeCode.Single * TCMAX + TypeCode.Int32, _ + TypeCode.Single * TCMAX + TypeCode.UInt32, _ + TypeCode.Single * TCMAX + TypeCode.Int64, _ + TypeCode.Single * TCMAX + TypeCode.UInt64, _ + TypeCode.Single * TCMAX + TypeCode.Decimal, _ + TypeCode.Single * TCMAX + TypeCode.Single, _ + TypeCode.Single * TCMAX + TypeCode.Double, _ + TypeCode.Double * TCMAX + TypeCode.SByte, _ + TypeCode.Double * TCMAX + TypeCode.Byte, _ + TypeCode.Double * TCMAX + TypeCode.Int16, _ + TypeCode.Double * TCMAX + TypeCode.UInt16, _ + TypeCode.Double * TCMAX + TypeCode.Int32, _ + TypeCode.Double * TCMAX + TypeCode.UInt32, _ + TypeCode.Double * TCMAX + TypeCode.Int64, _ + TypeCode.Double * TCMAX + TypeCode.UInt64, _ + TypeCode.Double * TCMAX + TypeCode.Decimal, _ + TypeCode.Double * TCMAX + TypeCode.Single, _ + TypeCode.Double * TCMAX + TypeCode.Double + + Return IntDivideInt64(conv1.ToInt64(Nothing), conv2.ToInt64(Nothing)) 'UNDONE: what about error messages on the overflow? not very useful coming from iconvertible code. + + Case TypeCode.SByte * TCMAX + TypeCode.String, _ + TypeCode.Byte * TCMAX + TypeCode.String, _ + TypeCode.Int16 * TCMAX + TypeCode.String, _ + TypeCode.UInt16 * TCMAX + TypeCode.String, _ + TypeCode.Int32 * TCMAX + TypeCode.String, _ + TypeCode.UInt32 * TCMAX + TypeCode.String, _ + TypeCode.Int64 * TCMAX + TypeCode.String, _ + TypeCode.UInt64 * TCMAX + TypeCode.String, _ + TypeCode.Decimal * TCMAX + TypeCode.String, _ + TypeCode.Single * TCMAX + TypeCode.String, _ + TypeCode.Double * TCMAX + TypeCode.String + + Return IntDivideInt64(conv1.ToInt64(Nothing), CLng(conv2.ToString(Nothing))) + + + Case TypeCode.Byte * TCMAX + TypeCode.Empty + Return IntDivideByte(conv1.ToByte(Nothing), Nothing) + + Case TypeCode.Byte * TCMAX + TypeCode.Boolean, _ + TypeCode.Int16 * TCMAX + TypeCode.Boolean + Return IntDivideInt16(conv1.ToInt16(Nothing), ToVBBool(conv2)) + + Case TypeCode.Byte * TCMAX + TypeCode.Byte + Return IntDivideByte(conv1.ToByte(Nothing), conv2.ToByte(Nothing)) + + Case TypeCode.Byte * TCMAX + TypeCode.UInt16, _ + TypeCode.UInt16 * TCMAX + TypeCode.Byte, _ + TypeCode.UInt16 * TCMAX + TypeCode.UInt16 + Return IntDivideUInt16(conv1.ToUInt16(Nothing), conv2.ToUInt16(Nothing)) + + Case TypeCode.Byte * TCMAX + TypeCode.UInt32, _ + TypeCode.UInt16 * TCMAX + TypeCode.UInt32, _ + TypeCode.UInt32 * TCMAX + TypeCode.Byte, _ + TypeCode.UInt32 * TCMAX + TypeCode.UInt16, _ + TypeCode.UInt32 * TCMAX + TypeCode.UInt32 + + Return IntDivideUInt32(conv1.ToUInt32(Nothing), conv2.ToUInt32(Nothing)) + + Case TypeCode.Byte * TCMAX + TypeCode.UInt64, _ + TypeCode.UInt16 * TCMAX + TypeCode.UInt64, _ + TypeCode.UInt32 * TCMAX + TypeCode.UInt64, _ + TypeCode.UInt64 * TCMAX + TypeCode.Byte, _ + TypeCode.UInt64 * TCMAX + TypeCode.UInt16, _ + TypeCode.UInt64 * TCMAX + TypeCode.UInt32, _ + TypeCode.UInt64 * TCMAX + TypeCode.UInt64 + + Return IntDivideUInt64(conv1.ToUInt64(Nothing), conv2.ToUInt64(Nothing)) + + + Case TypeCode.Int16 * TCMAX + TypeCode.Empty + Return IntDivideInt16(conv1.ToInt16(Nothing), Nothing) + + + Case TypeCode.UInt16 * TCMAX + TypeCode.Empty + Return IntDivideUInt16(conv1.ToUInt16(Nothing), Nothing) + + Case TypeCode.UInt16 * TCMAX + TypeCode.Boolean, _ + TypeCode.Int32 * TCMAX + TypeCode.Boolean + Return IntDivideInt32(conv1.ToInt32(Nothing), ToVBBool(conv2)) + + + Case TypeCode.Int32 * TCMAX + TypeCode.Empty + Return IntDivideInt32(conv1.ToInt32(Nothing), Nothing) + + + Case TypeCode.UInt32 * TCMAX + TypeCode.Empty + Return IntDivideUInt32(conv1.ToUInt32(Nothing), Nothing) + + Case TypeCode.UInt32 * TCMAX + TypeCode.Boolean, _ + TypeCode.Int64 * TCMAX + TypeCode.Boolean, _ + TypeCode.UInt64 * TCMAX + TypeCode.Boolean, _ + TypeCode.Decimal * TCMAX + TypeCode.Boolean, _ + TypeCode.Single * TCMAX + TypeCode.Boolean, _ + TypeCode.Double * TCMAX + TypeCode.Boolean + + Return IntDivideInt64(conv1.ToInt64(Nothing), ToVBBool(conv2)) + + + Case TypeCode.Int64 * TCMAX + TypeCode.Empty + Return IntDivideInt64(conv1.ToInt64(Nothing), Nothing) + + + Case TypeCode.UInt64 * TCMAX + TypeCode.Empty + Return IntDivideUInt64(conv1.ToUInt64(Nothing), Nothing) + + + Case TypeCode.Decimal * TCMAX + TypeCode.Empty, _ + TypeCode.Single * TCMAX + TypeCode.Empty, _ + TypeCode.Double * TCMAX + TypeCode.Empty + Return IntDivideInt64(conv1.ToInt64(Nothing), Nothing) + + + Case TypeCode.String * TCMAX + TypeCode.Empty + Return IntDivideInt64(CLng(conv1.ToString(Nothing)), Nothing) + + Case TypeCode.String * TCMAX + TypeCode.Boolean + Return IntDivideInt64(CLng(conv1.ToString(Nothing)), ToVBBool(conv2)) + + Case TypeCode.String * TCMAX + TypeCode.SByte, _ + TypeCode.String * TCMAX + TypeCode.Byte, _ + TypeCode.String * TCMAX + TypeCode.Int16, _ + TypeCode.String * TCMAX + TypeCode.UInt16, _ + TypeCode.String * TCMAX + TypeCode.Int32, _ + TypeCode.String * TCMAX + TypeCode.UInt32, _ + TypeCode.String * TCMAX + TypeCode.Int64, _ + TypeCode.String * TCMAX + TypeCode.UInt64, _ + TypeCode.String * TCMAX + TypeCode.Decimal, _ + TypeCode.String * TCMAX + TypeCode.Single, _ + TypeCode.String * TCMAX + TypeCode.Double + + Return IntDivideInt64(CLng(conv1.ToString(Nothing)), conv2.ToInt64(Nothing)) + + Case TypeCode.String * TCMAX + TypeCode.String + Return IntDivideInt64(CLng(conv1.ToString(Nothing)), CLng(conv2.ToString(Nothing))) + +#If 0 Then + 'ERROR CASES + Case TypeCode.Empty * TCMAX + TypeCode.DateTime 'XX + Case TypeCode.Empty * TCMAX + TypeCode.Char 'XX + Case TypeCode.Boolean * TCMAX + TypeCode.DateTime 'XX + Case TypeCode.Boolean * TCMAX + TypeCode.Char 'XX + Case TypeCode.SByte * TCMAX + TypeCode.DateTime 'XX + Case TypeCode.SByte * TCMAX + TypeCode.Char 'XX + Case TypeCode.Byte * TCMAX + TypeCode.DateTime 'XX + Case TypeCode.Byte * TCMAX + TypeCode.Char 'XX + Case TypeCode.Int16 * TCMAX + TypeCode.DateTime 'XX + Case TypeCode.Int16 * TCMAX + TypeCode.Char 'XX + Case TypeCode.UInt16 * TCMAX + TypeCode.DateTime 'XX + Case TypeCode.UInt16 * TCMAX + TypeCode.Char 'XX + Case TypeCode.Int32 * TCMAX + TypeCode.DateTime 'XX + Case TypeCode.Int32 * TCMAX + TypeCode.Char 'XX + Case TypeCode.UInt32 * TCMAX + TypeCode.DateTime 'XX + Case TypeCode.UInt32 * TCMAX + TypeCode.Char 'XX + Case TypeCode.Int64 * TCMAX + TypeCode.DateTime 'XX + Case TypeCode.Int64 * TCMAX + TypeCode.Char 'XX + Case TypeCode.UInt64 * TCMAX + TypeCode.DateTime 'XX + Case TypeCode.UInt64 * TCMAX + TypeCode.Char 'XX + Case TypeCode.Decimal * TCMAX + TypeCode.DateTime 'XX + Case TypeCode.Decimal * TCMAX + TypeCode.Char 'XX + Case TypeCode.Single * TCMAX + TypeCode.DateTime 'XX + Case TypeCode.Single * TCMAX + TypeCode.Char 'XX + Case TypeCode.Double * TCMAX + TypeCode.DateTime 'XX + Case TypeCode.Double * TCMAX + TypeCode.Char 'XX + Case TypeCode.DateTime * TCMAX + TypeCode.Empty 'XX + Case TypeCode.DateTime * TCMAX + TypeCode.Boolean 'XX + Case TypeCode.DateTime * TCMAX + TypeCode.SByte 'XX + Case TypeCode.DateTime * TCMAX + TypeCode.Byte 'XX + Case TypeCode.DateTime * TCMAX + TypeCode.Int16 'XX + Case TypeCode.DateTime * TCMAX + TypeCode.UInt16 'XX + Case TypeCode.DateTime * TCMAX + TypeCode.Int32 'XX + Case TypeCode.DateTime * TCMAX + TypeCode.UInt32 'XX + Case TypeCode.DateTime * TCMAX + TypeCode.Int64 'XX + Case TypeCode.DateTime * TCMAX + TypeCode.UInt64 'XX + Case TypeCode.DateTime * TCMAX + TypeCode.Decimal 'XX + Case TypeCode.DateTime * TCMAX + TypeCode.Single 'XX + Case TypeCode.DateTime * TCMAX + TypeCode.Double 'XX + Case TypeCode.DateTime * TCMAX + TypeCode.DateTime 'XX + Case TypeCode.DateTime * TCMAX + TypeCode.Char 'XX + Case TypeCode.DateTime * TCMAX + TypeCode.String 'XX + Case TypeCode.Char * TCMAX + TypeCode.Empty 'XX + Case TypeCode.Char * TCMAX + TypeCode.Boolean 'XX + Case TypeCode.Char * TCMAX + TypeCode.SByte 'XX + Case TypeCode.Char * TCMAX + TypeCode.Byte 'XX + Case TypeCode.Char * TCMAX + TypeCode.Int16 'XX + Case TypeCode.Char * TCMAX + TypeCode.UInt16 'XX + Case TypeCode.Char * TCMAX + TypeCode.Int32 'XX + Case TypeCode.Char * TCMAX + TypeCode.UInt32 'XX + Case TypeCode.Char * TCMAX + TypeCode.Int64 'XX + Case TypeCode.Char * TCMAX + TypeCode.UInt64 'XX + Case TypeCode.Char * TCMAX + TypeCode.Decimal 'XX + Case TypeCode.Char * TCMAX + TypeCode.Single 'XX + Case TypeCode.Char * TCMAX + TypeCode.Double 'XX + Case TypeCode.Char * TCMAX + TypeCode.DateTime 'XX + Case TypeCode.Char * TCMAX + TypeCode.Char 'XX + Case TypeCode.Char * TCMAX + TypeCode.String 'XX + Case TypeCode.String * TCMAX + TypeCode.DateTime 'XX + Case TypeCode.String * TCMAX + TypeCode.Char 'XX +#End If + End Select + + If tc1 = TypeCode.Object OrElse tc2 = TypeCode.Object Then + Return InvokeUserDefinedOperator(UserDefinedOperator.IntegralDivide, Left, Right) + End If + + Throw GetNoValidOperatorException(UserDefinedOperator.IntegralDivide, Left, Right) + + End Function + + Private Shared Function IntDivideSByte(ByVal Left As SByte, ByVal Right As SByte) As Object + If Left = Sbyte.MinValue AndAlso Right = -1 Then + Return -CShort(Sbyte.MinValue) + End If + + Return Left \ Right + End Function + + Private Shared Function IntDivideByte(ByVal Left As Byte, ByVal Right As Byte) As Object + Return Left \ Right + End Function + + Private Shared Function IntDivideInt16(ByVal Left As Int16, ByVal Right As Int16) As Object + If Left = Short.MinValue AndAlso Right = -1 Then + Return -CInt(Short.MinValue) + End If + + Return Left \ Right + End Function + + Private Shared Function IntDivideUInt16(ByVal Left As UInt16, ByVal Right As UInt16) As Object + Return Left \ Right + End Function + + Private Shared Function IntDivideInt32(ByVal Left As Int32, ByVal Right As Int32) As Object + If Left = Integer.MinValue AndAlso Right = -1 Then + Return -CLng(Integer.MinValue) + End If + + Return Left \ Right + End Function + + Private Shared Function IntDivideUInt32(ByVal Left As UInt32, ByVal Right As UInt32) As Object + Return Left \ Right + End Function + + Private Shared Function IntDivideInt64(ByVal Left As Int64, ByVal Right As Int64) As Object + Return Left \ Right + End Function + + Private Shared Function IntDivideUInt64(ByVal Left As UInt64, ByVal Right As UInt64) As Object + Return Left \ Right + End Function + +#End Region + +#Region " Operator Shift Left << " + + Public Shared Function LeftShiftObject(ByVal Operand As Object, ByVal Amount As Object) As Object + + 'VSW#395761: There's no benefit from making this look like negate, so don't change it. + Dim conv1, conv2 As IConvertible + Dim tc1, tc2 As TypeCode + + conv1 = TryCast(Operand, IConvertible) + + If conv1 Is Nothing Then + If Operand Is Nothing Then + tc1 = TypeCode.Empty + Else + tc1 = TypeCode.Object + End If + Else + tc1 = conv1.GetTypeCode() + End If + + conv2 = TryCast(Amount, IConvertible) + + If conv2 Is Nothing Then + If Amount Is Nothing Then + tc2 = TypeCode.Empty + Else + tc2 = TypeCode.Object + End If + Else + tc2 = conv2.GetTypeCode() + End If + + If tc1 = TypeCode.Object OrElse tc2 = TypeCode.Object Then + Return InvokeUserDefinedOperator(UserDefinedOperator.ShiftLeft, Operand, Amount) + End If + + Select Case tc1 + Case TypeCode.Empty + Return Nothing << CInt(Amount) + Case TypeCode.Boolean + Return CShort(conv1.ToBoolean(Nothing)) << CInt(Amount) + Case TypeCode.SByte + Return conv1.ToSByte(Nothing) << CInt(Amount) + Case TypeCode.Byte + Return conv1.ToByte(Nothing) << CInt(Amount) + Case TypeCode.Int16 + Return conv1.ToInt16(Nothing) << CInt(Amount) + Case TypeCode.UInt16 + Return conv1.ToUInt16(Nothing) << CInt(Amount) + Case TypeCode.Int32 + Return conv1.ToInt32(Nothing) << CInt(Amount) + Case TypeCode.UInt32 + Return conv1.ToUInt32(Nothing) << CInt(Amount) + Case TypeCode.Int64, _ + TypeCode.Single, _ + TypeCode.Double, _ + TypeCode.Decimal + Return conv1.ToInt64(Nothing) << CInt(Amount) + Case TypeCode.UInt64 + Return conv1.ToUInt64(Nothing) << CInt(Amount) + Case TypeCode.String + Return CLng(conv1.ToString(Nothing)) << CInt(Amount) + End Select + + Throw GetNoValidOperatorException(UserDefinedOperator.ShiftLeft, Operand) + End Function + +#End Region + +#Region " Operator Shift Right >> " + + Public Shared Function RightShiftObject(ByVal Operand As Object, ByVal Amount As Object) As Object + + 'VSW#395761: There's no benefit from making this look like negate, so don't change it. + Dim conv1, conv2 As IConvertible + Dim tc1, tc2 As TypeCode + + conv1 = TryCast(Operand, IConvertible) + + If conv1 Is Nothing Then + If Operand Is Nothing Then + tc1 = TypeCode.Empty + Else + tc1 = TypeCode.Object + End If + Else + tc1 = conv1.GetTypeCode() + End If + + conv2 = TryCast(Amount, IConvertible) + + If conv2 Is Nothing Then + If Amount Is Nothing Then + tc2 = TypeCode.Empty + Else + tc2 = TypeCode.Object + End If + Else + tc2 = conv2.GetTypeCode() + End If + + If tc1 = TypeCode.Object OrElse tc2 = TypeCode.Object Then + Return InvokeUserDefinedOperator(UserDefinedOperator.ShiftRight, Operand, Amount) + End If + + Select Case tc1 + Case TypeCode.Empty + Return Nothing >> CInt(Amount) + Case TypeCode.Boolean + Return CShort(conv1.ToBoolean(Nothing)) >> CInt(Amount) + Case TypeCode.SByte + Return conv1.ToSByte(Nothing) >> CInt(Amount) + Case TypeCode.Byte + Return conv1.ToByte(Nothing) >> CInt(Amount) + Case TypeCode.Int16 + Return conv1.ToInt16(Nothing) >> CInt(Amount) + Case TypeCode.UInt16 + Return conv1.ToUInt16(Nothing) >> CInt(Amount) + Case TypeCode.Int32 + Return conv1.ToInt32(Nothing) >> CInt(Amount) + Case TypeCode.UInt32 + Return conv1.ToUInt32(Nothing) >> CInt(Amount) + Case TypeCode.Int64, _ + TypeCode.Single, _ + TypeCode.Double, _ + TypeCode.Decimal + Return conv1.ToInt64(Nothing) >> CInt(Amount) + Case TypeCode.UInt64 + Return conv1.ToUInt64(Nothing) >> CInt(Amount) + Case TypeCode.String + Return CLng(conv1.ToString(Nothing)) >> CInt(Amount) + End Select + + Throw GetNoValidOperatorException(UserDefinedOperator.ShiftRight, Operand) + End Function + +#End Region + +#Region " Operator Like " + +#If Not TELESTO Then + + ' - Some odd refactoring happened here that we must live with. We no longer emit runtime helper calls to this function from the + 'compiler--we use the functions defined in LikeOperator.vb But we have to hang on to this because an Everett app running on a Whidbey+ + 'runtime could try to call this. There was a TODO to remove this during Whidbey but it's just as well they didn't because it would have toasted + 'Everett apps. + Public Shared Function LikeObject(ByVal Source As Object, ByVal Pattern As Object, ByVal CompareOption As CompareMethod) As Object + + Dim conv1, conv2 As IConvertible + Dim tc1, tc2 As TypeCode + + conv1 = TryCast(Source, IConvertible) + If conv1 Is Nothing Then + If Source Is Nothing Then + tc1 = TypeCode.Empty + Else + tc1 = TypeCode.Object + End If + Else + tc1 = conv1.GetTypeCode() + End If + + conv2 = TryCast(Pattern, IConvertible) + If conv2 Is Nothing Then + If Pattern Is Nothing Then + tc2 = TypeCode.Empty + Else + tc2 = TypeCode.Object + End If + Else + tc2 = conv2.GetTypeCode() + End If + + 'Special cases for Char() + If (tc1 = TypeCode.Object) AndAlso (TypeOf Source Is Char()) Then + tc1 = TypeCode.String + End If + + If (tc2 = TypeCode.Object) AndAlso (TypeOf Pattern Is Char()) Then + tc2 = TypeCode.String + End If + + If tc1 = TypeCode.Object OrElse tc2 = TypeCode.Object Then + Return InvokeUserDefinedOperator(UserDefinedOperator.Like, Source, Pattern) + End If + + Return LikeString(CStr(Source), CStr(Pattern), CompareOption) + End Function + + 'UNDONE: can't the code generator just call the right compare version? The can remove this function. + Public Shared Function LikeString(ByVal Source As String, ByVal Pattern As String, ByVal CompareOption As CompareMethod) As Boolean + If CompareOption = CompareMethod.Binary Then + Return LikeStringBinary(Source, Pattern) + Else + Return LikeStringText(Source, Pattern) + End If + End Function + + Private Shared Function LikeStringBinary(ByVal Source As String, ByVal Pattern As String) As Boolean + 'Match Source to Pattern using "?*#[!a-g]" pattern matching characters + Dim SourceIndex As Integer + Dim PatternIndex As Integer + Dim SourceEndIndex As Integer + Dim PatternEndIndex As Integer + Dim p As Char + Dim s As Char + Dim InsideBracket As Boolean + Dim SeenHyphen As Boolean + Dim StartRangeChar As Char + Dim EndRangeChar As Char + Dim Match As Boolean + Dim SeenLiteral As Boolean + Dim SeenNot As Boolean + Dim Skip As Integer + Const NullChar As Char = ChrW(0) + Dim LiteralIsRangeEnd As Boolean = False + + ' Options = CompareOptions.Ordinal + + If Pattern Is Nothing Then + PatternEndIndex = 0 + Else + PatternEndIndex = Pattern.Length + End If + + If Source Is Nothing Then + SourceEndIndex = 0 + Else + SourceEndIndex = Source.Length + End If + + If SourceIndex < SourceEndIndex Then + s = Source.Chars(SourceIndex) + End If + + Do While PatternIndex < PatternEndIndex + p = Pattern.Chars(PatternIndex) + + If p = "*"c AndAlso (Not InsideBracket) Then 'If Then Else has faster performance the Select Case + 'Determine how many source chars to skip + Skip = AsteriskSkip(Pattern.Substring(PatternIndex + 1), Source.Substring(SourceIndex), SourceEndIndex - SourceIndex, CompareMethod.Binary, m_InvariantCompareInfo) + + If Skip < 0 Then + Return False + ElseIf Skip > 0 Then + SourceIndex += Skip + If SourceIndex < SourceEndIndex Then + s = Source.Chars(SourceIndex) + End If + End If + + ElseIf p = "?"c AndAlso (Not InsideBracket) Then + 'Match any character + SourceIndex = SourceIndex + 1 + If SourceIndex < SourceEndIndex Then + s = Source.Chars(SourceIndex) + End If + + ElseIf p = "#"c AndAlso (Not InsideBracket) Then + If Not System.Char.IsDigit(s) Then + Exit Do + End If + SourceIndex = SourceIndex + 1 + If SourceIndex < SourceEndIndex Then + s = Source.Chars(SourceIndex) + End If + + ElseIf p = "-"c AndAlso _ + (InsideBracket AndAlso SeenLiteral AndAlso (Not LiteralIsRangeEnd) AndAlso (Not SeenHyphen)) AndAlso _ + (((PatternIndex + 1) >= PatternEndIndex) OrElse (Pattern.Chars(PatternIndex + 1) <> "]"c)) Then + + SeenHyphen = True + + ElseIf p = "!"c AndAlso _ + (InsideBracket AndAlso (Not SeenNot)) Then + + SeenNot = True + Match = True + + ElseIf p = "["c AndAlso (Not InsideBracket) Then + InsideBracket = True + StartRangeChar = NullChar + EndRangeChar = NullChar + SeenLiteral = False + + ElseIf p = "]"c AndAlso InsideBracket Then + InsideBracket = False + + If SeenLiteral Then + If Match Then + SourceIndex += 1 + If SourceIndex < SourceEndIndex Then + s = Source.Chars(SourceIndex) + End If + Else + Exit Do + End If + ElseIf SeenHyphen Then + If Not Match Then + Exit Do + End If + ElseIf SeenNot Then + '[!] should be matched to literal ! same as if outside brackets + If "!"c <> s Then + Exit Do + End If + SourceIndex += 1 + If SourceIndex < SourceEndIndex Then + s = Source.Chars(SourceIndex) + End If + End If + + Match = False + SeenLiteral = False + SeenNot = False + SeenHyphen = False + + Else + 'Literal character + SeenLiteral = True + LiteralIsRangeEnd = False + + If InsideBracket Then + If SeenHyphen Then + SeenHyphen = False + LiteralIsRangeEnd = True + EndRangeChar = p + + If StartRangeChar > EndRangeChar Then + Throw VbMakeException(vbErrors.BadPatStr) + ElseIf (SeenNot AndAlso Match) OrElse (Not SeenNot AndAlso Not Match) Then + 'Calls to ci.Compare are expensive, avoid them for good performance + Match = (s > StartRangeChar) AndAlso (s <= EndRangeChar) + + If SeenNot Then + Match = Not Match + End If + End If + Else + StartRangeChar = p + + 'This compare handles non range chars such as the "abc" and "uvw" + 'and the first char of a range such as "d" in "[abcd-tuvw]". + Match = LikeStringCompareBinary(SeenNot, Match, p, s) + End If + Else + If p <> s AndAlso Not SeenNot Then + Exit Do + End If + + SeenNot = False + SourceIndex += 1 + + If SourceIndex < SourceEndIndex Then + s = Source.Chars(SourceIndex) + ElseIf SourceIndex > SourceEndIndex Then + Return False + End If + End If + End If + + PatternIndex += 1 + Loop + + If InsideBracket Then + If SourceEndIndex = 0 Then + Return False + Else + Throw New ArgumentException(GetResourceString(ResID.Argument_InvalidValue1, "Pattern")) + End If + Else + Return (PatternIndex = PatternEndIndex) AndAlso (SourceIndex = SourceEndIndex) + End If + End Function + + Private Shared Function LikeStringText(ByVal Source As String, ByVal Pattern As String) As Boolean + 'Match Source to Pattern using "?*#[!a-g]" pattern matching characters + Dim SourceIndex As Integer + Dim PatternIndex As Integer + Dim SourceEndIndex As Integer + Dim PatternEndIndex As Integer + Dim p As Char + Dim s As Char + Dim InsideBracket As Boolean + Dim SeenHyphen As Boolean + Dim StartRangeChar As Char + Dim EndRangeChar As Char + Dim Match As Boolean + Dim SeenLiteral As Boolean + Dim SeenNot As Boolean + Dim Skip As Integer + Dim Options As CompareOptions + Dim ci As CompareInfo + Const NullChar As Char = ChrW(0) + Dim LiteralIsRangeEnd As Boolean = False + + If Pattern Is Nothing Then + PatternEndIndex = 0 + Else + PatternEndIndex = Pattern.Length + End If + + If Source Is Nothing Then + SourceEndIndex = 0 + Else + SourceEndIndex = Source.Length + End If + + If SourceIndex < SourceEndIndex Then + s = Source.Chars(SourceIndex) + End If + + ci = GetCultureInfo().CompareInfo + Options = CompareOptions.IgnoreCase Or _ + CompareOptions.IgnoreWidth Or _ + CompareOptions.IgnoreNonSpace Or _ + CompareOptions.IgnoreKanaType + + Do While PatternIndex < PatternEndIndex + p = Pattern.Chars(PatternIndex) + + If p = "*"c AndAlso (Not InsideBracket) Then 'If Then Else has faster performance the Select Case + 'Determine how many source chars to skip + Skip = AsteriskSkip(Pattern.Substring(PatternIndex + 1), Source.Substring(SourceIndex), SourceEndIndex - SourceIndex, CompareMethod.Text, ci) + + If Skip < 0 Then + Return False + ElseIf Skip > 0 Then + SourceIndex += Skip + If SourceIndex < SourceEndIndex Then + s = Source.Chars(SourceIndex) + End If + End If + + ElseIf p = "?"c AndAlso (Not InsideBracket) Then + 'Match any character + SourceIndex = SourceIndex + 1 + If SourceIndex < SourceEndIndex Then + s = Source.Chars(SourceIndex) + End If + + ElseIf p = "#"c AndAlso (Not InsideBracket) Then + If Not System.Char.IsDigit(s) Then + Exit Do + End If + SourceIndex = SourceIndex + 1 + If SourceIndex < SourceEndIndex Then + s = Source.Chars(SourceIndex) + End If + + ElseIf p = "-"c AndAlso _ + (InsideBracket AndAlso SeenLiteral AndAlso (Not LiteralIsRangeEnd) AndAlso (Not SeenHyphen)) AndAlso _ + (((PatternIndex + 1) >= PatternEndIndex) OrElse (Pattern.Chars(PatternIndex + 1) <> "]"c)) Then + + SeenHyphen = True + + ElseIf p = "!"c AndAlso _ + (InsideBracket AndAlso Not SeenNot) Then + SeenNot = True + Match = True + + ElseIf p = "["c AndAlso (Not InsideBracket) Then + InsideBracket = True + StartRangeChar = NullChar + EndRangeChar = NullChar + SeenLiteral = False + + ElseIf p = "]"c AndAlso InsideBracket Then + InsideBracket = False + + If SeenLiteral Then + If Match Then + SourceIndex += 1 + If SourceIndex < SourceEndIndex Then + s = Source.Chars(SourceIndex) + End If + Else + Exit Do + End If + ElseIf SeenHyphen Then + If Not Match Then + Exit Do + End If + ElseIf SeenNot Then + '[!] should be matched to literal ! same as if outside brackets + If (ci.Compare("!", s) <> 0) Then + Exit Do + End If + SourceIndex += 1 + If SourceIndex < SourceEndIndex Then + s = Source.Chars(SourceIndex) + End If + End If + + Match = False + SeenLiteral = False + SeenNot = False + SeenHyphen = False + + Else + 'Literal character + SeenLiteral = True + LiteralIsRangeEnd = False + + If InsideBracket Then + If SeenHyphen Then + SeenHyphen = False + LiteralIsRangeEnd = True + EndRangeChar = p + + If StartRangeChar > EndRangeChar Then + Throw VbMakeException(vbErrors.BadPatStr) + ElseIf (SeenNot AndAlso Match) OrElse (Not SeenNot AndAlso Not Match) Then + 'Calls to ci.Compare are expensive, avoid them for good performance + If Options = CompareOptions.Ordinal Then + Match = (s > StartRangeChar) AndAlso (s <= EndRangeChar) + Else + Match = (ci.Compare(StartRangeChar, s, Options) < 0) AndAlso (ci.Compare(EndRangeChar, s, Options) >= 0) + End If + + If SeenNot Then + Match = Not Match + End If + End If + Else + StartRangeChar = p + + 'This compare handles non range chars such as the "abc" and "uvw" + 'and the first char of a range such as "d" in "[abcd-tuvw]". + Match = LikeStringCompare(ci, SeenNot, Match, p, s, Options) + End If + Else + If Options = CompareOptions.Ordinal Then + If p <> s AndAlso Not SeenNot Then + Exit Do + End If + Else + ' Slurp up the diacritical marks, if any (both non-spacing marks and modifier symbols) + ' Note that typically, we'll only have at most one diacritical mark. Therefore, I'm not + ' using StringBuilder here, since the minimal overhead of appending a character doesn't + ' justify invoking a couple of instances of StringBuilder.. + Dim pstr As String = p + Dim sstr As String = s + Do While PatternIndex + 1 < PatternEndIndex AndAlso _ + (UnicodeCategory.ModifierSymbol = Char.GetUnicodeCategory(Pattern.Chars(PatternIndex + 1)) OrElse _ + UnicodeCategory.NonSpacingMark = Char.GetUnicodeCategory(Pattern.Chars(PatternIndex + 1))) + pstr = pstr & Pattern.Chars(PatternIndex + 1) + PatternIndex = PatternIndex + 1 + Loop + Do While SourceIndex + 1 < SourceEndIndex AndAlso _ + (UnicodeCategory.ModifierSymbol = Char.GetUnicodeCategory(Source.Chars(SourceIndex + 1)) OrElse _ + UnicodeCategory.NonSpacingMark = Char.GetUnicodeCategory(Source.Chars(SourceIndex + 1))) + sstr = sstr & Source.Chars(SourceIndex + 1) + SourceIndex = SourceIndex + 1 + Loop + + If (ci.Compare(pstr, sstr, OptionCompareTextFlags) <> 0) AndAlso Not SeenNot Then + Exit Do + End If + End If + + SeenNot = False + SourceIndex += 1 + + If SourceIndex < SourceEndIndex Then + s = Source.Chars(SourceIndex) + ElseIf SourceIndex > SourceEndIndex Then + Return False + End If + End If + End If + + PatternIndex += 1 + Loop + + If InsideBracket Then + If SourceEndIndex = 0 Then + Return False + Else + Throw New ArgumentException(GetResourceString(ResID.Argument_InvalidValue1, "Pattern")) + End If + Else + Return (PatternIndex = PatternEndIndex) AndAlso (SourceIndex = SourceEndIndex) + End If + End Function + + Private Shared Function LikeStringCompareBinary(ByVal SeenNot As Boolean, ByVal Match As Boolean, ByVal p As Char, ByVal s As Char) As Boolean + If SeenNot AndAlso Match Then + Return p <> s + ElseIf Not SeenNot AndAlso Not Match Then + Return p = s + Else + Return Match + End If + End Function + + Private Shared Function LikeStringCompare(ByVal ci As CompareInfo, ByVal SeenNot As Boolean, ByVal Match As Boolean, ByVal p As Char, ByVal s As Char, ByVal Options As CompareOptions) As Boolean + If SeenNot AndAlso Match Then + If Options = CompareOptions.Ordinal Then + Return p <> s + Else + Return Not (ci.Compare(p, s, Options) = 0) + End If + ElseIf Not SeenNot AndAlso Not Match Then + If Options = CompareOptions.Ordinal Then + Return p = s + Else + Return (ci.Compare(p, s, Options) = 0) + End If + Else + Return Match + End If + End Function + + Private Shared Function AsteriskSkip(ByVal Pattern As String, ByVal Source As String, ByVal SourceEndIndex As Integer, _ + ByVal CompareOption As CompareMethod, ByVal ci As CompareInfo) As Integer + + 'Returns the number of source characters to skip over to handle an asterisk in the pattern. + 'When there's only a single asterisk in the pattern, it computes how many pattern equivalent chars + 'follow the *: [a-z], [abcde], ?, # each count as one char. + 'Pattern contains the substring following the * + 'Source contains the substring not yet matched. + + Dim p As Char + Dim SeenLiteral As Boolean + Dim SeenSpecial As Boolean 'Remembers if we've seen #, ?, [abd-eg], or ! when they have their special meanings + Dim InsideBracket As Boolean + Dim Count As Integer + Dim PatternEndIndex As Integer + Dim PatternIndex As Integer + Dim TruncatedPattern As String + Dim Options As CompareOptions + + PatternEndIndex = Len(Pattern) + + 'Determine how many pattern equivalent chars follow the *, and if there are multiple *s + '[a-z], [abcde] each count as one char. + Do While PatternIndex < PatternEndIndex + p = Pattern.Chars(PatternIndex) + + Select Case p + Case "*"c + If Count > 0 Then + 'We found multiple asterisks with an intervening pattern + If SeenSpecial Then + 'Pattern uses special characters which means we can't compute easily how far to skip. + Count = MultipleAsteriskSkip(Pattern, Source, Count, CompareOption) + Return SourceEndIndex - Count + Else + 'Pattern uses only literals, so we can directly search for the pattern in the source + 'TODO: Handle cases where pattern could be replicated in the source. + TruncatedPattern = Pattern.Substring(0, PatternIndex) 'Remove the second * and everything trailing + + If CompareOption = CompareMethod.Binary Then + Options = CompareOptions.Ordinal + Else + Options = CompareOptions.IgnoreCase Or CompareOptions.IgnoreWidth Or CompareOptions.IgnoreNonSpace Or CompareOptions.IgnoreKanaType + End If + + 'Count = Source.LastIndexOf(TruncatedPattern) + Count = ci.LastIndexOf(Source, TruncatedPattern, Options) + Return Count + End If + + Else + 'Do nothing, which colalesces multiple asterisks together + End If + + Case "-"c + If Pattern.Chars(PatternIndex + 1) = "]"c Then + SeenLiteral = True + End If + + Case "!"c + If Pattern.Chars(PatternIndex + 1) = "]"c Then + SeenLiteral = True + Else + SeenSpecial = True + End If + + Case "["c + If InsideBracket Then + SeenLiteral = True + Else + InsideBracket = True + End If + + Case "]"c + If SeenLiteral OrElse Not InsideBracket Then + Count += 1 + SeenSpecial = True + End If + SeenLiteral = False + InsideBracket = False + + Case "?"c, "#"c + If InsideBracket Then + SeenLiteral = True + Else + Count += 1 + SeenSpecial = True + End If + + Case Else + If InsideBracket Then + SeenLiteral = True + Else + Count += 1 + End If + End Select + + PatternIndex += 1 + Loop + + Return SourceEndIndex - Count + End Function + + Private Shared Function MultipleAsteriskSkip(ByVal Pattern As String, ByVal Source As String, ByVal Count As Integer, ByVal CompareOption As CompareMethod) As Integer + 'Multiple asterisks with intervening chars were found in the pattern, such as "**". + 'Use a recursive approach to determine how many source chars to skip. + 'Start near the end of Source and move backwards one char at a time until a match is found or we reach start of Source. + + Dim SourceEndIndex As Integer + Dim NewSource As String + Dim Result As Boolean + + SourceEndIndex = Len(Source) + + Do While Count < SourceEndIndex + NewSource = Source.Substring(SourceEndIndex - Count) + + Try + Result = LikeString(NewSource, Pattern, CompareOption) + Catch ex As StackOverflowException + Throw ex + Catch ex As OutOfMemoryException + Throw ex + Catch ex As System.Threading.ThreadAbortException + Throw ex + Catch + Result = False + End Try + + If Result Then + Exit Do + End If + + Count += 1 + Loop + + Return Count + End Function + +#End If + +#End Region + +#Region " Operator Concatenate & " + + Public Shared Function ConcatenateObject(ByVal Left As Object, ByVal Right As Object) As Object + Dim conv1, conv2 As IConvertible + Dim tc1, tc2 As TypeCode + + conv1 = TryCast(Left, IConvertible) + If conv1 Is Nothing Then + If Left Is Nothing Then + tc1 = TypeCode.Empty + Else + tc1 = TypeCode.Object + End If + Else + tc1 = conv1.GetTypeCode() + End If + + conv2 = TryCast(Right, IConvertible) + If conv2 Is Nothing Then + If Right Is Nothing Then + tc2 = TypeCode.Empty + Else + tc2 = TypeCode.Object + End If + Else + tc2 = conv2.GetTypeCode() + End If + + 'Special cases for Char() + If (tc1 = TypeCode.Object) AndAlso (TypeOf Left Is Char()) Then + tc1 = TypeCode.String + End If + + If (tc2 = TypeCode.Object) AndAlso (TypeOf Right Is Char()) Then + tc2 = TypeCode.String + End If + + If tc1 = TypeCode.Object OrElse tc2 = TypeCode.Object Then + Return InvokeUserDefinedOperator(UserDefinedOperator.Concatenate, Left, Right) + End If + + Dim LeftIsNull As Boolean = (tc1 = TypeCode.DBNull) + Dim RightIsNull As Boolean = (tc2 = TypeCode.DBNull) + + If LeftIsNull And RightIsNull Then + Return Left + ElseIf LeftIsNull And Not RightIsNull Then + Left = "" + ElseIf RightIsNull And Not LeftIsNull Then + Right = "" + End If + + Return CStr(Left) & CStr(Right) + End Function + +#End Region + + End Class + +End Namespace + diff --git a/Microsoft.VisualBasic/runtime/msvbalib/Helpers/OverloadResolution.vb b/Microsoft.VisualBasic/runtime/msvbalib/Helpers/OverloadResolution.vb new file mode 100644 index 000000000..46b0566a9 --- /dev/null +++ b/Microsoft.VisualBasic/runtime/msvbalib/Helpers/OverloadResolution.vb @@ -0,0 +1,2964 @@ +' Copyright (c) Microsoft Corporation. All rights reserved. + +Option Strict On + +Imports System +Imports System.Reflection +Imports System.Collections.Generic +Imports System.Diagnostics +Imports System.Text + +Imports Microsoft.VisualBasic.CompilerServices.Symbols +Imports Microsoft.VisualBasic.CompilerServices.ConversionResolution +Imports Microsoft.VisualBasic.CompilerServices.Utils + +#Const BINDING_LOG = False +#Const GENERICITY_LOG = False + +Namespace Microsoft.VisualBasic.CompilerServices + + Friend Class OverloadResolution + ' Prevent creation. + Private Sub New() + End Sub + + Friend Enum ResolutionFailure + None + MissingMember + InvalidArgument + AmbiguousMatch + InvalidTarget + End Enum + + + 'perhaps this could go into the Symbols utility module + Private Shared Function IsExactSignatureMatch( _ + ByVal LeftSignature As ParameterInfo(), _ + ByVal LeftTypeParameterCount As Integer, _ + ByVal RightSignature As ParameterInfo(), _ + ByVal RightTypeParameterCount As Integer) As Boolean + + Dim LongerSignature As ParameterInfo() + Dim ShorterSignature As ParameterInfo() + + If LeftSignature.Length >= RightSignature.Length Then + LongerSignature = LeftSignature + ShorterSignature = RightSignature + Else + LongerSignature = RightSignature + ShorterSignature = LeftSignature + End If + + 'If the signatures differ in length, then the extra parameters of the + 'longer signature must all be optional to be an exact match. + + For Index As Integer = ShorterSignature.Length To LongerSignature.Length - 1 + If Not LongerSignature(Index).IsOptional Then + Return False + End If + Next + + For i As Integer = 0 To ShorterSignature.Length - 1 + + Dim Type1 As Type = ShorterSignature(i).ParameterType + Dim Type2 As Type = LongerSignature(i).ParameterType + + If Type1.IsByRef Then Type1 = Type1.GetElementType + If Type2.IsByRef Then Type2 = Type2.GetElementType + + If Type1 IsNot Type2 AndAlso _ + (Not ShorterSignature(i).IsOptional OrElse _ + Not LongerSignature(i).IsOptional) Then + Return False + End If + Next + + Return True + End Function + + Private Enum ComparisonType + ParameterSpecificty + GenericSpecificityBasedOnMethodGenericParams + GenericSpecificityBasedOnTypeGenericParams + End Enum + + Private Shared Sub CompareNumericTypeSpecificity( _ + ByVal LeftType As Type, _ + ByVal RightType As Type, _ + ByRef LeftWins As Boolean, _ + ByRef RightWins As Boolean) + + 'This function implements the notion that signed types are + 'preferred over unsigned types during overload resolution. + + Debug.Assert(IsNumericType(LeftType) AndAlso Not IsEnum(LeftType) AndAlso _ + IsNumericType(RightType) AndAlso Not IsEnum(RightType), _ + "expected only numerics here. : #12/10/2003#") + + If LeftType Is RightType Then + 'Do nothing since neither wins. + Else + Debug.Assert(GetTypeCode(LeftType) <> GetTypeCode(RightType), _ + "this should have been caught above") + + If NumericSpecificityRank(GetTypeCode(LeftType)) < _ + NumericSpecificityRank(GetTypeCode(RightType)) Then + LeftWins = True + Else + RightWins = True + End If + End If + + Return + End Sub + + Private Shared Sub CompareParameterSpecificity( _ + ByVal ArgumentType As Type, _ + ByVal LeftParameter As ParameterInfo, _ + ByVal LeftProcedure As MethodBase, _ + ByVal ExpandLeftParamArray As Boolean, _ + ByVal RightParameter As ParameterInfo, _ + ByVal RightProcedure As MethodBase, _ + ByVal ExpandRightParamArray As Boolean, _ + ByRef LeftWins As Boolean, _ + ByRef RightWins As Boolean, _ + ByRef BothLose As Boolean) + + + BothLose = False + Dim LeftType As Type = LeftParameter.ParameterType + Dim RightType As Type = RightParameter.ParameterType + + If LeftType.IsByRef Then LeftType = GetElementType(LeftType) + If RightType.IsByRef Then RightType = GetElementType(RightType) + + 'UNDONE: don't use IsParamArray -- for speed, pass that in as a parameter. + If ExpandLeftParamArray AndAlso IsParamArray(LeftParameter) Then + LeftType = GetElementType(LeftType) + End If + + 'UNDONE: don't use IsParamArray -- for speed, pass that in as a parameter. + If ExpandRightParamArray AndAlso IsParamArray(RightParameter) Then + RightType = GetElementType(RightType) + End If + + If IsNumericType(LeftType) AndAlso IsNumericType(RightType) AndAlso _ + Not IsEnum(LeftType) AndAlso Not IsEnum(RightType) Then + + CompareNumericTypeSpecificity(LeftType, RightType, LeftWins, RightWins) + Return + End If + + 'If both the types are different only by generic method type parameters + 'with the same index position, then treat as identity. + + If LeftProcedure IsNot Nothing AndAlso _ + RightProcedure IsNot Nothing AndAlso _ + IsRawGeneric(LeftProcedure) AndAlso _ + IsRawGeneric(RightProcedure) Then + + If LeftType Is RightType Then Return 'Check this first--shortcut. + + Dim LeftIndex As Integer = IndexIn(LeftType, LeftProcedure) + Dim RightIndex As Integer = IndexIn(RightType, RightProcedure) + + If LeftIndex = RightIndex AndAlso LeftIndex >= 0 Then Return + End If + + Dim OperatorMethod As Method = Nothing + Dim LeftToRight As ConversionClass = ClassifyConversion(RightType, LeftType, OperatorMethod) + + If LeftToRight = ConversionClass.Identity Then Return + + If LeftToRight = ConversionClass.Widening Then + + If OperatorMethod IsNot Nothing AndAlso _ + ClassifyConversion(LeftType, RightType, OperatorMethod) = ConversionClass.Widening Then + + ' Although W<-->W conversions don't exist in the set of predefined conversions, + ' it can occur with user-defined conversions. If the two param types widen to each other + ' (necessarily by using user-defined conversions), and the argument type is known and + ' is identical to one of the parameter types, then that parameter wins. Otherwise, + ' if the arugment type is not specified, we can't make a decision and both lose. + + If ArgumentType IsNot Nothing AndAlso ArgumentType Is LeftType Then + LeftWins = True + Return + End If + + If ArgumentType IsNot Nothing AndAlso ArgumentType Is RightType Then + RightWins = True + Return + End If + + BothLose = True + Return + + End If + + LeftWins = True + Return + End If + + Dim RightToLeft As ConversionClass = ClassifyConversion(LeftType, RightType, OperatorMethod) + + If RightToLeft = ConversionClass.Widening Then + RightWins = True + Return + End If + + BothLose = True + Return + + End Sub + + Private Shared Sub CompareGenericityBasedOnMethodGenericParams( _ + ByVal LeftParameter As ParameterInfo, _ + ByVal RawLeftParameter As ParameterInfo, _ + ByVal LeftMember As Method, _ + ByVal ExpandLeftParamArray As Boolean, _ + ByVal RightParameter As ParameterInfo, _ + ByVal RawRightParameter As ParameterInfo, _ + ByVal RightMember As Method, _ + ByVal ExpandRightParamArray As Boolean, _ + ByRef LeftIsLessGeneric As Boolean, _ + ByRef RightIsLessGeneric As Boolean, _ + ByRef SignatureMismatch As Boolean) + + If Not LeftMember.IsMethod OrElse Not RightMember.IsMethod Then + Return + End If + + Dim LeftType As Type = LeftParameter.ParameterType + Dim RightType As Type = RightParameter.ParameterType + + 'Since generic methods are instantiated by this point, the parameter + 'types are bound. However, we need to compare against the unbound types. + Dim RawLeftType As Type = RawLeftParameter.ParameterType + Dim RawRightType As Type = RawRightParameter.ParameterType + + If LeftType.IsByRef Then + LeftType = GetElementType(LeftType) + RawLeftType = GetElementType(RawLeftType) + End If + + If RightType.IsByRef Then + RightType = GetElementType(RightType) + RawRightType = GetElementType(RawRightType) + End If + + 'UNDONE: don't use IsParamArray -- for speed, pass that in as a parameter. + If ExpandLeftParamArray AndAlso IsParamArray(LeftParameter) Then + LeftType = GetElementType(LeftType) + RawLeftType = GetElementType(RawLeftType) + End If + + 'UNDONE: don't use IsParamArray -- for speed, pass that in as a parameter. + If ExpandRightParamArray AndAlso IsParamArray(RightParameter) Then + RightType = GetElementType(RightType) + RawRightType = GetElementType(RawRightType) + End If +#If TELESTO Then + If LeftType IsNot RightType Then +#Else + 'Need to check type equivalency for the NoPIA case + If LeftType IsNot RightType AndAlso Not IsEquivalentType(LeftType, RightType) Then +#End If + 'The signatures of the two methods are not identical and so the "least generic" rule + 'does not apply. + SignatureMismatch = True + Return + End If + + Dim LeftProcedure As MethodBase = LeftMember.AsMethod + Dim RightProcedure As MethodBase = RightMember.AsMethod + + If IsGeneric(LeftProcedure) Then LeftProcedure = DirectCast(LeftProcedure, MethodInfo).GetGenericMethodDefinition + If IsGeneric(RightProcedure) Then RightProcedure = DirectCast(RightProcedure, MethodInfo).GetGenericMethodDefinition + + ' Only references to generic parameters of the procedures count. For the purpose of this + ' function, references to generic parameters of a type do not make a procedure more generic. + + If RefersToGenericParameter(RawLeftType, LeftProcedure) Then + If Not RefersToGenericParameter(RawRightType, RightProcedure) Then + RightIsLessGeneric = True + End If + ElseIf RefersToGenericParameter(RawRightType, RightProcedure) Then + If Not RefersToGenericParameter(RawLeftType, LeftProcedure) Then + LeftIsLessGeneric = True + End If + End If + + End Sub + + Private Shared Sub CompareGenericityBasedOnTypeGenericParams( _ + ByVal LeftParameter As ParameterInfo, _ + ByVal RawLeftParameter As ParameterInfo, _ + ByVal LeftMember As Method, _ + ByVal ExpandLeftParamArray As Boolean, _ + ByVal RightParameter As ParameterInfo, _ + ByVal RawRightParameter As ParameterInfo, _ + ByVal RightMember As Method, _ + ByVal ExpandRightParamArray As Boolean, _ + ByRef LeftIsLessGeneric As Boolean, _ + ByRef RightIsLessGeneric As Boolean, _ + ByRef SignatureMismatch As Boolean) + + Dim LeftType As Type = LeftParameter.ParameterType + Dim RightType As Type = RightParameter.ParameterType + + 'Since generic methods are instantiated by this point, the parameter + 'types are bound. However, we need to compare against the unbound types. + Dim RawLeftType As Type = RawLeftParameter.ParameterType + Dim RawRightType As Type = RawRightParameter.ParameterType + + If LeftType.IsByRef Then + LeftType = GetElementType(LeftType) + RawLeftType = GetElementType(RawLeftType) + End If + + If RightType.IsByRef Then + RightType = GetElementType(RightType) + RawRightType = GetElementType(RawRightType) + End If + + 'UNDONE: don't use IsParamArray -- for speed, pass that in as a parameter. + If ExpandLeftParamArray AndAlso IsParamArray(LeftParameter) Then + LeftType = GetElementType(LeftType) + RawLeftType = GetElementType(RawLeftType) + End If + + 'UNDONE: don't use IsParamArray -- for speed, pass that in as a parameter. + If ExpandRightParamArray AndAlso IsParamArray(RightParameter) Then + RightType = GetElementType(RightType) + RawRightType = GetElementType(RawRightType) + End If + + 'Need to check type equivalency for the NoPIA case +#If TELESTO Then + If LeftType IsNot RightType Then +#Else + If LeftType IsNot RightType AndAlso Not IsEquivalentType(LeftType, RightType) Then +#End If + 'The signatures of the two methods are not identical and so the "least generic" rule + 'does not apply. + SignatureMismatch = True + Return + End If + + ' Only references to generic parameters of the generic types count. For the purpose of this + ' function, references to generic parameters of a method do not make a procedure more generic. + ' + Dim LeftDeclaringType As Type = LeftMember.RawDeclaringType + Dim RightDeclaringType As Type = RightMember.RawDeclaringType + +#If GENERICITY_LOG Then + Console.Writeline("----------CompareGenericityBasedOnTypeGenericParams---------") + Console.Writeline("LeftType: " & LeftType.MetaDataToken & " - " & LeftType.ToString()) + Console.Writeline("LeftRawType: " & RawLeftType.MetaDataToken & " - " & RawLeftType.ToString()) + Console.Writeline("LeftDeclaringType: " & LeftDeclaringType.MetaDataToken & " - " & LeftDeclaringType.ToString()) + Console.Writeline("RightType: " & RightType.MetaDataToken & " - " & RightType.ToString()) + Console.Writeline("RightRawType: " & RawRightType.MetaDataToken & " - " & RawRightType.ToString()) + Console.Writeline("RightDeclaringType: " & RightDeclaringType.MetaDataToken & " - " & RightDeclaringType.ToString()) +#End If + + If RefersToGenericParameterCLRSemantics(RawLeftType, LeftDeclaringType) Then + If Not RefersToGenericParameterCLRSemantics(RawRightType, RightDeclaringType) Then + RightIsLessGeneric = True + End If + ElseIf RefersToGenericParameterCLRSemantics(RawRightType, RightDeclaringType) Then + LeftIsLessGeneric = True + End If + End Sub + + Private Shared Function LeastGenericProcedure( _ + ByVal Left As Method, _ + ByVal Right As Method, _ + ByVal CompareGenericity As ComparisonType, _ + ByRef SignatureMismatch As Boolean) As Method + + Dim LeftWinsAtLeastOnce As Boolean = False + Dim RightWinsAtLeastOnce As Boolean = False + SignatureMismatch = False + + If Not Left.IsMethod OrElse Not Right.IsMethod Then Return Nothing + + Dim ParamIndex As Integer = 0 + Dim LeftParamsLen As Integer = Left.Parameters.Length + Dim RightParamsLen As Integer = Right.Parameters.Length + + Do While ParamIndex < LeftParamsLen AndAlso ParamIndex < RightParamsLen + + Select Case CompareGenericity + Case ComparisonType.GenericSpecificityBasedOnMethodGenericParams + + CompareGenericityBasedOnMethodGenericParams( _ + Left.Parameters(ParamIndex), _ + Left.RawParameters(ParamIndex), _ + Left, _ + Left.ParamArrayExpanded, _ + Right.Parameters(ParamIndex), _ + Right.RawParameters(ParamIndex), _ + Right, _ + False, _ + LeftWinsAtLeastOnce, _ + RightWinsAtLeastOnce, _ + SignatureMismatch) + + Case ComparisonType.GenericSpecificityBasedOnTypeGenericParams + + CompareGenericityBasedOnTypeGenericParams( _ + Left.Parameters(ParamIndex), _ + Left.RawParameters(ParamIndex), _ + Left, _ + Left.ParamArrayExpanded, _ + Right.Parameters(ParamIndex), _ + Right.RawParameters(ParamIndex), _ + Right, _ + False, _ + LeftWinsAtLeastOnce, _ + RightWinsAtLeastOnce, _ + SignatureMismatch) + + Case Else +#If TELESTO Then + Debug.Assert(False, "Unexpected comparison type!!!") ' Silverlight CLR does not have Debug.Fail. +#Else + Debug.Fail("Unexpected comparison type!!!") +#End If + End Select + + If SignatureMismatch OrElse (LeftWinsAtLeastOnce AndAlso RightWinsAtLeastOnce) Then + Return Nothing + End If + + ParamIndex += 1 + Loop + + Debug.Assert(Not (LeftWinsAtLeastOnce AndAlso RightWinsAtLeastOnce), _ + "Least generic method logic is confused.") + + If ParamIndex < LeftParamsLen OrElse ParamIndex < RightParamsLen Then + 'The procedures have different numbers of parameters, and so don't have matching signatures. + Return Nothing + End If + + If LeftWinsAtLeastOnce Then + Return Left + End If + + If RightWinsAtLeastOnce Then + Return Right + End If + + Return Nothing + End Function + + Friend Shared Function LeastGenericProcedure( _ + ByVal Left As Method, _ + ByVal Right As Method) As Method + + If Not (Left.IsGeneric OrElse _ + Right.IsGeneric OrElse _ + IsGeneric(Left.DeclaringType) OrElse _ + IsGeneric(Right.DeclaringType)) Then + Return Nothing + End If + + Dim SignatureMismatch As Boolean = False + + Dim LeastGeneric As Method = _ + LeastGenericProcedure( _ + Left, _ + Right, _ + ComparisonType.GenericSpecificityBasedOnMethodGenericParams, _ + SignatureMismatch) + + If LeastGeneric Is Nothing AndAlso Not SignatureMismatch Then + + LeastGeneric = _ + LeastGenericProcedure( _ + Left, _ + Right, _ + ComparisonType.GenericSpecificityBasedOnTypeGenericParams, _ + SignatureMismatch) + End If + + Return LeastGeneric + + End Function + + Private Shared Sub InsertIfMethodAvailable( _ + ByVal NewCandidate As MemberInfo, _ + ByVal NewCandidateSignature As ParameterInfo(), _ + ByVal NewCandidateParamArrayIndex As Integer, _ + ByVal ExpandNewCandidateParamArray As Boolean, _ + ByVal Arguments As Object(), _ + ByVal ArgumentCount As Integer, _ + ByVal ArgumentNames As String(), _ + ByVal TypeArguments As Type(), _ + ByVal CollectOnlyOperators As Boolean, _ + ByVal Candidates As List(Of Method), + ByVal BaseReference As Container) + + 'Note that Arguments, ArgumentNames and TypeNames will be nothing when collecting operators. + ' + Debug.Assert(Arguments Is Nothing OrElse Arguments.Length = ArgumentCount, "Inconsistency in arguments!!!") + + Dim NewCandidateNode As Method = Nothing + + 'If we're collecting only operators, then hiding by name and signature doesn't apply (neither do + 'ParamArrays), so skip all of this logic. + If Not CollectOnlyOperators Then + + Dim NewCandidateMethod As MethodBase = TryCast(NewCandidate, MethodBase) + Dim InferenceFailedForNewCandidate As Boolean = False + + ' Note that operators cannot be generic methods + ' + ' Need to complete type argument inference for generic methods when no type arguments + ' have been supplied and when type arguments are supplied, the generic method needs to + ' to be instantiated. Need to complete this so early in the overload process so that + ' hid-by-sig, paramarray disambiguation etc. are done using the substitued signature. + ' + If NewCandidate.MemberType = MemberTypes.Method AndAlso IsRawGeneric(NewCandidateMethod) Then + + NewCandidateNode = _ + New Method( _ + NewCandidateMethod, _ + NewCandidateSignature, _ + NewCandidateParamArrayIndex, _ + ExpandNewCandidateParamArray) + + ' Inferring of type arguments is done when when determining the callability of this + ' procedure with these arguments by comparing them against the corresponding parameters. + ' + ' Note that although RejectUncallableProcedure needs to be invoked on the non-generics + ' candidates too, it is not done here because some of the candidates might be rejected + ' for various reasons like hide-by-sig, paramarray disambiguation, etc. and RejectUncall- + ' -ableProcedure would not have to be invoked for them. This is especially important + ' because the RejectUncallableProcedure task is expensive. + ' + + RejectUncallableProcedure( _ + NewCandidateNode, _ + Arguments, _ + ArgumentNames, _ + TypeArguments) + + + ' Get the instantiated method for this candidate + ' + NewCandidate = NewCandidateNode.AsMethod + NewCandidateSignature = NewCandidateNode.Parameters + + End If + + ' Verify if TypeInference succeeded. This should only happen for Methods + If NewCandidate IsNot Nothing AndAlso _ + NewCandidate.MemberType = MemberTypes.Method AndAlso _ + IsRawGeneric(TryCast(NewCandidate, MethodBase)) Then + InferenceFailedForNewCandidate = True + End If + + For Index As Integer = 0 To Candidates.Count - 1 + + Dim Existing As Method = Candidates.Item(Index) + If Existing Is Nothing Then Continue For 'This item was killed earlier, so skip it. + + Dim ExistingCandidateSignature As ParameterInfo() = Existing.Parameters + Dim ExistingCandidate As MethodBase + If Existing.IsMethod Then ExistingCandidate = Existing.AsMethod Else ExistingCandidate = Nothing + + If NewCandidate = Existing Then Continue For 'UNDONE how is this possible? + + Dim NewCandidateParameterIndex As Integer = 0 + Dim ExistingCandidateParameterIndex As Integer = 0 + + For CurrentArgument As Integer = 1 To ArgumentCount + + Dim BothLose As Boolean = False + Dim NewCandidateWins As Boolean = False + Dim ExistingCandidateWins As Boolean = False + + CompareParameterSpecificity( _ + Nothing, _ + NewCandidateSignature(NewCandidateParameterIndex), _ + NewCandidateMethod, _ + ExpandNewCandidateParamArray, _ + ExistingCandidateSignature(ExistingCandidateParameterIndex), _ + ExistingCandidate, _ + Existing.ParamArrayExpanded, _ + NewCandidateWins, _ + ExistingCandidateWins, _ + BothLose) + + If BothLose Or NewCandidateWins Or ExistingCandidateWins Then + GoTo continueloop + End If + + 'If a parameter is a param array, there is no next parameter and so advancing + 'through the parameter list is bad. + + If NewCandidateParameterIndex <> NewCandidateParamArrayIndex OrElse Not ExpandNewCandidateParamArray Then + NewCandidateParameterIndex += 1 + End If + + If ExistingCandidateParameterIndex <> Existing.ParamArrayIndex OrElse Not Existing.ParamArrayExpanded Then + ExistingCandidateParameterIndex += 1 + End If + + Next + + 'UNDONE: the call to GetTypeParameters will create a cloned array instance each time. Fix this perf issue. + Dim ExactSignature As Boolean = _ + IsExactSignatureMatch( _ + NewCandidateSignature, _ + GetTypeParameters(NewCandidate).Length, _ + Existing.Parameters, _ + Existing.TypeParameters.Length) + + If Not ExactSignature Then + + ' If inference failed for any of the candidates, then don't compare them. + ' + ' This simple strategy besides fixing the problems associated with an inference + ' failed candidate beating a inference passing candidate also helps with better + ' error reporting by showing the inference failed candidates too. + ' + If InferenceFailedForNewCandidate OrElse _ + (ExistingCandidate IsNot Nothing AndAlso IsRawGeneric(ExistingCandidate)) Then + Continue For + End If + + + If Not ExpandNewCandidateParamArray AndAlso Existing.ParamArrayExpanded Then + 'Delete current item from list and continue. + Candidates.Item(Index) = Nothing + Continue For + + ElseIf ExpandNewCandidateParamArray AndAlso Not Existing.ParamArrayExpanded Then + Return + + ElseIf Not ExpandNewCandidateParamArray AndAlso Not Existing.ParamArrayExpanded Then + 'In theory, this shouldn't happen, but another language could + 'theoretically define two methods with optional arguments that + 'end up being equivalent. So don't prefer one over the other. + Continue For + + Else + 'If both are expanded, then see if one uses more on actual + 'parameters than the other. If so, we prefer the one that uses + 'more on actual parameters. + + If (NewCandidateParameterIndex > ExistingCandidateParameterIndex) Then + 'Delete current item from list and continue. + Candidates.Item(Index) = Nothing + Continue For + + ElseIf ExistingCandidateParameterIndex > NewCandidateParameterIndex Then + Return + + End If + + Continue For + + End If + Else + Debug.Assert((BaseReference IsNot Nothing AndAlso BaseReference.IsWindowsRuntimeObject) OrElse + IsOrInheritsFrom(Existing.DeclaringType, NewCandidate.DeclaringType), _ + "expected inheritance or WinRT collection types here") + + If NewCandidate.DeclaringType Is Existing.DeclaringType Then + 'If the two members are declared in the same container, both should be added to the set + 'of overloads. This results in intelligent ambiguity error messages. + Exit For + + End If + + ' If the base container is a WinRT object implementing collection interfaces they could have + ' the same method name with the same signature. We need to add them to the set to throw + ' ambiguity errors. + If BaseReference IsNot Nothing AndAlso BaseReference.IsWindowsRuntimeObject() AndAlso + Symbols.IsCollectionInterface(NewCandidate.DeclaringType) AndAlso + Symbols.IsCollectionInterface(Existing.DeclaringType) Then + Exit For + End If + + 'If inference did not fail for the base candidate, but failed for the derived candidate, then + 'the derived candidate cannot hide the base candidate. VSWhidbey Bug 369042. + ' + If Not InferenceFailedForNewCandidate AndAlso _ + (ExistingCandidate IsNot Nothing AndAlso IsRawGeneric(ExistingCandidate)) Then + Continue For + End If + + Return + + End If + +#If TELESTO Then + Debug.Assert(False,"unexpected code path") +#Else + Debug.Fail("unexpected code path") +#End If + +continueloop: + Next + End If + + If NewCandidateNode IsNot Nothing Then + Candidates.Add(NewCandidateNode) + ElseIf NewCandidate.MemberType = MemberTypes.Property Then + Candidates.Add( _ + New Method( _ + DirectCast(NewCandidate, PropertyInfo), _ + NewCandidateSignature, _ + NewCandidateParamArrayIndex, _ + ExpandNewCandidateParamArray)) + Else + Candidates.Add( _ + New Method( _ + DirectCast(NewCandidate, MethodBase), _ + NewCandidateSignature, _ + NewCandidateParamArrayIndex, _ + ExpandNewCandidateParamArray)) + End If + + End Sub + + Friend Shared Function CollectOverloadCandidates( _ + ByVal Members As MemberInfo(), _ + ByVal Arguments As Object(), _ + ByVal ArgumentCount As Integer, _ + ByVal ArgumentNames As String(), _ + ByVal TypeArguments As Type(), _ + ByVal CollectOnlyOperators As Boolean, _ + ByVal TerminatingScope As System.Type, _ + ByRef RejectedForArgumentCount As Integer, _ + ByRef RejectedForTypeArgumentCount As Integer, + ByVal BaseReference As Container) As List(Of Method) + + + 'Note that Arguments, ArgumentNames and TypeNames will be nothing when collecting operators. + ' + Debug.Assert(Arguments Is Nothing OrElse Arguments.Length = ArgumentCount, "Inconsistency in arguments!!!") + + Dim TypeArgumentCount As Integer = 0 + If TypeArguments IsNot Nothing Then + TypeArgumentCount = TypeArguments.Length + End If + + Dim Candidates As List(Of Method) = New List(Of Method)(Members.Length) + + If Members.Length = 0 Then + Return Candidates + End If + + Dim KeepSearching As Boolean = True + Dim Index As Integer = 0 + + Do + Dim CurrentScope As Type = Members(Index).DeclaringType + + 'The terminating scope parameter controls at which point candidate collection + 'will stop. This is useful for overloaded operator resolution where the left + 'and right operands may have a common ancestor and we wish to collect the common + 'candidates only once. + If TerminatingScope IsNot Nothing AndAlso IsOrInheritsFrom(TerminatingScope, CurrentScope) Then Exit Do + Do + Dim Candidate As MemberInfo = Members(Index) + Dim CandidateSignature As ParameterInfo() = Nothing + Dim TypeParameterCount As Integer = 0 + + Select Case Candidate.MemberType + + Case MemberTypes.Constructor, _ + MemberTypes.Method + + Dim CurrentMethod As MethodBase = DirectCast(Candidate, MethodBase) + + If CollectOnlyOperators AndAlso Not IsUserDefinedOperator(CurrentMethod) Then + GoTo nextcandidate + End If + + CandidateSignature = CurrentMethod.GetParameters + TypeParameterCount = GetTypeParameters(CurrentMethod).Length + + If IsShadows(CurrentMethod) Then KeepSearching = False + + Case MemberTypes.Property + + If CollectOnlyOperators Then GoTo nextcandidate + + Dim PropertyBlock As PropertyInfo = DirectCast(Candidate, PropertyInfo) + Dim GetMethod As MethodInfo = PropertyBlock.GetGetMethod + + If GetMethod IsNot Nothing Then + CandidateSignature = GetMethod.GetParameters + + Debug.Assert(PropertyBlock.GetSetMethod Is Nothing OrElse _ + IsShadows(PropertyBlock.GetSetMethod) = IsShadows(GetMethod), _ + "unexpected mismatched shadows on accessors") + If IsShadows(GetMethod) Then KeepSearching = False + + Else + Dim SetMethod As MethodInfo = PropertyBlock.GetSetMethod + Debug.Assert(SetMethod IsNot Nothing, "must have set here") + + Dim SetParameters As ParameterInfo() = SetMethod.GetParameters + CandidateSignature = New ParameterInfo(SetParameters.Length - 2) {} + System.Array.Copy(SetParameters, CandidateSignature, CandidateSignature.Length) + + If IsShadows(SetMethod) Then KeepSearching = False + + End If + + Case MemberTypes.Custom, _ + MemberTypes.Event, _ + MemberTypes.Field, _ + MemberTypes.TypeInfo, _ + MemberTypes.NestedType + + 'All of these items automatically shadow. + If Not CollectOnlyOperators Then + KeepSearching = False + End If + GoTo nextcandidate + + Case Else +#If TELESTO Then + Debug.Assert(False, "what is this? just ignore it.") ' Silverlight CLR does not have Debug.Fail. +#Else + Debug.Fail("what is this? just ignore it.") +#End If + GoTo nextcandidate + + End Select + + + 'We have a possible candidate method if we make it this far. Insert it into the + 'list if it qualifies. + + Debug.Assert(CandidateSignature IsNot Nothing, "must have signature if we have a method") + + Dim RequiredParameterCount As Integer = 0 + Dim MaximumParameterCount As Integer = 0 + Dim ParamArrayIndex As Integer = -1 + + 'Weed out procedures that cannot accept the number of supplied arguments. + + GetAllParameterCounts(CandidateSignature, RequiredParameterCount, MaximumParameterCount, ParamArrayIndex) + + Dim HasParamArray As Boolean = ParamArrayIndex >= 0 + If ArgumentCount < RequiredParameterCount OrElse _ + (Not HasParamArray AndAlso ArgumentCount > MaximumParameterCount) Then + RejectedForArgumentCount += 1 + GoTo nextcandidate + End If + + 'If type arguments have been supplied, weed out procedures that don't have an + 'appropriate number of type parameters. + + If TypeArgumentCount > 0 AndAlso TypeArgumentCount <> TypeParameterCount Then + RejectedForTypeArgumentCount += 1 + GoTo nextcandidate + End If + + ' A method with a paramarray can be considered in two forms: in an + ' expanded form or in an unexpanded form (i.e. as if the paramarray + ' decoration was not specified). Weirdly, it can apply in both forms, as + ' in the case of passing Object() to ParamArray x As Object() (because + ' Object() converts to both Object() and Object). + + ' Does the method apply in its unexpanded form? This can only happen if + ' either there is no paramarray or if the argument count matches exactly + ' (if it's less, then the paramarray is expanded to nothing, if it's more, + ' it's expanded to one or more parameters). + + If Not HasParamArray OrElse ArgumentCount = MaximumParameterCount Then + InsertIfMethodAvailable( _ + Candidate, _ + CandidateSignature, _ + ParamArrayIndex, _ + False, _ + Arguments, _ + ArgumentCount, _ + ArgumentNames, _ + TypeArguments, _ + CollectOnlyOperators, _ + Candidates, + BaseReference) + End If + + 'How about it's expanded form? It always applies if there's a paramarray. + + If HasParamArray Then + Debug.Assert(Not CollectOnlyOperators, "didn't expect operator with paramarray") + InsertIfMethodAvailable( _ + Candidate, _ + CandidateSignature, _ + ParamArrayIndex, _ + True, _ + Arguments, _ + ArgumentCount, _ + ArgumentNames, _ + TypeArguments, _ + CollectOnlyOperators, _ + Candidates, + BaseReference) + End If + +nextcandidate: + Index += 1 + + Loop While Index < Members.Length AndAlso Members(Index).DeclaringType Is CurrentScope + + Loop While KeepSearching AndAlso Index < Members.Length + +#If BINDING_LOG Then + Console.WriteLine("== COLLECTION AND SHADOWING ==") + For Each item As Method In Candidates + If item Is Nothing Then + Console.WriteLine("dead") + Else + Console.WriteLine(item.DumpContents) + End If + Next +#End If + + 'Remove the dead entries from the list--simplifies code later on. + 'CONSIDER: this costs time, but dead entires should be relatively rare. + Index = 0 + While Index < Candidates.Count + If Candidates(Index) Is Nothing Then + Dim Span As Integer = Index + 1 + While Span < Candidates.Count AndAlso Candidates(Span) Is Nothing + Span += 1 + End While + Candidates.RemoveRange(Index, Span - Index) + End If + Index += 1 + End While + + Return Candidates + End Function + + Private Shared Function CanConvert( _ + ByVal TargetType As Type, _ + ByVal SourceType As Type, _ + ByVal RejectNarrowingConversion As Boolean, _ + ByVal Errors As List(Of String), _ + ByVal ParameterName As String, _ + ByVal IsByRefCopyBackContext As Boolean, _ + ByRef RequiresNarrowingConversion As Boolean, _ + ByRef AllNarrowingIsFromObject As Boolean) As Boolean + + Dim ConversionResult As ConversionClass = ClassifyConversion(TargetType, SourceType, Nothing) + + Select Case ConversionResult + + Case ConversionClass.Identity, ConversionClass.Widening + Return True + + Case ConversionClass.Narrowing + + If RejectNarrowingConversion Then + If Errors IsNot Nothing Then + ReportError( _ + Errors, _ + IIf(IsByRefCopyBackContext, ResID.ArgumentNarrowingCopyBack3, ResID.ArgumentNarrowing3), _ + ParameterName, _ + SourceType, _ + TargetType) + End If + + Return False + Else + RequiresNarrowingConversion = True + If SourceType IsNot GetType(Object) Then AllNarrowingIsFromObject = False + + Return True + End If + + End Select + + If Errors IsNot Nothing Then + ReportError( _ + Errors, _ + IIf(ConversionResult = ConversionClass.Ambiguous, _ + IIf(IsByRefCopyBackContext, _ + ResID.ArgumentMismatchAmbiguousCopyBack3, _ + ResID.ArgumentMismatchAmbiguous3), _ + IIf(IsByRefCopyBackContext, _ + ResID.ArgumentMismatchCopyBack3, _ + ResID.ArgumentMismatch3)), _ + ParameterName, _ + SourceType, _ + TargetType) + End If + + Return False + End Function + + Private Shared Function InferTypeArgumentsFromArgument( _ + ByVal ArgumentType As Type, _ + ByVal ParameterType As Type, _ + ByVal TypeInferenceArguments As Type(), _ + ByVal TargetProcedure As MethodBase, _ + ByVal DigThroughToBasesAndImplements As Boolean) As Boolean + + Dim Inferred As Boolean = _ + InferTypeArgumentsFromArgumentDirectly( _ + ArgumentType, _ + ParameterType, _ + TypeInferenceArguments, _ + TargetProcedure, _ + DigThroughToBasesAndImplements) + + + If (Inferred OrElse _ + Not DigThroughToBasesAndImplements OrElse _ + Not IsInstantiatedGeneric(ParameterType) OrElse _ + (Not ParameterType.IsClass AndAlso Not ParameterType.IsInterface)) Then + + 'can only inherit from classes or interfaces. + 'can ignore generic parameters here because it + 'were a generic parameter, inference would + 'definitely have succeeded. + + Return Inferred + End If + + + Dim RawGenericParameterType As Type = ParameterType.GetGenericTypeDefinition + + If (IsArrayType(ArgumentType)) Then + + '1. Generic IList is implemented only by one dimensional arrays + ' + '2. If parameter type is a class, then no other inference from + ' array is possible. + ' + If (ArgumentType.GetArrayRank > 1 OrElse _ + ParameterType.IsClass) Then + + Return False + End If + 'For arrays, change the argument type to be IList(Of Array element type) + + ArgumentType = _ + GetType(System.Collections.Generic.IList(Of )).MakeGenericType(New Type() {ArgumentType.GetElementType}) + + If (GetType(System.Collections.Generic.IList(Of )) Is RawGenericParameterType) Then + GoTo RetryInference + End If + + ElseIf (Not ArgumentType.IsClass AndAlso _ + Not ArgumentType.IsInterface) Then + + Debug.Assert(Not IsGenericParameter(ArgumentType), "Generic parameter unexpected!!!") + + Return False + + ElseIf (IsInstantiatedGeneric(ArgumentType) AndAlso _ + ArgumentType.GetGenericTypeDefinition Is RawGenericParameterType) Then + + Return False + End If + + + If (ParameterType.IsClass) Then + + If (Not ArgumentType.IsClass) Then + Return False + End If + + Dim Base As Type = ArgumentType.BaseType + + While (Base IsNot Nothing) + + If (IsInstantiatedGeneric(Base) AndAlso _ + Base.GetGenericTypeDefinition Is RawGenericParameterType) Then + + Exit While + End If + + Base = Base.BaseType + End While + + ArgumentType = Base + Else + + Dim ImplementedMatch As Type = Nothing + For Each Implemented As Type In ArgumentType.GetInterfaces + + If (IsInstantiatedGeneric(Implemented) AndAlso _ + Implemented.GetGenericTypeDefinition Is RawGenericParameterType) Then + + If (ImplementedMatch IsNot Nothing) Then + 'Ambiguous + ' + Return False + End If + + ImplementedMatch = Implemented + End If + Next + + ArgumentType = ImplementedMatch + End If + + If (ArgumentType Is Nothing) Then + Return False + End If + +RetryInference: + + Return _ + InferTypeArgumentsFromArgumentDirectly( _ + ArgumentType, _ + ParameterType, _ + TypeInferenceArguments, _ + TargetProcedure, _ + DigThroughToBasesAndImplements) + + End Function + + + Private Shared Function InferTypeArgumentsFromArgumentDirectly( _ + ByVal ArgumentType As Type, _ + ByVal ParameterType As Type, _ + ByVal TypeInferenceArguments As Type(), _ + ByVal TargetProcedure As MethodBase, _ + ByVal DigThroughToBasesAndImplements As Boolean) As Boolean + + Debug.Assert(Not ParameterType.IsByRef, "didn't expect byref parameter type here") + Debug.Assert(IsRawGeneric(TargetProcedure), "Type inference for instantiated generic unexpected!!!") + + If Not RefersToGenericParameter(ParameterType, TargetProcedure) Then + Return True + End If + + 'If a generic method is parameterized by T, an argument of type A matching a parameter of type + 'P can be used to infer a type for T by these patterns: + ' + ' -- If P is T, then infer A for T + ' -- If P is G(Of T) and A is G(Of X), then infer X for T + ' -- If P is or implements G(Of T) and A is G(Of X), then infer X for T + ' -- If P is Array Of T, and A is Array Of X, then infer X for T + + If IsGenericParameter(ParameterType) Then + If AreGenericMethodDefsEqual(ParameterType.DeclaringMethod, TargetProcedure) Then + Dim ParameterIndex As Integer = ParameterType.GenericParameterPosition + If TypeInferenceArguments(ParameterIndex) Is Nothing Then + TypeInferenceArguments(ParameterIndex) = ArgumentType + + ElseIf TypeInferenceArguments(ParameterIndex) IsNot ArgumentType Then + Return False + + End If + End If + + ElseIf IsInstantiatedGeneric(ParameterType) Then + + Dim BestMatchType As Type = Nothing + + If IsInstantiatedGeneric(ArgumentType) AndAlso _ + ArgumentType.GetGenericTypeDefinition Is ParameterType.GetGenericTypeDefinition Then + BestMatchType = ArgumentType + End If + + If BestMatchType Is Nothing AndAlso DigThroughToBasesAndImplements Then + For Each PossibleGenericType As Type In ArgumentType.GetInterfaces + If IsInstantiatedGeneric(PossibleGenericType) AndAlso _ + PossibleGenericType.GetGenericTypeDefinition Is ParameterType.GetGenericTypeDefinition Then + + If BestMatchType Is Nothing Then + BestMatchType = PossibleGenericType + Else + ' Multiple generic interfaces match the parameter type + Return False + End If + End If + Next + End If + + If BestMatchType IsNot Nothing Then + Dim ParameterTypeParameters As Type() = GetTypeArguments(ParameterType) + Dim ArgumentTypeArguments As Type() = GetTypeArguments(BestMatchType) + + Debug.Assert(ParameterTypeParameters.Length = ArgumentTypeArguments.Length, _ + "inconsistent parameter counts") + + For Index As Integer = 0 To ArgumentTypeArguments.Length - 1 + If Not InferTypeArgumentsFromArgument( _ + ArgumentTypeArguments(Index), _ + ParameterTypeParameters(Index), _ + TypeInferenceArguments, _ + TargetProcedure, _ + False) Then 'Don't dig through because generics covariance is not allowed + Return False + End If + Next + + Return True + End If + + Return False + + ElseIf IsArrayType(ParameterType) Then + + If IsArrayType(ArgumentType) Then + If ParameterType.GetArrayRank = ArgumentType.GetArrayRank Then + Return _ + InferTypeArgumentsFromArgument( _ + GetElementType(ArgumentType), _ + GetElementType(ParameterType), _ + TypeInferenceArguments, _ + TargetProcedure, _ + DigThroughToBasesAndImplements) + End If + End If + + Return False + End If + + Return True + + End Function + + Private Shared Function CanPassToParamArray( _ + ByVal TargetProcedure As Method, _ + ByVal Argument As Object, _ + ByVal Parameter As ParameterInfo) As Boolean + + 'This method generates no errors because errors are reported only on the expanded form and + 'the unexpanded form is always accompanied by the expanded form. + + Debug.Assert(IsParamArray(Parameter), "expected ParamArray parameter") + + 'A Nothing argument can be passed as an unexpanded ParamArray. + If Argument Is Nothing Then Return True + + Dim ParameterType As Type = Parameter.ParameterType +#If TELESTO Then + Dim ArgumentType As Type = GetArgumentType(Argument) +#Else + Dim ArgumentType As Type = GetArgumentTypeInContextOfParameterType(Argument, ParameterType) +#End If + Dim ConversionResult As ConversionClass = ClassifyConversion(ParameterType, ArgumentType, Nothing) + Return ConversionResult = ConversionClass.Widening OrElse ConversionResult = ConversionClass.Identity + End Function + + Friend Shared Function CanPassToParameter( _ + ByVal TargetProcedure As Method, _ + ByVal Argument As Object, _ + ByVal Parameter As ParameterInfo, _ + ByVal IsExpandedParamArray As Boolean, _ + ByVal RejectNarrowingConversions As Boolean, _ + ByVal Errors As List(Of String), _ + ByRef RequiresNarrowingConversion As Boolean, _ + ByRef AllNarrowingIsFromObject As Boolean) As Boolean + + 'A Nothing argument always matches a parameter. Also, it doesn't contribute to type inferencing. + If Argument Is Nothing Then Return True + + Dim ParameterType As Type = Parameter.ParameterType + Dim IsByRef As Boolean = ParameterType.IsByRef + + If IsByRef OrElse IsExpandedParamArray Then + ParameterType = GetElementType(ParameterType) + End If +#If TELESTO + Dim ArgumentType As Type = GetArgumentType(Argument) +#Else + Dim ArgumentType As Type = GetArgumentTypeInContextOfParameterType(Argument, ParameterType) +#End If + 'A Missing argument always matches an optional parameter. + If Argument Is System.Reflection.Missing.Value Then + If Parameter.IsOptional Then + Return True + ElseIf Not IsRootObjectType(ParameterType) OrElse Not IsExpandedParamArray Then + 'Trying to pass a Missing argument to a non-optional parameter. + 'VSW#489299: CLR throws if that's the case, so we disallow it here. + If Errors IsNot Nothing Then + If IsExpandedParamArray Then + 'Trying to pass a Missing argument to an expanded ParamArray. + ReportError(Errors, ResID.OmittedParamArrayArgument) + Else + ReportError(Errors, ResID.OmittedArgument1, Parameter.Name) + End If + End If + Return False + End If + End If + + 'Check if the conversion from the argument type to the + 'parameter type can succeed. + Dim CanCopyIn As Boolean = _ + CanConvert( _ + ParameterType, _ + ArgumentType, _ + RejectNarrowingConversions, _ + Errors, _ + Parameter.Name, _ + False, _ + RequiresNarrowingConversion, _ + AllNarrowingIsFromObject) + + If Not IsByRef OrElse Not CanCopyIn Then + Return CanCopyIn + End If + + 'If the parameter is ByRef, check if the conversion from + 'the parameter type to the argument type can succeed. + Return _ + CanConvert( _ + ArgumentType, _ + ParameterType, _ + RejectNarrowingConversions, _ + Errors, _ + Parameter.Name, _ + True, _ + RequiresNarrowingConversion, _ + AllNarrowingIsFromObject) + + End Function + + Friend Shared Function InferTypeArgumentsFromArgument( _ + ByVal TargetProcedure As Method, _ + ByVal Argument As Object, _ + ByVal Parameter As ParameterInfo, _ + ByVal IsExpandedParamArray As Boolean, _ + ByVal Errors As List(Of String)) As Boolean + + 'A Nothing argument doesn't contribute to type inferencing. + If Argument Is Nothing Then Return True + + Dim ParameterType As Type = Parameter.ParameterType + Dim IsByRef As Boolean = ParameterType.IsByRef + + If IsByRef OrElse IsExpandedParamArray Then + ParameterType = GetElementType(ParameterType) + End If +#If TELESTO Then + Dim ArgumentType As Type = GetArgumentType(Argument) +#Else + Dim ArgumentType As Type = GetArgumentTypeInContextOfParameterType(Argument, ParameterType) +#End If + Debug.Assert(TargetProcedure.IsMethod, "we shouldn't be infering type arguments for non-methods") + If Not InferTypeArgumentsFromArgument( _ + ArgumentType, _ + ParameterType, _ + TargetProcedure.TypeArguments, _ + TargetProcedure.AsMethod, _ + True) Then + + If Errors IsNot Nothing Then + ReportError(Errors, ResID.TypeInferenceFails1, Parameter.Name) + End If + Return False + End If + + Return True + End Function + + Friend Shared Function PassToParameter( _ + ByVal Argument As Object, _ + ByVal Parameter As ParameterInfo, _ + ByVal ParameterType As Type) As Object + + 'This function takes an object and modifies it so it can be passed + 'as the parameter described by the ParameterInfo. This involves casting it + 'to the parameter type and/or substituting optional values for Missing + 'arguments. + + Debug.Assert(Parameter IsNot Nothing AndAlso ParameterType IsNot Nothing) + + Dim IsByRef As Boolean = ParameterType.IsByRef + If IsByRef Then + ParameterType = ParameterType.GetElementType + End If + + 'An argument represented by a TypedNothing is actually a Nothing + 'reference with a type. This argument passes to the parameter as Nothing. + If TypeOf Argument Is TypedNothing Then + Argument = Nothing + End If + + 'A Missing argument loads the parameter's optional value. + If Argument Is System.Reflection.Missing.Value AndAlso Parameter.IsOptional Then + Argument = Parameter.DefaultValue + End If + + 'If the argument is a boxed ValueType and we're passing it to a + 'ByRef parameter, then we must forcefully copy it to avoid + 'aliasing since the invocation will modify the boxed ValueType + 'in place. See VS7#304212, changelist #175254. + If IsByRef Then +#If TELESTO + Dim ArgumentType As Type = GetArgumentType(Argument) +#Else + Dim ArgumentType As Type = GetArgumentTypeInContextOfParameterType(Argument, ParameterType) +#End If + If ArgumentType IsNot Nothing AndAlso IsValueType(ArgumentType) Then + Argument = Conversions.ForceValueCopy(Argument, ArgumentType) + End If + End If + + 'Peform the conversion to the parameter type and return the result. + Return Conversions.ChangeType(Argument, ParameterType) + End Function + + Private Shared Function FindParameterByName(ByVal Parameters As ParameterInfo(), ByVal Name As String, ByRef Index As Integer) As Boolean + 'Find the Index of the parameter in Parameters which matches Name. Return True if such a parameter is found. + + Dim ParamIndex As Integer = 0 + Do While ParamIndex < Parameters.Length + If Operators.CompareString(Name, Parameters(ParamIndex).Name, True) = 0 Then + Index = ParamIndex + Return True + End If + ParamIndex += 1 + Loop + Return False + End Function + + Private Shared Function CreateMatchTable(ByVal Size As Integer, ByVal LastPositionalMatchIndex As Integer) As Boolean() + 'Create a table for keeping track of which parameters have been matched with + 'an argument. Used for detecting multiple matches during named argument matching, + 'and also for loading the optional values of unmatched parameters. + + Dim Result As Boolean() = New Boolean(Size - 1) {} + For Index As Integer = 0 To LastPositionalMatchIndex + Result(Index) = True + Next + Return Result + End Function + + Friend Shared Function CanMatchArguments( _ + ByVal TargetProcedure As Method, _ + ByVal Arguments As Object(), _ + ByVal ArgumentNames As String(), _ + ByVal TypeArguments As Type(), _ + ByVal RejectNarrowingConversions As Boolean, _ + ByVal Errors As List(Of String)) As Boolean + + Dim ReportErrors As Boolean = Errors IsNot Nothing + + TargetProcedure.ArgumentsValidated = True + + 'First instantiate the generic method. If type arguments aren't supplied, + 'we need to infer them first. + 'In error cases, the method might already be instantiated, so need to use the + 'passed in type params only if the method has not yet been instantiated. + 'In the non-error case, the method is always uninstantiated at this time. + ' +#If DEBUG AND NOt TELESTO + Debug.Assert(Not (Errors Is Nothing AndAlso TargetProcedure.IsMethod AndAlso IsInstantiatedGeneric(TargetProcedure.AsMethod)), _ + "Instantiated generic method unexpected!!!") +#Else + Debug.Assert(Not (Errors Is Nothing AndAlso _ + TargetProcedure.IsMethod AndAlso _ + TargetProcedure.AsMethod.IsGenericMethod AndAlso (Not TargetProcedure.AsMethod.IsGenericMethodDefinition)), _ + "Instantiated generic method unexpected!!!") +#End If + + If TargetProcedure.IsMethod AndAlso IsRawGeneric(TargetProcedure.AsMethod) Then + If TypeArguments.Length = 0 Then + TypeArguments = New Type(TargetProcedure.TypeParameters.Length - 1) {} + TargetProcedure.TypeArguments = TypeArguments + + If Not InferTypeArguments(TargetProcedure, Arguments, ArgumentNames, TypeArguments, Errors) Then + Return False + End If + Else + TargetProcedure.TypeArguments = TypeArguments + End If + + If Not InstantiateGenericMethod(TargetProcedure, TypeArguments, Errors) Then + Return False + End If + End If + + Dim Parameters As ParameterInfo() = TargetProcedure.Parameters + Debug.Assert(Arguments.Length <= Parameters.Length OrElse _ + (TargetProcedure.ParamArrayExpanded AndAlso TargetProcedure.ParamArrayIndex >= 0), _ + "argument count mismatch -- this method should have been rejected already") + + Dim ArgIndex As Integer = ArgumentNames.Length + Dim ParamIndex As Integer = 0 + + 'STEP 1 + 'Match all positional arguments until we encounter a ParamArray or run out of positional arguments. + Do While ArgIndex < Arguments.Length + + 'The loop is finished if we encounter a ParamArray. + If ParamIndex = TargetProcedure.ParamArrayIndex Then Exit Do + + If Not CanPassToParameter( _ + TargetProcedure, _ + Arguments(ArgIndex), _ + Parameters(ParamIndex), _ + False, _ + RejectNarrowingConversions, _ + Errors, _ + TargetProcedure.RequiresNarrowingConversion, _ + TargetProcedure.AllNarrowingIsFromObject) Then + + 'If errors are needed, keep going to catch them all. + If Not ReportErrors Then Return False + + End If + + ArgIndex += 1 + ParamIndex += 1 + Loop + + 'STEP 2 + 'Match all remaining positional arguments to the ParamArray. + If TargetProcedure.HasParamArray Then + + Debug.Assert(ParamIndex = TargetProcedure.ParamArrayIndex, _ + "current parameter must be param array by this point") + + If TargetProcedure.ParamArrayExpanded Then + 'Treat the ParamArray in its expanded form. Match remaining arguments to the + 'ParamArray's element type. + + 'Nothing passed to a ParamArray will widen to both the type of the ParamArray and the + 'array type of the ParamArray. In that case, we explicitly disallow matching an + 'expanded ParamArray. If one argument remains and it is Nothing, reject the match. + + If ArgIndex = Arguments.Length - 1 AndAlso Arguments(ArgIndex) Is Nothing Then + 'No need to generate an error for this case since Nothing will always match + 'the associated unexpanded form. + Return False + End If + + Do While ArgIndex < Arguments.Length + + If Not CanPassToParameter( _ + TargetProcedure, _ + Arguments(ArgIndex), _ + Parameters(ParamIndex), _ + True, _ + RejectNarrowingConversions, _ + Errors, _ + TargetProcedure.RequiresNarrowingConversion, _ + TargetProcedure.AllNarrowingIsFromObject) Then + + 'If errors are needed, keep going to catch them all. + If Not ReportErrors Then Return False + + End If + + ArgIndex += 1 + Loop + + Else + 'Treat the ParamArray in its unexpanded form. Determine if the argument can + 'be passed directly as a ParamArray. + + Debug.Assert(Arguments.Length - ArgIndex <= 1, _ + "must have zero or one arg left to match the unexpanded paramarray") 'Candidate collection guarantees this. + + 'Need one argument left over for the unexpanded form to be applicable. + If Arguments.Length - ArgIndex <> 1 Then + 'No need to generate an error for this case because the error + 'reporting will be done on the expanded form. All we need to do is + 'disqualify the unexpanded form. + Return False + End If + + If Not CanPassToParamArray( _ + TargetProcedure, _ + Arguments(ArgIndex), _ + Parameters(ParamIndex)) Then + + ' VSW 259007: We do need to report errors when only the + ' unexpanded form is being considered. + If ReportErrors Then +#If TELESTO Then + ReportError( _ + Errors, _ + ResID.ArgumentMismatch3, _ + Parameters(ParamIndex).Name, _ + GetArgumentType(Arguments(ArgIndex)), _ + Parameters(ParamIndex).ParameterType) +#Else + ReportError( _ + Errors, _ + ResID.ArgumentMismatch3, _ + Parameters(ParamIndex).Name, _ + GetArgumentTypeInContextOfParameterType(Arguments(ArgIndex), + Parameters(ParamIndex).ParameterType), _ + Parameters(ParamIndex).ParameterType) +#End If + End If + + Return False + End If + + End If + + 'Matching the ParamArray consumes this parameter. Increment the parameter index. + ParamIndex += 1 + End If + + 'If needed, create the table which keeps track of matched Parameters. + 'Initialize it using the positional matches we've found thus far. + 'This table is needed if we potentially have unmatched Optional parameters. + 'This can happen when named arguments exist or when the number of positional + 'arguments is less than the number of parameters. + Dim MatchedParameters As Boolean() = Nothing + + If ArgumentNames.Length > 0 OrElse ParamIndex < Parameters.Length Then + MatchedParameters = CreateMatchTable(Parameters.Length, ParamIndex - 1) + End If + + 'STEP 3 + 'Match all named arguments. + If ArgumentNames.Length > 0 Then + + Debug.Assert(Parameters.Length > 0, "expected some parameters here") 'Candidate collection guarantees this. + + 'The named argument mapping table contains indicies into the + 'parameters array to describe the association between arguments + 'and parameters. + ' + 'Given an array of arguments and an array of argument names, the + 'index n into each of these arrays represents the nth named argument + 'and its assocated name. If argument n matches the name of the + 'parameter at index m in the array of parameters, then the named + 'argument mapping table will contain the value m at index n. + + Dim NamedArgumentMapping As Integer() = New Integer(ArgumentNames.Length - 1) {} + + ArgIndex = 0 + Do While ArgIndex < ArgumentNames.Length + + If Not FindParameterByName(Parameters, ArgumentNames(ArgIndex), ParamIndex) Then + 'This named argument does not match the name of any parameter. + 'If errors are needed, keep going to catch them all. + If Not ReportErrors Then Return False + ReportError(Errors, ResID.NamedParamNotFound2, ArgumentNames(ArgIndex), TargetProcedure) + GoTo skipargument + End If + + If ParamIndex = TargetProcedure.ParamArrayIndex Then + 'This named argument matches a ParamArray parameter. + 'If errors are needed, keep going to catch them all. + If Not ReportErrors Then Return False + ReportError(Errors, ResID.NamedParamArrayArgument1, ArgumentNames(ArgIndex)) + GoTo skipargument + End If + + If MatchedParameters(ParamIndex) Then + 'This named argument matches a parameter which has already been specified. + 'If errors are needed, keep going to catch them all. + If Not ReportErrors Then Return False + ReportError(Errors, ResID.NamedArgUsedTwice2, ArgumentNames(ArgIndex), TargetProcedure) + GoTo skipargument + End If + + If Not CanPassToParameter( _ + TargetProcedure, _ + Arguments(ArgIndex), _ + Parameters(ParamIndex), _ + False, _ + RejectNarrowingConversions, _ + Errors, _ + TargetProcedure.RequiresNarrowingConversion, _ + TargetProcedure.AllNarrowingIsFromObject) Then + + 'If errors are needed, keep going to catch them all. + If Not ReportErrors Then Return False + + End If + + MatchedParameters(ParamIndex) = True + NamedArgumentMapping(ArgIndex) = ParamIndex +skipargument: + ArgIndex += 1 + Loop + + 'Store this away for use when/if we invoke this method. + TargetProcedure.NamedArgumentMapping = NamedArgumentMapping + End If + + 'All remaining unmatched parameters must be Optional. + If MatchedParameters IsNot Nothing Then + For Index As Integer = 0 To MatchedParameters.Length - 1 + If MatchedParameters(Index) = False AndAlso Not Parameters(Index).IsOptional Then + 'This parameter is not optional. + 'If errors are needed, keep going to catch them all. + If Not ReportErrors Then Return False + ReportError(Errors, ResID.OmittedArgument1, Parameters(Index).Name) + End If + Next + End If + + 'If errors were generated, the arguments failed to match the target procedure. + If Errors IsNot Nothing AndAlso Errors.Count > 0 Then + Return False + End If + + Return True + + End Function + + Private Shared Function InstantiateGenericMethod( _ + ByVal TargetProcedure As Method, _ + ByVal TypeArguments As Type(), _ + ByVal Errors As List(Of String)) As Boolean + + 'Verify that all type arguments have been supplied. + Debug.Assert(TypeArguments.Length = TargetProcedure.TypeParameters.Length, "expected length match") + + Dim ReportErrors As Boolean = Errors IsNot Nothing + + For TypeArgumentIndex As Integer = 0 To TypeArguments.Length - 1 + + If TypeArguments(TypeArgumentIndex) Is Nothing Then + If Not ReportErrors Then Return False + ReportError( _ + Errors, _ + ResID.UnboundTypeParam1, _ + TargetProcedure.TypeParameters(TypeArgumentIndex).Name) + End If + + Next + + If Errors Is Nothing OrElse Errors.Count = 0 Then + 'Create the instantiated form of the generic method using the type arguments + 'inferred during argument matching. + If Not TargetProcedure.BindGenericArguments Then + If Not ReportErrors Then Return False + ReportError(Errors, ResID.FailedTypeArgumentBinding) + End If + End If + + 'If errors were generated, the instantiation failed. + If Errors IsNot Nothing AndAlso Errors.Count > 0 Then + Return False + End If + + Return True + End Function + + 'may not want Method as TargetProcedure - may instead want to pass the required information in separately. + 'this means that for the simple case of only one method we do not need to allocate a Method object. + Friend Shared Sub MatchArguments( _ + ByVal TargetProcedure As Method, _ + ByVal Arguments As Object(), _ + ByVal MatchedArguments As Object()) + + Dim Parameters As ParameterInfo() = TargetProcedure.Parameters + + Debug.Assert(TargetProcedure.ArgumentsValidated, _ + "expected validation of arguments to be made before matching") + Debug.Assert(MatchedArguments.Length = Parameters.Length OrElse _ + MatchedArguments.Length = Parameters.Length + 1, _ + "size of matched arguments array must equal number of parameters") + Debug.Assert(Arguments.Length <= Parameters.Length OrElse _ + (TargetProcedure.ParamArrayExpanded AndAlso TargetProcedure.ParamArrayIndex >= 0), _ + "argument count mismatch -- this method should have been rejected already") + + Dim NamedArgumentMapping As Integer() = TargetProcedure.NamedArgumentMapping + + Dim ArgIndex As Integer = 0 + If NamedArgumentMapping IsNot Nothing Then ArgIndex = NamedArgumentMapping.Length + Dim ParamIndex As Integer = 0 + + 'STEP 1 + 'Match all positional arguments until we encounter a ParamArray or run out of positional arguments. + Do While ArgIndex < Arguments.Length + + 'The loop is finished if we encounter a ParamArray. + If ParamIndex = TargetProcedure.ParamArrayIndex Then Exit Do + + MatchedArguments(ParamIndex) = _ + PassToParameter(Arguments(ArgIndex), Parameters(ParamIndex), Parameters(ParamIndex).ParameterType) + + ArgIndex += 1 + ParamIndex += 1 + Loop + + 'STEP 2 + 'Match all remaining positional arguments to the ParamArray. + If TargetProcedure.HasParamArray Then + Debug.Assert(ParamIndex = TargetProcedure.ParamArrayIndex, _ + "current parameter must be param array by this point") + + If TargetProcedure.ParamArrayExpanded Then + 'Treat the ParamArray in its expanded form. Pass the remaining arguments into + 'the ParamArray. + + Dim RemainingArgumentCount As Integer = Arguments.Length - ArgIndex + Dim ParamArrayParameter As ParameterInfo = Parameters(ParamIndex) + Dim ParamArrayElementType As System.Type = ParamArrayParameter.ParameterType.GetElementType + + Dim ParamArrayArgument As System.Array = _ + System.Array.CreateInstance(ParamArrayElementType, RemainingArgumentCount) + + Dim ParamArrayIndex As Integer = 0 + Do While ArgIndex < Arguments.Length + + ParamArrayArgument.SetValue( _ + PassToParameter(Arguments(ArgIndex), ParamArrayParameter, ParamArrayElementType), _ + ParamArrayIndex) + + ArgIndex += 1 + ParamArrayIndex += 1 + Loop + + MatchedArguments(ParamIndex) = ParamArrayArgument + + Else + Debug.Assert(Arguments.Length - ArgIndex = 1, _ + "must have one arg left to match the unexpanded paramarray") + + 'Treat the ParamArray in its unexpanded form. Pass the one remaining argument + 'directly as a ParamArray. + MatchedArguments(ParamIndex) = _ + PassToParameter(Arguments(ArgIndex), Parameters(ParamIndex), Parameters(ParamIndex).ParameterType) + End If + + 'Matching the ParamArray consumes this parameter. Increment the parameter index. + ParamIndex += 1 + End If + + 'If needed, create the table which keeps track of matched Parameters. + 'Initialize it using the positional matches we've found thus far. + 'This table is needed if we potentially have unmatched Optional parameters. + 'This can happen when named arguments exist or when the number of positional + 'arguments is less than the number of parameters. + Dim MatchedParameters As Boolean() = Nothing + + If NamedArgumentMapping IsNot Nothing OrElse ParamIndex < Parameters.Length Then + MatchedParameters = CreateMatchTable(Parameters.Length, ParamIndex - 1) + End If + + 'STEP 3 + 'Match all named arguments. + If NamedArgumentMapping IsNot Nothing Then + + Debug.Assert(Parameters.Length > 0, "expected some parameters here") 'Candidate collection guarantees this. + + 'The named argument mapping table contains indicies into the + 'parameters array to describe the association between arguments + 'and parameters. + ' + 'Given an array of arguments and an array of argument names, the + 'index n into each of these arrays represents the nth named argument + 'and its assocated name. If argument n matches the name of the + 'parameter at index m in the array of parameters, then the named + 'argument mapping table will contain the value m at index n. + + ArgIndex = 0 + Do While ArgIndex < NamedArgumentMapping.Length + ParamIndex = NamedArgumentMapping(ArgIndex) + + MatchedArguments(ParamIndex) = _ + PassToParameter(Arguments(ArgIndex), Parameters(ParamIndex), Parameters(ParamIndex).ParameterType) + + Debug.Assert(Not MatchedParameters(ParamIndex), "named argument match collision") + MatchedParameters(ParamIndex) = True + ArgIndex += 1 + Loop + + End If + + 'If all has gone well, by this point any unmatched parameters are Optional. + 'Fill in unmatched parameters with their optional values. + If MatchedParameters IsNot Nothing Then + For Index As Integer = 0 To MatchedParameters.Length - 1 + If MatchedParameters(Index) = False Then + Debug.Assert(Parameters(Index).IsOptional, _ + "unmatched, non-optional parameter. How did we get this far?") + MatchedArguments(Index) = _ + PassToParameter(System.Reflection.Missing.Value, Parameters(Index), Parameters(Index).ParameterType) + End If + Next + End If + + Return + End Sub + + Private Shared Function InferTypeArguments( _ + ByVal TargetProcedure As Method, _ + ByVal Arguments As Object(), _ + ByVal ArgumentNames As String(), _ + ByVal TypeArguments As Type(), _ + ByVal Errors As List(Of String)) As Boolean + + Dim ReportErrors As Boolean = Errors IsNot Nothing + + Dim Parameters As ParameterInfo() = TargetProcedure.RawParameters + + Debug.Assert(Arguments.Length <= Parameters.Length OrElse _ + (TargetProcedure.ParamArrayExpanded AndAlso TargetProcedure.ParamArrayIndex >= 0), _ + "argument count mismatch -- this method should have been rejected already") + + Dim ArgIndex As Integer = ArgumentNames.Length + Dim ParamIndex As Integer = 0 + + 'STEP 1 + 'Infer from all positional arguments until we encounter a ParamArray or run out of positional arguments. + Do While ArgIndex < Arguments.Length + + 'The loop is finished if we encounter a ParamArray. + If ParamIndex = TargetProcedure.ParamArrayIndex Then Exit Do + + If Not InferTypeArgumentsFromArgument( _ + TargetProcedure, _ + Arguments(ArgIndex), _ + Parameters(ParamIndex), _ + False, _ + Errors) Then + + 'If errors are needed, keep going to catch them all. + If Not ReportErrors Then Return False + End If + + ArgIndex += 1 + ParamIndex += 1 + Loop + + 'STEP 2 + 'Infer from all remaining positional arguments matching a ParamArray. + If TargetProcedure.HasParamArray Then + + Debug.Assert(ParamIndex = TargetProcedure.ParamArrayIndex, _ + "current parameter must be param array by this point") + + If TargetProcedure.ParamArrayExpanded Then + 'Treat the ParamArray in its expanded form. Infer the element type from the remaining arguments. + Do While ArgIndex < Arguments.Length + + If Not InferTypeArgumentsFromArgument( _ + TargetProcedure, _ + Arguments(ArgIndex), _ + Parameters(ParamIndex), _ + True, _ + Errors) Then + + 'If errors are needed, keep going to catch them all. + If Not ReportErrors Then Return False + + End If + + ArgIndex += 1 + Loop + + Else + 'Treat the ParamArray in its unexpanded form. Infer the ParamArray type from the argument. + + Debug.Assert(Arguments.Length - ArgIndex <= 1, _ + "must have zero or one arg left to match the unexpanded paramarray") 'Candidate collection guarantees this. + + If Arguments.Length - ArgIndex <> 1 Then + 'Type inferencing not possible here. + Return True + End If + + If Not InferTypeArgumentsFromArgument( _ + TargetProcedure, _ + Arguments(ArgIndex), _ + Parameters(ParamIndex), _ + False, _ + Errors) Then + Return False + End If + + End If + + 'Matching the ParamArray consumes this parameter. Increment the parameter index. + ParamIndex += 1 + End If + + 'STEP 3 + 'Infer from named arguments. + If ArgumentNames.Length > 0 Then + + Debug.Assert(Parameters.Length > 0, "expected some parameters here") 'Candidate collection guarantees this. + + ArgIndex = 0 + Do While ArgIndex < ArgumentNames.Length + + If Not FindParameterByName(Parameters, ArgumentNames(ArgIndex), ParamIndex) Then + GoTo skipargument + End If + + If ParamIndex = TargetProcedure.ParamArrayIndex Then + GoTo skipargument + End If + + If Not InferTypeArgumentsFromArgument( _ + TargetProcedure, _ + Arguments(ArgIndex), _ + Parameters(ParamIndex), _ + False, _ + Errors) Then + + 'If errors are needed, keep going to catch them all. + If Not ReportErrors Then Return False + + End If +skipargument: + ArgIndex += 1 + Loop + + End If + + 'If errors were generated, inference of type arguments failed. + If Errors IsNot Nothing AndAlso Errors.Count > 0 Then + Return False + End If + + Return True + End Function + + Friend Shared Sub ReorderArgumentArray( _ + ByVal TargetProcedure As Method, _ + ByVal ParameterResults As Object(), _ + ByVal Arguments As Object(), _ + ByVal CopyBack As Boolean(), _ + ByVal LookupFlags As BindingFlags) + + 'No need to copy back if there are no valid targets . + 'The copy back array will be be Nothing if the compiler determined that all + 'arguments are Rvalues. + If CopyBack Is Nothing Then + Return + End If + + 'Initialize the copy back array to all ByVal. + For Index As Integer = 0 To CopyBack.Length - 1 + CopyBack(Index) = False + Next + + 'No need to copy back if there are no byref parameters. Properties can't have + 'ByRef arguments, so skip these as well. + 'CONSIDER: how to know when TargetProcedure is a Get property accessor? + If HasFlag(LookupFlags, BindingFlags.SetProperty) OrElse _ + Not TargetProcedure.HasByRefParameter Then + Return + End If + + Debug.Assert(CopyBack.Length = Arguments.Length, "array sizes must match") + Debug.Assert(ParameterResults.Length = TargetProcedure.Parameters.Length, "parameter arrays must match") + + Dim Parameters As ParameterInfo() = TargetProcedure.Parameters + Dim NamedArgumentMapping As Integer() = TargetProcedure.NamedArgumentMapping + + Dim ArgIndex As Integer = 0 + If NamedArgumentMapping IsNot Nothing Then ArgIndex = NamedArgumentMapping.Length + Dim ParamIndex As Integer = 0 + + 'STEP 1 + 'Copy back all positional parameters until we encounter a ParamArray or run out of positional arguments. + Do While ArgIndex < Arguments.Length + + 'The loop is finished if we encounter a ParamArray. + If ParamIndex = TargetProcedure.ParamArrayIndex Then Exit Do + + If Parameters(ParamIndex).ParameterType.IsByRef Then + Arguments(ArgIndex) = ParameterResults(ParamIndex) + CopyBack(ArgIndex) = True + End If + + ArgIndex += 1 + ParamIndex += 1 + Loop + + 'STEP 2 + 'No need to copy back from the ParamArray because they can't be ByRef. Skip it. + + 'STEP 3 + 'Copy back all named arguments. + If NamedArgumentMapping IsNot Nothing Then + ArgIndex = 0 + Do While ArgIndex < NamedArgumentMapping.Length + ParamIndex = NamedArgumentMapping(ArgIndex) + + If Parameters(ParamIndex).ParameterType.IsByRef Then + Arguments(ArgIndex) = ParameterResults(ParamIndex) + CopyBack(ArgIndex) = True + End If + + ArgIndex += 1 + Loop + End If + + Return + End Sub + + Private Shared Function RejectUncallableProcedures( _ + ByVal Candidates As List(Of Method), _ + ByVal Arguments As Object(), _ + ByVal ArgumentNames As String(), _ + ByVal TypeArguments As Type(), _ + ByRef CandidateCount As Integer, _ + ByRef SomeCandidatesAreGeneric As Boolean) As Method + + Dim BestCandidate As Method = Nothing + + For Index As Integer = 0 To Candidates.Count - 1 + + Dim CandidateProcedure As Method = Candidates(Index) + + If Not CandidateProcedure.ArgumentMatchingDone Then + + RejectUncallableProcedure( _ + CandidateProcedure, _ + Arguments, _ + ArgumentNames, _ + TypeArguments) + End If + + If CandidateProcedure.NotCallable Then + CandidateCount -= 1 + Else + BestCandidate = CandidateProcedure + + If CandidateProcedure.IsGeneric OrElse IsGeneric(CandidateProcedure.DeclaringType) Then + + SomeCandidatesAreGeneric = True + + 'ElseIf Not RequiresSomeConversion Then + ' 'This candidate is an exact match which means the audition is over, + ' 'but only if the candidate is not generic. (A less-generic method + ' 'might have the same signature.) + ' + ' 'CONSIDER: This shortcut is possible only if overridden methods are + ' 'eliminated from the set of callable methods, or if the call is known + ' 'to be virtual. + ' CandidateCount = 1 + ' Exit For + + End If + + End If + + Next + + +#If BINDING_LOG Then + Console.WriteLine("== REJECT UNCALLABLE ==") + For Each item As Method In Candidates + If item Is Nothing Then + Console.WriteLine("dead ** didn't expect this here.") + Else + Console.WriteLine(item.DumpContents) + End If + Next +#End If + Return BestCandidate + + End Function + + Private Shared Sub RejectUncallableProcedure( _ + ByVal Candidate As Method, _ + ByVal Arguments As Object(), _ + ByVal ArgumentNames As String(), _ + ByVal TypeArguments As Type()) + + Debug.Assert(Candidate.ArgumentMatchingDone = False, "Argument matching being done multiple times!!!") + + If Not CanMatchArguments( _ + Candidate, _ + Arguments, _ + ArgumentNames, _ + TypeArguments, _ + False, _ + Nothing) Then + + Candidate.NotCallable = True + End If + + Candidate.ArgumentMatchingDone = True + + End Sub + +' Type.IsEquivalentTo is not supported in .Net 4.0 +#If NOT TELESTO Then + 'For NoPIA, if Argument is of type __ComObject, then treat it as its corresponding PIA type + ' + Private Shared Function GetArgumentTypeInContextOfParameterType( + ByVal Argument As Object, + ByVal ParameterType As Type) As Type + + Dim ArgumentType As Type = GetArgumentType(Argument) + + If ArgumentType Is Nothing OrElse + ParameterType Is Nothing Then Return ArgumentType + + 'Check if Argument's runtime type is equivalent to the PIA type. If it is then we + 'want to use the PIA type instead of __ComObject + If (ParameterType.IsImport AndAlso + ParameterType.IsInterface AndAlso + ParameterType.IsInstanceOfType(Argument) + ) OrElse + IsEquivalentType(ArgumentType, ParameterType) Then + + ArgumentType = ParameterType + + End If + + Return ArgumentType + End Function +#End If + Private Shared Function GetArgumentType(ByVal Argument As Object) As Type + 'A Nothing object has no type. + If Argument Is Nothing Then Return Nothing + 'A typed Nothing object stores the type that Nothing should be considered as. + Dim TypedNothingArgument As TypedNothing = TryCast(Argument, TypedNothing) + + If TypedNothingArgument IsNot Nothing Then Return TypedNothingArgument.Type + 'Otherwise, just return the type of the object. + Return Argument.GetType + End Function + + Private Shared Function MoreSpecificProcedure( _ + ByVal Left As Method, _ + ByVal Right As Method, _ + ByVal Arguments As Object(), _ + ByVal ArgumentNames As String(), _ + ByVal CompareGenericity As ComparisonType, _ + Optional ByRef BothLose As Boolean = False, _ + Optional ByVal ContinueWhenBothLose As Boolean = False) As Method + + BothLose = False + Dim LeftWinsAtLeastOnce As Boolean = False + Dim RightWinsAtLeastOnce As Boolean = False + + 'Compare the parameters that match the supplied positional arguments. + + Dim LeftMethod As MethodBase + Dim RightMethod As MethodBase + If Left.IsMethod Then LeftMethod = Left.AsMethod Else LeftMethod = Nothing + If Right.IsMethod Then RightMethod = Right.AsMethod Else RightMethod = Nothing + + Dim LeftParamIndex As Integer = 0 + Dim RightParamIndex As Integer = 0 + + Dim ArgIndex As Integer = ArgumentNames.Length + Do While ArgIndex < Arguments.Length + + 'Compare parameters only for supplied arguments. + 'UNDONE + 'if ArgumentSupplied then + Dim ArgumentType As Type = GetArgumentType(Arguments(ArgIndex)) + + Select Case CompareGenericity + Case ComparisonType.GenericSpecificityBasedOnMethodGenericParams + ' Compare GenericSpecificity + CompareGenericityBasedOnMethodGenericParams( _ + Left.Parameters(LeftParamIndex), _ + Left.RawParameters(LeftParamIndex), _ + Left, _ + Left.ParamArrayExpanded, _ + Right.Parameters(RightParamIndex), _ + Right.RawParameters(RightParamIndex), _ + Right, _ + Right.ParamArrayExpanded, _ + LeftWinsAtLeastOnce, _ + RightWinsAtLeastOnce, _ + BothLose) + + Case ComparisonType.GenericSpecificityBasedOnTypeGenericParams + ' Compare GenericSpecificity + CompareGenericityBasedOnTypeGenericParams( _ + Left.Parameters(LeftParamIndex), _ + Left.RawParametersFromType(LeftParamIndex), _ + Left, _ + Left.ParamArrayExpanded, _ + Right.Parameters(RightParamIndex), _ + Right.RawParametersFromType(RightParamIndex), _ + Right, _ + Right.ParamArrayExpanded, _ + LeftWinsAtLeastOnce, _ + RightWinsAtLeastOnce, _ + BothLose) + + Case ComparisonType.ParameterSpecificty + ' Compare ParameterSpecificity + CompareParameterSpecificity( _ + ArgumentType, _ + Left.Parameters(LeftParamIndex), _ + LeftMethod, _ + Left.ParamArrayExpanded, _ + Right.Parameters(RightParamIndex), _ + RightMethod, _ + Right.ParamArrayExpanded, _ + LeftWinsAtLeastOnce, _ + RightWinsAtLeastOnce, _ + BothLose) + + Case Else +#If TELESTO Then + Debug.Assert(False, "Unexpected comparison type!!!") ' Silverlight CLR does not have Debug.Fail. +#Else + Debug.Fail("Unexpected comparison type!!!") +#End If + End Select + + If (BothLose AndAlso (Not ContinueWhenBothLose)) OrElse _ + (LeftWinsAtLeastOnce AndAlso RightWinsAtLeastOnce) Then + Return Nothing + End If + + 'UNDONE + 'end if + + If LeftParamIndex <> Left.ParamArrayIndex Then LeftParamIndex += 1 + If RightParamIndex <> Right.ParamArrayIndex Then RightParamIndex += 1 + ArgIndex += 1 + Loop + + ArgIndex = 0 + Do While ArgIndex < ArgumentNames.Length + + Dim LeftParameterFound As Boolean = FindParameterByName(Left.Parameters, ArgumentNames(ArgIndex), LeftParamIndex) + Dim RightParameterFound As Boolean = FindParameterByName(Right.Parameters, ArgumentNames(ArgIndex), RightParamIndex) + + If Not LeftParameterFound OrElse Not RightParameterFound Then + Throw New InternalErrorException() + End If + + Dim ArgumentType As Type = GetArgumentType(Arguments(ArgIndex)) + + Select Case CompareGenericity + Case ComparisonType.GenericSpecificityBasedOnMethodGenericParams + ' Compare GenericSpecificity + CompareGenericityBasedOnMethodGenericParams( _ + Left.Parameters(LeftParamIndex), _ + Left.RawParameters(LeftParamIndex), _ + Left, _ + True, _ + Right.Parameters(RightParamIndex), _ + Right.RawParameters(RightParamIndex), _ + Right, _ + True, _ + LeftWinsAtLeastOnce, _ + RightWinsAtLeastOnce, _ + BothLose) + + Case ComparisonType.GenericSpecificityBasedOnTypeGenericParams + ' Compare GenericSpecificity + CompareGenericityBasedOnTypeGenericParams( _ + Left.Parameters(LeftParamIndex), _ + Left.RawParameters(LeftParamIndex), _ + Left, _ + True, _ + Right.Parameters(RightParamIndex), _ + Right.RawParameters(RightParamIndex), _ + Right, _ + True, _ + LeftWinsAtLeastOnce, _ + RightWinsAtLeastOnce, _ + BothLose) + + Case ComparisonType.ParameterSpecificty + ' Compare ParameterSpecificity + CompareParameterSpecificity( _ + ArgumentType, _ + Left.Parameters(LeftParamIndex), _ + LeftMethod, _ + True, _ + Right.Parameters(RightParamIndex), _ + RightMethod, _ + True, _ + LeftWinsAtLeastOnce, _ + RightWinsAtLeastOnce, _ + BothLose) + End Select + + If (BothLose AndAlso (Not ContinueWhenBothLose)) OrElse _ + (LeftWinsAtLeastOnce AndAlso RightWinsAtLeastOnce) Then + Return Nothing + End If + + ArgIndex += 1 + Loop + + Debug.Assert(Not (LeftWinsAtLeastOnce AndAlso RightWinsAtLeastOnce), _ + "Most specific method logic is confused.") + + If LeftWinsAtLeastOnce Then Return Left + If RightWinsAtLeastOnce Then Return Right + + Return Nothing + End Function + + Private Shared Function MostSpecificProcedure( _ + ByVal Candidates As List(Of Method), _ + ByRef CandidateCount As Integer, _ + ByVal Arguments As Object(), _ + ByVal ArgumentNames As String()) As Method + + + For Each CurrentCandidate As Method In Candidates + + If CurrentCandidate.NotCallable OrElse CurrentCandidate.RequiresNarrowingConversion Then + Continue For + End If + + Dim CurrentCandidateIsBest As Boolean = True + + For Each Contender As Method In Candidates + + If Contender.NotCallable OrElse _ + Contender.RequiresNarrowingConversion OrElse _ + (Contender = CurrentCandidate AndAlso _ + Contender.ParamArrayExpanded = CurrentCandidate.ParamArrayExpanded) Then + + Continue For + End If + + Dim BestOfTheTwo As Method = _ + MoreSpecificProcedure( _ + CurrentCandidate, _ + Contender, _ + Arguments, _ + ArgumentNames, _ + ComparisonType.ParameterSpecificty, _ + ContinueWhenBothLose:=True) 'Bug VSWhidbey 501632 + + If BestOfTheTwo Is CurrentCandidate Then + If Not Contender.LessSpecific Then + Contender.LessSpecific = True + CandidateCount -= 1 + End If + Else + 'The current candidate can't be the most specific. + CurrentCandidateIsBest = False + + If BestOfTheTwo Is Contender AndAlso Not CurrentCandidate.LessSpecific Then + CurrentCandidate.LessSpecific = True + CandidateCount -= 1 + End If + + ' Can't exit early because of VSW 211832 + End If + + Next + + If CurrentCandidateIsBest Then + Debug.Assert(CandidateCount = 1, "Surprising overload candidate remains.") + Return CurrentCandidate + End If + + Next + + Return Nothing + End Function + + Private Shared Function RemoveRedundantGenericProcedures( _ + ByVal Candidates As List(Of Method), _ + ByRef CandidateCount As Integer, _ + ByVal Arguments As Object(), _ + ByVal ArgumentNames As String()) As Method + + + For LeftIndex As Integer = 0 To Candidates.Count - 1 + Dim Left As Method = Candidates(LeftIndex) + + If Not Left.NotCallable Then + + For RightIndex As Integer = LeftIndex + 1 To Candidates.Count - 1 + Dim Right As Method = Candidates(RightIndex) + + If Not Right.NotCallable AndAlso _ + Left.RequiresNarrowingConversion = Right.RequiresNarrowingConversion Then + + Dim LeastGeneric As Method = Nothing + Dim SignatureMismatch As Boolean = False + + ' Least generic based on generic method's type parameters + + If Left.IsGeneric() OrElse Right.IsGeneric() Then + + LeastGeneric = _ + MoreSpecificProcedure( _ + Left, _ + Right, _ + Arguments, _ + ArgumentNames, _ + ComparisonType.GenericSpecificityBasedOnMethodGenericParams, _ + SignatureMismatch) + + If LeastGeneric IsNot Nothing Then + CandidateCount -= 1 + If CandidateCount = 1 Then + Return LeastGeneric + End If + If LeastGeneric Is Left Then + Right.NotCallable = True + Else + Left.NotCallable = True + Exit For + End If + End If + End If + + + ' Least generic based on method's generic parent's type parameters + + If Not SignatureMismatch AndAlso _ + LeastGeneric Is Nothing AndAlso _ + (IsGeneric(Left.DeclaringType) OrElse IsGeneric(Right.DeclaringType)) Then + + LeastGeneric = _ + MoreSpecificProcedure( _ + Left, _ + Right, _ + Arguments, _ + ArgumentNames, _ + ComparisonType.GenericSpecificityBasedOnTypeGenericParams, _ + SignatureMismatch) + + If LeastGeneric IsNot Nothing Then + CandidateCount -= 1 + If CandidateCount = 1 Then + Return LeastGeneric + End If + If LeastGeneric Is Left Then + Right.NotCallable = True + Else + Left.NotCallable = True + Exit For + End If + End If + End If + End If + + Next + + End If + Next + + Return Nothing + End Function + + + Private Shared Sub ReportError( _ + ByVal Errors As List(Of String), _ + ByVal ResourceID As String, _ + ByVal Substitution1 As String, _ + ByVal Substitution2 As Type, _ + ByVal Substitution3 As Type) + + Debug.Assert(Errors IsNot Nothing, "expected error table") + Errors.Add( _ + GetResourceString( _ + ResourceID, _ + Substitution1, _ + VBFriendlyName(Substitution2), _ + VBFriendlyName(Substitution3))) + End Sub + + Private Shared Sub ReportError( _ + ByVal Errors As List(Of String), _ + ByVal ResourceID As String, _ + ByVal Substitution1 As String, _ + ByVal Substitution2 As Method) + + Debug.Assert(Errors IsNot Nothing, "expected error table") + Errors.Add( _ + GetResourceString( _ + ResourceID, _ + Substitution1, _ + Substitution2.ToString)) + End Sub + + Private Shared Sub ReportError( _ + ByVal Errors As List(Of String), _ + ByVal ResourceID As String, _ + ByVal Substitution1 As String) + + Debug.Assert(Errors IsNot Nothing, "expected error table") + Errors.Add( _ + GetResourceString( _ + ResourceID, _ + Substitution1)) + End Sub + + Private Shared Sub ReportError(ByVal Errors As List(Of String), ByVal ResourceID As String) + + Debug.Assert(Errors IsNot Nothing, "expected error table") + Errors.Add( _ + GetResourceString(ResourceID)) + End Sub + + Private Delegate Function ArgumentDetector( _ + ByVal TargetProcedure As Method, _ + ByVal Arguments As Object(), _ + ByVal ArgumentNames As String(), _ + ByVal TypeArguments As Type(), _ + ByVal Errors As List(Of String)) As Boolean + + Private Delegate Function CandidateProperty(ByVal Candidate As Method) As Boolean + + Private Shared Function ReportOverloadResolutionFailure( _ + ByVal OverloadedProcedureName As String, _ + ByVal Candidates As List(Of Method), _ + ByVal Arguments As Object(), _ + ByVal ArgumentNames As String(), _ + ByVal TypeArguments As Type(), _ + ByVal ErrorID As String, _ + ByVal Failure As ResolutionFailure, _ + ByVal Detector As ArgumentDetector, _ + ByVal CandidateFilter As CandidateProperty) As Exception + + Dim ErrorMessage As StringBuilder = New StringBuilder + Dim Errors As New List(Of String) + Dim CandidateReportCount As Integer = 0 + + For Index As Integer = 0 To Candidates.Count - 1 + + Dim CandidateProcedure As Method = Candidates(Index) + + If CandidateFilter(CandidateProcedure) Then + + If CandidateProcedure.HasParamArray Then + 'We may have two versions of paramarray methods in the list. So skip the first + 'one (the unexpanded one). However, we don't want to skip the unexpanded form + 'if the expanded form will fail the filter. + Dim IndexAhead As Integer = Index + 1 + While IndexAhead < Candidates.Count + If CandidateFilter(Candidates(IndexAhead)) AndAlso _ + Candidates(IndexAhead) = CandidateProcedure Then + Continue For + End If + IndexAhead += 1 + End While + End If + + CandidateReportCount += 1 + + Errors.Clear() + Dim Result As Boolean = _ + Detector(CandidateProcedure, Arguments, ArgumentNames, TypeArguments, Errors) + Debug.Assert(Result = False AndAlso Errors.Count > 0, "expected this candidate to fail") + + ErrorMessage.Append(vbCrLf & " '") + ErrorMessage.Append(CandidateProcedure.ToString) + ErrorMessage.Append("':") + For Each ErrorString As String In Errors + ErrorMessage.Append(vbCrLf & " ") + ErrorMessage.Append(ErrorString) + Next + End If + + Next + + Debug.Assert(CandidateReportCount > 0, "expected at least one candidate") + + Dim Message As String = GetResourceString(ErrorID, OverloadedProcedureName, ErrorMessage.ToString) + If CandidateReportCount = 1 Then + 'ParamArrays may cause only one candidate to get reported. In this case, reporting an + 'ambiguity is misleading. + 'CONSIDER 3/1/2004: Using the same error message for the single-candidate case + 'is also misleading, but the benefit is not high enough for constructing a better message. + 'CONSIDER 2/26/2004: InvalidCastException is thrown only for back compat. It would + 'be nice if the latebinder had its own set of exceptions to throw. + Return New InvalidCastException(Message) + Else + Return New AmbiguousMatchException(Message) + End If + End Function + + Private Shared Function DetectArgumentErrors( _ + ByVal TargetProcedure As Method, _ + ByVal Arguments As Object(), _ + ByVal ArgumentNames As String(), _ + ByVal TypeArguments As Type(), _ + ByVal Errors As List(Of String)) As Boolean + + Return _ + CanMatchArguments( _ + TargetProcedure, _ + Arguments, _ + ArgumentNames, _ + TypeArguments, _ + False, _ + Errors) + End Function + + Private Shared Function CandidateIsNotCallable(ByVal Candidate As Method) As Boolean + Return Candidate.NotCallable + End Function + + Private Shared Function ReportUncallableProcedures( _ + ByVal OverloadedProcedureName As String, _ + ByVal Candidates As List(Of Method), _ + ByVal Arguments As Object(), _ + ByVal ArgumentNames As String(), _ + ByVal TypeArguments As Type(), _ + ByVal Failure As ResolutionFailure) As Exception + + Return _ + ReportOverloadResolutionFailure( _ + OverloadedProcedureName, _ + Candidates, _ + Arguments, _ + ArgumentNames, _ + TypeArguments, _ + ResID.NoCallableOverloadCandidates2, _ + Failure, _ + AddressOf DetectArgumentErrors, _ + AddressOf CandidateIsNotCallable) + End Function + + Private Shared Function DetectArgumentNarrowing( _ + ByVal TargetProcedure As Method, _ + ByVal Arguments As Object(), _ + ByVal ArgumentNames As String(), _ + ByVal TypeArguments As Type(), _ + ByVal Errors As List(Of String)) As Boolean + + Return _ + CanMatchArguments( _ + TargetProcedure, _ + Arguments, _ + ArgumentNames, _ + TypeArguments, _ + True, _ + Errors) + End Function + + Private Shared Function CandidateIsNarrowing(ByVal Candidate As Method) As Boolean + Return Not Candidate.NotCallable AndAlso Candidate.RequiresNarrowingConversion + End Function + + Private Shared Function ReportNarrowingProcedures( _ + ByVal OverloadedProcedureName As String, _ + ByVal Candidates As List(Of Method), _ + ByVal Arguments As Object(), _ + ByVal ArgumentNames As String(), _ + ByVal TypeArguments As Type(), _ + ByVal Failure As ResolutionFailure) As Exception + + Return _ + ReportOverloadResolutionFailure( _ + OverloadedProcedureName, _ + Candidates, _ + Arguments, _ + ArgumentNames, _ + TypeArguments, _ + ResID.NoNonNarrowingOverloadCandidates2, _ + Failure, _ + AddressOf DetectArgumentNarrowing, _ + AddressOf CandidateIsNarrowing) + End Function + + Private Shared Function DetectUnspecificity( _ + ByVal TargetProcedure As Method, _ + ByVal Arguments As Object(), _ + ByVal ArgumentNames As String(), _ + ByVal TypeArguments As Type(), _ + ByVal Errors As List(Of String)) As Boolean + + ReportError(Errors, ResID.NotMostSpecificOverload) + Return False + End Function + + Private Shared Function CandidateIsUnspecific(ByVal Candidate As Method) As Boolean + Return Not Candidate.NotCallable AndAlso Not Candidate.RequiresNarrowingConversion AndAlso Not Candidate.LessSpecific + End Function + + Private Shared Function ReportUnspecificProcedures( _ + ByVal OverloadedProcedureName As String, _ + ByVal Candidates As List(Of Method), _ + ByVal Failure As ResolutionFailure) As Exception + + Return _ + ReportOverloadResolutionFailure( _ + OverloadedProcedureName, _ + Candidates, _ + Nothing, _ + Nothing, _ + Nothing, _ + ResID.NoMostSpecificOverload2, _ + Failure, _ + AddressOf DetectUnspecificity, _ + AddressOf CandidateIsUnspecific) + End Function + + Friend Shared Function ResolveOverloadedCall( _ + ByVal MethodName As String, _ + ByVal Candidates As List(Of Method), _ + ByVal Arguments As Object(), _ + ByVal ArgumentNames As String(), _ + ByVal TypeArguments As Type(), _ + ByVal LookupFlags As BindingFlags, _ + ByVal ReportErrors As Boolean, _ + ByRef Failure As ResolutionFailure) As Method + + 'Optimistically hope to succeed. + Failure = ResolutionFailure.None + + 'From here on, CandidateCount will be used to keep track of the + 'number of remaining viable Candidates in the list. + Dim CandidateCount As Integer = Candidates.Count + Dim SomeCandidatesAreGeneric As Boolean = False + + Dim Best As Method = _ + RejectUncallableProcedures( _ + Candidates, _ + Arguments, _ + ArgumentNames, _ + TypeArguments, _ + CandidateCount, _ + SomeCandidatesAreGeneric) + + If CandidateCount = 1 Then + Return Best + End If + + If CandidateCount = 0 Then + Failure = ResolutionFailure.InvalidArgument + If ReportErrors Then + Throw ReportUncallableProcedures(MethodName, Candidates, Arguments, ArgumentNames, TypeArguments, Failure) + End If + Return Nothing + End If + + If SomeCandidatesAreGeneric Then + Best = RemoveRedundantGenericProcedures(Candidates, CandidateCount, Arguments, ArgumentNames) + If CandidateCount = 1 Then + Return Best + End If + End If + + 'See if only one does not require narrowing. If all candidates require narrowing, + 'but one does so only from Object, pick that candidate. + + Dim NarrowOnlyFromObjectCount As Integer = 0 + Dim BestNarrowingCandidate As Method = Nothing + + For Each Candidate As Method In Candidates + + If Not Candidate.NotCallable Then + If Candidate.RequiresNarrowingConversion Then + + CandidateCount -= 1 + + If Candidate.AllNarrowingIsFromObject Then + NarrowOnlyFromObjectCount += 1 + BestNarrowingCandidate = Candidate + End If + Else + Best = Candidate + End If + End If + + Next + + If CandidateCount = 1 Then + Return Best + End If + + If CandidateCount = 0 Then + If NarrowOnlyFromObjectCount = 1 Then + Return BestNarrowingCandidate + End If + + Failure = ResolutionFailure.AmbiguousMatch + If ReportErrors Then + Throw ReportNarrowingProcedures(MethodName, Candidates, Arguments, ArgumentNames, TypeArguments, Failure) + End If + Return Nothing + End If + + Best = MostSpecificProcedure(Candidates, CandidateCount, Arguments, ArgumentNames) + + If Best IsNot Nothing Then + Return Best + End If + + Failure = ResolutionFailure.AmbiguousMatch + If ReportErrors Then + Throw ReportUnspecificProcedures(MethodName, Candidates, Failure) + End If + Return Nothing + End Function + + Friend Shared Function ResolveOverloadedCall( _ + ByVal MethodName As String, _ + ByVal Members As MemberInfo(), _ + ByVal Arguments As Object(), _ + ByVal ArgumentNames As String(), _ + ByVal TypeArguments As Type(), _ + ByVal LookupFlags As BindingFlags, _ + ByVal ReportErrors As Boolean, _ + ByRef Failure As ResolutionFailure, + ByVal BaseReference As Container) As Method + +#If BINDING_LOG Then + Console.WriteLine("== MEMBERS ==") + For Each m As MemberInfo In Members + Console.WriteLine(MemberToString(m)) + Next +#End If + + 'Build the list of candidate Methods, one of which overload resolution will + 'select. + Dim RejectedForArgumentCount As Integer = 0 + Dim RejectedForTypeArgumentCount As Integer = 0 + + Dim Candidates As List(Of Method) = _ + CollectOverloadCandidates( _ + Members, _ + Arguments, _ + Arguments.Length, _ + ArgumentNames, _ + TypeArguments, _ + False, _ + Nothing, _ + RejectedForArgumentCount, _ + RejectedForTypeArgumentCount, + BaseReference) + + ' If there is only one candidate and it is NotCallable, let ResolveOverloadedCall + ' figure out the error message and exception. + If Candidates.Count = 1 AndAlso Not Candidates.Item(0).NotCallable Then + Return Candidates.Item(0) + End If + + If Candidates.Count = 0 Then + Failure = ResolutionFailure.MissingMember + + If ReportErrors Then + Dim ErrorID As String = ResID.NoViableOverloadCandidates1 + + If RejectedForArgumentCount > 0 Then + ErrorID = ResID.NoArgumentCountOverloadCandidates1 + ElseIf RejectedForTypeArgumentCount > 0 Then + ErrorID = ResID.NoTypeArgumentCountOverloadCandidates1 + End If + Throw New MissingMemberException(GetResourceString(ErrorID, MethodName)) + End If + Return Nothing + End If + + Return _ + ResolveOverloadedCall( _ + MethodName, _ + Candidates, _ + Arguments, _ + ArgumentNames, _ + TypeArguments, _ + LookupFlags, _ + ReportErrors, _ + Failure) + + End Function + + End Class + +End Namespace diff --git a/Microsoft.VisualBasic/runtime/msvbalib/Helpers/ProjectData.vb b/Microsoft.VisualBasic/runtime/msvbalib/Helpers/ProjectData.vb new file mode 100644 index 000000000..d45388dc3 --- /dev/null +++ b/Microsoft.VisualBasic/runtime/msvbalib/Helpers/ProjectData.vb @@ -0,0 +1,255 @@ +' Copyright (c) Microsoft Corporation. All rights reserved. + +Imports System +Imports System.Globalization +Imports System.Threading +Imports System.Security +Imports System.Security.Permissions +Imports System.Runtime.ConstrainedExecution + +Imports Microsoft.VisualBasic.CompilerServices.Utils + +Namespace Microsoft.VisualBasic.CompilerServices + +#If Not TELESTO Then + _ + Friend NotInheritable Class AssemblyData + + Friend Sub New() + Dim i As Integer + Dim o As Object + Dim files As Collections.ArrayList = New Collections.ArrayList(256) + + o = Nothing + + For i = 0 To 255 + files.Add(o) + Next + m_Files = files + End Sub + + Friend Function GetChannelObj(ByVal lChannel As Integer) As VB6File + Dim o As Object + + If (lChannel < m_Files.Count) Then + o = m_Files.Item(lChannel) + Else + o = Nothing + End If + + Return CType(o, VB6File) + End Function + + Friend Sub SetChannelObj(ByVal lChannel As Integer, ByVal oFile As VB6File) + If m_Files Is Nothing Then + m_Files = New Collections.ArrayList(256) + End If + + Dim o As Object + + If oFile Is Nothing Then + Dim f As VB6File + f = CType(m_Files.Item(lChannel), VB6File) + If (Not f Is Nothing) Then + f.CloseFile() + End If + m_Files.Item(lChannel) = Nothing + Else + o = oFile + m_Files.Item(lChannel) = o + End If + End Sub + + Public m_Files As Collections.ArrayList + Friend m_DirFiles() As IO.FileSystemInfo + Friend m_DirNextFileIndex As Integer + Friend m_DirAttributes As IO.FileAttributes + + End Class + +#End If 'Not TELESTO + +#If TELESTO Then + 'FIXME: + Public NotInheritable Class ProjectData +#Else + _ + Public NotInheritable Class ProjectData +#End If + + Friend m_Err As ErrObject + Friend m_rndSeed As Integer = &H50000I + Friend m_numprsPtr() As Byte + Friend m_DigitArray() As Byte + + 'm_oProject is per-Thread for each AppDomain + Private Shared m_oProject As ProjectData +#If Not TELESTO Then + Friend m_AssemblyData As Collections.Hashtable +#End If + + Private Sub New() + MyBase.New() + +#If Not TELESTO Then + m_AssemblyData = New System.Collections.Hashtable +#End If + Const DIGIT_ARRAY_SIZE As Integer = 30 + Const NUMPRS_SIZE As Integer = 24 + + ReDim m_numprsPtr(NUMPRS_SIZE - 1) + ReDim m_DigitArray(DIGIT_ARRAY_SIZE - 1) + + End Sub + +#If Not TELESTO Then + Private m_CachedMSCoreLibAssembly As System.Reflection.Assembly = GetType(System.Int32).Assembly + + Friend Function GetAssemblyData(ByVal assem As System.Reflection.Assembly) As AssemblyData + 'The first time, we will get an exception, but the remainder of the time there will be less overhead + ' + 'BEGIN: SECURITY SECURITY SECURITY SECURITY SECURITY SECURITY SECURITY SECURITY + ' + If assem Is Utils.VBRuntimeAssembly OrElse assem Is m_CachedMSCoreLibAssembly Then + 'Must have been from a latebound call to our own apis (potentially through context of mscorlib) + 'This must not be allowed, as it would cause files to be shared across assemblies + Throw New Security.SecurityException(GetResourceString(ResID.Security_LateBoundCallsNotPermitted)) + End If + ' + 'END: SECURITY SECURITY SECURITY SECURITY SECURITY SECURITY SECURITY SECURITY + ' + + Dim AssemData As AssemblyData = CType(m_AssemblyData.Item(assem), AssemblyData) + If (AssemData Is Nothing) Then + AssemData = New AssemblyData + m_AssemblyData.Item(assem) = AssemData + End If + + Return AssemData + End Function +#End If 'NOT Telesto + + Friend Shared Function GetProjectData() As ProjectData + '************************* + '*** PERFORMANCE NOTE: *** + '************************* + ' m_oProject is + ' and is pretty expensive to access so we cache to a local + ' to cut the number of accesses in half + GetProjectData = m_oProject + If GetProjectData Is Nothing Then + GetProjectData = New ProjectData + m_oProject = GetProjectData + End If + End Function + + ''' + ''' This function is called by the compiler in response to err code, e.g. err 123 + ''' It is also called when the compiler encounters a resume that isn't preceded by an On Error command + ''' + ''' + ''' + ''' + Public Shared Function CreateProjectError(ByVal hr As Integer) As System.Exception + '************************* + '*** PERFORMANCE NOTE: *** + '************************* + ' Err Object is and is pretty expensive to access so we cache to a local to cut the number of accesses + Dim ErrObj As ErrObject = Err() + ErrObj.Clear() + Dim ErrNumber As Integer = ErrObj.MapErrorNumber(hr) + Return ErrObj.CreateException(hr, GetResourceString(CType(ErrNumber, vbErrors))) + End Function + + ''' + ''' Called by the compiler in response to falling into a catch block. + ''' Inside the catch statement the compiler generates code to call: + ''' ProjectData::SetProjectError(exception) That call + ''' in turns sets the ErrObject which is accessed via the VB Err statement. + ''' So a VB6 programmer would typically then do something like: + ''' if err.Number = * do something where err accesses the ErrObject that + ''' is set by this method. + ''' + ''' + ''' +#If TELESTO Then + _ + Public Overloads Shared Sub SetProjectError(ByVal ex As Exception) + 'Telesto doesn't uspport Reliability contracts or constrained regions +#Else + _ + _ + Public Overloads Shared Sub SetProjectError(ByVal ex As Exception) + ' The Try/Finally and constrained regions calls guarantee success under high + ' stress conditions by enabling eager jitting of the finally block + System.Runtime.CompilerServices.RuntimeHelpers.PrepareConstrainedRegions() +#End If + Try + Finally + Err.CaptureException(ex) + End Try + End Sub + + ''' + ''' Called by the compiler in response to falling into a catch block. + ''' Inside the catch statement the compiler generates code to call: + ''' ProjectData::SetProjectError(exception, lineNumber) This call + ''' differs from SetProjectError(ex as Exception)because it is called + ''' when the exception is thrown from a specific line number, e.g: + ''' 123: Throw new Exception + ''' 123: Error x80004003 + ''' This method in turn sets the ErrObject which is accessed via the + ''' VB "Err" statement. + ''' So a VB6 programmer could then do something like: + ''' if err.Number = * + ''' err.Erl will also be set + ''' is set by this class. + ''' + ''' + ''' + ''' +#If TELESTO Then + _ + Public Overloads Shared Sub SetProjectError(ByVal ex As Exception, ByVal lErl As Integer) +#Else + _ + _ + Public Overloads Shared Sub SetProjectError(ByVal ex As Exception, ByVal lErl As Integer) + ' The Try/Finally and constrained regions calls guarantee success under high + ' stress conditions by enabling eager jitting of the finally block + System.Runtime.CompilerServices.RuntimeHelpers.PrepareConstrainedRegions() +#End If + Try + Finally + Err.CaptureException(ex, lErl) + End Try + End Sub + +#If TELESTO Then + _ + Public Shared Sub ClearProjectError() +#Else + _ + _ + Public Shared Sub ClearProjectError() + ' The Try/Finally and constrained regions calls guarantee success under high + ' stress conditions by enabling eager jitting of the finally block + System.Runtime.CompilerServices.RuntimeHelpers.PrepareConstrainedRegions() +#End If + Try + Finally + Err.Clear() + End Try + End Sub + +#If Not TELESTO Then 'No vb6 filesystem support in Telesto - this closes out files #1-255. No way to End App a Telesto App (they run in the browser) + _ + _ + _ + Public Shared Sub EndApp() + FileSystem.CloseAllFiles(System.Reflection.Assembly.GetCallingAssembly()) + System.Environment.Exit(0) 'System.Environment.Exit will cause finalizers to be run at shutdown + End Sub +#End If + End Class +End Namespace diff --git a/Microsoft.VisualBasic/runtime/msvbalib/Helpers/SafeNativeMethods.vb b/Microsoft.VisualBasic/runtime/msvbalib/Helpers/SafeNativeMethods.vb new file mode 100644 index 000000000..3e15f3a13 --- /dev/null +++ b/Microsoft.VisualBasic/runtime/msvbalib/Helpers/SafeNativeMethods.vb @@ -0,0 +1,50 @@ +' Copyright (c) Microsoft Corporation. All rights reserved. + +Imports System +Imports System.Security +Imports System.Security.Permissions +Imports System.Text +Imports System.Runtime.InteropServices +Imports System.Runtime.Versioning + +Namespace Microsoft.VisualBasic.CompilerServices + + _ + _ + _ + _ + Friend NotInheritable Class _ + SafeNativeMethods + + _ + Friend Declare Function _ + IsWindowEnabled _ + Lib "user32" (ByVal hwnd As IntPtr) As Boolean + + _ + Friend Declare Function _ + IsWindowVisible _ + Lib "user32" (ByVal hwnd As IntPtr) As Boolean + + _ + Friend Declare Function _ + GetWindowThreadProcessId _ + Lib "user32" (ByVal hwnd As IntPtr, ByRef lpdwProcessId As Integer) As Integer + + _ + Friend Declare Sub _ + GetLocalTime _ + Lib "kernel32" (ByVal systime As NativeTypes.SystemTime) + + '''************************************************************************* + ''' ;New + ''' + ''' FxCop violation: Avoid uninstantiated internal class. + ''' Adding a private constructor to prevent the compiler from generating a default constructor. + ''' + Private Sub New() + End Sub + End Class + +End Namespace + diff --git a/Microsoft.VisualBasic/runtime/msvbalib/Helpers/ShortType.vb b/Microsoft.VisualBasic/runtime/msvbalib/Helpers/ShortType.vb new file mode 100644 index 000000000..abd52132f --- /dev/null +++ b/Microsoft.VisualBasic/runtime/msvbalib/Helpers/ShortType.vb @@ -0,0 +1,137 @@ +' Copyright (c) Microsoft Corporation. All rights reserved. + +Imports System +Imports System.Globalization + +Imports Microsoft.VisualBasic.CompilerServices.Utils + +Namespace Microsoft.VisualBasic.CompilerServices + +#Region " BACKWARDS COMPATIBILITY " + + 'WARNING WARNING WARNING WARNING WARNING + 'This code exists to support Everett compiled applications. Make sure you understand + 'the backwards compatibility ramifications of any edit you make in this region. + 'WARNING WARNING WARNING WARNING WARNING + + _ + Public NotInheritable Class ShortType + ' Prevent creation. + Private Sub New() + End Sub + + Public Shared Function FromString(ByVal Value As String) As Short + + If Value Is Nothing Then + Return 0 + End If + + Try + Dim i64Value As Int64 + + If IsHexOrOctValue(Value, i64Value) Then + Return CShort(i64Value) + End If + + Return CShort(DoubleType.Parse(Value)) + + Catch e As FormatException + Throw New InvalidCastException(GetResourceString(ResID.InvalidCast_FromStringTo, Left(Value, 32), "Short"), e) + End Try + + End Function + + + Public Shared Function FromObject(ByVal Value As Object) As Short + + If Value Is Nothing Then + Return 0S + End If + + Dim ValueInterface As IConvertible + Dim ValueTypeCode As TypeCode + + ValueInterface = TryCast(Value, IConvertible) + + If ValueInterface Is Nothing Then + GoTo ThrowInvalidCast + End If + + ValueTypeCode = ValueInterface.GetTypeCode() + + Select Case ValueTypeCode + + Case TypeCode.Boolean + Return CShort(ValueInterface.ToBoolean(Nothing)) + + Case TypeCode.Byte + If TypeOf Value Is System.Byte Then + Return CShort(DirectCast(Value, Byte)) + Else + Return CShort(ValueInterface.ToByte(Nothing)) + End If + + Case TypeCode.Int16 + If TypeOf Value Is System.Int16 Then + Return CShort(DirectCast(Value, Int16)) + Else + Return CShort(ValueInterface.ToInt16(Nothing)) + End If + + Case TypeCode.Int32 + If TypeOf Value Is System.Int32 Then + Return CShort(DirectCast(Value, Int32)) + Else + Return CShort(ValueInterface.ToInt32(Nothing)) + End If + + Case TypeCode.Int64 + If TypeOf Value Is System.Int64 Then + Return CShort(DirectCast(Value, Int64)) + Else + Return CShort(ValueInterface.ToInt64(Nothing)) + End If + + Case TypeCode.Single + If TypeOf Value Is System.Single Then + Return CShort(DirectCast(Value, Single)) + Else + Return CShort(ValueInterface.ToSingle(Nothing)) + End If + + Case TypeCode.Double + If TypeOf Value Is System.Double Then + Return CShort(DirectCast(Value, Double)) + Else + Return CShort(ValueInterface.ToDouble(Nothing)) + End If + + Case TypeCode.Decimal + 'Do not use .ToDecimal because of jit temp issue effects all perf + Return DecimalToShort(ValueInterface) + + Case TypeCode.String + Return ShortType.FromString(ValueInterface.ToString(Nothing)) + Case TypeCode.Char, _ + TypeCode.DateTime + ' Fall through to error + + Case Else + ' Fall through to error + End Select +ThrowInvalidCast: + Throw New InvalidCastException(GetResourceString(ResID.InvalidCast_FromTo, VBFriendlyName(Value), "Short")) + + End Function + + Private Shared Function DecimalToShort(ByVal ValueInterface As IConvertible) As Short + Return CShort(ValueInterface.ToDecimal(Nothing)) + End Function + + End Class + +#End Region + +End Namespace + + diff --git a/Microsoft.VisualBasic/runtime/msvbalib/Helpers/SingleType.vb b/Microsoft.VisualBasic/runtime/msvbalib/Helpers/SingleType.vb new file mode 100644 index 000000000..2f5275fc5 --- /dev/null +++ b/Microsoft.VisualBasic/runtime/msvbalib/Helpers/SingleType.vb @@ -0,0 +1,150 @@ +' Copyright (c) Microsoft Corporation. All rights reserved. + +Imports System +Imports System.Globalization + +Imports Microsoft.VisualBasic.CompilerServices.Utils + +Namespace Microsoft.VisualBasic.CompilerServices + +#Region " BACKWARDS COMPATIBILITY " + + 'WARNING WARNING WARNING WARNING WARNING + 'This code exists to support Everett compiled applications. Make sure you understand + 'the backwards compatibility ramifications of any edit you make in this region. + 'WARNING WARNING WARNING WARNING WARNING + + _ + Public NotInheritable Class SingleType + ' Prevent creation. + Private Sub New() + End Sub + + Public Shared Function FromString(ByVal Value As String) As Single + Return FromString(Value, Nothing) + End Function + + Public Shared Function FromString(ByVal Value As String, ByVal NumberFormat As NumberFormatInfo) As Single + + If Value Is Nothing Then + Return 0 + End If + + Try + Dim i64Value As Int64 + + If IsHexOrOctValue(Value, i64Value) Then + Return CSng(i64Value) + End If + + Dim Result As Double = DoubleType.Parse(Value, NumberFormat) + If (Result < System.Single.MinValue OrElse Result > System.Single.MaxValue) AndAlso _ + Not System.Double.IsInfinity(Result) Then + Throw New OverflowException + End If + Return CSng(Result) + + Catch e As FormatException + Throw New InvalidCastException(GetResourceString(ResID.InvalidCast_FromStringTo, Left(Value, 32), "Single"), e) + End Try + + End Function + + Public Shared Function FromObject(ByVal Value As Object) As Single + Return FromObject(Value, Nothing) + End Function + + Public Shared Function FromObject(ByVal Value As Object, ByVal NumberFormat As NumberFormatInfo) As Single + + If Value Is Nothing Then + Return 0 + End If + + Dim ValueInterface As IConvertible + Dim ValueTypeCode As TypeCode + + ValueInterface = TryCast(Value, IConvertible) + + If ValueInterface Is Nothing Then + GoTo ThrowInvalidCast + End If + + ValueTypeCode = ValueInterface.GetTypeCode() + + Select Case ValueTypeCode + + Case TypeCode.Boolean + Return CSng(ValueInterface.ToBoolean(Nothing)) + + Case TypeCode.Byte + If TypeOf Value Is System.Byte Then + Return CSng(DirectCast(Value, Byte)) + Else + Return CSng(ValueInterface.ToByte(Nothing)) + End If + + Case TypeCode.Int16 + If TypeOf Value Is System.Int16 Then + Return CSng(DirectCast(Value, Int16)) + Else + Return CSng(ValueInterface.ToInt16(Nothing)) + End If + + Case TypeCode.Int32 + If TypeOf Value Is System.Int32 Then + Return CSng(DirectCast(Value, Int32)) + Else + Return CSng(ValueInterface.ToInt32(Nothing)) + End If + + Case TypeCode.Int64 + If TypeOf Value Is System.Int64 Then + Return CSng(DirectCast(Value, Int64)) + Else + Return CSng(ValueInterface.ToInt64(Nothing)) + End If + + Case TypeCode.Single + If TypeOf Value Is System.Single Then + Return DirectCast(Value, Single) + Else + Return ValueInterface.ToSingle(Nothing) + End If + + Case TypeCode.Double + If TypeOf Value Is System.Double Then + Return CSng(DirectCast(Value, Double)) + Else + Return CSng(ValueInterface.ToDouble(Nothing)) + End If + + Case TypeCode.Decimal + 'Do not use .ToDecimal because of jit temp issue effects all perf + Return DecimalToSingle(ValueInterface) + + Case TypeCode.String + Return SingleType.FromString(ValueInterface.ToString(Nothing), NumberFormat) + + Case TypeCode.Char, _ + TypeCode.DateTime + ' Fall through to error + + Case Else + ' Fall through to error + End Select + +ThrowInvalidCast: + Throw New InvalidCastException(GetResourceString(ResID.InvalidCast_FromTo, VBFriendlyName(Value), "Single")) + End Function + + Private Shared Function DecimalToSingle(ByVal ValueInterface As IConvertible) As Single + Return CSng(ValueInterface.ToDecimal(Nothing)) + End Function + + End Class + +#End Region + +End Namespace + + diff --git a/Microsoft.VisualBasic/runtime/msvbalib/Helpers/StaticLocals.vb b/Microsoft.VisualBasic/runtime/msvbalib/Helpers/StaticLocals.vb new file mode 100644 index 000000000..f5890a45f --- /dev/null +++ b/Microsoft.VisualBasic/runtime/msvbalib/Helpers/StaticLocals.vb @@ -0,0 +1,60 @@ +' Copyright (c) Microsoft Corporation. All rights reserved. + +Namespace Microsoft.VisualBasic.CompilerServices + +#If TELESTO Then + 'FIXME: + Public NotInheritable Class StaticLocalInitFlag +#Else + _ + _ + Public NotInheritable Class StaticLocalInitFlag +#End If + Public State As Short + End Class + +#If TELESTO Then + 'FIXME: + Public NotInheritable Class IncompleteInitialization +#Else + _ + _ + Public NotInheritable Class IncompleteInitialization +#End If + + Inherits System.Exception + +#If Not TELESTO Then + ' FxCop: deserialization constructor must be defined as private. + _ + Private Sub New(ByVal info As System.Runtime.Serialization.SerializationInfo, ByVal context As System.Runtime.Serialization.StreamingContext) + MyBase.New(info, context) + End Sub +#End If + +#If TELESTO Then + Public Sub New(ByVal message As String) +#Else + _ + Public Sub New(ByVal message As String) +#End If + MyBase.New(message) + End Sub + +#If TELESTO Then + Public Sub New(ByVal message As String, ByVal innerException As System.Exception) +#Else + _ + Public Sub New(ByVal message As String, ByVal innerException As System.Exception) +#End If + MyBase.New(message, innerException) + End Sub + + ' default constructor + Public Sub New() + MyBase.New() + End Sub + + End Class + +End Namespace diff --git a/Microsoft.VisualBasic/runtime/msvbalib/Helpers/StringType.vb b/Microsoft.VisualBasic/runtime/msvbalib/Helpers/StringType.vb new file mode 100644 index 000000000..0039fe38b --- /dev/null +++ b/Microsoft.VisualBasic/runtime/msvbalib/Helpers/StringType.vb @@ -0,0 +1,861 @@ +' Copyright (c) Microsoft Corporation. All rights reserved. + +Imports System +Imports System.Globalization +Imports System.Text + +Imports Microsoft.VisualBasic.CompilerServices.ExceptionUtils +Imports Microsoft.VisualBasic.CompilerServices.Utils + +Namespace Microsoft.VisualBasic.CompilerServices + +#Region " BACKWARDS COMPATIBILITY " + + 'WARNING WARNING WARNING WARNING WARNING + 'This code exists to support Everett compiled applications. Make sure you understand + 'the backwards compatibility ramifications of any edit you make in this region. + 'WARNING WARNING WARNING WARNING WARNING + + _ + Public NotInheritable Class StringType + ' Prevent creation. + Private Sub New() + End Sub + + Private Const GENERAL_FORMAT As String = "G" + + '============================================================================ + ' Coercion to functions. + '============================================================================ + Public Shared Function FromBoolean(ByVal Value As Boolean) As String + If Value Then + Return System.Boolean.TrueString + Else + Return System.Boolean.FalseString + End If + End Function + + Public Shared Function FromByte(ByVal Value As Byte) As String + Return Value.ToString(Nothing, Nothing) + End Function + + Public Shared Function FromChar(ByVal Value As Char) As String + Return Value.ToString() + End Function + + Public Shared Function FromShort(ByVal Value As Short) As String + Return Value.ToString(Nothing, Nothing) + End Function + + Public Shared Function FromInteger(ByVal Value As Integer) As String + Return Value.ToString(Nothing, Nothing) + End Function + + Public Shared Function FromLong(ByVal Value As Long) As String + Return Value.ToString(Nothing, Nothing) + End Function + + Public Shared Function FromSingle(ByVal Value As Single) As String + Return FromSingle(Value, Nothing) + End Function + + Public Shared Function FromDouble(ByVal Value As Double) As String + Return FromDouble(Value, Nothing) + End Function + + 'Change to this code after the NDP drop includes the formatting changes + Public Shared Function FromSingle(ByVal Value As Single, ByVal NumberFormat As NumberFormatInfo) As String + Return Value.ToString(Nothing, NumberFormat) + End Function + + Public Shared Function FromDouble(ByVal Value As Double, ByVal NumberFormat As NumberFormatInfo) As String + Return Value.ToString("G", NumberFormat) + End Function + +#If CHANGE_TO_NDP_GENERAL_FORMAT Then + + Public Shared Function FromDate(ByVal Value As DateTime) As String + Return Value.ToString(Nothing, Nothing) + End Function + +#Else + Public Shared Function FromDate(ByVal Value As Date) As String + Dim TimeTicks As Long = Value.TimeOfDay.Ticks + + If (TimeTicks = Value.Ticks) OrElse _ + (Value.Year = 1899 AndAlso Value.Month = 12 AndAlso Value.Day = 30) Then 'OA Date with no date is 1899-12-30 + 'No date (1/1/1) + 'UNDONE: REVIEW OA DATE HACK + Return Value.ToString("T", Nothing) + ElseIf TimeTicks = 0 Then + 'No time, or is midnight + Return Value.ToString("d", Nothing) + Else + Return Value.ToString(GENERAL_FORMAT, Nothing) + End If + End Function +#End If + + Public Shared Function FromDecimal(ByVal Value As Decimal) As String + Return FromDecimal(Value, Nothing) + End Function + + Public Shared Function FromDecimal(ByVal Value As Decimal, ByVal NumberFormat As NumberFormatInfo) As String + Return Value.ToString("G", NumberFormat) + End Function + + Public Shared Function FromObject(ByVal Value As Object) As String + + If Value Is Nothing Then + Return Nothing + + Else + Dim StringValue As String = TryCast(Value, String) + + If StringValue IsNot Nothing Then + Return StringValue + End If + End If + + Dim ValueInterface As IConvertible + Dim ValueTypeCode As TypeCode + + ValueInterface = TryCast(Value, IConvertible) + + If Not ValueInterface Is Nothing Then + + ValueTypeCode = ValueInterface.GetTypeCode() + + Select Case ValueTypeCode + Case TypeCode.Boolean + Return FromBoolean(ValueInterface.ToBoolean(Nothing)) + + Case TypeCode.Byte + Return FromByte(ValueInterface.ToByte(Nothing)) + + Case TypeCode.Int16 + Return FromShort(ValueInterface.ToInt16(Nothing)) + + Case TypeCode.Int32 + Return FromInteger(ValueInterface.ToInt32(Nothing)) + + Case TypeCode.Int64 + Return FromLong(ValueInterface.ToInt64(Nothing)) + + Case TypeCode.Single + Return FromSingle(ValueInterface.ToSingle(Nothing)) + + Case TypeCode.Double + Return FromDouble(ValueInterface.ToDouble(Nothing)) + + Case TypeCode.Decimal + Return FromDecimal(ValueInterface.ToDecimal(Nothing)) + + Case TypeCode.String + Return ValueInterface.ToString(Nothing) + + Case TypeCode.Char + Return FromChar(ValueInterface.ToChar(Nothing)) + + Case TypeCode.DateTime + Return FromDate(ValueInterface.ToDateTime(Nothing)) + + Case Else + ' Fall through to error + End Select + + Else + Dim CharArray As Char() = TryCast(Value, Char()) + + If CharArray IsNot Nothing AndAlso CharArray.Rank = 1 Then + Return New String(CharArrayType.FromObject(Value)) + End If + End If + + Throw New InvalidCastException(GetResourceString(ResID.InvalidCast_FromTo, VBFriendlyName(Value), "String")) + + End Function + + '============================================================================ + ' Compare/concat/len functions. + '============================================================================ + Public Shared Function StrCmp(ByVal sLeft As String, ByVal sRight As String, ByVal TextCompare As Boolean) As Integer + + If sLeft Is sRight Then + Return 0 + End If + + If sLeft Is Nothing Then + If sRight.Length() = 0 Then + Return 0 + End If + + Return -1 + End If + + If sRight Is Nothing Then + If sLeft.Length() = 0 Then + Return 0 + End If + + Return 1 + End If + + If TextCompare Then + Return GetCultureInfo().CompareInfo.Compare(sLeft, sRight, OptionCompareTextFlags) + Else + Return System.String.CompareOrdinal(sLeft, sRight) + End If + + End Function + +#Region " BACKWARDS COMPATIBILITY " + + 'WARNING WARNING WARNING WARNING WARNING + 'This code exists to support Everett compiled applications. Make sure you understand + 'the backwards compatibility ramifications of any edit you make in this region. + 'WARNING WARNING WARNING WARNING WARNING + + Public Shared Function StrLike(ByVal Source As String, ByVal Pattern As String, ByVal CompareOption As CompareMethod) As Boolean + If CompareOption = CompareMethod.Binary Then + Return StrLikeBinary(Source, Pattern) + Else + Return StrLikeText(Source, Pattern) + End If + End Function + + Public Shared Function StrLikeBinary(ByVal Source As String, ByVal Pattern As String) As Boolean + 'Match Source to Pattern using "?*#[!a-g]" pattern matching characters + Dim SourceIndex As Integer + Dim PatternIndex As Integer + Dim SourceEndIndex As Integer + Dim PatternEndIndex As Integer + Dim p As Char + Dim s As Char + Dim InsideBracket As Boolean + Dim SeenHyphen As Boolean + Dim StartRangeChar As Char + Dim EndRangeChar As Char + Dim Match As Boolean + Dim SeenLiteral As Boolean + Dim SeenNot As Boolean + Dim Skip As Integer + Const NullChar As Char = ChrW(0) + Dim LiteralIsRangeEnd As Boolean = False + + ' Options = CompareOptions.Ordinal + + If Pattern Is Nothing Then + PatternEndIndex = 0 + Else + PatternEndIndex = Pattern.Length + End If + + If Source Is Nothing Then + SourceEndIndex = 0 + Else + SourceEndIndex = Source.Length + End If + + If SourceIndex < SourceEndIndex Then + s = Source.Chars(SourceIndex) + End If + + Do While PatternIndex < PatternEndIndex + p = Pattern.Chars(PatternIndex) + + If p = "*"c AndAlso (Not InsideBracket) Then 'If Then Else has faster performance the Select Case + 'Determine how many source chars to skip + Skip = AsteriskSkip(Pattern.Substring(PatternIndex + 1), Source.Substring(SourceIndex), SourceEndIndex - SourceIndex, CompareMethod.Binary, m_InvariantCompareInfo) + + If Skip < 0 Then + Return False + ElseIf Skip > 0 Then + SourceIndex += Skip + If SourceIndex < SourceEndIndex Then + s = Source.Chars(SourceIndex) + End If + End If + + ElseIf p = "?"c AndAlso (Not InsideBracket) Then + 'Match any character + SourceIndex = SourceIndex + 1 + If SourceIndex < SourceEndIndex Then + s = Source.Chars(SourceIndex) + End If + + ElseIf p = "#"c AndAlso (Not InsideBracket) Then + If Not System.Char.IsDigit(s) Then + Exit Do + End If + SourceIndex = SourceIndex + 1 + If SourceIndex < SourceEndIndex Then + s = Source.Chars(SourceIndex) + End If + + ElseIf p = "-"c AndAlso _ + (InsideBracket AndAlso SeenLiteral AndAlso (Not LiteralIsRangeEnd) AndAlso (Not SeenHyphen)) AndAlso _ + (((PatternIndex + 1) >= PatternEndIndex) OrElse (Pattern.Chars(PatternIndex + 1) <> "]"c)) Then + + SeenHyphen = True + + ElseIf p = "!"c AndAlso _ + (InsideBracket AndAlso (Not SeenNot)) Then + + SeenNot = True + Match = True + + ElseIf p = "["c AndAlso (Not InsideBracket) Then + InsideBracket = True + StartRangeChar = NullChar + EndRangeChar = NullChar + SeenLiteral = False + + ElseIf p = "]"c AndAlso InsideBracket Then + InsideBracket = False + + If SeenLiteral Then + If Match Then + SourceIndex += 1 + If SourceIndex < SourceEndIndex Then + s = Source.Chars(SourceIndex) + End If + Else + Exit Do + End If + ElseIf SeenHyphen Then + If Not Match Then + Exit Do + End If + ElseIf SeenNot Then + '[!] should be matched to literal ! same as if outside brackets + If "!"c <> s Then + Exit Do + End If + SourceIndex += 1 + If SourceIndex < SourceEndIndex Then + s = Source.Chars(SourceIndex) + End If + End If + + Match = False + SeenLiteral = False + SeenNot = False + SeenHyphen = False + + Else + 'Literal character + SeenLiteral = True + LiteralIsRangeEnd = False + + If InsideBracket Then + If SeenHyphen Then + SeenHyphen = False + LiteralIsRangeEnd = True + EndRangeChar = p + + If StartRangeChar > EndRangeChar Then + Throw VbMakeException(vbErrors.BadPatStr) + ElseIf (SeenNot AndAlso Match) OrElse (Not SeenNot AndAlso Not Match) Then + 'Calls to ci.Compare are expensive, avoid them for good performance + Match = (s > StartRangeChar) AndAlso (s <= EndRangeChar) + + If SeenNot Then + Match = Not Match + End If + End If + Else + StartRangeChar = p + + 'This compare handles non range chars such as the "abc" and "uvw" + 'and the first char of a range such as "d" in "[abcd-tuvw]". + Match = StrLikeCompareBinary(SeenNot, Match, p, s) + End If + Else + If p <> s AndAlso Not SeenNot Then + Exit Do + End If + + SeenNot = False + SourceIndex += 1 + + If SourceIndex < SourceEndIndex Then + s = Source.Chars(SourceIndex) + ElseIf SourceIndex > SourceEndIndex Then + Return False + End If + End If + End If + + PatternIndex += 1 + Loop + + If InsideBracket Then + If SourceEndIndex = 0 Then + Return False + Else + Throw New ArgumentException(GetResourceString(ResID.Argument_InvalidValue1, "Pattern")) + End If + Else + Return (PatternIndex = PatternEndIndex) AndAlso (SourceIndex = SourceEndIndex) + End If + End Function + + Public Shared Function StrLikeText(ByVal Source As String, ByVal Pattern As String) As Boolean + 'Match Source to Pattern using "?*#[!a-g]" pattern matching characters + Dim SourceIndex As Integer + Dim PatternIndex As Integer + Dim SourceEndIndex As Integer + Dim PatternEndIndex As Integer + Dim p As Char + Dim s As Char + Dim InsideBracket As Boolean + Dim SeenHyphen As Boolean + Dim StartRangeChar As Char + Dim EndRangeChar As Char + Dim Match As Boolean + Dim SeenLiteral As Boolean + Dim SeenNot As Boolean + Dim Skip As Integer + Dim Options As CompareOptions + Dim ci As CompareInfo + Const NullChar As Char = ChrW(0) + Dim LiteralIsRangeEnd As Boolean = False + + If Pattern Is Nothing Then + PatternEndIndex = 0 + Else + PatternEndIndex = Pattern.Length + End If + + If Source Is Nothing Then + SourceEndIndex = 0 + Else + SourceEndIndex = Source.Length + End If + + If SourceIndex < SourceEndIndex Then + s = Source.Chars(SourceIndex) + End If + + ci = GetCultureInfo().CompareInfo + Options = CompareOptions.IgnoreCase Or _ + CompareOptions.IgnoreWidth Or _ + CompareOptions.IgnoreNonSpace Or _ + CompareOptions.IgnoreKanaType + + Do While PatternIndex < PatternEndIndex + p = Pattern.Chars(PatternIndex) + + If p = "*"c AndAlso (Not InsideBracket) Then 'If Then Else has faster performance the Select Case + 'Determine how many source chars to skip + Skip = AsteriskSkip(Pattern.Substring(PatternIndex + 1), Source.Substring(SourceIndex), SourceEndIndex - SourceIndex, CompareMethod.Text, ci) + + If Skip < 0 Then + Return False + ElseIf Skip > 0 Then + SourceIndex += Skip + If SourceIndex < SourceEndIndex Then + s = Source.Chars(SourceIndex) + End If + End If + + ElseIf p = "?"c AndAlso (Not InsideBracket) Then + 'Match any character + SourceIndex = SourceIndex + 1 + If SourceIndex < SourceEndIndex Then + s = Source.Chars(SourceIndex) + End If + + ElseIf p = "#"c AndAlso (Not InsideBracket) Then + If Not System.Char.IsDigit(s) Then + Exit Do + End If + SourceIndex = SourceIndex + 1 + If SourceIndex < SourceEndIndex Then + s = Source.Chars(SourceIndex) + End If + + ElseIf p = "-"c AndAlso _ + (InsideBracket AndAlso SeenLiteral AndAlso (Not LiteralIsRangeEnd) AndAlso (Not SeenHyphen)) AndAlso _ + (((PatternIndex + 1) >= PatternEndIndex) OrElse (Pattern.Chars(PatternIndex + 1) <> "]"c)) Then + + SeenHyphen = True + + ElseIf p = "!"c AndAlso _ + (InsideBracket AndAlso Not SeenNot) Then + SeenNot = True + Match = True + + ElseIf p = "["c AndAlso (Not InsideBracket) Then + InsideBracket = True + StartRangeChar = NullChar + EndRangeChar = NullChar + SeenLiteral = False + + ElseIf p = "]"c AndAlso InsideBracket Then + InsideBracket = False + + If SeenLiteral Then + If Match Then + SourceIndex += 1 + If SourceIndex < SourceEndIndex Then + s = Source.Chars(SourceIndex) + End If + Else + Exit Do + End If + ElseIf SeenHyphen Then + If Not Match Then + Exit Do + End If + ElseIf SeenNot Then + '[!] should be matched to literal ! same as if outside brackets + If (ci.Compare("!", s) <> 0) Then + Exit Do + End If + SourceIndex += 1 + If SourceIndex < SourceEndIndex Then + s = Source.Chars(SourceIndex) + End If + End If + + Match = False + SeenLiteral = False + SeenNot = False + SeenHyphen = False + + Else + 'Literal character + SeenLiteral = True + LiteralIsRangeEnd = False + + If InsideBracket Then + If SeenHyphen Then + SeenHyphen = False + LiteralIsRangeEnd = True + EndRangeChar = p + + If StartRangeChar > EndRangeChar Then + Throw VbMakeException(vbErrors.BadPatStr) + ElseIf (SeenNot AndAlso Match) OrElse (Not SeenNot AndAlso Not Match) Then + 'Calls to ci.Compare are expensive, avoid them for good performance + If Options = CompareOptions.Ordinal Then + Match = (s > StartRangeChar) AndAlso (s <= EndRangeChar) + Else + Match = (ci.Compare(StartRangeChar, s, Options) < 0) AndAlso (ci.Compare(EndRangeChar, s, Options) >= 0) + End If + + If SeenNot Then + Match = Not Match + End If + End If + Else + StartRangeChar = p + + 'This compare handles non range chars such as the "abc" and "uvw" + 'and the first char of a range such as "d" in "[abcd-tuvw]". + Match = StrLikeCompare(ci, SeenNot, Match, p, s, Options) + End If + Else + If Options = CompareOptions.Ordinal Then + If p <> s AndAlso Not SeenNot Then + Exit Do + End If + Else + ' Slurp up the diacritical marks, if any (both non-spacing marks and modifier symbols) + ' Note that typically, we'll only have at most one diacritical mark. Therefore, I'm not + ' using StringBuilder here, since the minimal overhead of appending a character doesn't + ' justify invoking a couple of instances of StringBuilder. . + Dim pstr As String = p + Dim sstr As String = s + Do While PatternIndex + 1 < PatternEndIndex AndAlso _ + (UnicodeCategory.ModifierSymbol = Char.GetUnicodeCategory(Pattern.Chars(PatternIndex + 1)) OrElse _ + UnicodeCategory.NonSpacingMark = Char.GetUnicodeCategory(Pattern.Chars(PatternIndex + 1))) + pstr = pstr & Pattern.Chars(PatternIndex + 1) + PatternIndex = PatternIndex + 1 + Loop + Do While SourceIndex + 1 < SourceEndIndex AndAlso _ + (UnicodeCategory.ModifierSymbol = Char.GetUnicodeCategory(Source.Chars(SourceIndex + 1)) OrElse _ + UnicodeCategory.NonSpacingMark = Char.GetUnicodeCategory(Source.Chars(SourceIndex + 1))) + sstr = sstr & Source.Chars(SourceIndex + 1) + SourceIndex = SourceIndex + 1 + Loop + + If (ci.Compare(pstr, sstr, OptionCompareTextFlags) <> 0) AndAlso Not SeenNot Then + Exit Do + End If + End If + + SeenNot = False + SourceIndex += 1 + + If SourceIndex < SourceEndIndex Then + s = Source.Chars(SourceIndex) + ElseIf SourceIndex > SourceEndIndex Then + Return False + End If + End If + End If + + PatternIndex += 1 + Loop + + If InsideBracket Then + If SourceEndIndex = 0 Then + Return False + Else + Throw New ArgumentException(GetResourceString(ResID.Argument_InvalidValue1, "Pattern")) + End If + Else + Return (PatternIndex = PatternEndIndex) AndAlso (SourceIndex = SourceEndIndex) + End If + End Function + + Private Shared Function StrLikeCompareBinary(ByVal SeenNot As Boolean, ByVal Match As Boolean, ByVal p As Char, ByVal s As Char) As Boolean + If SeenNot AndAlso Match Then + Return p <> s + ElseIf Not SeenNot AndAlso Not Match Then + Return p = s + Else + Return Match + End If + End Function + + Private Shared Function StrLikeCompare(ByVal ci As CompareInfo, ByVal SeenNot As Boolean, ByVal Match As Boolean, ByVal p As Char, ByVal s As Char, ByVal Options As CompareOptions) As Boolean + If SeenNot AndAlso Match Then + If Options = CompareOptions.Ordinal Then + Return p <> s + Else + Return Not (ci.Compare(p, s, Options) = 0) + End If + ElseIf Not SeenNot AndAlso Not Match Then + If Options = CompareOptions.Ordinal Then + Return p = s + Else + Return (ci.Compare(p, s, Options) = 0) + End If + Else + Return Match + End If + End Function + + Private Shared Function AsteriskSkip(ByVal Pattern As String, ByVal Source As String, ByVal SourceEndIndex As Integer, _ + ByVal CompareOption As CompareMethod, ByVal ci As CompareInfo) As Integer + + 'Returns the number of source characters to skip over to handle an asterisk in the pattern. + 'When there's only a single asterisk in the pattern, it computes how many pattern equivalent chars + 'follow the *: [a-z], [abcde], ?, # each count as one char. + 'Pattern contains the substring following the * + 'Source contains the substring not yet matched. + + Dim p As Char + Dim SeenLiteral As Boolean + Dim SeenSpecial As Boolean 'Remembers if we've seen #, ?, [abd-eg], or ! when they have their special meanings + Dim InsideBracket As Boolean + Dim Count As Integer + Dim PatternEndIndex As Integer + Dim PatternIndex As Integer + Dim TruncatedPattern As String + Dim Options As CompareOptions + + PatternEndIndex = Len(Pattern) + + 'Determine how many pattern equivalent chars follow the *, and if there are multiple *s + '[a-z], [abcde] each count as one char. + Do While PatternIndex < PatternEndIndex + p = Pattern.Chars(PatternIndex) + + Select Case p + Case "*"c + If Count > 0 Then + 'We found multiple asterisks with an intervening pattern + If SeenSpecial Then + 'Pattern uses special characters which means we can't compute easily how far to skip. + Count = MultipleAsteriskSkip(Pattern, Source, Count, CompareOption) + Return SourceEndIndex - Count + Else + 'Pattern uses only literals, so we can directly search for the pattern in the source + 'TODO: Handle cases where pattern could be replicated in the source. + TruncatedPattern = Pattern.Substring(0, PatternIndex) 'Remove the second * and everything trailing + + If CompareOption = CompareMethod.Binary Then + Options = CompareOptions.Ordinal + Else + Options = CompareOptions.IgnoreCase Or CompareOptions.IgnoreWidth Or CompareOptions.IgnoreNonSpace Or CompareOptions.IgnoreKanaType + End If + + 'Count = Source.LastIndexOf(TruncatedPattern) + Count = ci.LastIndexOf(Source, TruncatedPattern, Options) + Return Count + End If + + Else + 'Do nothing, which colalesces multiple asterisks together + End If + + Case "-"c + If Pattern.Chars(PatternIndex + 1) = "]"c Then + SeenLiteral = True + End If + + Case "!"c + If Pattern.Chars(PatternIndex + 1) = "]"c Then + SeenLiteral = True + Else + SeenSpecial = True + End If + + Case "["c + If InsideBracket Then + SeenLiteral = True + Else + InsideBracket = True + End If + + Case "]"c + If SeenLiteral OrElse Not InsideBracket Then + Count += 1 + SeenSpecial = True + End If + SeenLiteral = False + InsideBracket = False + + Case "?"c, "#"c + If InsideBracket Then + SeenLiteral = True + Else + Count += 1 + SeenSpecial = True + End If + + Case Else + If InsideBracket Then + SeenLiteral = True + Else + Count += 1 + End If + End Select + + PatternIndex += 1 + Loop + + Return SourceEndIndex - Count + End Function + + Private Shared Function MultipleAsteriskSkip(ByVal Pattern As String, ByVal Source As String, ByVal Count As Integer, ByVal CompareOption As CompareMethod) As Integer + 'Multiple asterisks with intervening chars were found in the pattern, such as "**". + 'Use a recursive approach to determine how many source chars to skip. + 'Start near the end of Source and move backwards one char at a time until a match is found or we reach start of Source. + + Dim SourceEndIndex As Integer + Dim NewSource As String + Dim Result As Boolean + + SourceEndIndex = Len(Source) + + Do While Count < SourceEndIndex + NewSource = Source.Substring(SourceEndIndex - Count) + + Try + Result = StrLike(NewSource, Pattern, CompareOption) + Catch ex As StackOverflowException + Throw ex + Catch ex As OutOfMemoryException + Throw ex + Catch ex As System.Threading.ThreadAbortException + Throw ex + Catch + Result = False + End Try + + If Result Then + Exit Do + End If + + Count += 1 + Loop + + Return Count + End Function +#End Region ' BACKWARDS COMPATIBILITY + + + Public Shared Sub MidStmtStr(ByRef sDest As String, ByVal StartPosition As Integer, ByVal MaxInsertLength As Integer, ByVal sInsert As String) + Dim DestLength As Integer + Dim InsertLength As Integer + Dim EndSegmentLength As Integer + + If sDest Is Nothing Then + 'DestLength = 0 + Else + DestLength = sDest.Length + End If + + If sInsert Is Nothing Then + 'InsertLength = 0 + Else + InsertLength = sInsert.Length + End If + + 'Zero base the index + StartPosition -= 1 + + If StartPosition < 0 OrElse StartPosition >= DestLength Then + Throw New ArgumentException(GetResourceString(ResID.Argument_InvalidValue1, "Start")) + End If + + If MaxInsertLength < 0 Then + Throw New ArgumentException(GetResourceString(ResID.Argument_InvalidValue1, "Length")) + End If + + ' first, limit the length of the source string + ' to lenChange + + If (InsertLength > MaxInsertLength) Then + InsertLength = MaxInsertLength + End If + + ' second, limit the length to the available space + ' in the destination string + + If (InsertLength > DestLength - StartPosition) Then + InsertLength = DestLength - StartPosition + End If + + If InsertLength = 0 Then + 'Destination string remains unchanged + Exit Sub + End If + + 'This looks a bit complex for removing and inserting strings + 'but when manipulating long strings, it should provide + 'better performance because of fewer memcpys + + Dim sb As StringBuilder + + sb = New StringBuilder(DestLength) + + If StartPosition > 0 Then + 'Append first part of destination string + sb.Append(sDest, 0, StartPosition) + End If + + 'Append InsertString + sb.Append(sInsert, 0, InsertLength) + EndSegmentLength = DestLength - (StartPosition + InsertLength) + + If EndSegmentLength > 0 Then + 'Append remainder of destination string + sb.Append(sDest, StartPosition + InsertLength, EndSegmentLength) + End If + + sDest = sb.ToString() + End Sub + + End Class + +#End Region + +End Namespace + diff --git a/Microsoft.VisualBasic/runtime/msvbalib/Helpers/StructUtils.vb b/Microsoft.VisualBasic/runtime/msvbalib/Helpers/StructUtils.vb new file mode 100644 index 000000000..9bbc30f16 --- /dev/null +++ b/Microsoft.VisualBasic/runtime/msvbalib/Helpers/StructUtils.vb @@ -0,0 +1,274 @@ +' Copyright (c) Microsoft Corporation. All rights reserved. + +Imports System +Imports System.Globalization +Imports System.Diagnostics +Imports System.Reflection + +Imports Microsoft.VisualBasic.CompilerServices.ExceptionUtils +Imports Microsoft.VisualBasic.CompilerServices.Utils + +Namespace Microsoft.VisualBasic.CompilerServices + + Friend Interface IRecordEnum + Function Callback(ByVal FieldInfo As System.Reflection.FieldInfo, ByRef Value As Object) As Boolean + End Interface + + + + _ + Friend Class StructUtils + ' Prevent creation. + Private Sub New() + End Sub + + Friend Shared Function EnumerateUDT(ByVal oStruct As ValueType, ByVal intfRecEnum As IRecordEnum, ByVal fGet As Boolean) As System.Object + Dim fi() As System.Reflection.FieldInfo + Dim iLowerBound As Integer + Dim iUpperBound As Integer + Dim typ As System.Type + Dim i As Integer + Dim FieldType As System.Type + Dim FieldInfo As System.Reflection.FieldInfo + Dim vt As VariantType + Dim obj As Object + + typ = oStruct.GetType() + vt = VarTypeFromComType(typ) + + If vt <> VariantType.UserDefinedType OrElse typ.IsPrimitive Then + Throw New ArgumentException(GetResourceString(ResID.Argument_InvalidValue1, "oStruct")) + End If + + fi = typ.GetFields(BindingFlags.Instance Or BindingFlags.Public) + iLowerBound = 0 + iUpperBound = fi.GetUpperBound(0) + + For i = iLowerBound To iUpperBound + FieldInfo = fi(i) + FieldType = FieldInfo.FieldType + obj = FieldInfo.GetValue(oStruct) + + If VarTypeFromComType(FieldType) = VariantType.UserDefinedType Then + If FieldType.IsPrimitive Then + Throw VbMakeException(New ArgumentException(GetResourceString(ResID.Argument_UnsupportedFieldType2, FieldInfo.Name, FieldType.Name)), vbErrors.IllegalFuncCall) + Else + Call EnumerateUDT(CType(obj, ValueType), intfRecEnum, fGet) + End If + Else + Call intfRecEnum.Callback(FieldInfo, obj) + End If + + If fGet Then + FieldInfo.SetValue(oStruct, obj) + End If + Next i + + Return Nothing + End Function + + + Friend Shared Function GetRecordLength(ByVal o As Object, Optional ByVal PackSize As Integer = -1) As Integer + If o Is Nothing Then + Return 0 + End If + + Dim intf As IRecordEnum + Dim ph As StructByteLengthHandler + + ph = New StructByteLengthHandler(PackSize) + intf = ph + + If intf Is Nothing Then + Throw VbMakeException(vbErrors.IllegalFuncCall) + End If + + EnumerateUDT(CType(o, ValueType), intf, False) + Return ph.Length + End Function + + + + Private NotInheritable Class StructByteLengthHandler + Implements IRecordEnum + Private m_StructLength As Integer + Private m_PackSize As Integer + + + Friend Sub New(ByVal PackSize As Integer) + 'PackSize - Only 1 and multiples of 2 allowed + Debug.Assert(PackSize = 1, "PackSize is not actually set to anything other than 1 in the current library. " _ + & "If this is changed, care will need to be taken that the current code actually sets alignment correctly.") + m_PackSize = PackSize + End Sub + + + + Friend ReadOnly Property Length() As Integer + Get + If m_PackSize = 1 Then + Return m_StructLength + Else + Return (m_StructLength + (m_StructLength Mod m_PackSize)) + End If + End Get + End Property + + + + Friend Sub SetAlignment(ByVal size As Integer) + If m_PackSize <> 1 Then + m_StructLength += (m_StructLength Mod size) + End If + End Sub + + + + Friend Function Callback(ByVal field_info As Reflection.FieldInfo, ByRef vValue As Object) As Boolean Implements IRecordEnum.Callback + Dim FieldType As System.Type + Dim align, size As Integer + + FieldType = field_info.FieldType + + If FieldType Is Nothing Then + Throw VbMakeException(New ArgumentException(GetResourceString(ResID.Argument_UnsupportedFieldType2, field_info.Name, "Empty")), vbErrors.IllegalFuncCall) + End If + + If FieldType.IsArray() Then + Dim attributeList As Object() + Dim ElementType As System.Type + Dim attrFixedArray As VBFixedArrayAttribute + Dim ElementCount, ElementSize As Integer + + attributeList = field_info.GetCustomAttributes(GetType(VBFixedArrayAttribute), False) + If Not attributeList Is Nothing AndAlso attributeList.Length <> 0 Then + attrFixedArray = CType(attributeList(0), VBFixedArrayAttribute) + Else + attrFixedArray = Nothing + End If + + ElementType = FieldType.GetElementType() + + If attrFixedArray Is Nothing Then + + ElementCount = 1 + ElementSize = 4 + + Else + + 'This kind of mismatch will be ignored in length calculation + ' Structure ABC + ' Public x As Integer() + ' End Structure + 'We are going to ignore possible mismatch errors in what the + 'attribute has for the dimensions and the actual field declaration is + 'The FilePut will catch these problems. + 'The array might not be initialized and parsing the name correctly to calculate the dims + 'isn't worth the possible bugs we could introduce + ElementCount = attrFixedArray.Length + + GetFieldSize(field_info, ElementType, align, ElementSize) + + End If + + SetAlignment(align) + m_StructLength += (ElementCount * ElementSize) + + Return False + + End If + + GetFieldSize(field_info, FieldType, align, size) + SetAlignment(align) + m_StructLength += size + + Return False + + End Function + + + Private Sub GetFieldSize(ByVal field_info As Reflection.FieldInfo, ByVal FieldType As System.Type, ByRef align As Integer, ByRef size As Integer) + + Select Case Type.GetTypeCode(FieldType) + + Case TypeCode.String + Dim attributeList As Object() = field_info.GetCustomAttributes(GetType(VBFixedStringAttribute), False) + + If attributeList Is Nothing OrElse attributeList.Length = 0 Then + align = 4 + size = 4 + Else + + Dim ma As VBFixedStringAttribute + Dim length As Integer + + ma = CType(attributeList(0), VBFixedStringAttribute) + + length = ma.Length + If length = 0 Then + length = -1 + End If + size = length + End If + + Case TypeCode.Single + align = 4 + size = 4 + + Case TypeCode.Double + align = 8 + size = 8 + + Case TypeCode.Int16 + align = 2 + size = 2 + + Case TypeCode.Int32 + align = 4 + size = 4 + + Case TypeCode.Byte + align = 1 + size = 1 + + Case TypeCode.Int64 + align = 8 + size = 8 + + Case TypeCode.DateTime + align = 8 + size = 8 + + Case TypeCode.Boolean + align = 2 + size = 2 + + Case TypeCode.Decimal + align = 16 + size = 16 + + Case TypeCode.Char + align = 2 + size = 2 + + Case TypeCode.DBNull + Throw VbMakeException(New ArgumentException(GetResourceString(ResID.Argument_UnsupportedFieldType2, field_info.Name, "DBNull")), vbErrors.IllegalFuncCall) + End Select + + If FieldType Is GetType(System.Exception) Then + Throw VbMakeException(New ArgumentException(GetResourceString(ResID.Argument_UnsupportedFieldType2, field_info.Name, "Exception")), vbErrors.IllegalFuncCall) + ElseIf FieldType Is GetType(System.Reflection.Missing) Then + Throw VbMakeException(New ArgumentException(GetResourceString(ResID.Argument_UnsupportedFieldType2, field_info.Name, "Missing")), vbErrors.IllegalFuncCall) + + 'If type defined for the Field is Object, then throw an exception + 'NOTE: THIS IS NOT THE SAME AS "TypeOf FieldType Is Object" + ElseIf FieldType Is GetType(Object) Then + Throw VbMakeException(New ArgumentException(GetResourceString(ResID.Argument_UnsupportedFieldType2, field_info.Name, "Object")), vbErrors.IllegalFuncCall) + End If + End Sub + + End Class + + + End Class +End Namespace diff --git a/Microsoft.VisualBasic/runtime/msvbalib/Helpers/Symbols.vb b/Microsoft.VisualBasic/runtime/msvbalib/Helpers/Symbols.vb new file mode 100644 index 000000000..a40a6d400 --- /dev/null +++ b/Microsoft.VisualBasic/runtime/msvbalib/Helpers/Symbols.vb @@ -0,0 +1,1908 @@ +' Copyright (c) Microsoft Corporation. All rights reserved. + +Imports System +Imports System.Reflection +Imports System.Diagnostics +Imports System.Collections +Imports System.Collections.Generic +#If Not TELESTO Then +Imports System.Runtime.Remoting +#End If + +Imports Microsoft.VisualBasic.CompilerServices.NewLateBinding +Imports Microsoft.VisualBasic.CompilerServices.OverloadResolution +Imports Microsoft.VisualBasic.CompilerServices.ExceptionUtils +Imports Microsoft.VisualBasic.CompilerServices.Utils + +Namespace Microsoft.VisualBasic.CompilerServices + + Friend Class Symbols + ' Prevent creation. + Private Sub New() + End Sub + + Friend Enum UserDefinedOperator As SByte + UNDEF + Narrow + Widen + IsTrue + IsFalse + Negate + [Not] + UnaryPlus + Plus + Minus + Multiply + Divide + Power + IntegralDivide + Concatenate + ShiftLeft + ShiftRight + Modulus + [Or] + [Xor] + [And] + [Like] + Equal + NotEqual + Less + LessEqual + GreaterEqual + Greater + MAX + End Enum + + Friend Shared ReadOnly NoArguments As Object() = {} + Friend Shared ReadOnly NoArgumentNames As String() = {} + Friend Shared ReadOnly NoTypeArguments As Type() = {} + Friend Shared ReadOnly NoTypeParameters As Type() = {} + + Friend Shared ReadOnly OperatorCLSNames As String() + Friend Shared ReadOnly OperatorNames As String() + + Shared Sub New() + OperatorCLSNames = New String(UserDefinedOperator.MAX - 1) {} + OperatorCLSNames(UserDefinedOperator.Narrow) = "op_Explicit" + OperatorCLSNames(UserDefinedOperator.Widen) = "op_Implicit" + OperatorCLSNames(UserDefinedOperator.IsTrue) = "op_True" + OperatorCLSNames(UserDefinedOperator.IsFalse) = "op_False" + OperatorCLSNames(UserDefinedOperator.Negate) = "op_UnaryNegation" + OperatorCLSNames(UserDefinedOperator.Not) = "op_OnesComplement" + OperatorCLSNames(UserDefinedOperator.UnaryPlus) = "op_UnaryPlus" + OperatorCLSNames(UserDefinedOperator.Plus) = "op_Addition" + OperatorCLSNames(UserDefinedOperator.Minus) = "op_Subtraction" + OperatorCLSNames(UserDefinedOperator.Multiply) = "op_Multiply" + OperatorCLSNames(UserDefinedOperator.Divide) = "op_Division" + OperatorCLSNames(UserDefinedOperator.Power) = "op_Exponent" + OperatorCLSNames(UserDefinedOperator.IntegralDivide) = "op_IntegerDivision" + OperatorCLSNames(UserDefinedOperator.Concatenate) = "op_Concatenate" + OperatorCLSNames(UserDefinedOperator.ShiftLeft) = "op_LeftShift" + OperatorCLSNames(UserDefinedOperator.ShiftRight) = "op_RightShift" + OperatorCLSNames(UserDefinedOperator.Modulus) = "op_Modulus" + OperatorCLSNames(UserDefinedOperator.Or) = "op_BitwiseOr" + OperatorCLSNames(UserDefinedOperator.Xor) = "op_ExclusiveOr" + OperatorCLSNames(UserDefinedOperator.And) = "op_BitwiseAnd" + OperatorCLSNames(UserDefinedOperator.Like) = "op_Like" + OperatorCLSNames(UserDefinedOperator.Equal) = "op_Equality" + OperatorCLSNames(UserDefinedOperator.NotEqual) = "op_Inequality" + OperatorCLSNames(UserDefinedOperator.Less) = "op_LessThan" + OperatorCLSNames(UserDefinedOperator.LessEqual) = "op_LessThanOrEqual" + OperatorCLSNames(UserDefinedOperator.GreaterEqual) = "op_GreaterThanOrEqual" + OperatorCLSNames(UserDefinedOperator.Greater) = "op_GreaterThan" + + + OperatorNames = New String(UserDefinedOperator.MAX - 1) {} + OperatorNames(UserDefinedOperator.Narrow) = "CType" + OperatorNames(UserDefinedOperator.Widen) = "CType" + OperatorNames(UserDefinedOperator.IsTrue) = "IsTrue" + OperatorNames(UserDefinedOperator.IsFalse) = "IsFalse" + OperatorNames(UserDefinedOperator.Negate) = "-" + OperatorNames(UserDefinedOperator.Not) = "Not" + OperatorNames(UserDefinedOperator.UnaryPlus) = "+" + OperatorNames(UserDefinedOperator.Plus) = "+" + OperatorNames(UserDefinedOperator.Minus) = "-" + OperatorNames(UserDefinedOperator.Multiply) = "*" + OperatorNames(UserDefinedOperator.Divide) = "/" + OperatorNames(UserDefinedOperator.Power) = "^" + OperatorNames(UserDefinedOperator.IntegralDivide) = "\" + OperatorNames(UserDefinedOperator.Concatenate) = "&" + OperatorNames(UserDefinedOperator.ShiftLeft) = "<<" + OperatorNames(UserDefinedOperator.ShiftRight) = ">>" + OperatorNames(UserDefinedOperator.Modulus) = "Mod" + OperatorNames(UserDefinedOperator.Or) = "Or" + OperatorNames(UserDefinedOperator.Xor) = "Xor" + OperatorNames(UserDefinedOperator.And) = "And" + OperatorNames(UserDefinedOperator.Like) = "Like" + OperatorNames(UserDefinedOperator.Equal) = "=" + OperatorNames(UserDefinedOperator.NotEqual) = "<>" + OperatorNames(UserDefinedOperator.Less) = "<" + OperatorNames(UserDefinedOperator.LessEqual) = "<=" + OperatorNames(UserDefinedOperator.GreaterEqual) = ">=" + OperatorNames(UserDefinedOperator.Greater) = ">" + End Sub + + Friend Shared Function IsUnaryOperator(ByVal Op As UserDefinedOperator) As Boolean + Select Case Op + Case UserDefinedOperator.Narrow, _ + UserDefinedOperator.Widen, _ + UserDefinedOperator.IsTrue, _ + UserDefinedOperator.IsFalse, _ + UserDefinedOperator.Negate, _ + UserDefinedOperator.Not, _ + UserDefinedOperator.UnaryPlus + + Return True + + End Select + Return False + End Function + + Friend Shared Function IsBinaryOperator(ByVal Op As UserDefinedOperator) As Boolean + Select Case Op + Case UserDefinedOperator.Plus, _ + UserDefinedOperator.Minus, _ + UserDefinedOperator.Multiply, _ + UserDefinedOperator.Divide, _ + UserDefinedOperator.Power, _ + UserDefinedOperator.IntegralDivide, _ + UserDefinedOperator.Concatenate, _ + UserDefinedOperator.ShiftLeft, _ + UserDefinedOperator.ShiftRight, _ + UserDefinedOperator.Modulus, _ + UserDefinedOperator.Or, _ + UserDefinedOperator.Xor, _ + UserDefinedOperator.And, _ + UserDefinedOperator.Like, _ + UserDefinedOperator.Equal, _ + UserDefinedOperator.NotEqual, _ + UserDefinedOperator.Less, _ + UserDefinedOperator.LessEqual, _ + UserDefinedOperator.GreaterEqual, _ + UserDefinedOperator.Greater + + Return True + + End Select + Return False + End Function + + Friend Shared Function IsUserDefinedOperator(ByVal Method As MethodBase) As Boolean + Return Method.IsSpecialName AndAlso Method.Name.StartsWith("op_", StringComparison.Ordinal) + End Function + + Friend Shared Function IsNarrowingConversionOperator(ByVal Method As MethodBase) As Boolean + Return Method.IsSpecialName AndAlso Method.Name.Equals(OperatorCLSNames(UserDefinedOperator.Narrow)) + End Function + + Friend Shared Function MapToUserDefinedOperator(ByVal Method As MethodBase) As UserDefinedOperator + Debug.Assert(IsUserDefinedOperator(Method), "expected operator here") + + For Cursor As Integer = UserDefinedOperator.UNDEF + 1 To UserDefinedOperator.MAX - 1 + If Method.Name.Equals(OperatorCLSNames(Cursor)) Then + + Dim ParamCount As Integer = Method.GetParameters.Length + Dim Op As UserDefinedOperator = CType(Cursor, UserDefinedOperator) + + If (ParamCount = 1 AndAlso IsUnaryOperator(Op)) OrElse _ + (ParamCount = 2 AndAlso IsBinaryOperator(Op)) Then + 'Match found, so quit loop early. + Return Op + End If + + End If + Next + + Return UserDefinedOperator.UNDEF + End Function + + Friend Shared Function GetTypeCode(ByVal Type As System.Type) As TypeCode + Return System.Type.GetTypeCode(Type) + End Function + + Friend Shared Function MapTypeCodeToType(ByVal TypeCode As TypeCode) As Type + + Select Case TypeCode + + Case TypeCode.Boolean : Return GetType(Boolean) + Case TypeCode.SByte : Return GetType(SByte) + Case TypeCode.Byte : Return GetType(Byte) + Case TypeCode.Int16 : Return GetType(Short) + Case TypeCode.UInt16 : Return GetType(UShort) + Case TypeCode.Int32 : Return GetType(Integer) + Case TypeCode.UInt32 : Return GetType(UInteger) + Case TypeCode.Int64 : Return GetType(Long) + Case TypeCode.UInt64 : Return GetType(ULong) + Case TypeCode.Decimal : Return GetType(Decimal) + Case TypeCode.Single : Return GetType(Single) + Case TypeCode.Double : Return GetType(Double) + Case TypeCode.DateTime : Return GetType(Date) + Case TypeCode.Char : Return GetType(Char) + Case TypeCode.String : Return GetType(String) + Case TypeCode.Object : Return GetType(Object) + Case TypeCode.DBNull : Return GetType(System.DBNull) + + Case TypeCode.Empty + 'fall through + + End Select + + Return Nothing + End Function + + Friend Shared Function IsRootObjectType(ByVal Type As System.Type) As Boolean + Return Type Is GetType(Object) + End Function + + Friend Shared Function IsRootEnumType(ByVal Type As System.Type) As Boolean + Return Type Is GetType(System.Enum) + End Function + + Friend Shared Function IsValueType(ByVal Type As System.Type) As Boolean + Return Type.IsValueType + End Function + + Friend Shared Function IsEnum(ByVal Type As System.Type) As Boolean + Return Type.IsEnum + End Function + + Friend Shared Function IsArrayType(ByVal Type As System.Type) As Boolean + Return Type.IsArray + End Function + + Friend Shared Function IsStringType(ByVal Type As System.Type) As Boolean + Return Type Is GetType(String) + End Function + + Friend Shared Function IsCharArrayRankOne(ByVal Type As System.Type) As Boolean + Return Type Is GetType(Char()) + End Function + + Friend Shared Function IsIntegralType(ByVal TypeCode As System.TypeCode) As Boolean + Select Case TypeCode + Case TypeCode.SByte, _ + TypeCode.Byte, _ + TypeCode.Int16, _ + TypeCode.UInt16, _ + TypeCode.Int32, _ + TypeCode.UInt32, _ + TypeCode.Int64, _ + TypeCode.UInt64 + + Return True + + Case TypeCode.Empty, _ + TypeCode.Object, _ + TypeCode.DBNull, _ + TypeCode.Boolean, _ + TypeCode.Decimal, _ + TypeCode.Single, _ + TypeCode.Double, _ + TypeCode.DateTime, _ + TypeCode.Char, _ + TypeCode.String + + 'Fall through to end. + End Select + + Return False + End Function + +#if 0 then + Friend Shared Function IsIntegralType(ByVal Type As System.Type) As Boolean + Return IsIntegralType(GetTypeCode(Type)) + End Function +#end if + + Friend Shared Function IsNumericType(ByVal TypeCode As System.TypeCode) As Boolean + Select Case TypeCode + Case TypeCode.SByte, _ + TypeCode.Byte, _ + TypeCode.Int16, _ + TypeCode.UInt16, _ + TypeCode.Int32, _ + TypeCode.UInt32, _ + TypeCode.Int64, _ + TypeCode.UInt64, _ + TypeCode.Decimal, _ + TypeCode.Single, _ + TypeCode.Double + + Return True + + Case TypeCode.Empty, _ + TypeCode.Object, _ + TypeCode.DBNull, _ + TypeCode.Boolean, _ + TypeCode.DateTime, _ + TypeCode.Char, _ + TypeCode.String + + 'Fall through to end. + End Select + + Return False + End Function + + Friend Shared Function IsNumericType(ByVal Type As System.Type) As Boolean + Return IsNumericType(GetTypeCode(Type)) + End Function + + Friend Shared Function IsIntrinsicType(ByVal TypeCode As System.TypeCode) As Boolean + Select Case TypeCode + Case TypeCode.Boolean, _ + TypeCode.SByte, _ + TypeCode.Byte, _ + TypeCode.Int16, _ + TypeCode.UInt16, _ + TypeCode.Int32, _ + TypeCode.UInt32, _ + TypeCode.Int64, _ + TypeCode.UInt64, _ + TypeCode.Decimal, _ + TypeCode.Single, _ + TypeCode.Double, _ + TypeCode.DateTime, _ + TypeCode.Char, _ + TypeCode.String + + Return True + + Case TypeCode.Empty, _ + TypeCode.Object, _ + TypeCode.DBNull + + 'Fall through to end. + End Select + + Return False + End Function + + Friend Shared Function IsIntrinsicType(ByVal Type As System.Type) As Boolean + Return IsIntrinsicType(GetTypeCode(Type)) AndAlso Not IsEnum(Type) + End Function + +#if 0 then + Friend Shared Function IsUnsignedType(ByVal TypeCode As System.TypeCode) As Boolean + Select Case TypeCode + Case TypeCode.Byte, _ + TypeCode.UInt16, _ + TypeCode.UInt32, _ + TypeCode.UInt64 + Return True + + Case TypeCode.Empty, _ + TypeCode.Object, _ + TypeCode.DBNull, _ + TypeCode.Boolean, _ + TypeCode.SByte, _ + TypeCode.Int16, _ + TypeCode.Int32, _ + TypeCode.Int64, _ + TypeCode.Decimal, _ + TypeCode.Single, _ + TypeCode.Double, _ + TypeCode.DateTime, _ + TypeCode.Char, _ + TypeCode.String + + 'Fall through to end. + End Select + + Return False + End Function + + ' Friend Shared Function IsUnsignedType(ByVal Type As System.Type) As Boolean + ' Return IsUnsignedType(GetTypeCode(Type)) + ' End Function +#end if + + Friend Shared Function IsClass(ByVal Type As System.Type) As Boolean + Return Type.IsClass OrElse IsRootEnumType(Type) + End Function + + Friend Shared Function IsClassOrValueType(ByVal Type As System.Type) As Boolean + Return IsValueType(Type) OrElse IsClass(Type) + End Function + + Friend Shared Function IsInterface(ByVal Type As System.Type) As Boolean + Return Type.IsInterface + End Function + + Friend Shared Function IsClassOrInterface(ByVal Type As System.Type) As Boolean + Return IsClass(Type) OrElse IsInterface(Type) + End Function + + Friend Shared Function IsReferenceType(ByVal Type As System.Type) As Boolean + Return IsClass(Type) OrElse IsInterface(Type) + End Function + + Friend Shared Function IsGenericParameter(ByVal Type As System.Type) As Boolean + Return Type.IsGenericParameter + End Function + + 'SHIQIC: Port this function when build with clr4.0 +#If Not TELESTO Then + Friend Shared Function IsEquivalentType(ByVal Left As System.Type, ByVal Right As System.Type) As Boolean + ' Type.IsEquivalentTo(Type) doesn't work properly for instantiated + ' generic types other than interfaces. For example: + ' 1. IList(Of NoPiaType) should be equivalent to IList(Of PiaType) + ' 2. List(Of NoPiaType) should be equivalent to List(Of PiaType) + ' In CLR 4, the first example works, but the second doesn't. We workaround it here. + ' When this is fixed in CLR we can remove this code. + If IsInstantiatedGeneric(Left) AndAlso Not Left.IsInterface AndAlso _ + IsInstantiatedGeneric(Right) AndAlso Not Right.IsInterface Then + + ' Compare generic type defintion + If Not IsEquivalentType(Left.GetGenericTypeDefinition, Right.GetGenericTypeDefinition) Then + Return False + End If + + ' Compare generic arguments + Dim LeftArgs As Type() = Left.GetGenericArguments + Dim RightArgs As Type() = Right.GetGenericArguments + If LeftArgs.Length <> RightArgs.Length Then + Return False + End If + + For i As Integer = 0 To LeftArgs.Length - 1 + If Not IsEquivalentType(LeftArgs(i), RightArgs(i)) Then + Return False + End If + Next + + Return True + End If + Return Left.IsEquivalentTo(Right) + End Function +#End If + +#If LATEBINDING + Friend Shared Function IsCollectionInterface(ByVal Type As System.Type) As Boolean + If Type.IsInterface AndAlso + ((Type.IsGenericType AndAlso + (Type.GetGenericTypeDefinition() Is GetType(System.Collections.Generic.IList(Of )) OrElse + Type.GetGenericTypeDefinition() Is GetType(System.Collections.Generic.ICollection(Of )) OrElse + Type.GetGenericTypeDefinition() Is GetType(System.Collections.Generic.IEnumerable(Of )) OrElse + Type.GetGenericTypeDefinition() Is GetType(System.Collections.Generic.IReadOnlyList(Of )) OrElse + Type.GetGenericTypeDefinition() Is GetType(System.Collections.Generic.IReadOnlyCollection(Of )) OrElse + Type.GetGenericTypeDefinition() Is GetType(System.Collections.Generic.IDictionary(Of ,)) OrElse + Type.GetGenericTypeDefinition() Is GetType(System.Collections.Generic.IReadOnlyDictionary(Of ,)))) OrElse + Type Is GetType(System.Collections.IList) OrElse + Type Is GetType(System.Collections.ICollection) OrElse + Type Is GetType(System.Collections.IEnumerable) OrElse + Type Is GetType(System.ComponentModel.INotifyPropertyChanged) OrElse + Type Is GetType(System.Collections.Specialized.INotifyCollectionChanged)) Then + Return True + End If + + Return False + End Function +#Else + Friend Shared Function IsCollectionInterface(ByVal Type As System.Type) As Boolean + If Type.IsInterface AndAlso + ((Type.IsGenericType AndAlso + (Type.GetGenericTypeDefinition() = GetType(System.Collections.Generic.IList(Of )) OrElse + Type.GetGenericTypeDefinition() = GetType(System.Collections.Generic.ICollection(Of )) OrElse + Type.GetGenericTypeDefinition() = GetType(System.Collections.Generic.IEnumerable(Of )) OrElse + Type.GetGenericTypeDefinition() = GetType(System.Collections.Generic.IReadOnlyList(Of )) OrElse + Type.GetGenericTypeDefinition() = GetType(System.Collections.Generic.IReadOnlyCollection(Of )) OrElse + Type.GetGenericTypeDefinition() = GetType(System.Collections.Generic.IDictionary(Of ,)) OrElse + Type.GetGenericTypeDefinition() = GetType(System.Collections.Generic.IReadOnlyDictionary(Of ,)))) OrElse + Type = GetType(System.Collections.IList) OrElse + Type = GetType(System.Collections.ICollection) OrElse + Type = GetType(System.Collections.IEnumerable) OrElse + Type = GetType(System.ComponentModel.INotifyPropertyChanged) OrElse + Type = GetType(System.Collections.Specialized.INotifyCollectionChanged)) Then + Return True + End If + + Return False + End Function +#End If + Friend Shared Function [Implements](ByVal Implementor As System.Type, ByVal [Interface] As System.Type) As Boolean + + Debug.Assert(Not IsInterface(Implementor), "interfaces can't implement, so why call this?") + Debug.Assert(IsInterface([Interface]), "expected interface, not " & [Interface].FullName) + + For Each Implemented As Type In Implementor.GetInterfaces + 'CONSIDER: the call to getinterfaces is expensive, and may involve doing a QueryInterface. how to combine? +#If TELESTO Then + If Implemented Is [Interface] Then +#Else + ' Check identity and NoPIA type equivalency + If Implemented Is [Interface] OrElse IsEquivalentType(Implemented, [Interface]) Then +#End If + Return True + End If + Next + + Return False + + End Function + + Friend Shared Function IsOrInheritsFrom(ByVal Derived As System.Type, ByVal Base As System.Type) As Boolean + Debug.Assert((Not Derived.IsByRef) AndAlso (Not Derived.IsPointer)) + Debug.Assert((Not Base.IsByRef) AndAlso (Not Base.IsPointer)) + + If Derived Is Base Then Return True + + If Derived.IsGenericParameter() Then + If IsClass(Base) AndAlso _ + (CBool(Derived.GenericParameterAttributes() And GenericParameterAttributes.NotNullableValueTypeConstraint)) AndAlso _ + IsOrInheritsFrom(GetType(System.ValueType), Base) Then + Return True + End If + + For Each TypeConstraint As Type In Derived.GetGenericParameterConstraints + If IsOrInheritsFrom(TypeConstraint, Base) Then + Return True + End If + Next + + ElseIf IsInterface(Derived) Then + If IsInterface(Base) Then + 'CONSIDER: the call to getinterfaces is expensive, and may involve doing a QueryInterface. how to combine? + For Each BaseInterface As Type In Derived.GetInterfaces + If BaseInterface Is Base Then + Return True + End If + Next + End If + + ElseIf IsClass(Base) AndAlso IsClassOrValueType(Derived) Then + Return Derived.IsSubclassOf(Base) + End If + + Return False + End Function + + Friend Shared Function IsGeneric(ByVal Type As Type) As Boolean + Return Type.IsGenericType + End Function + + Friend Shared Function IsInstantiatedGeneric(ByVal Type As Type) As Boolean + Return Type.IsGenericType AndAlso (Not Type.IsGenericTypeDefinition) + End Function + + Friend Shared Function IsGeneric(ByVal Method As MethodBase) As Boolean + Return Method.IsGenericMethod + End Function + + Friend Shared Function IsGeneric(ByVal Member As MemberInfo) As Boolean + 'Returns True whether Method is an instantiated or uninstantiated generic method. + Dim Method As MethodBase = TryCast(Member, MethodBase) + If Method Is Nothing Then Return False + Return IsGeneric(Method) + End Function + +#If DEBUG Then + Friend Shared Function IsInstantiatedGeneric(ByVal Method As MethodBase) As Boolean + Return Method.IsGenericMethod AndAlso (Not Method.IsGenericMethodDefinition) + End Function +#End If + + Friend Shared Function IsRawGeneric(ByVal Method As MethodBase) As Boolean + Return Method.IsGenericMethod AndAlso Method.IsGenericMethodDefinition + End Function + + Friend Shared Function GetTypeParameters(ByVal Member As MemberInfo) As Type() + Dim Method As MethodBase = TryCast(Member, MethodBase) + If Method Is Nothing Then Return NoTypeParameters + Return Method.GetGenericArguments + End Function + + Friend Shared Function GetTypeParameters(ByVal Type As Type) As Type() + Debug.Assert(Type.GetGenericTypeDefinition Is Nothing, "expected unbound generic type") + Return Type.GetGenericArguments + End Function + + Friend Shared Function GetTypeArguments(ByVal Type As Type) As Type() + Debug.Assert(Type.GetGenericTypeDefinition IsNot Nothing, "expected bound generic type") + Return Type.GetGenericArguments + End Function + + Friend Shared Function GetInterfaceConstraints(ByVal GenericParameter As Type) As Type() + 'Returns the interface constraints for the type parameter. + Debug.Assert(IsGenericParameter(GenericParameter), "expected type parameter") + Return GenericParameter.GetInterfaces() + End Function + + Friend Shared Function GetClassConstraint(ByVal GenericParameter As Type) As Type + 'Returns the class constraint for the type parameter, Nothing if it has + 'no class constraint. + Debug.Assert(IsGenericParameter(GenericParameter), "expected type parameter") + + 'Type parameters with no class constraint have System.Object as their base type. + Dim ClassConstraint As Type = GenericParameter.BaseType + If IsRootObjectType(ClassConstraint) Then Return Nothing + Return ClassConstraint + End Function + + Friend Shared Function IndexIn(ByVal PossibleGenericParameter As Type, ByVal GenericMethodDef As MethodBase) As Integer + 'Returns the index of PossibleGenericParameter in Method. If the generic param cannot be found, + 'returns -1 + + Debug.Assert(GenericMethodDef IsNot Nothing AndAlso IsRawGeneric(GenericMethodDef), "Uninstantiated generic expected!!!") + + If IsGenericParameter(PossibleGenericParameter) AndAlso _ + PossibleGenericParameter.DeclaringMethod IsNot Nothing AndAlso _ + AreGenericMethodDefsEqual(PossibleGenericParameter.DeclaringMethod, GenericMethodDef) Then + Return PossibleGenericParameter.GenericParameterPosition + End If + Return -1 + End Function + + Friend Shared Function RefersToGenericParameter(ByVal ReferringType As Type, ByVal Method As MethodBase) As Boolean + 'Given ReferringType, determine if it contains any usages of the generic parameters of Method. + 'For example, the referring types T and C1(Of T) and T() refer to a generic param of Sub Foo(Of T). + + If Not IsRawGeneric(Method) Then Return False + + If ReferringType.IsByRef Then ReferringType = GetElementType(ReferringType) + + If IsGenericParameter(ReferringType) Then + 'Is T a generic parameter of Method? + + Debug.Assert(ReferringType.DeclaringMethod.IsGenericMethodDefinition, "Unexpected generic method instantiation!!!") + + If AreGenericMethodDefsEqual(ReferringType.DeclaringMethod, Method) Then + Return True + End If + + ElseIf IsGeneric(ReferringType) Then + 'For C1(Of T, U, V), recurse on T, U, and V. + For Each Param As Type In GetTypeArguments(ReferringType) + If RefersToGenericParameter(Param, Method) Then + Return True + End If + Next + + ElseIf IsArrayType(ReferringType) Then + 'For T(), recurse on T. + Return RefersToGenericParameter(ReferringType.GetElementType, Method) + + End If + + Return False + + End Function + + 'Is T a generic parameter of Type. Note that the clr way of representing type params will + 'cause us to return true for the copies of the type params of all the parent types that are + 'on the passed in Typ. Note that this clr behavior has been retained because in the run time + 'for the uses of this function, this functionality is desired. + ' + Friend Shared Function RefersToGenericParameterCLRSemantics(ByVal ReferringType As Type, ByVal Typ As Type) As Boolean + 'Given ReferringType, determine if it contains any usages of the generic parameters of Typ. + 'For example, the referring types T and C1(Of T) and T() refer to a generic param of Class Cls1(Of T). + + If ReferringType.IsByRef Then ReferringType = GetElementType(ReferringType) + + If IsGenericParameter(ReferringType) Then + 'Is T a generic parameter of Type. Note that the clr way of representing type params will + 'return true for the copies of the type params of all the parent types that are on the + 'passed in Typ. + + If ReferringType.DeclaringType Is Typ Then + Return True + End If + + ElseIf IsGeneric(ReferringType) Then + 'For C1(Of T, U, V), recurse on T, U, and V. + For Each Param As Type In GetTypeArguments(ReferringType) + If RefersToGenericParameterCLRSemantics(Param, Typ) Then + Return True + End If + Next + + ElseIf IsArrayType(ReferringType) Then + 'For T(), recurse on T. + Return RefersToGenericParameterCLRSemantics(ReferringType.GetElementType, Typ) + + End If + + Return False + + End Function + + 'Friend Shared Function AreMethodsEqual(ByVal Method1 As MethodBase, ByVal Method2 As MethodBase) As Boolean + ' ' Need to do this kind of comparison because the MethodInfo obtained for a + ' ' base method through type1 is not the same as that obtained from type2 + ' ' + ' ' UNDONE: - currently there is no way to compare generic method instantions + ' ' because methodhandles for different instantiations might be equal if based on clr + ' ' optimizations, they end up sharing the same IL + ' ' + ' Return _ + ' Method1 Is Method2 OrElse _ + ' (Method1 IsNot Nothing AndAlso _ + ' Method2 IsNot Nothing AndAlso _ + ' (Method1.MethodHandle.Equals(Method2.MethodHandle) AndAlso _ + ' ((Method1.IsGenericMethodDefinition AndAlso _ + ' Method2.IsGenericMethodDefinition) OrElse _ + ' (Not Method1.HasGenericArguments AndAlso _ + ' Not Method2.HasGenericArguments)))) + 'End Function + + Friend Shared Function AreGenericMethodDefsEqual(ByVal Method1 As MethodBase, ByVal Method2 As MethodBase) As Boolean + Debug.Assert(Method1 IsNot Nothing AndAlso IsRawGeneric(Method1), "Generic method def expected!!!") + Debug.Assert(Method2 IsNot Nothing AndAlso IsRawGeneric(Method2), "Generic method def expected!!!") + + ' Need to do this kind of comparison because the MethodInfo obtained for a + ' base method through type1 is not the same as that obtained from type2 + ' + Return _ + Method1 Is Method2 OrElse _ + Method1.MetadataToken = Method2.MetadataToken + End Function + + Friend Shared Function IsShadows(ByVal Method As MethodBase) As Boolean + If Method.IsHideBySig Then Return False + If Method.IsVirtual AndAlso (Method.Attributes And MethodAttributes.NewSlot) = 0 Then + + 'Only the most derived Overrides member shows up in the member list returned by reflection. + 'However, we have to check the most base (Overridable) member because the Shadowing information + 'is stored only there. + If (DirectCast(Method, MethodInfo).GetBaseDefinition().Attributes And MethodAttributes.NewSlot) = 0 Then + Return False + End If + End If + Return True + End Function + + Friend Shared Function IsShared(ByVal Member As MemberInfo) As Boolean + + Select Case Member.MemberType + Case MemberTypes.Method + Return DirectCast(Member, MethodInfo).IsStatic + + Case MemberTypes.Field + Return DirectCast(Member, FieldInfo).IsStatic + + Case MemberTypes.Constructor + Return DirectCast(Member, ConstructorInfo).IsStatic + + Case MemberTypes.Property + Return DirectCast(Member, PropertyInfo).GetGetMethod.IsStatic + + Case Else +#If TELESTO Then + Debug.Assert(False, "unexpected membertype") ' Silverlight CLR does not have Debug.Fail. +#Else + Debug.Fail("unexpected membertype") +#End If + End Select + + Return False + + End Function + + Friend Shared Function IsParamArray(ByVal Parameter As ParameterInfo) As Boolean + Return IsArrayType(Parameter.ParameterType) AndAlso Parameter.IsDefined(GetType(ParamArrayAttribute), False) + End Function + + Friend Shared Function GetElementType(ByVal Type As System.Type) As Type + Debug.Assert(Type.HasElementType, "expected type with element type") + Return Type.GetElementType + End Function + + Friend Shared Function AreParametersAndReturnTypesValid( _ + ByVal Parameters As ParameterInfo(), _ + ByVal ReturnType As Type) As Boolean + + If ReturnType IsNot Nothing AndAlso (ReturnType.IsPointer OrElse ReturnType.IsByRef) Then + Return False + End If + + If Parameters IsNot Nothing Then + For Each Parameter As ParameterInfo In Parameters + If Parameter.ParameterType.IsPointer Then + Return False + End If + Next + End If + + Return True + End Function + + Friend Shared Sub GetAllParameterCounts( _ + ByVal Parameters As ParameterInfo(), _ + ByRef RequiredParameterCount As Integer, _ + ByRef MaximumParameterCount As Integer, _ + ByRef ParamArrayIndex As Integer) + + + Debug.Assert(Parameters IsNot Nothing, "expected parameter array") + + MaximumParameterCount = Parameters.Length + + 'All optional parameters are grouped at the end, so the index of the + 'last non-optional (+1) gives us the count of required parameters. + For Index As Integer = MaximumParameterCount - 1 To 0 Step -1 + If Not Parameters(Index).IsOptional Then + RequiredParameterCount = Index + 1 + Exit For + End If + Next + + 'Only the last parameter can be a ParamArray, so check it. + If MaximumParameterCount <> 0 AndAlso IsParamArray(Parameters(MaximumParameterCount - 1)) Then + ParamArrayIndex = MaximumParameterCount - 1 + RequiredParameterCount -= 1 + End If + End Sub + + Friend Shared Function IsNonPublicRuntimeMember(ByVal Member As MemberInfo) As Boolean + + 'Disallow latebound calls to internal Microsoft.VisualBasic types + Dim DeclaringType As System.Type = Member.DeclaringType + + ' VSW#430608: For nested types IsNotPublic doesn't return the right value so + ' we need to use Not IsPublic. + ' + ' The following code will only allow calls to members of top level public types + ' in the runtime library. Read the reflection documentation and test with + ' nested types before changing this code. + + Return Not DeclaringType.IsPublic AndAlso DeclaringType.Assembly Is Utils.VBRuntimeAssembly + + End Function + + 'this is a utility function, so it doesn't really belong in Symbols, but... + Friend Shared Function HasFlag(ByVal Flags As BindingFlags, ByVal FlagToTest As BindingFlags) As Boolean + Return CBool(Flags And FlagToTest) + End Function + + Friend NotInheritable Class Container + + Private Class InheritanceSorter : Implements IComparer(Of MemberInfo) + + Private Sub New() + End Sub + + Private Function Compare(ByVal Left As MemberInfo, ByVal Right As MemberInfo) As Integer Implements IComparer(Of MemberInfo).Compare + Dim LeftType As Type = Left.DeclaringType + Dim RightType As Type = Right.DeclaringType + +#If BINDING_LOG Then + 'Console.WriteLine("compare: " & LeftType.Name & " " & RightType.Name) +#End If + If LeftType Is RightType Then Return 0 + If LeftType.IsSubclassOf(RightType) Then Return -1 + + 'Necessary to return 1 only for RightType.IsSubclassOf(LeftType)? If no inheritance + 'relationhip exists, which is possible when members come from IReflect, returning 1 + 'is still okay. Returning 1 in this IReflect case will not cause qsort to never terminate. + Return 1 + End Function + + Friend Shared ReadOnly Instance As InheritanceSorter = New InheritanceSorter + + End Class + + Private ReadOnly m_Instance As Object + Private ReadOnly m_Type As Type + Private ReadOnly m_IReflect As IReflect + Private ReadOnly m_UseCustomReflection As Boolean + + Friend Sub New(ByVal Instance As Object) + + If Instance Is Nothing Then + Throw VbMakeException(vbErrors.ObjNotSet) + End If + + m_Instance = Instance + m_Type = Instance.GetType + + ' For a System.Type Object, we always use the underlying System.Type's IReflect implementation, because a System.Type's Implementation + ' returns information about the Type it represents and not its own information. If we did not do this, latebound calls to a System.Type + ' Object would fail. + + ' We don't support this for COM Objects because this is not a valid COM scenario and the performance cost is intolerable + + m_UseCustomReflection = False + +#If TELESTO Then + If Not m_Type.IsCOMObject AndAlso Not TypeOf Instance Is System.Type Then 'No RemotingServices in Telesto +#Else + If Not m_Type.IsCOMObject AndAlso _ + Not RemotingServices.IsTransparentProxy(Instance) AndAlso _ + Not TypeOf Instance Is System.Type Then +#End If + m_IReflect = TryCast(Instance, IReflect) + + If m_IReflect IsNot Nothing Then + m_UseCustomReflection = True + End If + End If + + If Not m_UseCustomReflection Then + m_IReflect = DirectCast(m_Type, IReflect) + End If + + CheckForClassExtendingCOMClass() + End Sub + + Friend Sub New(ByVal Type As Type) + + If Type Is Nothing Then + Throw VbMakeException(vbErrors.ObjNotSet) + End If + + m_Instance = Nothing + m_Type = Type + m_IReflect = DirectCast(Type, IReflect) + Debug.Assert(m_IReflect.UnderlyingSystemType Is m_Type, "system.type is returning a different type?") + m_UseCustomReflection = False + + CheckForClassExtendingCOMClass() + End Sub + + Friend ReadOnly Property IsCOMObject() As Boolean + Get + Return m_Type.IsCOMObject + End Get + End Property + + ' Try to determine if this object represents a WindowsRuntime object - i.e. it either + ' is coming from a WinMD file or is derived from a class coming from a WinMD. + ' The logic here matches the CLR's logic of finding a WinRT object. + + Friend ReadOnly Property IsWindowsRuntimeObject() As Boolean + Get + Dim curType As Type = m_Type + While curType IsNot Nothing + If curType.Attributes.HasFlag(System.Reflection.TypeAttributes.WindowsRuntime) Then + ' Found a WinRT COM object + Return True + ElseIf curType.Attributes.HasFlag(System.Reflection.TypeAttributes.Import) Then + ' Found a class that is actually imported from COM but not WinRT + ' this is definitely a non-WinRT COM object + Return False + End If + curType = curType.BaseType + End While + Return False + + End Get + End Property + + Friend ReadOnly Property VBFriendlyName() As String + Get + Return Utils.VBFriendlyName(m_Type, m_Instance) + End Get + End Property + + Friend ReadOnly Property IsArray() As Boolean + Get + Return IsArrayType(m_Type) AndAlso m_Instance IsNot Nothing + End Get + End Property + + Friend ReadOnly Property IsValueType() As Boolean + Get + Return Symbols.IsValueType(m_Type) AndAlso m_Instance IsNot Nothing + End Get + End Property + + Private Const DefaultLookupFlags As BindingFlags = _ + BindingFlags.IgnoreCase Or _ + BindingFlags.FlattenHierarchy Or _ + BindingFlags.Public Or _ + BindingFlags.Static Or _ + BindingFlags.Instance + + Private Shared ReadOnly NoMembers As MemberInfo() = {} + + ' CONSIDER: Move this function directly into OverloadResolution so + ' that we don't expand all the signatures. It would require adding + ' a flag to GetMembers to either do the filtering or not. + Private Shared Function FilterInvalidMembers(ByVal Members As MemberInfo()) As MemberInfo() + + If Members Is Nothing OrElse Members.Length = 0 Then + Return Nothing + End If + + Dim ValidMemberCount As Integer = 0 + Dim MemberIndex As Integer = 0 + + For MemberIndex = 0 To Members.Length - 1 + Dim Parameters As ParameterInfo() = Nothing + Dim ReturnType As Type = Nothing + + Select Case Members(MemberIndex).MemberType + + Case MemberTypes.Constructor, _ + MemberTypes.Method + + Dim CurrentMethod As MethodInfo = DirectCast(Members(MemberIndex), MethodInfo) + + Parameters = CurrentMethod.GetParameters + ReturnType = CurrentMethod.ReturnType + + Case MemberTypes.Property + + Dim PropertyBlock As PropertyInfo = DirectCast(Members(MemberIndex), PropertyInfo) + Dim GetMethod As MethodInfo = PropertyBlock.GetGetMethod + + If GetMethod IsNot Nothing Then + Parameters = GetMethod.GetParameters + Else + Dim SetMethod As MethodInfo = PropertyBlock.GetSetMethod + Dim SetParameters As ParameterInfo() = SetMethod.GetParameters + + Parameters = New ParameterInfo(SetParameters.Length - 2) {} + System.Array.Copy(SetParameters, Parameters, Parameters.Length) + End If + + ReturnType = PropertyBlock.PropertyType + + Case MemberTypes.Field + ReturnType = DirectCast(Members(MemberIndex), FieldInfo).FieldType + + End Select + + If AreParametersAndReturnTypesValid(Parameters, ReturnType) Then + ValidMemberCount += 1 + Else + Members(MemberIndex) = Nothing + End If + Next + + If ValidMemberCount = Members.Length Then + Return Members + ElseIf ValidMemberCount > 0 Then + + Dim ValidMembers(ValidMemberCount - 1) As MemberInfo + Dim ValidMemberIndex As Integer = 0 + + For MemberIndex = 0 To Members.Length - 1 + If Members(MemberIndex) IsNot Nothing Then + ValidMembers(ValidMemberIndex) = Members(MemberIndex) + ValidMemberIndex += 1 + End If + Next + + Return ValidMembers + End If + + Return Nothing + End Function + + ' For a WinRT object, we want to treat members of it's collection interfaces as members of the object + ' itself. So GetMembers calls here to find the member in all the collection interfaces that this object + ' implements. + Friend Function LookupWinRTCollectionInterfaceMembers(ByVal MemberName As String) As List(Of MemberInfo) + Debug.Assert(Me.IsWindowsRuntimeObject(), "Expected a Windows Runtime Object") + + Dim Result As New List(Of MemberInfo) + For Each Implemented As Type In m_Type.GetInterfaces() + If IsCollectionInterface(Implemented) Then + Dim members As MemberInfo() = Implemented.GetMember(MemberName, DefaultLookupFlags) + If (members IsNot Nothing) Then + Result.AddRange(members) + End If + End If + Next + + Return Result + End Function + + Friend Function LookupNamedMembers(ByVal MemberName As String) As MemberInfo() + 'Returns an array of members matching MemberName sorted by inheritance (most derived first). + 'If no members match MemberName, returns an empty array. + + Dim Result As MemberInfo() + + If IsGenericParameter(m_Type) Then + 'Getting the members of a generic parameter follows a special rule. + 'In a Latebound context, only members of the class constraint are + 'applicable. We will ignore interface constraints. Also, custom + 'Reflection can't be involved, so no need to use that. + + Dim ClassConstraint As Type = GetClassConstraint(m_Type) + If ClassConstraint IsNot Nothing Then + Result = ClassConstraint.GetMember(MemberName, DefaultLookupFlags) + Else + Result = Nothing + End If + Else + Result = m_IReflect.GetMember(MemberName, DefaultLookupFlags) + End If + + If Me.IsWindowsRuntimeObject() Then + Dim CollectionMethods As List(Of MemberInfo) = LookupWinRTCollectionInterfaceMembers(MemberName) + If Result IsNot Nothing Then + CollectionMethods.AddRange(Result) + End If + + Result = CollectionMethods.ToArray() + End If + + Result = FilterInvalidMembers(Result) + + If Result Is Nothing Then + Result = NoMembers + ElseIf Result.Length > 1 Then + Array.Sort(Of MemberInfo)(Result, InheritanceSorter.Instance) + End If + + Return Result + End Function + + ' For a WinRT object, we want to treat members of it's collection interfaces as members of the object + ' itself. Search through all the collection interfaces for default members. + Private Function LookupWinRTCollectionDefaultMembers(ByRef DefaultMemberName As String) As List(Of MemberInfo) + Debug.Assert(Me.IsWindowsRuntimeObject(), "Expected a Windows Runtime Object") + + Dim Result As New List(Of MemberInfo) + For Each Implemented As Type In m_Type.GetInterfaces() + If IsCollectionInterface(Implemented) Then + Dim members As MemberInfo() = LookupDefaultMembers(DefaultMemberName, Implemented) + If (members IsNot Nothing) Then + Result.AddRange(members) + End If + End If + Next + + Return Result + End Function + + Private Function LookupDefaultMembers(ByRef DefaultMemberName As String, ByVal SearchType As Type) As MemberInfo() + 'Returns an array of default members sorted by inheritance (most derived first). + 'If no members match MemberName, returns an empty array. + 'The default member name is determined by walking up the inheritance hierarchy looking + 'for a DefaultMemberAttribute. + + Dim PotentialDefaultMemberName As String = Nothing + + 'Find the default member name. + Dim Current As Type = SearchType + Do + 'CONSIDER: if one exists, use the generic form of GetCustomAttributes. + Dim Attributes As Object() = Current.GetCustomAttributes(GetType(DefaultMemberAttribute), False) + + If Attributes IsNot Nothing AndAlso Attributes.Length > 0 Then + PotentialDefaultMemberName = DirectCast(Attributes(0), DefaultMemberAttribute).MemberName + Exit Do + End If + Current = Current.BaseType + + Loop While Current IsNot Nothing AndAlso Not IsRootObjectType(Current) + + If PotentialDefaultMemberName IsNot Nothing Then + Dim Result As MemberInfo() = Current.GetMember(PotentialDefaultMemberName, DefaultLookupFlags) + + Result = FilterInvalidMembers(Result) + + If Result IsNot Nothing Then + DefaultMemberName = PotentialDefaultMemberName + If Result.Length > 1 Then + Array.Sort(Result, InheritanceSorter.Instance) + End If + Return Result + End If + End If + + Return NoMembers + End Function + + Friend Function GetMembers( _ + ByRef MemberName As String, _ + ByVal ReportErrors As Boolean) As MemberInfo() + + Dim Result As MemberInfo() + If MemberName Is Nothing Then MemberName = "" + + If MemberName = "" Then + + If m_UseCustomReflection Then + Result = Me.LookupNamedMembers(MemberName) + Else + Result = Me.LookupDefaultMembers(MemberName, m_Type) 'MemberName is set during this call. + End If + + If Me.IsWindowsRuntimeObject() Then + Dim CollectionMethods As List(Of MemberInfo) = LookupWinRTCollectionDefaultMembers(MemberName) + If Result IsNot Nothing Then + CollectionMethods.AddRange(Result) + End If + + Result = CollectionMethods.ToArray() + End If + + If Result.Length = 0 Then + If ReportErrors Then + Throw New MissingMemberException( _ + GetResourceString(ResID.MissingMember_NoDefaultMemberFound1, Me.VBFriendlyName)) + End If + + Return Result + End If + + If m_UseCustomReflection Then MemberName = Result(0).Name + + Else + Result = Me.LookupNamedMembers(MemberName) + + If Result.Length = 0 Then + If ReportErrors Then + Throw New MissingMemberException( _ + GetResourceString(ResID.MissingMember_MemberNotFoundOnType2, MemberName, Me.VBFriendlyName)) + End If + + Return Result + End If + End If + + Return Result + End Function + + Private Sub CheckForClassExtendingCOMClass() + If Me.IsCOMObject AndAlso Not Me.IsWindowsRuntimeObject AndAlso m_Type.FullName <> "System.__ComObject" AndAlso m_Type.BaseType.FullName <> "System.__ComObject" Then + Throw New InvalidOperationException(GetResourceString(ResID.LateboundCallToInheritedComClass)) + End If + End Sub + + + Friend Function GetFieldValue(ByVal Field As FieldInfo) As Object + If m_Instance Is Nothing AndAlso Not IsShared(Field) Then + 'Reference to non-shared member '|1' requires an object reference. + Throw New NullReferenceException( _ + GetResourceString(ResID.NullReference_InstanceReqToAccessMember1, FieldToString(Field))) + End If + ' + 'BEGIN: SECURITY SECURITY SECURITY SECURITY SECURITY SECURITY SECURITY SECURITY + ' + If IsNonPublicRuntimeMember(Field) Then + 'No message text intentional - Default BCL message used + Throw New MissingMemberException + End If + ' + 'END: SECURITY SECURITY SECURITY SECURITY SECURITY SECURITY SECURITY SECURITY + ' + Return Field.GetValue(m_Instance) + End Function + + Friend Sub SetFieldValue(ByVal Field As FieldInfo, ByVal Value As Object) + If Field.IsInitOnly Then + 'REVIEW: Should this really be MissingMemberException, or something else? + Throw New MissingMemberException( _ + GetResourceString(ResID.MissingMember_ReadOnlyField2, Field.Name, Me.VBFriendlyName)) + End If + + If m_Instance Is Nothing AndAlso Not IsShared(Field) Then + 'Reference to non-shared member '|1' requires an object reference. + Throw New NullReferenceException( _ + GetResourceString(ResID.NullReference_InstanceReqToAccessMember1, FieldToString(Field))) + End If + ' + 'BEGIN: SECURITY SECURITY SECURITY SECURITY SECURITY SECURITY SECURITY SECURITY + ' + If IsNonPublicRuntimeMember(Field) Then + 'No message text intentional - Default BCL message used + Throw New MissingMemberException + End If + ' + 'END: SECURITY SECURITY SECURITY SECURITY SECURITY SECURITY SECURITY SECURITY + ' + Field.SetValue(m_Instance, Conversions.ChangeType(Value, Field.FieldType)) + Return + End Sub + + Friend Function GetArrayValue(ByVal Indices As Object()) As Object + Debug.Assert(Me.IsArray, "expected array when getting array value") + Debug.Assert(Indices IsNot Nothing, "expected valid indices") + + + Dim ArrayInstance As Array = DirectCast(m_Instance, System.Array) + Dim Rank As Integer = ArrayInstance.Rank + + If Indices.Length <> Rank Then + Throw New RankException 'UNDONE: more detailed information here? + End If + + 'We use ChangeType to handle potential user-defined conversion operators. + Dim ZerothIndex As Integer = _ + DirectCast(Conversions.ChangeType(Indices(0), GetType(Integer)), Integer) + + If Rank = 1 Then + Return ArrayInstance.GetValue(ZerothIndex) + Else + Dim FirstIndex As Integer = _ + DirectCast(Conversions.ChangeType(Indices(1), GetType(Integer)), Integer) + + If Rank = 2 Then + Return ArrayInstance.GetValue(ZerothIndex, FirstIndex) + Else + Dim SecondIndex As Integer = _ + DirectCast(Conversions.ChangeType(Indices(2), GetType(Integer)), Integer) + + If Rank = 3 Then + Return ArrayInstance.GetValue(ZerothIndex, FirstIndex, SecondIndex) + Else + Dim IndexArray As Integer() = New Integer(Rank - 1) {} + IndexArray(0) = ZerothIndex : IndexArray(1) = FirstIndex : IndexArray(2) = SecondIndex + + For i As Integer = 3 To Rank - 1 + IndexArray(i) = _ + DirectCast(Conversions.ChangeType(Indices(i), GetType(Integer)), Integer) + Next + + Return ArrayInstance.GetValue(IndexArray) + End If + End If + End If + + End Function + + Friend Sub SetArrayValue(ByVal Arguments As Object()) + 'The last argument is the Value to be stored into the array. The other arguments are + 'the indices into the array. + Debug.Assert(Me.IsArray, "expected array when setting array value") + Debug.Assert(Arguments IsNot Nothing, "expected valid indices") + + + Dim ArrayInstance As Array = DirectCast(m_Instance, System.Array) + Dim Rank As Integer = ArrayInstance.Rank + + If Arguments.Length - 1 <> Rank Then + Throw New RankException 'UNDONE: more detailed information here? + End If + + 'To ensure order of evaulation, we must evaluate the Value argument after + 'evaluating each index argument. + Dim Value As Object = Arguments(Arguments.Length - 1) + Dim ElementType As Type = m_Type.GetElementType + + 'We use ChangeType to handle potential user-defined conversion operators. + Dim ZerothIndex As Integer = _ + DirectCast(Conversions.ChangeType(Arguments(0), GetType(Integer)), Integer) + + If Rank = 1 Then + ArrayInstance.SetValue(Conversions.ChangeType(Value, ElementType), ZerothIndex) + Return + Else + Dim FirstIndex As Integer = _ + DirectCast(Conversions.ChangeType(Arguments(1), GetType(Integer)), Integer) + + If Rank = 2 Then + ArrayInstance.SetValue(Conversions.ChangeType(Value, ElementType), ZerothIndex, FirstIndex) + Return + Else + Dim SecondIndex As Integer = _ + DirectCast(Conversions.ChangeType(Arguments(2), GetType(Integer)), Integer) + + If Rank = 3 Then + ArrayInstance.SetValue(Conversions.ChangeType(Value, ElementType), ZerothIndex, FirstIndex, SecondIndex) + Return + Else + Dim IndexArray As Integer() = New Integer(Rank - 1) {} + IndexArray(0) = ZerothIndex : IndexArray(1) = FirstIndex : IndexArray(2) = SecondIndex + + For i As Integer = 3 To Rank - 1 + IndexArray(i) = _ + DirectCast(Conversions.ChangeType(Arguments(i), GetType(Integer)), Integer) + Next + + ArrayInstance.SetValue(Conversions.ChangeType(Value, ElementType), IndexArray) + Return + End If + End If + End If + + End Sub + + Friend Function InvokeMethod( _ + ByVal TargetProcedure As Method, _ + ByVal Arguments As Object(), _ + ByVal CopyBack As Boolean(), _ + ByVal Flags As BindingFlags) As Object + + + Dim CallTarget As MethodBase = GetCallTarget(TargetProcedure, Flags) + Debug.Assert(CallTarget IsNot Nothing, "must have valid MethodBase") + + Debug.Assert(Not TargetProcedure.IsGeneric OrElse _ + DirectCast(TargetProcedure.AsMethod, MethodInfo).GetGenericMethodDefinition IsNot Nothing, _ + "expected bound generic method by this point") + + Dim CallArguments As Object() = _ + ConstructCallArguments(TargetProcedure, Arguments, Flags) + + If m_Instance Is Nothing AndAlso Not IsShared(CallTarget) Then + 'Reference to non-shared member '|1' requires an object reference. + Throw New NullReferenceException( _ + GetResourceString(ResID.NullReference_InstanceReqToAccessMember1, TargetProcedure.ToString)) + End If + ' + 'BEGIN: SECURITY SECURITY SECURITY SECURITY SECURITY SECURITY SECURITY SECURITY + ' + If IsNonPublicRuntimeMember(CallTarget) Then + 'No message text intentional - Default BCL message used + Throw New MissingMemberException + End If + ' + 'END: SECURITY SECURITY SECURITY SECURITY SECURITY SECURITY SECURITY SECURITY + ' + + Dim Result As Object + Try + Result = CallTarget.Invoke(m_Instance, CallArguments) + + Catch ex As TargetInvocationException When ex.InnerException IsNot Nothing + 'For backwards compatiblity, throw the inner exception of a TargetInvocationException. + Throw ex.InnerException + + End Try + + ReorderArgumentArray(TargetProcedure, CallArguments, Arguments, CopyBack, Flags) + Return Result + End Function + +#If 0 Then + Friend Function InvokeCOMMethod( _ + ByVal MethodName As String, _ + ByVal Arguments As Object(), _ + ByVal ArgumentNames As String(), _ + ByVal CopyBack As Boolean(), _ + ByVal InvocationFlags As BindingFlags) As Object + + + Debug.Assert(Me.IsCOMObject, "this function intended for COM objects only") + + If MethodName Is Nothing Then MethodName = "" + Dim Modifiers As ParameterModifier() = Nothing + + If CopyBack IsNot Nothing AndAlso _ + m_Instance IsNot Nothing AndAlso _ + Not Runtime.Remoting.RemotingServices.IsTransparentProxy(m_Instance) Then + + Dim Modifier As ParameterModifier = New ParameterModifier(Arguments.Length) + Modifiers = New ParameterModifier() {Modifier} + + 'Set all flags to ByRef, except for Missing arguments. + For Index As Integer = 0 To Arguments.Length - 1 + If Arguments(Index) IsNot System.Reflection.Missing.Value Then + Modifier.Item(Index) = CopyBack(Index) + End If + Next + + End If + + Try + ' + 'BEGIN: SECURITY SECURITY SECURITY SECURITY SECURITY SECURITY SECURITY SECURITY + ' + Call (New SecurityPermission(PermissionState.Unrestricted)).Demand() + Return _ + m_IReflect.InvokeMember( _ + MethodName, _ + InvocationFlags Or DefaultLookupFlags, _ + Nothing, _ + m_Instance, _ + Arguments, _ + Modifiers, _ + Nothing, _ + ArgumentNames) + ' + 'END: SECURITY SECURITY SECURITY SECURITY SECURITY SECURITY SECURITY SECURITY + ' + Catch InvocationError As Exception When IsMissingMemberException(InvocationError) + Throw _ + New MissingMemberException( _ + GetResourceString( _ + ResID.MissingMember_MemberNotFoundOnType2, _ + MethodName, _ + Me.VBFriendlyName), _ + InvocationError) + + Catch InvocationError As TargetInvocationException + Throw InvocationError.InnerException + + End Try + + End Function + + Friend Function InvokeCOMMethod2( _ + ByVal MethodName As String, _ + ByVal Arguments As Object(), _ + ByVal ArgumentNames As String(), _ + ByVal CopyBack As Boolean(), _ + ByVal InvocationFlags As BindingFlags) As Object + + + Debug.Assert(Me.IsCOMObject, "this function intended for COM objects only") + + If MethodName Is Nothing Then MethodName = "" + Dim Modifiers As ParameterModifier() = Nothing + + Try + 'Return binder.InvokeMember(name, flags, objType, objIReflect, o, args, paramnames) + + If CopyBack IsNot Nothing AndAlso _ + m_Instance IsNot Nothing AndAlso _ + Not Runtime.Remoting.RemotingServices.IsTransparentProxy(m_Instance) Then + + Dim Modifier As ParameterModifier = New ParameterModifier(Arguments.Length) + Modifiers = New ParameterModifier() {Modifier} + + 'Set all flags to ByRef, except for Missing arguments. + For Index As Integer = 0 To Arguments.Length - 1 + If Arguments(Index) IsNot System.Reflection.Missing.Value Then + Modifier.Item(Index) = CopyBack(Index) + End If + Next + + End If + + Try + ' + 'BEGIN: SECURITY SECURITY SECURITY SECURITY SECURITY SECURITY SECURITY SECURITY + ' + Call (New SecurityPermission(PermissionState.Unrestricted)).Demand() + Return _ + m_IReflect.InvokeMember( _ + MethodName, _ + InvocationFlags Or DefaultLookupFlags, _ + Nothing, _ + m_Instance, _ + Arguments, _ + Modifiers, _ + Nothing, _ + ArgumentNames) + ' + 'END: SECURITY SECURITY SECURITY SECURITY SECURITY SECURITY SECURITY SECURITY + ' + Catch InvocationError As MissingMemberException + Throw _ + New MissingMemberException( _ + GetResourceString( _ + ResID.MissingMember_MemberNotFoundOnType2, _ + MethodName, _ + Me.VBFriendlyName), _ + InvocationError) + End Try + ' + ' + ' There may be a property or field that returns an array or object with a default member + ' We get the field or property then try using a LateIndexGet + 'UNDONE: handle this in the binder code once the com+ team has completed the Beta2 DCR work + Catch ex As Exception When IsMissingMemberException(ex) + + Dim SecondaryResult As Object + + Try + Modifiers = Nothing + + If CopyBack IsNot Nothing AndAlso _ + m_Instance IsNot Nothing AndAlso _ + Not Runtime.Remoting.RemotingServices.IsTransparentProxy(m_Instance) Then + + Dim Modifier As ParameterModifier = New ParameterModifier(Arguments.Length) + Modifiers = New ParameterModifier() {Modifier} + + 'Set all flags to ByRef, except for Missing arguments. + For Index As Integer = 0 To Arguments.Length - 1 + If Arguments(Index) IsNot System.Reflection.Missing.Value Then + Modifier.Item(Index) = CopyBack(Index) + End If + Next + + End If + + Try + ' + 'BEGIN: SECURITY SECURITY SECURITY SECURITY SECURITY SECURITY SECURITY SECURITY + ' + Call (New SecurityPermission(PermissionState.Unrestricted)).Demand() + SecondaryResult = _ + m_IReflect.InvokeMember( _ + MethodName, _ + InvocationFlags Or DefaultLookupFlags, _ + Nothing, _ + m_Instance, _ + Nothing, _ + Modifiers, _ + Nothing, _ + Nothing) + ' + 'END: SECURITY SECURITY SECURITY SECURITY SECURITY SECURITY SECURITY SECURITY + ' + Catch InvocationError As MissingMemberException + Throw _ + New MissingMemberException( _ + GetResourceString( _ + ResID.MissingMember_MemberNotFoundOnType2, _ + MethodName, _ + Me.VBFriendlyName), _ + InvocationError) + End Try + Catch exInner As AccessViolationException + Throw exInner + Catch exInner As StackOverflowException + Throw exInner + Catch exInner As OutOfMemoryException + Throw exInner + Catch exInner As System.Threading.ThreadAbortException + Throw exInner + Catch + SecondaryResult = Nothing + End Try + + If SecondaryResult Is Nothing Then + Throw _ + New MissingMemberException( _ + GetResourceString( _ + ResID.MissingMember_MemberNotFoundOnType2, _ + MethodName, _ + Me.VBFriendlyName)) + Else + Try + Return NewLateBinding.LateIndexGet(SecondaryResult, Arguments, ArgumentNames) + Catch exInner As Exception When IsMissingMemberException(exInner) AndAlso (TypeOf ex Is MissingMemberException) + Throw ex + End Try + End If + + Catch ex As TargetInvocationException + Throw ex.InnerException + End Try + + End Function +#End If + + End Class + + Friend NotInheritable Class Method + + Private m_Item As MemberInfo 'The underlying method or property reflection object. + Private m_RawItem As MethodBase 'The unsubstituted raw generic method. + Private m_Parameters As ParameterInfo() 'The parameters used for this method by overload resolution. + Private m_RawParameters As ParameterInfo() 'The unsubstituted raw parameters of a generic method. + Private m_RawParametersFromType As ParameterInfo() 'The unsubstituted raw parameters of a generic method in the raw type. + Private m_RawDeclaringType As Type 'The uninstantiated type containing this method. + + Friend ReadOnly ParamArrayIndex As Integer 'The index of the ParamArray in the parameters array, -1 if method has no ParamArray. + Friend ReadOnly ParamArrayExpanded As Boolean 'Indicates if this method's ParamArray should be considered in its expanded form. + + Friend NotCallable As Boolean 'Indicates if this method has been rejected as uncallable. + Friend RequiresNarrowingConversion As Boolean 'Indicates if an argument requires narrowing one of the method's parameters. + Friend AllNarrowingIsFromObject As Boolean 'Indicates if the type of all arguments which require narrowing to this method's parameters are Object. + Friend LessSpecific As Boolean 'Indicates is this method loses the competition for most specific procedure. + + Friend ArgumentsValidated As Boolean 'Indicates if the arguments have been validated against this method. + Friend NamedArgumentMapping As Integer() 'Table of indices into the argument array for mapping named arguments to parameters. + Friend TypeArguments As Type() 'Set of type arguments either supplied or inferred for this method. + Friend ArgumentMatchingDone As Boolean 'Indicates whether the argument matching task (CanMatchArguments) has already been completed for this Method + + Private Sub New( _ + ByVal Parameters As ParameterInfo(), _ + ByVal ParamArrayIndex As Integer, _ + ByVal ParamArrayExpanded As Boolean) + + Me.m_Parameters = Parameters + Me.m_RawParameters = Parameters + Me.ParamArrayIndex = ParamArrayIndex + Me.ParamArrayExpanded = ParamArrayExpanded + + Me.AllNarrowingIsFromObject = True 'Assume True until non-object narrowing is encountered. + End Sub + + Friend Sub New( _ + ByVal Method As MethodBase, _ + ByVal Parameters As ParameterInfo(), _ + ByVal ParamArrayIndex As Integer, _ + ByVal ParamArrayExpanded As Boolean) + + MyClass.New(Parameters, ParamArrayIndex, ParamArrayExpanded) + Me.m_Item = Method + Me.m_RawItem = Method + End Sub + + Friend Sub New( _ + ByVal [Property] As PropertyInfo, _ + ByVal Parameters As ParameterInfo(), _ + ByVal ParamArrayIndex As Integer, _ + ByVal ParamArrayExpanded As Boolean) + + MyClass.New(Parameters, ParamArrayIndex, ParamArrayExpanded) + Me.m_Item = [Property] + End Sub + + Friend ReadOnly Property Parameters() As ParameterInfo() + Get + Return m_Parameters + End Get + End Property + + Friend ReadOnly Property RawParameters() As ParameterInfo() + Get + 'After a generic method has been bound, we still need access + 'to the raw, unbound parameters. + Return m_RawParameters + End Get + End Property + + Friend ReadOnly Property RawParametersFromType() As ParameterInfo() + Get + If m_RawParametersFromType Is Nothing Then + If Not IsProperty Then + Dim MethodToken As Integer = m_Item.MetadataToken + Dim DeclaringType As Type = m_Item.DeclaringType + + Dim RawMethod As MethodBase = DeclaringType.Module.ResolveMethod(MethodToken, Nothing, Nothing) + + m_RawParametersFromType = RawMethod.GetParameters() + Else + m_RawParametersFromType = m_RawParameters + End If + End If + + Return m_RawParametersFromType + End Get + End Property + + Friend ReadOnly Property DeclaringType() As Type + Get + Return m_Item.DeclaringType + End Get + End Property + + Friend ReadOnly Property RawDeclaringType() As Type + Get + If m_RawDeclaringType Is Nothing Then + Dim DeclaringType As Type = m_Item.DeclaringType + Dim TypeToken As Integer = DeclaringType.MetadataToken + + m_RawDeclaringType = DeclaringType.Module.ResolveType(TypeToken, Nothing, Nothing) + End If + + Return m_RawDeclaringType + End Get + End Property + + Friend ReadOnly Property HasParamArray() As Boolean + Get + Return ParamArrayIndex > -1 + End Get + End Property + + 'UNDONE: this is slow -- can't it be made better by caching the value away? + Friend ReadOnly Property HasByRefParameter() As Boolean + Get + For Each Parameter As ParameterInfo In Parameters + If Parameter.ParameterType.IsByRef Then + Return True + End If + Next + Return False + End Get + End Property + + Friend ReadOnly Property IsProperty() As Boolean + Get + Return m_Item.MemberType = MemberTypes.Property + End Get + End Property + + Friend ReadOnly Property IsMethod() As Boolean + Get + Return m_Item.MemberType = MemberTypes.Method OrElse _ + m_Item.MemberType = MemberTypes.Constructor + End Get + End Property + + Friend ReadOnly Property IsGeneric() As Boolean + Get + Return Symbols.IsGeneric(m_Item) + End Get + End Property + + Friend ReadOnly Property TypeParameters() As Type() + Get + 'CONSIDER: for performance, cache away this result since each consecutive call creates + 'a clone of the type parameter array. + Return Symbols.GetTypeParameters(m_Item) + End Get + End Property + + Friend Function BindGenericArguments() As Boolean + 'This function instantiates a generic method with the type arguments supplied or inferred + 'for this method. + ' + 'ISSUE (3/4/2004): Constructing the generic binding using Reflection peforms + ' constraint checking. This is bad if the binding will be used + ' to resolve overloaded calls since constraints should not participate + ' in the selection process. Instead, constraints should be checked + ' after overload resolution has selected a method. For now, there is + ' nothing reasonble we can do since Reflection does not allow the + ' instantiation of generic methods with arguments that violate the + ' constraints. If a violation occurs, catch the exception and return + ' false signifying that the binding failed. + + Debug.Assert(Me.ArgumentsValidated, "can't bind without validating arguments") + Debug.Assert(Me.IsMethod, "binding to a non-method") + Try + 'We use the original raw generic method so we can rebind an already bound Method. + m_Item = DirectCast(m_RawItem, MethodInfo).MakeGenericMethod(TypeArguments) + m_Parameters = Me.AsMethod.GetParameters + Return True + Catch ex As ArgumentException + Return False + End Try + End Function + + Friend Function AsMethod() As MethodBase + Debug.Assert(Me.IsMethod, "casting a non-method to a method") + Return TryCast(m_Item, MethodBase) + End Function + + Friend Function AsProperty() As PropertyInfo + Debug.Assert(Me.IsProperty, "casting a non-property to a property") + Return TryCast(m_Item, PropertyInfo) + End Function + + Public Shared Operator =(ByVal Left As Method, ByVal Right As Method) As Boolean + Return Left.m_Item Is Right.m_Item + End Operator + + Public Shared Operator <>(ByVal Left As Method, ByVal right As Method) As Boolean + Return Left.m_Item IsNot right.m_Item + End Operator + + Public Shared Operator =(ByVal Left As MemberInfo, ByVal Right As Method) As Boolean + Return Left Is Right.m_Item + End Operator + + Public Shared Operator <>(ByVal Left As MemberInfo, ByVal Right As Method) As Boolean + Return Left IsNot Right.m_Item + End Operator + + Public Overrides Function ToString() As String + Return MemberToString(m_Item) + End Function + +#If BINDING_LOG Then + Private Function BoolStr(ByVal x As Boolean) As String + If x Then Return "T" Else Return "F" + End Function + + Friend Function DumpContents() As String + Dim result As String = IIf(Me.IsMethod, "Meth", "Prop") + result &= " " & m_Item.Name & " PAExpanded:" & BoolStr(ParamArrayExpanded) & " Container:" & m_Item.DeclaringType.Name & " (" + For Each p As ParameterInfo In Parameters + result &= p.ParameterType.Name & "," + Next + result &= ")" + result &= " PAIndex:" & CStr(ParamArrayIndex) + result &= " NotCallable:" & BoolStr(NotCallable) + result &= " ReqNar:" & BoolStr(RequiresNarrowingConversion) + + If RequiresNarrowingConversion Then + result &= " AllNarFromObj:" & BoolStr(AllNarrowingIsFromObject) + End If + + Return result + End Function +#End If + + End Class + + Friend NotInheritable Class TypedNothing + 'A class which represents a Nothing reference but with a particular type. + 'Normally, a Nothing reference converts to any type. However, during operator + 'resolution Nothing should match only one type. This class acts as a place holder + 'for Nothing in the argument array and stores the Type which "Nothing" should have. + + Friend ReadOnly Type As Type + + Friend Sub New(ByVal Type As Type) + Me.Type = Type + End Sub + End Class + + End Class + +End Namespace diff --git a/Microsoft.VisualBasic/runtime/msvbalib/Helpers/UnsafeNativeMethods.vb b/Microsoft.VisualBasic/runtime/msvbalib/Helpers/UnsafeNativeMethods.vb new file mode 100644 index 000000000..af7fe2a01 --- /dev/null +++ b/Microsoft.VisualBasic/runtime/msvbalib/Helpers/UnsafeNativeMethods.vb @@ -0,0 +1,461 @@ +' Copyright (c) Microsoft Corporation. All rights reserved. + +Imports System +Imports System.Security +Imports System.Security.Permissions +Imports System.Text +Imports System.Runtime.InteropServices +Imports System.Runtime.ConstrainedExecution +Imports System.Runtime.Versioning + +Namespace Microsoft.VisualBasic.CompilerServices + + _ + _ + Friend NotInheritable Class UnsafeNativeMethods + + _ + _ + _ + Friend Declare Ansi Function LCMapStringA _ + Lib "kernel32" Alias "LCMapStringA" (ByVal Locale As Integer, ByVal dwMapFlags As Integer, _ + ByVal lpSrcStr As Byte(), ByVal cchSrc As Integer, ByVal lpDestStr As Byte(), ByVal cchDest As Integer) As Integer + + _ + _ + _ + Friend Declare Auto Function LCMapString _ + Lib "kernel32" (ByVal Locale As Integer, ByVal dwMapFlags As Integer, _ + ByVal lpSrcStr As String, ByVal cchSrc As Integer, ByVal lpDestStr As String, ByVal cchDest As Integer) As Integer + + _ + _ + _ + Friend Shared Function VarParseNumFromStr( _ + <[In](), MarshalAs(UnmanagedType.LPWStr)> ByVal str As String, _ + ByVal lcid As Integer, _ + ByVal dwFlags As Integer, _ + ByVal numprsPtr As Byte(), _ + ByVal digits As Byte()) As Integer + End Function + + _ + _ + _ + Friend Shared Function VarNumFromParseNum( _ + ByVal numprsPtr As Byte(), _ + ByVal DigitArray As Byte(), _ + ByVal dwVtBits As Int32) As Object + End Function + + _ + _ + _ + Friend Shared Sub VariantChangeType( _ + ByRef dest As Object, _ + <[In]()> ByRef Src As Object, _ + ByVal wFlags As Int16, _ + ByVal vt As Int16) + End Sub + + _ + _ + _ + Friend Shared Function MessageBeep(ByVal uType As Integer) As Integer + End Function + + _ + _ + _ + Friend Shared Function SetLocalTime(ByVal systime As NativeTypes.SystemTime) As Integer + End Function + + _ + _ + _ + Friend Shared Function MoveFile(<[In](), MarshalAs(UnmanagedType.LPTStr)> ByVal lpExistingFileName As String, _ + <[In](), MarshalAs(UnmanagedType.LPTStr)> ByVal lpNewFileName As String) As Integer + End Function + + _ + _ + _ + Friend Shared Function GetLogicalDrives() As Integer + End Function + + _ + _ + _ + Friend Shared Function CreateFileMapping(ByVal hFile As HandleRef, ByVal lpAttributes As NativeTypes.SECURITY_ATTRIBUTES, ByVal flProtect As Integer, ByVal dwMaxSizeHi As Integer, ByVal dwMaxSizeLow As Integer, ByVal lpName As String) As Win32.SafeHandles.SafeFileHandle + End Function + + _ + _ + _ + Friend Shared Function OpenFileMapping(ByVal dwDesiredAccess As Integer, ByVal bInheritHandle As Boolean, ByVal lpName As String) As Win32.SafeHandles.SafeFileHandle + End Function + + _ + _ + _ + Friend Shared Function MapViewOfFile(ByVal hFileMapping As IntPtr, ByVal dwDesiredAccess As Integer, ByVal dwFileOffsetHigh As Integer, ByVal dwFileOffsetLow As Integer, ByVal dwNumberOfBytesToMap As UintPtr) As SafeMemoryMappedViewOfFileHandle + End Function + + _ + _ + _ + _ + Friend Shared Function UnmapViewOfFile(ByVal pvBaseAddress As IntPtr) As Boolean + End Function + + Public Const MEMBERID_NIL As Integer = 0 + Public Const LCID_US_ENGLISH As Integer = &H409 + + + _ + Public Enum tagSYSKIND + SYS_WIN16 = 0 + SYS_MAC = 2 + End Enum + + ' REVIEW : - c# version was class, does it make a difference? + ' [StructLayout(LayoutKind.Sequential)] + ' Public class tagTLIBATTR { + _ + Public Structure tagTLIBATTR + Public guid As Guid + Public lcid As Integer + Public syskind As tagSYSKIND + Public wMajorVerNum As Short + Public wMinorVerNum As Short + Public wLibFlags As Short + End Structure + + _ + Public Interface ITypeComp + + _ + _ + Sub RemoteBind( _ + <[In](), MarshalAs(UnmanagedType.LPWStr)> ByVal szName As String, _ + <[In](), MarshalAs(UnmanagedType.U4)> ByVal lHashVal As Integer, _ + <[In](), MarshalAs(UnmanagedType.U2)> ByVal wFlags As Short, _ + ByVal ppTInfo As ITypeInfo(), _ + ByVal pDescKind As ComTypes.DESCKIND(), _ + ByVal ppFuncDesc As ComTypes.FUNCDESC(), _ + ByVal ppVarDesc As ComTypes.VARDESC(), _ + ByVal ppTypeComp As ITypeComp(), _ + ByVal pDummy As Integer()) + + _ + Sub RemoteBindType( _ + <[In](), MarshalAs(UnmanagedType.LPWStr)> ByVal szName As String, _ + <[In](), MarshalAs(UnmanagedType.U4)> ByVal lHashVal As Integer, _ + ByVal ppTInfo As ITypeInfo()) + End Interface + + + + _ + Public Interface IDispatch + + _ + _ + _ + Function GetTypeInfoCount() As Integer + + _ + _ + Function GetTypeInfo( _ + <[In]()> ByVal index As Integer, _ + <[In]()> ByVal lcid As Integer, _ + <[Out](), MarshalAs(UnmanagedType.Interface)> ByRef pTypeInfo As ITypeInfo) As Integer + + ' WARNING : - This api NOT COMPLETELY DEFINED, DO NOT CALL! + _ + _ + Function GetIDsOfNames() As Integer + + ' WARNING : - This api NOT COMPLETELY DEFINED, DO NOT CALL! + _ + _ + Function Invoke() As Integer + End Interface + + + + _ + Public Interface ITypeInfo + _ + _ + Function GetTypeAttr( _ + ByRef pTypeAttr As IntPtr) As Integer + + _ + _ + Function GetTypeComp( _ + ByRef pTComp As ITypeComp) As Integer + + + _ + _ + Function GetFuncDesc( _ + <[In](), MarshalAs(UnmanagedType.U4)> ByVal index As Integer, _ + ByRef pFuncDesc As IntPtr) As Integer + + _ + _ + Function GetVarDesc( _ + <[In](), MarshalAs(UnmanagedType.U4)> ByVal index As Integer, _ + ByRef pVarDesc As IntPtr) As Integer + + _ + _ + Function GetNames( _ + <[In]()> ByVal memid As Integer, _ + ByVal rgBstrNames As String(), _ + <[In](), MarshalAs(UnmanagedType.U4)> ByVal cMaxNames As Integer, _ + ByRef cNames As Integer) As Integer + + _ + _ + _ + Function GetRefTypeOfImplType( _ + <[In](), MarshalAs(UnmanagedType.U4)> ByVal index As Integer, _ + ByRef pRefType As Integer) As Integer + + _ + _ + _ + Function GetImplTypeFlags( _ + <[In](), MarshalAs(UnmanagedType.U4)> ByVal index As Integer, _ + ByVal pImplTypeFlags As Integer) As Integer + + _ + _ + Function GetIDsOfNames( _ + <[In]()> ByVal rgszNames As IntPtr, _ + <[In](), MarshalAs(UnmanagedType.U4)> ByVal cNames As Integer, _ + ByRef pMemId As IntPtr) As Integer + + _ + _ + _ + Function Invoke() As Integer + + _ + _ + Function GetDocumentation( _ + <[In]()> ByVal memid As Integer, _ + ByRef pBstrName As String, _ + ByRef pBstrDocString As String, _ + ByRef pdwHelpContext As Integer, _ + ByRef pBstrHelpFile As String) As Integer + + _ + _ + _ + Function GetDllEntry( _ + <[In]()> ByVal memid As Integer, _ + <[In]()> ByVal invkind As ComTypes.INVOKEKIND, _ + ByVal pBstrDllName As String, _ + ByVal pBstrName As String, _ + ByVal pwOrdinal As Short) As Integer + + _ + _ + Function GetRefTypeInfo( _ + <[In]()> ByVal hreftype As IntPtr, _ + ByRef pTypeInfo As ITypeInfo) As Integer + + _ + _ + _ + Function AddressOfMember() As Integer + + _ + _ + _ + Function CreateInstance( _ + <[In]()> ByRef pUnkOuter As IntPtr, _ + <[In]()> ByRef riid As Guid, _ + ByVal ppvObj As Object) As Integer + + _ + _ + _ + Function GetMops( _ + <[In]()> ByVal memid As Integer, _ + ByVal pBstrMops As String) As Integer + + _ + _ + Function GetContainingTypeLib( _ + ByVal ppTLib As ITypeLib(), _ + ByVal pIndex As Integer()) As Integer + + _ + _ + Sub ReleaseTypeAttr(ByVal typeAttr As IntPtr) + + _ + _ + Sub ReleaseFuncDesc(ByVal funcDesc As IntPtr) + + _ + _ + Sub ReleaseVarDesc(ByVal varDesc As IntPtr) + End Interface + + + + _ + Public Interface IProvideClassInfo + _ + Function GetClassInfo() As ITypeInfo + End Interface + + + + _ + Public Interface ITypeLib + _ + _ + Sub RemoteGetTypeInfoCount( _ + ByVal pcTInfo As Integer()) + + _ + Sub GetTypeInfo( _ + <[In](), MarshalAs(UnmanagedType.U4)> ByVal index As Integer, _ + ByVal ppTInfo As ITypeInfo()) + + _ + Sub GetTypeInfoType( _ + <[In](), MarshalAs(UnmanagedType.U4)> ByVal index As Integer, _ + ByVal pTKind As ComTypes.TYPEKIND()) + + _ + Sub GetTypeInfoOfGuid( _ + <[In]()> ByRef guid As Guid, _ + ByVal ppTInfo As ITypeInfo()) + + _ + _ + Sub RemoteGetLibAttr( _ + ByVal ppTLibAttr As tagTLIBATTR(), _ + ByVal pDummy As Integer()) + + _ + Sub GetTypeComp( _ + ByVal ppTComp As ITypeComp()) + + _ + _ + Sub RemoteGetDocumentation( _ + ByVal index As Integer, _ + <[In](), MarshalAs(UnmanagedType.U4)> ByVal refPtrFlags As Integer, _ + ByVal pBstrName As String(), _ + ByVal pBstrDocString As String(), _ + ByVal pdwHelpContext As Integer(), _ + ByVal pBstrHelpFile As String()) + + _ + _ + Sub RemoteIsName( _ + <[In](), MarshalAs(UnmanagedType.LPWStr)> ByVal szNameBuf As String, _ + <[In](), MarshalAs(UnmanagedType.U4)> ByVal lHashVal As Integer, _ + ByVal pfName As IntPtr(), _ + ByVal pBstrLibName As String()) + + _ + _ + Sub RemoteFindName( _ + <[In](), MarshalAs(UnmanagedType.LPWStr)> ByVal szNameBuf As String, _ + <[In](), MarshalAs(UnmanagedType.U4)> ByVal lHashVal As Integer, _ + ByVal ppTInfo As ITypeInfo(), _ + ByVal rgMemId As Integer(), _ + <[In](), Out(), MarshalAs(UnmanagedType.LPArray)> ByVal pcFound As Short(), _ + ByVal pBstrLibName As String()) + + _ + _ + Sub LocalReleaseTLibAttr() + End Interface + + '***************************************************************************** + ';GetKeyState + ' + 'Summary: + ' Gets the state of the specified key on the keyboard when the function + ' is called. + 'Params: + ' KeyCode - Integer representing the key in question. + 'Returns: + ' The high order byte is 1 if the key is down. The low order byte is one + ' if the key is toggled on (i.e. for keys like CapsLock) + '***************************************************************************** + _ + _ + _ + Friend Shared Function GetKeyState(ByVal KeyCode As Integer) As Short + End Function + + '''************************************************************************* + ''';LocalFree + ''' + ''' Frees memory allocated from the local heap. i.e. frees memory allocated + ''' by LocalAlloc or LocalReAlloc.n + ''' + ''' + ''' + ''' + _ + _ + _ + Friend Shared Function LocalFree(ByVal LocalHandle As IntPtr) As IntPtr + End Function + + ''' ************************************************************************** + ''' ;GetDiskFreeSpaceEx + ''' + ''' Used to determine how much free space is on a disk + ''' + ''' Path including drive we're getting information about + ''' The amount of free sapce available to the current user + ''' The total amount of space on the disk relative to the current user + ''' The amount of free spave on the disk. + ''' True if function succeeds in getting info otherwise False + _ + _ + _ + Friend Shared Function GetDiskFreeSpaceEx(ByVal Directory As String, ByRef UserSpaceFree As Long, ByRef TotalUserSpace As Long, ByRef TotalFreeSpace As Long) As Boolean + End Function + + '''************************************************************************* + ''' ;New + ''' + ''' FxCop violation: Avoid uninstantiated internal class. + ''' Adding a private constructor to prevent the compiler from generating a default constructor. + ''' + _ + Private Sub New() + End Sub + End Class + +End Namespace + diff --git a/Microsoft.VisualBasic/runtime/msvbalib/Helpers/Utils.vb b/Microsoft.VisualBasic/runtime/msvbalib/Helpers/Utils.vb new file mode 100644 index 000000000..96893b29d --- /dev/null +++ b/Microsoft.VisualBasic/runtime/msvbalib/Helpers/Utils.vb @@ -0,0 +1,1251 @@ +' Copyright (c) Microsoft Corporation. All rights reserved. + + + + +Imports System +Imports System.Security +Imports System.Security.Permissions +Imports System.Text +Imports System.Globalization +Imports System.Runtime.InteropServices +Imports System.Reflection +Imports System.Diagnostics +Imports Microsoft.VisualBasic.CompilerServices.ExceptionUtils +Imports Microsoft.VisualBasic.CompilerServices.Symbols +Imports System.Runtime.ConstrainedExecution +Imports System.Runtime.Versioning + +Namespace Microsoft.VisualBasic.CompilerServices + + _ + Public NotInheritable Class Utils + ' Prevent creation. + Private Sub New() + End Sub + + Friend Const SEVERITY_ERROR As Integer = &H80000000I + Friend Const FACILITY_CONTROL As Integer = &HA0000I + Friend Const FACILITY_RPC As Integer = &H10000I + Friend Const FACILITY_ITF As Integer = &H40000I + Friend Const SCODE_FACILITY As Integer = &H1FFF0000I + Private Const ERROR_INVALID_PARAMETER As Integer = 87 + + Friend Const chPeriod As Char = "."c + Friend Const chSpace As Char = ChrW(32) + Friend Const chIntlSpace As Char = ChrW(&H3000) + Friend Const chZero As Char = "0"c + Friend Const chHyphen As Char = "-"c + Friend Const chPlus As Char = "+"c + Friend Const chLetterA As Char = "A"c + Friend Const chLetterZ As Char = "Z"c + Friend Const chColon As Char = ":"c + Friend Const chSlash As Char = "/"c + Friend Const chBackslash As Char = "\"c + Friend Const chTab As Char = ControlChars.Tab + Friend Const chCharH0A As Char = ChrW(&HA) + Friend Const chCharH0B As Char = ChrW(&HB) + Friend Const chCharH0C As Char = ChrW(&HC) + Friend Const chCharH0D As Char = ChrW(&HD) + Friend Const chLineFeed As Char = ChrW(10) + Friend Const chDblQuote As Char = ChrW(34) + + Friend Const chGenericManglingChar As Char = "`"c + + Friend Const OptionCompareTextFlags As CompareOptions = (CompareOptions.IgnoreCase Or CompareOptions.IgnoreWidth Or CompareOptions.IgnoreKanaType) + + ' DON'T ACCESS DIRECTLY! Go through the property below + Private Shared m_VBAResourceManager As System.Resources.ResourceManager + Private Shared m_TriedLoadingResourceManager As Boolean + Private Const ResourceMsgDefault As String = "Message text unavailable. Resource file 'Microsoft.VisualBasic resources' not found." + Private Const VBDefaultErrorID As String = "ID95" + Friend Shared m_achIntlSpace() As Char = {chSpace, chIntlSpace} + Private Shared ReadOnly VoidType As Type = System.Type.GetType("System.Void") + Private Shared ReadOnly ResourceManagerSyncObj As Object = New Object + Private Shared m_VBRuntimeAssembly As System.Reflection.Assembly + + '============================================================================ + ' Shared Error functions + '============================================================================ + Friend Shared ReadOnly Property VBAResourceManager() As System.Resources.ResourceManager + Get + If Not m_VBAResourceManager Is Nothing Then + Return m_VBAResourceManager + End If + + SyncLock ResourceManagerSyncObj + If Not m_TriedLoadingResourceManager Then + Try + m_VBAResourceManager = New System.Resources.ResourceManager("Microsoft.VisualBasic", System.Reflection.Assembly.GetExecutingAssembly()) + Catch ex As StackOverflowException + Throw ex + Catch ex As OutOfMemoryException + Throw ex + Catch ex As System.Threading.ThreadAbortException + Throw ex + Catch + End Try + m_TriedLoadingResourceManager = True + End If + End SyncLock + + Return m_VBAResourceManager + End Get + End Property + + Friend Shared Function GetResourceString(ByVal ResourceId As vbErrors) As String + Return GetResourceString("ID" & CStr(ResourceId)) + End Function + + Friend Shared Function GetResourceString(ByVal ResourceKey As String) As String + Dim s As String + + If VBAResourceManager Is Nothing Then + Return ResourceMsgDefault + End If + + Try + s = VBAResourceManager.GetString(ResourceKey, GetCultureInfo()) + If s Is Nothing Then + 'Try default resources if not found + s = VBAResourceManager.GetString(VBDefaultErrorID) + End If + Catch ex As StackOverflowException + Throw ex + Catch ex As OutOfMemoryException + Throw ex + Catch ex As System.Threading.ThreadAbortException + Throw ex + Catch + s = ResourceMsgDefault + End Try + + Return s + End Function + + Friend Shared Function GetResourceString(ByVal ResourceKey As String, ByVal NotUsed As Boolean) As String + 'This version does NOT return a default message if not found. + Dim s As String + + If VBAResourceManager Is Nothing Then + Return ResourceMsgDefault + End If + + Try + s = VBAResourceManager.GetString(ResourceKey, GetCultureInfo()) + If s Is Nothing Then + 'Try default resources if not found + s = VBAResourceManager.GetString(ResourceKey) + End If + Catch ex As StackOverflowException + Throw ex + Catch ex As OutOfMemoryException + Throw ex + Catch ex As System.Threading.ThreadAbortException + Throw ex + Catch + s = Nothing + End Try + Return s + End Function + + '***************************************************************************** + ';GetResourceString + ' + 'Summary: Retrieves a resource string and formats it by replacing placeholders + ' with params. For example if the unformatted string is + ' "Hello, {0}" then GetString("StringID", "World") will return "Hello, World" + ' This one is exposed because I have to be able to get at localized error + ' strings from the MY template + ' Param: ID - Identifier for the string to be retrieved + ' Param: Args - An array of params used to replace placeholders. + 'Returns: The resource string if found or an error message string + '***************************************************************************** + Public Shared Function GetResourceString(ByVal ResourceKey As String, ByVal ParamArray Args() As String) As String + + System.Diagnostics.Debug.Assert(Not ResourceKey = "", "ResourceKey is missing") + System.Diagnostics.Debug.Assert(Not Args Is Nothing, "No Args") + + Dim UnformattedString As String = Nothing + Dim FormattedString As String = Nothing + Try + 'Get unformatted string which may have place holders ie "Hello, {0}. How is {1}?" + UnformattedString = GetResourceString(ResourceKey) + + 'Replace plceholders with items from the passed in array + '[688666] - changing CurrentUICulture to CurrentCulture due to FxCop warning CA1305. + ' The guideline seems to be to use CurrentUICulture when pulling things out + ' of a ResourceManager, and to use CurrentCulture for any type of formatting + ' on a value that will be displayed to the user. Technically we should + ' probably modify the above overload of GetResourceString to use CurrentUICulture, + ' but we don't want to change it at this point because it's been that way forever. + ' We can change it later if the need ever arises. + FormattedString = String.Format(System.Threading.Thread.CurrentThread.CurrentCulture, UnformattedString, Args) + + 'Rethrow hosting exceptions + Catch ex As StackOverflowException + Throw ex + Catch ex As OutOfMemoryException + Throw ex + Catch ex As System.Threading.ThreadAbortException + Throw ex + + Catch ex As Exception + System.Diagnostics.Debug.Fail("Unable to get and format string for ResourceKey: " & ResourceKey) + Finally + System.Diagnostics.Debug.Assert(Not UnformattedString = "", "Unable to get string for ResourceKey: " & ResourceKey) + System.Diagnostics.Debug.Assert(Not FormattedString = "", "Unable to format string for ResourceKey: " & ResourceKey) + End Try + + 'Return the string if we have one otherwise return a default error message + If Not FormattedString = "" Then + Return FormattedString + Else + Return UnformattedString 'will contain an error string from the attempt to load via the GetResourceString() overload we call internally + End If + End Function + + ' *** VB6 COMMENTS FOR STDFORMAT FUNCTION *** + ' writing "standard format". We must use '.' for decimal and we must not + ' have a leading zero. First, replace the system decimal with a period. + ' second. Strip the leading zero if one exists. This is post-processing + ' work to deal with standard OLE functionality where all variant conversions + ' are based on the system LCID but where Str$()/Write# is supposed to always + ' use a fixed format. + + Friend Shared Function StdFormat(ByVal s As String) As String + Dim nfi As NumberFormatInfo + Dim iIndex As Integer + Dim c0, c1, c2 As Char + Dim sb As StringBuilder + + nfi = Threading.Thread.CurrentThread.CurrentCulture.NumberFormat + iIndex = s.IndexOf(nfi.NumberDecimalSeparator) + + If iIndex = -1 Then + Return s + End If + + Try + c0 = s.Chars(0) + c1 = s.Chars(1) + c2 = s.Chars(2) + Catch ex As StackOverflowException + Throw ex + Catch ex As OutOfMemoryException + Throw ex + Catch ex As System.Threading.ThreadAbortException + Throw ex + Catch + 'Ignore, should default to 0 values + End Try + + If s.Chars(iIndex) = chPeriod Then + 'Optimization: no period replacement needed + 'avoids creating stringbuilder and copying string + + 'If format is "0.xxxx" then replace 0 with space + If c0 = chZero AndAlso c1 = chPeriod Then + Return s.Substring(1) + + 'If format is "-0.xxxx", "+0.xxxx", " 0.xxxx" then shift everything down over the zero + ElseIf (c0 = chHyphen OrElse c0 = chPlus OrElse c0 = chSpace) AndAlso c1 = chZero AndAlso c2 = chPeriod Then + 'Fall down below and use a stringbuilder + Else + 'No change + Return s + End If + End If + + sb = New StringBuilder(s) + sb.Chars(iIndex) = chPeriod ' change decimal separator to "." + + 'If format is "0.xxxx" then replace 0 with space + If (c0 = chZero AndAlso c1 = chPeriod) Then + StdFormat = sb.ToString(1, sb.Length - 1) + 'If format is "-0.xxxx", "+0.xxxx", " 0.xxxx" then shift everything down over the zero + ElseIf (c0 = chHyphen OrElse c0 = chPlus OrElse c0 = chSpace) AndAlso c1 = chZero AndAlso c2 = chPeriod Then + sb.Remove(1, 1) + StdFormat = sb.ToString() + Else + StdFormat = sb.ToString() + End If + End Function + + Friend Shared Function OctFromLong(ByVal Val As Long) As String + 'System.Radix is being removed from the .NET platform, so compute this locally. + Dim Buffer As String = "" + Dim ModVal As Integer + Dim CharZero As Integer = Convert.ToInt32(chZero) + Dim Negative As Boolean + + If Val < 0 Then + Val = Int64.MaxValue + Val + 1 + Negative = True + End If + + 'Pull apart the number and put the digits (in reverse order) into the buffer. + Do + ModVal = CInt(Val Mod 8) + Val = Val >> 3 + Buffer = Buffer & ChrW(ModVal + CharZero) + Loop While Val > 0 + + Buffer = StrReverse(Buffer) + + If Negative Then + Buffer = "1" & Buffer + End If + + Return Buffer + End Function + + Friend Shared Function OctFromULong(ByVal Val As ULong) As String + 'System.Radix is being removed from the .NET platform, so compute this locally. + Dim Buffer As String = "" + Dim ModVal As Integer + Dim CharZero As Integer = Convert.ToInt32(chZero) + + 'Pull apart the number and put the digits (in reverse order) into the buffer. + Do + ModVal = CInt(Val Mod 8UL) + Val = Val >> 3 + Buffer = Buffer & ChrW(ModVal + CharZero) + Loop While Val <> 0UL + + Buffer = StrReverse(Buffer) + + Return Buffer + End Function + + '*** SECURITY CHECK - SECURITY CHECK - SECURITY CHECK - SECURITY CHECK - SECURITY CHECK - SECURITY CHECK *** + _ + _ + _ + _ + Friend Shared Sub SetTime(ByVal dtTime As DateTime) + Dim systime As New NativeTypes.SystemTime + + SafeNativeMethods.GetLocalTime(systime) + + systime.wHour = CShort(dtTime.Hour) + systime.wMinute = CShort(dtTime.Minute) + systime.wSecond = CShort(dtTime.Second) + systime.wMilliseconds = CShort(dtTime.Millisecond) + + If UnsafeNativeMethods.SetLocalTime(systime) = 0 Then + If Marshal.GetLastWin32Error() = ERROR_INVALID_PARAMETER Then + Throw New ArgumentException(GetResourceString(ResID.Argument_InvalidValue)) + Else + Throw New SecurityException(GetResourceString(ResID.SetLocalTimeFailure)) + End If + End If + + End Sub + + '*** SECURITY CHECK - SECURITY CHECK - SECURITY CHECK - SECURITY CHECK - SECURITY CHECK - SECURITY CHECK *** + _ + _ + _ + _ + Friend Shared Sub SetDate(ByVal vDate As DateTime) + Dim systime As New NativeTypes.SystemTime + + SafeNativeMethods.GetLocalTime(systime) + + systime.wYear = CShort(vDate.Year) + systime.wMonth = CShort(vDate.Month) + systime.wDay = CShort(vDate.Day) + + If UnsafeNativeMethods.SetLocalTime(systime) = 0 Then + If Marshal.GetLastWin32Error() = ERROR_INVALID_PARAMETER Then + Throw New ArgumentException(GetResourceString(ResID.Argument_InvalidValue)) + Else + Throw New SecurityException(GetResourceString(ResID.SetLocalDateFailure)) + End If + End If + + End Sub + + Friend Shared Function GetDateTimeFormatInfo() As DateTimeFormatInfo + Return System.Threading.Thread.CurrentThread.CurrentCulture.DateTimeFormat + End Function + + Public Shared Sub ThrowException(ByVal hr As Integer) + Throw VbMakeException(hr) + End Sub + + Friend Shared Function MapHRESULT(ByVal lNumber As Integer) As Integer + If lNumber > 0 Then + Return lNumber + End If + + If (lNumber And SCODE_FACILITY) = FACILITY_CONTROL Then + Return (lNumber And &HFFFFI) + End If + + Select Case lNumber + + ' FACILITY_NULL errors + Case E_NOTIMPL + MapHRESULT = vbErrors.NotYetImplemented + + Case E_NOINTERFACE + MapHRESULT = vbErrors.OLENotSupported + + Case E_ABORT + MapHRESULT = vbErrors.Abort + + ' FACILITY_DISPATCH - IDispatch errors. + Case DISP_E_UNKNOWNINTERFACE + MapHRESULT = vbErrors.OLENoPropOrMethod + Case DISP_E_MEMBERNOTFOUND + MapHRESULT = vbErrors.OLENoPropOrMethod + Case DISP_E_PARAMNOTFOUND + MapHRESULT = vbErrors.NamedParamNotFound + Case DISP_E_TYPEMISMATCH + MapHRESULT = vbErrors.TypeMismatch + Case DISP_E_UNKNOWNNAME + MapHRESULT = vbErrors.OLENoPropOrMethod + Case DISP_E_NONAMEDARGS + MapHRESULT = vbErrors.NamedArgsNotSupported + Case DISP_E_BADVARTYPE + MapHRESULT = vbErrors.InvalidTypeLibVariable + Case DISP_E_OVERFLOW + MapHRESULT = vbErrors.Overflow + Case DISP_E_BADINDEX + MapHRESULT = vbErrors.OutOfBounds + Case DISP_E_UNKNOWNLCID + MapHRESULT = vbErrors.LocaleSettingNotSupported + Case DISP_E_ARRAYISLOCKED + MapHRESULT = vbErrors.ArrayLocked + Case DISP_E_BADPARAMCOUNT + MapHRESULT = vbErrors.FuncArityMismatch + Case DISP_E_PARAMNOTOPTIONAL + MapHRESULT = vbErrors.ParameterNotOptional + Case DISP_E_NOTACOLLECTION + MapHRESULT = vbErrors.NotEnum + Case DISP_E_DIVBYZERO + MapHRESULT = vbErrors.DivByZero + ' FACILITY_DISPATCH - Typelib errors. + Case TYPE_E_BUFFERTOOSMALL + MapHRESULT = vbErrors.BufferTooSmall + Case &H80028017I + MapHRESULT = vbErrors.IdentNotMember + Case TYPE_E_INVDATAREAD + MapHRESULT = vbErrors.InvDataRead + Case TYPE_E_UNSUPFORMAT + MapHRESULT = vbErrors.UnsupFormat + Case TYPE_E_REGISTRYACCESS + MapHRESULT = vbErrors.RegistryAccess + Case TYPE_E_LIBNOTREGISTERED + MapHRESULT = vbErrors.LibNotRegistered + Case TYPE_E_UNDEFINEDTYPE + MapHRESULT = vbErrors.UndefinedType + Case TYPE_E_QUALIFIEDNAMEDISALLOWED + MapHRESULT = vbErrors.QualifiedNameDisallowed + Case TYPE_E_INVALIDSTATE + MapHRESULT = vbErrors.InvalidState + Case TYPE_E_WRONGTYPEKIND + MapHRESULT = vbErrors.WrongTypeKind + Case TYPE_E_ELEMENTNOTFOUND + MapHRESULT = vbErrors.ElementNotFound + Case TYPE_E_AMBIGUOUSNAME + MapHRESULT = vbErrors.AmbiguousName + Case TYPE_E_NAMECONFLICT + MapHRESULT = vbErrors.ModNameConflict + Case TYPE_E_UNKNOWNLCID + MapHRESULT = vbErrors.UnknownLcid + Case TYPE_E_DLLFUNCTIONNOTFOUND + MapHRESULT = vbErrors.InvalidDllFunctionName + Case TYPE_E_BADMODULEKIND + MapHRESULT = vbErrors.BadModuleKind + Case TYPE_E_SIZETOOBIG + MapHRESULT = vbErrors.SizeTooBig + Case TYPE_E_TYPEMISMATCH + MapHRESULT = vbErrors.TypeMismatch + Case TYPE_E_OUTOFBOUNDS + MapHRESULT = vbErrors.OutOfBounds + Case TYPE_E_IOERROR + MapHRESULT = vbErrors.IOError + Case TYPE_E_CANTCREATETMPFILE + MapHRESULT = vbErrors.CantCreateTmpFile + Case TYPE_E_CANTLOADLIBRARY + MapHRESULT = vbErrors.DLLLoadErr + Case TYPE_E_INCONSISTENTPROPFUNCS + MapHRESULT = vbErrors.InconsistentPropFuncs + Case TYPE_E_CIRCULARTYPE + MapHRESULT = vbErrors.CircularType + + ' FACILITY_STORAGE errors + Case STG_E_INVALIDFUNCTION + MapHRESULT = vbErrors.BadFunctionId + Case STG_E_FILENOTFOUND + MapHRESULT = vbErrors.FileNotFound + Case STG_E_PATHNOTFOUND + MapHRESULT = vbErrors.PathNotFound + Case STG_E_TOOMANYOPENFILES + MapHRESULT = vbErrors.TooManyFiles + Case STG_E_ACCESSDENIED + MapHRESULT = vbErrors.PermissionDenied + Case STG_E_INVALIDHANDLE + MapHRESULT = vbErrors.ReadFault + Case STG_E_INSUFFICIENTMEMORY + MapHRESULT = vbErrors.OutOfMemory + Case STG_E_NOMOREFILES + MapHRESULT = vbErrors.TooManyFiles + Case STG_E_DISKISWRITEPROTECTED + MapHRESULT = vbErrors.PermissionDenied + Case STG_E_SEEKERROR + MapHRESULT = vbErrors.SeekErr + Case STG_E_WRITEFAULT + MapHRESULT = vbErrors.WriteFault + Case STG_E_READFAULT + MapHRESULT = vbErrors.ReadFault + Case STG_E_SHAREVIOLATION + MapHRESULT = vbErrors.PathFileAccess + Case STG_E_LOCKVIOLATION + MapHRESULT = vbErrors.PermissionDenied + Case STG_E_FILEALREADYEXISTS + MapHRESULT = vbErrors.FileAlreadyExists + Case STG_E_MEDIUMFULL + MapHRESULT = vbErrors.DiskFull + Case STG_E_INVALIDHEADER + MapHRESULT = vbErrors.InvDataRead + Case STG_E_INVALIDNAME + MapHRESULT = vbErrors.FileNotFound + Case STG_E_UNKNOWN + MapHRESULT = vbErrors.InvDataRead + Case STG_E_UNIMPLEMENTEDFUNCTION + MapHRESULT = vbErrors.NotYetImplemented + Case STG_E_INUSE + MapHRESULT = vbErrors.PermissionDenied + Case STG_E_NOTCURRENT + MapHRESULT = vbErrors.PermissionDenied + Case STG_E_REVERTED + MapHRESULT = vbErrors.WriteFault + Case STG_E_CANTSAVE + MapHRESULT = vbErrors.IOError + Case STG_E_OLDFORMAT + MapHRESULT = vbErrors.UnsupFormat + Case STG_E_OLDDLL + MapHRESULT = vbErrors.UnsupFormat + Case STG_E_SHAREREQUIRED + MapHRESULT = vbErrors.ShareRequired + Case STG_E_NOTFILEBASEDSTORAGE + MapHRESULT = vbErrors.UnsupFormat + Case STG_E_EXTANTMARSHALLINGS + MapHRESULT = vbErrors.UnsupFormat + + ' FACILITY_ITF errors. + Case CLASS_E_NOTLICENSED + MapHRESULT = vbErrors.CantCreateObject + Case REGDB_E_CLASSNOTREG + MapHRESULT = vbErrors.CantCreateObject + Case MK_E_UNAVAILABLE + MapHRESULT = vbErrors.CantCreateObject + Case MK_E_INVALIDEXTENSION + MapHRESULT = vbErrors.OLEFileNotFound + Case MK_E_CANTOPENFILE + MapHRESULT = vbErrors.OLEFileNotFound + Case CO_E_CLASSSTRING + MapHRESULT = vbErrors.CantCreateObject + Case CO_E_APPNOTFOUND + MapHRESULT = vbErrors.CantCreateObject + Case CO_E_APPDIDNTREG + MapHRESULT = vbErrors.CantCreateObject + + ' FACILITY_WIN32 errors + Case E_ACCESSDENIED + MapHRESULT = vbErrors.PermissionDenied + Case E_OUTOFMEMORY + MapHRESULT = vbErrors.OutOfMemory + Case E_INVALIDARG + MapHRESULT = vbErrors.IllegalFuncCall + Case &H800706BAI + MapHRESULT = vbErrors.ServerNotFound + + ' FACILITY_WINDOWS - I don't know why this differs from FACILITY_WIN32 + Case CO_E_SERVER_EXEC_FAILURE + MapHRESULT = vbErrors.CantCreateObject + + Case Else + + MapHRESULT = lNumber + + End Select + + End Function + + Friend Shared Function GetCultureInfo() As CultureInfo + Return System.Threading.Thread.CurrentThread.CurrentCulture + End Function + + _ + Public Shared Function SetCultureInfo(ByVal Culture As CultureInfo) As System.Object + Dim PreviousCulture As CultureInfo = System.Threading.Thread.CurrentThread.CurrentCulture + System.Threading.Thread.CurrentThread.CurrentCulture = Culture + Return PreviousCulture + End Function + + Friend Shared Function GetInvariantCultureInfo() As CultureInfo + Return CultureInfo.InvariantCulture + End Function + + Friend Shared Function GetFileIOEncoding() As Encoding + Return System.Text.Encoding.Default + End Function + + Friend Shared Function GetLocaleCodePage() As Integer + Return System.Threading.Thread.CurrentThread.CurrentCulture.TextInfo.ANSICodePage + End Function + + Friend Shared ReadOnly Property VBRuntimeAssembly() As System.Reflection.Assembly + Get + If Not m_VBRuntimeAssembly Is Nothing Then + Return m_VBRuntimeAssembly + End If + + ' if the cached assembly ref has not been set, then set it here + m_VBRuntimeAssembly = System.Reflection.Assembly.GetExecutingAssembly() + Return m_VBRuntimeAssembly + End Get + End Property + + 'Helper that gets called for Redim + Public Shared Function CopyArray(ByVal arySrc As System.Array, ByVal aryDest As System.Array) As System.Array + + If arySrc Is Nothing Then + Return aryDest + End If + + Dim lLength As Integer + + lLength = arySrc.Length + If lLength = 0 Then + Return aryDest + End If + + If aryDest.Rank() <> arySrc.Rank() Then + Throw VbMakeException(New InvalidCastException(GetResourceString(ResID.Array_RankMismatch)), vbErrors.OutOfBounds) + End If + + 'Validate the upper has not changed + Dim iDim As Integer + For iDim = 0 To aryDest.Rank() - 2 'Do not check last dimension + If aryDest.GetUpperBound(iDim) <> arySrc.GetUpperBound(iDim) Then + Throw VbMakeException(New ArrayTypeMismatchException(GetResourceString(ResID.Array_TypeMismatch)), vbErrors.OutOfBounds) + End If + Next iDim + + If lLength > aryDest.Length Then + lLength = aryDest.Length + End If + + 'if this is multi-dimensional, we have to do our own copy + 'REVIEW VSW#395788: the BCL should have a member that does this for us + If arySrc.Rank > 1 Then + + Dim LastRank As Integer = arySrc.Rank + Dim lenSrcLastRank As Integer = arySrc.GetLength(LastRank - 1) + Dim lenDestLastRank As Integer = aryDest.GetLength(LastRank - 1) + + 'if the last rank has 0 size, then this array has no elements, so just return + If lenDestLastRank = 0 Then + Return aryDest + End If + + 'get the correct copy length, regardless if the user increased or decreased the last rank's size + Dim lenCopy As Integer = System.Math.Min(lenSrcLastRank, lenDestLastRank) + + Dim i As Integer + 'split the source array into chunks the size of the last rank and copy each chunk one-by-one + For i = 0 To (arySrc.Length \ lenSrcLastRank) - 1 + System.Array.Copy(arySrc, i * lenSrcLastRank, aryDest, i * lenDestLastRank, lenCopy) + Next i + + Else + System.Array.Copy(arySrc, aryDest, lLength) + End If + + Return aryDest + + End Function + + Friend Shared Function ToHalfwidthNumbers(ByVal s As String, ByVal culture As CultureInfo) As String + + Const LANG_CHINESE As Integer = &H4I + Const LANG_JAPANESE As Integer = &H11I + Const LANG_KOREAN As Integer = &H12I + + Dim lcid As Integer = culture.LCID + Dim langid As Integer = (lcid And &H3FF) + + If langid <> LANG_CHINESE AndAlso langid <> LANG_JAPANESE AndAlso langid <> LANG_KOREAN Then + Return s + End If + + + Return vbLCMapString( _ + culture, _ + NativeTypes.LCMAP_HALFWIDTH, _ + s) +#If 0 Then + 'Keep this around for a while + 'The above code is compatible with VB6, but to be more + 'unicode aware, all languages should support fullwidth numbers &HFF10 - &HFF19 + 'The problem arises when fullwidth decimal and other symbols are used + 'we need to understand what rules should apply when converting these to + 'halfwidth values + For i = 0 To s.Length - 1 + ch = s.Chars(i) + If Convert.ToInt32(ch) > 255 Then + If Char.IsDigit(ch) Then + If sb Is Nothing Then + sb = New Text.StringBuilder(s) + End If + sb.Chars(i) = Convert.ToChar(CShort(Char.GetNumericValue(ch) + &h30)) + ElseIf ch = ChrW(&HFF0E) Then + If sb Is Nothing Then + sb = New Text.StringBuilder(s) + End If + sb.Chars(i) = "."c + End If + End If + + Next i + If sb Is Nothing Then + Return s + End If + Return sb.ToString() +#End If + + End Function + + 'CONSIDER: Seems odd that this function would throw exceptions when the name suggests that + 'no exceptions will be thrown in failure cases. + Friend Shared Function IsHexOrOctValue(ByVal Value As String, ByRef i64Value As Int64) As Boolean + + Dim ch As Char + Dim Length As Integer + Dim FirstNonspace As Integer + Dim TmpValue As String + + Length = Value.Length + + Do While (FirstNonspace < Length) + ch = Value.Chars(FirstNonspace) + 'We check that the length is at least FirstNonspace + 2 because otherwise the function + 'will throw undesired exceptions. + If ch = "&"c AndAlso FirstNonspace + 2 < Length Then + GoTo GetSpecialValue + End If + If ch <> chSpace AndAlso ch <> chIntlSpace Then + Return False + End If + FirstNonspace += 1 + Loop + + Return False + +GetSpecialValue: + ch = System.Char.ToLower(Value.Chars(FirstNonspace + 1), CultureInfo.InvariantCulture) + + TmpValue = ToHalfwidthNumbers(Value.Substring(FirstNonspace + 2), GetCultureInfo()) + If ch = "h"c Then + i64Value = System.Convert.ToInt64(TmpValue, 16) + ElseIf ch = "o"c Then + i64Value = System.Convert.ToInt64(TmpValue, 8) + Else + Throw New FormatException + End If + Return True + End Function + + 'CONSIDER: Seems odd that this function would throw exceptions when the name suggests that + 'no exceptions will be thrown in failure cases. + Friend Shared Function IsHexOrOctValue(ByVal Value As String, ByRef ui64Value As UInt64) As Boolean + + Dim ch As Char + Dim Length As Integer + Dim FirstNonspace As Integer + Dim TmpValue As String + + Length = Value.Length + + Do While (FirstNonspace < Length) + ch = Value.Chars(FirstNonspace) + 'We check that the length is at least FirstNonspace + 2 because otherwise the function + 'will throw undesired exceptions. + If ch = "&"c AndAlso FirstNonspace + 2 < Length Then + GoTo GetSpecialValue + End If + If ch <> chSpace AndAlso ch <> chIntlSpace Then + Return False + End If + FirstNonspace += 1 + Loop + + Return False + +GetSpecialValue: + ch = System.Char.ToLower(Value.Chars(FirstNonspace + 1), CultureInfo.InvariantCulture) + + TmpValue = ToHalfwidthNumbers(Value.Substring(FirstNonspace + 2), GetCultureInfo()) + If ch = "h"c Then + ui64Value = System.Convert.ToUInt64(TmpValue, 16) + ElseIf ch = "o"c Then + ui64Value = System.Convert.ToUInt64(TmpValue, 8) + Else + Throw New FormatException + End If + Return True + End Function + + Friend Shared Function VBFriendlyName(ByVal Obj As Object) As String + If Obj Is Nothing Then + Return "Nothing" + End If + + Return VBFriendlyName(Obj.GetType, Obj) + End Function + + Friend Shared Function VBFriendlyName(ByVal typ As System.Type) As String + Return VBFriendlyNameOfType(typ) + End Function + + Friend Shared Function VBFriendlyName(ByVal typ As System.Type, ByVal o As Object) As String + If typ.IsCOMObject AndAlso (typ.FullName = "System.__ComObject") Then + Return TypeNameOfCOMObject(o, False) + End If + + Return VBFriendlyNameOfType(typ) + End Function + + Friend Shared Function VBFriendlyNameOfType(ByVal typ As System.Type, Optional ByVal FullName As Boolean = False) As String + + Dim Result As String + Dim ArraySuffix As String + + ArraySuffix = GetArraySuffixAndElementType(typ) + + Debug.Assert(typ IsNot Nothing AndAlso Not typ.IsArray, "Error in array type processing!!!") + + + Dim tc As TypeCode + If typ.IsEnum Then + tc = TypeCode.Object + Else + tc = Type.GetTypeCode(typ) + End If + + Select Case tc + + Case TypeCode.Boolean : Result = "Boolean" + Case TypeCode.SByte : Result = "SByte" + Case TypeCode.Byte : Result = "Byte" + Case TypeCode.Int16 : Result = "Short" + Case TypeCode.UInt16 : Result = "UShort" + Case TypeCode.Int32 : Result = "Integer" + Case TypeCode.UInt32 : Result = "UInteger" + Case TypeCode.Int64 : Result = "Long" + Case TypeCode.UInt64 : Result = "ULong" + Case TypeCode.Decimal : Result = "Decimal" + Case TypeCode.Single : Result = "Single" + Case TypeCode.Double : Result = "Double" + Case TypeCode.DateTime : Result = "Date" + Case TypeCode.Char : Result = "Char" + Case TypeCode.String : Result = "String" + Case TypeCode.DBNull : Result = "DBNull" + + Case Else + + If IsGenericParameter(typ) Then + Result = typ.Name + Exit Select + End If + + Dim Qualifier As String = Nothing 'yes, defaults to nothing but makes a warning go away about use before assignment + Dim Name As String + + Dim GenericArgsSuffix As String = GetGenericArgsSuffix(typ) + + If FullName Then + If typ.IsNested Then + Qualifier = VBFriendlyNameOfType(typ.DeclaringType, FullName:=True) + Name = typ.Name + Else + Name = typ.FullName + End If + Else + Name = typ.Name + End If + + If GenericArgsSuffix IsNot Nothing Then + Dim ManglingCharIndex As Integer = Name.LastIndexOf(chGenericManglingChar) + + If ManglingCharIndex <> -1 Then + Name = Name.Substring(0, ManglingCharIndex) + End If + + Result = Name & GenericArgsSuffix + Else + Result = Name + End If + + If Qualifier IsNot Nothing Then + Result = Qualifier & chPeriod & Result + End If + + End Select + + + If ArraySuffix IsNot Nothing Then + Result = Result & ArraySuffix + End If + + Return Result + End Function + + Private Shared Function GetArraySuffixAndElementType(ByRef typ As Type) As String + + If Not typ.IsArray Then + Return Nothing + End If + + Dim ArraySuffix As New Text.StringBuilder + + 'Notice the reversing - VB array notation is reverse of clr array notation + 'i.e. (,)() in VB is [][,] in clr + ' + Do + + ArraySuffix.Append("(") + ArraySuffix.Append(","c, typ.GetArrayRank() - 1) + ArraySuffix.Append(")") + + typ = typ.GetElementType + + Loop While typ.IsArray + + Return ArraySuffix.ToString() + End Function + + Private Shared Function GetGenericArgsSuffix(ByVal typ As Type) As String + + If Not typ.IsGenericType Then + Return Nothing + End If + + Dim TypeArgs As Type() = typ.GetGenericArguments + Dim TotalTypeArgsCount As Integer = TypeArgs.Length + Dim TypeArgsCount As Integer = TotalTypeArgsCount + + If typ.IsNested AndAlso typ.DeclaringType.IsGenericType Then + TypeArgsCount = TypeArgsCount - typ.DeclaringType.GetGenericArguments().Length + End If + + If TypeArgsCount = 0 Then + Return Nothing + End If + + Dim GenericArgsSuffix As New Text.StringBuilder + GenericArgsSuffix.Append("(Of ") + + For i As Integer = TotalTypeArgsCount - TypeArgsCount To TotalTypeArgsCount - 1 + + GenericArgsSuffix.Append(VBFriendlyNameOfType(TypeArgs(i))) + + If i <> TotalTypeArgsCount - 1 Then + GenericArgsSuffix.Append(","c) + End If + Next + + GenericArgsSuffix.Append(")") + + Return GenericArgsSuffix.ToString + End Function + + Friend Shared Function ParameterToString(ByVal Parameter As ParameterInfo) As String + + Dim ResultString As String = "" + Dim ParameterType As Type = Parameter.ParameterType + + If Parameter.IsOptional Then + ResultString &= "[" + End If + + If ParameterType.IsByRef Then + ResultString &= "ByRef " + ParameterType = ParameterType.GetElementType + ElseIf IsParamArray(Parameter) Then + ResultString &= "ParamArray " + End If + + ResultString &= Parameter.Name & " As " & VBFriendlyNameOfType(ParameterType, FullName:=True) + + If Parameter.IsOptional Then + + Dim DefaultValue As Object = Parameter.DefaultValue + + If DefaultValue Is Nothing Then + ResultString &= " = Nothing" + Else + Dim DefaultValueType As System.Type = DefaultValue.GetType + If DefaultValueType IsNot VoidType Then + If IsEnum(DefaultValueType) Then + ResultString &= " = " & System.Enum.GetName(DefaultValueType, DefaultValue) + Else + ResultString &= " = " & CStr(DefaultValue) + End If + End If + End If + + ResultString &= "]" + End If + + Return ResultString + End Function + + Public Shared Function MethodToString(ByVal Method As Reflection.MethodBase) As String + + Dim ReturnType As System.Type = Nothing + Dim First As Boolean + MethodToString = "" + + If Method.MemberType = MemberTypes.Method Then ReturnType = DirectCast(Method, MethodInfo).ReturnType + + If Method.IsPublic Then + MethodToString &= "Public " + ElseIf Method.IsPrivate Then + MethodToString &= "Private " + ElseIf Method.IsAssembly Then + MethodToString &= "Friend " + End If + + If (Method.Attributes And System.Reflection.MethodAttributes.Virtual) <> 0 Then + If Not Method.DeclaringType.IsInterface Then + MethodToString &= "Overrides " + End If + ElseIf IsShared(Method) Then + MethodToString &= "Shared " + End If + + Dim Op As UserDefinedOperator = UserDefinedOperator.UNDEF + If IsUserDefinedOperator(Method) Then + Op = MapToUserDefinedOperator(Method) + End If + + If Op <> UserDefinedOperator.UNDEF Then + If Op = UserDefinedOperator.Narrow Then + MethodToString &= "Narrowing " + ElseIf Op = UserDefinedOperator.Widen Then + MethodToString &= "Widening " + End If + MethodToString &= "Operator " + ElseIf ReturnType Is Nothing OrElse ReturnType Is VoidType Then + MethodToString &= "Sub " + Else + MethodToString &= "Function " + End If + + If Op <> UserDefinedOperator.UNDEF Then + MethodToString &= OperatorNames(Op) + ElseIf Method.MemberType = MemberTypes.Constructor Then + MethodToString &= "New" + Else + MethodToString &= Method.Name + End If + + If IsGeneric(Method) Then + MethodToString &= "(Of " + First = True + For Each t As Type In GetTypeParameters(Method) + If Not First Then MethodToString &= ", " Else First = False + MethodToString &= VBFriendlyNameOfType(t) + Next + MethodToString &= ")" + End If + + MethodToString &= "(" + First = True + + For Each Parameter As ParameterInfo In Method.GetParameters() + + If Not First Then + MethodToString &= ", " + Else + First = False + End If + + MethodToString &= ParameterToString(Parameter) + Next + + MethodToString &= ")" + + If ReturnType Is Nothing OrElse ReturnType Is VoidType Then + 'Sub has no return type + Else + MethodToString &= " As " & VBFriendlyNameOfType(ReturnType, FullName:=True) + End If + + End Function + + Private Enum PropertyKind + ReadWrite + [ReadOnly] + [WriteOnly] + End Enum + + Friend Shared Function PropertyToString(ByVal Prop As Reflection.PropertyInfo) As String + + Dim ResultString As String = "" + + Dim Kind As PropertyKind = PropertyKind.ReadWrite + Dim Parameters As ParameterInfo() + Dim PropertyType As Type + + 'Most of the work will be done using the Getter or Setter. + Dim Accessor As MethodInfo = Prop.GetGetMethod + + If Accessor IsNot Nothing Then + If Prop.GetSetMethod IsNot Nothing Then + Kind = PropertyKind.ReadWrite + Else + Kind = PropertyKind.ReadOnly + End If + + Parameters = Accessor.GetParameters + PropertyType = Accessor.ReturnType + Else + Kind = PropertyKind.WriteOnly + + Accessor = Prop.GetSetMethod + Dim SetParameters As ParameterInfo() = Accessor.GetParameters + Parameters = New ParameterInfo(SetParameters.Length - 2) {} + System.Array.Copy(SetParameters, Parameters, Parameters.Length) + PropertyType = SetParameters(SetParameters.Length - 1).ParameterType + End If + + ResultString &= "Public " + + If (Accessor.Attributes And MethodAttributes.Virtual) <> 0 Then + If Not Prop.DeclaringType.IsInterface Then + ResultString &= "Overrides " + End If + ElseIf IsShared(Accessor) Then + ResultString &= "Shared " + End If + + If Kind = PropertyKind.ReadOnly Then ResultString &= "ReadOnly " + If Kind = PropertyKind.WriteOnly Then ResultString &= "WriteOnly " + + ResultString &= "Property " & Prop.Name & "(" + + Dim First As Boolean = True + + For Each Parameter As ParameterInfo In Parameters + If Not First Then ResultString &= ", " Else First = False + + ResultString &= ParameterToString(Parameter) + Next + + ResultString &= ") As " & VBFriendlyNameOfType(PropertyType, FullName:=True) + + Return ResultString + End Function + + Friend Shared Function AdjustArraySuffix(ByVal sRank As String) As String + Dim OneChar As Char + Dim RevResult As String = Nothing + Dim length As Integer = sRank.Length + While length > 0 + OneChar = sRank.Chars(length - 1) + Select Case OneChar + Case ")"c + RevResult = RevResult + "("c + Case "("c + RevResult = RevResult + ")"c + Case ","c + RevResult = RevResult + OneChar + Case Else + RevResult = OneChar + RevResult + End Select + length = length - 1 + End While + Return RevResult + End Function + + Friend Shared Function MemberToString(ByVal Member As MemberInfo) As String + Select Case Member.MemberType + Case MemberTypes.Method, MemberTypes.Constructor + Return MethodToString(DirectCast(Member, MethodBase)) + + Case MemberTypes.Field + Return FieldToString(DirectCast(Member, FieldInfo)) + + Case MemberTypes.Property + Return PropertyToString(DirectCast(Member, PropertyInfo)) + + Case Else + Return Member.Name + End Select + End Function + + Friend Shared Function FieldToString(ByVal Field As FieldInfo) As String + Dim rtype As System.Type + FieldToString = "" + + rtype = Field.FieldType + + If Field.IsPublic Then + FieldToString &= "Public " + ElseIf Field.IsPrivate Then + FieldToString &= "Private " + ElseIf Field.IsAssembly Then + FieldToString &= "Friend " + ElseIf Field.IsFamily Then + FieldToString &= "Protected " + ElseIf Field.IsFamilyOrAssembly Then + FieldToString &= "Protected Friend " + End If + + FieldToString &= Field.Name + FieldToString &= " As " + FieldToString &= VBFriendlyNameOfType(rtype, FullName:=True) + End Function + + End Class + + _ + Friend NotInheritable Class SafeMemoryMappedViewOfFileHandle : Inherits Microsoft.Win32.SafeHandles.SafeHandleZeroOrMinusOneIsInvalid + + Friend Sub New() + MyBase.New(True) + End Sub + + Friend Sub New(ByVal handle As System.IntPtr, ByVal ownsHandle As Boolean) + MyBase.New(ownsHandle) + SetHandle(handle) + End Sub + + _ + _ + _ + _ + Protected Overrides Function ReleaseHandle() As Boolean + Try + If UnsafeNativeMethods.UnmapViewOfFile(handle) Then + Return True + End If + Return False + Finally + handle = IntPtr.Zero 'either way mark this as invalid now + End Try + End Function + End Class + +End Namespace + diff --git a/Microsoft.VisualBasic/runtime/msvbalib/Helpers/VB6BinaryFile.vb b/Microsoft.VisualBasic/runtime/msvbalib/Helpers/VB6BinaryFile.vb new file mode 100644 index 000000000..39b7f8ac0 --- /dev/null +++ b/Microsoft.VisualBasic/runtime/msvbalib/Helpers/VB6BinaryFile.vb @@ -0,0 +1,262 @@ +' Copyright (c) Microsoft Corporation. All rights reserved. + +Imports System +Imports System.Text +Imports System.IO +Imports System.Security + +Imports Microsoft.VisualBasic.CompilerServices.ExceptionUtils +Imports Microsoft.VisualBasic.CompilerServices.Utils + +Namespace Microsoft.VisualBasic.CompilerServices + +#Region " BACKWARDS COMPATIBILITY " + + 'WARNING WARNING WARNING WARNING WARNING + 'This code exists to support Everett compiled applications. Make sure you understand + 'the backwards compatibility ramifications of any edit you make in this region. + 'WARNING WARNING WARNING WARNING WARNING + + _ + Friend Class VB6BinaryFile + + '============================================================================ + ' Declarations + '============================================================================ + + Inherits VB6RandomFile + + '============================================================================ + ' Constructor + '============================================================================ + Public Sub New(ByVal FileName As String, ByVal access As OpenAccess, ByVal share As OpenShare) + MyBase.New(FileName, access, share, -1) + End Sub + + ' the implementation of Lock in base class VB6RandomFile does not handle m_lRecordLen=-1 + Friend Overloads Overrides Sub Lock(ByVal lStart As Long, ByVal lEnd As Long) + If lStart > lEnd Then + Throw New ArgumentException(GetResourceString(ResID.Argument_InvalidValue1, "Start")) + End If + + Dim absRecordLength As Long + Dim lStartByte As Long + Dim lLength As Long + + If m_lRecordLen = -1 Then + ' if record len is -1, then using absolute bytes + absRecordLength = 1 + Else + absRecordLength = m_lRecordLen + End If + + lStartByte = (lStart - 1) * absRecordLength + lLength = (lEnd - lStart + 1) * absRecordLength + + m_file.Lock(lStartByte, lLength) + End Sub + + + ' see Lock description + Friend Overloads Overrides Sub Unlock(ByVal lStart As Long, ByVal lEnd As Long) + If lStart > lEnd Then + Throw New ArgumentException(GetResourceString(ResID.Argument_InvalidValue1, "Start")) + End If + + Dim absRecordLength As Long + Dim lStartByte As Long + Dim lLength As Long + + If m_lRecordLen = -1 Then + ' if record len is -1, then using absolute bytes + absRecordLength = 1 + Else + absRecordLength = m_lRecordLen + End If + + lStartByte = (lStart - 1) * absRecordLength + lLength = (lEnd - lStart + 1) * absRecordLength + m_file.Unlock(lStartByte, lLength) + End Sub + + + Public Overrides Function GetMode() As OpenMode + Return OpenMode.Binary + End Function + + + + Friend Overloads Overrides Function Seek() As Long + 'm_file.position is the last read byte as a zero based offset + 'Seek returns the position of the next byte to read + Return (m_position + 1) + End Function + + + + Friend Overloads Overrides Sub Seek(ByVal BaseOnePosition As Long) + If BaseOnePosition <= 0 Then + Throw VbMakeException(vbErrors.BadRecordNum) + End If + + Dim BaseZeroPosition As Long = BaseOnePosition - 1 + + m_file.Position = BaseZeroPosition + m_position = BaseZeroPosition + + If Not m_sr Is Nothing Then + m_sr.DiscardBufferedData() + End If + End Sub + + + + Friend Overrides Function LOC() As Long + Return m_position + End Function + + + + Friend Overrides Function CanInput() As Boolean + Return True + End Function + + + + Friend Overrides Function CanWrite() As Boolean + Return True + End Function + + + _ + Friend Overloads Overrides Sub Input(ByRef Value As Object) + Value = InputStr() + End Sub + + + + Friend Overloads Overrides Sub Input(ByRef Value As String) + Value = InputStr() + End Sub + + + + Friend Overloads Overrides Sub Input(ByRef Value As Char) + Dim s As String = InputStr() + + If s.Length > 0 Then + Value = s.Chars(0) + Else + Value = ControlChars.NullChar + End If + End Sub + + + + Friend Overloads Overrides Sub Input(ByRef Value As Boolean) + Value = BooleanType.FromString(InputStr()) + End Sub + + + + Friend Overloads Overrides Sub Input(ByRef Value As Byte) + Value = ByteType.FromObject(InputNum(VariantType.Byte)) + End Sub + + + + Friend Overloads Overrides Sub Input(ByRef Value As Short) + Value = ShortType.FromObject(InputNum(VariantType.Short)) + End Sub + + + + Friend Overloads Overrides Sub Input(ByRef Value As Integer) + Value = IntegerType.FromObject(InputNum(VariantType.Integer)) + End Sub + + + + Friend Overloads Overrides Sub Input(ByRef Value As Long) + Value = LongType.FromObject(InputNum(VariantType.Long)) + End Sub + + + + Friend Overloads Overrides Sub Input(ByRef Value As Single) + Value = SingleType.FromObject(InputNum(VariantType.Single)) + End Sub + + + + Friend Overloads Overrides Sub Input(ByRef Value As Double) + Value = DoubleType.FromObject(InputNum(VariantType.Double)) + End Sub + + + + Friend Overloads Overrides Sub Input(ByRef Value As Decimal) + Value = DecimalType.FromObject(InputNum(VariantType.Decimal)) + End Sub + + + + Friend Overloads Overrides Sub Input(ByRef Value As Date) + Value = DateType.FromString(InputStr(), GetCultureInfo()) + End Sub + + + + Friend Overloads Overrides Sub Put(ByVal Value As String, Optional ByVal RecordNumber As Long = 0, Optional ByVal StringIsFixedLength As Boolean = False) + ValidateWriteable() + + PutString(RecordNumber, Value) + End Sub + + Friend Overloads Overrides Sub [Get](ByRef Value As String, Optional ByVal RecordNumber As Long = 0, Optional ByVal StringIsFixedLength As Boolean = False) + ValidateReadable() + + Dim ByteLength As Integer + If Value Is Nothing Then + ByteLength = 0 + Else + Diagnostics.Debug.Assert(Not m_Encoding Is Nothing) + ByteLength = m_Encoding.GetByteCount(Value) + End If + Value = GetFixedLengthString(RecordNumber, ByteLength) + End Sub + + + + Protected Overrides Function InputStr() As String + Dim lChar As Integer + + ': The deal here is that this functionality was moved from vb6randomfile.vb to fix VSWhidbey 32408. The problem is, it introduced a breaking change. The bug this fix + 'originally addressed was that we'd get a NullReference exception when you did a read of any kind on a file that was write-only. So Huy made a better error here so the user knows + 'what is going on. But that is a breaking change from Everett behavior. So I'm taking his functionality, throwing the original NullReference exception, but putting Huy's better + 'exception in there as the inner-exception. Since we have to go back to Everett behavior, at least having the inner exception helps a little. + If (m_access <> OpenAccess.ReadWrite) AndAlso (m_access <> OpenAccess.Read) Then + Dim JustNeedTheMessage As New NullReferenceException 'Hack. I don't have access to the localized resources for this string, and I can't skip providing it if I want to supply the inner exception, so I'll get the string this way. + Throw New NullReferenceException(JustNeedTheMessage.Message, New IO.IOException(GetResourceString(ResID.FileOpenedNoRead))) + End If + + ' read past any leading spaces or tabs + 'Skip over leading whitespace + lChar = SkipWhiteSpaceEOF() + + If lChar = lchDoubleQuote Then + lChar = m_sr.Read() + m_position += 1 + InputStr = ReadInField(FIN_QSTRING) + Else + InputStr = ReadInField(FIN_STRING) + End If + + SkipTrailingWhiteSpace() + End Function + + End Class + +#End Region + +End Namespace diff --git a/Microsoft.VisualBasic/runtime/msvbalib/Helpers/VB6File.vb b/Microsoft.VisualBasic/runtime/msvbalib/Helpers/VB6File.vb new file mode 100644 index 000000000..3ad889105 --- /dev/null +++ b/Microsoft.VisualBasic/runtime/msvbalib/Helpers/VB6File.vb @@ -0,0 +1,3041 @@ +' Copyright (c) Microsoft Corporation. All rights reserved. + +Imports System +Imports System.Diagnostics +Imports System.Security +Imports System.Globalization +Imports System.IO +Imports System.Text +Imports System.Runtime.InteropServices + +Imports Microsoft.VisualBasic.CompilerServices.StructUtils +Imports Microsoft.VisualBasic.CompilerServices.ExceptionUtils +Imports Microsoft.VisualBasic.CompilerServices.Utils + +Namespace Microsoft.VisualBasic.CompilerServices + +#Region " BACKWARDS COMPATIBILITY " + + 'WARNING WARNING WARNING WARNING WARNING + 'This code exists to support Everett compiled applications. Make sure you understand + 'the backwards compatibility ramifications of any edit you make in this region. + 'WARNING WARNING WARNING WARNING WARNING + + Friend Enum tagVT As Short + VT_EMPTY = 0 + VT_NULL = 1 + VT_I2 = 2 + VT_I4 = 3 + VT_R4 = 4 + VT_R8 = 5 + VT_CY = 6 + VT_DATE = 7 + VT_BSTR = 8 + VT_DISPATCH = 9 + VT_ERROR = 10 + VT_BOOL = 11 + VT_VARIANT = 12 + VT_UNKNOWN = 13 + VT_DECIMAL = 14 + VT_I1 = 16 + VT_UI1 = 17 + VT_UI2 = 18 + VT_UI4 = 19 + VT_I8 = 20 + VT_UI8 = 21 + VT_INT = 22 + VT_UINT = 23 + VT_VOID = 24 + VT_HRESULT = 25 + VT_PTR = 26 + VT_SAFEARRAY = 27 + VT_CARRAY = 28 + VT_USERDEFINED = 29 + VT_LPSTR = 30 + VT_LPWSTR = 31 + VT_RECORD = 36 + VT_FILETIME = 64 + VT_BLOB = 65 + VT_STREAM = 66 + VT_STORAGE = 67 + VT_STREAMED_OBJECT = 68 + VT_STORED_OBJECT = 69 + VT_BLOB_OBJECT = 70 + VT_CF = 71 + VT_CLSID = 72 + VT_BSTR_BLOB = 4095 + VT_VECTOR = 4096 + VT_ARRAY = 8192 + VT_BYREF = 16384 + VT_RESERVED = &H8000S + VT_ILLEGAL = &HFFFFS + VT_ILLEGALMASKED = 4095 + VT_TYPEMASK = 4095 + End Enum + + Friend Enum VT As Short + [Error] = tagVT.VT_ERROR + [Boolean] = tagVT.VT_BOOL + [Byte] = tagVT.VT_UI1 + [Short] = tagVT.VT_I2 + [Integer] = tagVT.VT_I4 + [Decimal] = tagVT.VT_DECIMAL + [Single] = tagVT.VT_R4 + [Double] = tagVT.VT_R8 + [String] = tagVT.VT_BSTR + [ByteArray] = tagVT.VT_UI1 Or _ + tagVT.VT_ARRAY + [CharArray] = tagVT.VT_UI2 Or _ + tagVT.VT_ARRAY + [Date] = tagVT.VT_DATE + [Long] = tagVT.VT_I8 + [Char] = tagVT.VT_UI2 + [Variant] = tagVT.VT_VARIANT + [Array] = tagVT.VT_ARRAY + [DBNull] = tagVT.VT_NULL + [Empty] = tagVT.VT_EMPTY + [Structure] = tagVT.VT_RECORD + [Currency] = tagVT.VT_CY + End Enum + + _ + Friend NotInheritable Class PutHandler + Implements IRecordEnum + Public m_oFile As VB6File + + Sub New(ByVal oFile As VB6File) + MyBase.New() + m_oFile = oFile + End Sub + + + + Function Callback(ByVal field_info As Reflection.FieldInfo, ByRef vValue As Object) As Boolean Implements IRecordEnum.Callback + Dim FieldType As System.Type = field_info.FieldType + + If FieldType Is Nothing Then + Throw VbMakeException(New ArgumentException(GetResourceString(ResID.Argument_UnsupportedFieldType2, field_info.Name, "Empty")), vbErrors.IllegalFuncCall) + End If + + If FieldType.IsArray() Then + Dim attributeList As Object() + Dim ElementType As System.Type + Dim attrFixedArray As VBFixedArrayAttribute + Dim FixedStringLength As Integer = -1 + + attributeList = field_info.GetCustomAttributes(GetType(VBFixedArrayAttribute), False) + If Not attributeList Is Nothing AndAlso attributeList.Length <> 0 Then + attrFixedArray = CType(attributeList(0), VBFixedArrayAttribute) + Else + attrFixedArray = Nothing + End If + + ElementType = FieldType.GetElementType() + + If ElementType Is GetType(System.String) Then + attributeList = field_info.GetCustomAttributes(GetType(VBFixedStringAttribute), False) + If attributeList Is Nothing OrElse attributeList.Length = 0 Then + FixedStringLength = -1 + Else + FixedStringLength = CType(attributeList(0), VBFixedStringAttribute).Length + End If + End If + + If attrFixedArray Is Nothing Then + + m_oFile.PutDynamicArray(0, CType(vValue, System.Array), False, FixedStringLength) + + Else + + m_oFile.PutFixedArray(0, CType(vValue, System.Array), ElementType, FixedStringLength, attrFixedArray.FirstBound, attrFixedArray.SecondBound) + + End If + + Else + Select Case Type.GetTypeCode(FieldType) + Case TypeCode.String + Dim s As String + + If Not vValue Is Nothing Then + s = vValue.ToString() + Else + s = Nothing + End If + + Dim attributeList As Object() = field_info.GetCustomAttributes(GetType(VBFixedStringAttribute), False) + + 'If (field_info.Attributes And Reflection.FieldAttributes.HasFieldMarshal) <> Reflection.FieldAttributes.HasFieldMarshal Then + If attributeList Is Nothing OrElse attributeList.Length = 0 Then + m_oFile.PutStringWithLength(0, s) + Else + Dim ma As VBFixedStringAttribute + Dim length As Integer + + ma = CType(attributeList(0), VBFixedStringAttribute) + length = ma.Length + + If length = 0 Then + length = -1 + End If + + m_oFile.PutFixedLengthString(0, s, length) + End If + Case TypeCode.Single + m_oFile.PutSingle(0, SingleType.FromObject(vValue)) + Case TypeCode.Double + m_oFile.PutDouble(0, DoubleType.FromObject(vValue)) + Case TypeCode.Int16 + m_oFile.PutShort(0, ShortType.FromObject(vValue)) + Case TypeCode.Int32 + m_oFile.PutInteger(0, IntegerType.FromObject(vValue)) + Case TypeCode.Byte + m_oFile.PutByte(0, ByteType.FromObject(vValue)) + Case TypeCode.Int64 + m_oFile.PutLong(0, LongType.FromObject(vValue)) + Case TypeCode.DateTime + m_oFile.PutDate(0, DateType.FromObject(vValue)) + Case TypeCode.Boolean + m_oFile.PutBoolean(0, BooleanType.FromObject(vValue)) + Case TypeCode.Decimal + m_oFile.PutDecimal(0, DecimalType.FromObject(vValue)) + Case TypeCode.Char + m_oFile.PutChar(0, CharType.FromObject(vValue)) + Case TypeCode.DBNull + Throw VbMakeException(New ArgumentException(GetResourceString(ResID.Argument_UnsupportedFieldType2, field_info.Name, "DBNull")), vbErrors.IllegalFuncCall) + Case Else 'Case TypeCode.Object + If FieldType Is GetType(Object) Then + m_oFile.PutObject(vValue, 0) + ElseIf FieldType Is GetType(System.Exception) Then + Throw VbMakeException(New ArgumentException(GetResourceString(ResID.Argument_UnsupportedFieldType2, field_info.Name, "Exception")), vbErrors.IllegalFuncCall) + ElseIf FieldType Is GetType(System.Reflection.Missing) Then + Throw VbMakeException(New ArgumentException(GetResourceString(ResID.Argument_UnsupportedFieldType2, field_info.Name, "Missing")), vbErrors.IllegalFuncCall) + Else + Throw VbMakeException(New ArgumentException(GetResourceString(ResID.Argument_UnsupportedFieldType2, field_info.Name, FieldType.Name)), vbErrors.IllegalFuncCall) + End If + End Select + End If + + End Function + End Class + + + + _ + Friend NotInheritable Class GetHandler + Implements IRecordEnum + Dim m_oFile As VB6File + + Sub New(ByVal oFile As VB6File) + MyBase.New() + m_oFile = oFile + End Sub + + + + Function Callback(ByVal field_info As Reflection.FieldInfo, ByRef vValue As Object) As Boolean Implements IRecordEnum.Callback + Dim FieldType As System.Type + + FieldType = field_info.FieldType + + If FieldType Is Nothing Then + Throw VbMakeException(New ArgumentException(GetResourceString(ResID.Argument_UnsupportedFieldType2, field_info.Name, "Empty")), vbErrors.IllegalFuncCall) + End If + + If FieldType.IsArray() Then + Dim attributeList As Object() = field_info.GetCustomAttributes(GetType(VBFixedArrayAttribute), False) + Dim arr As System.Array = Nothing + Dim FixedStringLength As Integer = -1 + + Dim FixedStringAttributeList As Object() = field_info.GetCustomAttributes(GetType(VBFixedStringAttribute), False) + If Not FixedStringAttributeList Is Nothing AndAlso FixedStringAttributeList.Length > 0 Then + Dim FixedStringAttribute As VBFixedStringAttribute = CType(FixedStringAttributeList(0), VBFixedStringAttribute) + If FixedStringAttribute.Length > 0 Then + FixedStringLength = FixedStringAttribute.Length + End If + End If + + If attributeList Is Nothing OrElse attributeList.Length = 0 Then + m_oFile.GetDynamicArray(arr, FieldType.GetElementType, FixedStringLength) + Else + Dim attr As VBFixedArrayAttribute = CType(attributeList(0), VBFixedArrayAttribute) + Dim FirstBound As Integer = attr.FirstBound + Dim SecondBound As Integer = attr.SecondBound + arr = CType(vValue, System.Array) + + m_oFile.GetFixedArray(0, arr, FieldType.GetElementType(), FirstBound, SecondBound, FixedStringLength) + End If + + vValue = arr + Else + Select Case Type.GetTypeCode(FieldType) + Case TypeCode.String + Dim attributeList As Object() = field_info.GetCustomAttributes(GetType(VBFixedStringAttribute), False) + + If attributeList Is Nothing OrElse attributeList.Length = 0 Then + vValue = m_oFile.GetLengthPrefixedString(0) + Else + + Dim ma As VBFixedStringAttribute = CType(attributeList(0), VBFixedStringAttribute) + Dim length As Integer = ma.Length + + If length = 0 Then + length = -1 + End If + vValue = m_oFile.GetFixedLengthString(0, length) + End If + Case TypeCode.Single + vValue = m_oFile.GetSingle(0) + Case TypeCode.Double + vValue = m_oFile.GetDouble(0) + Case TypeCode.Int16 + vValue = m_oFile.GetShort(0) + Case TypeCode.Int32 + vValue = m_oFile.GetInteger(0) + Case TypeCode.Byte + vValue = m_oFile.GetByte(0) + Case TypeCode.Int64 + vValue = m_oFile.GetLong(0) + Case TypeCode.DateTime + vValue = m_oFile.GetDate(0) + Case TypeCode.Boolean + vValue = m_oFile.GetBoolean(0) + Case TypeCode.Decimal + vValue = m_oFile.GetDecimal(0) + Case TypeCode.Char + vValue = m_oFile.GetChar(0) + Case TypeCode.DBNull + Throw VbMakeException(New ArgumentException(GetResourceString(ResID.Argument_UnsupportedFieldType2, field_info.Name, "DBNull")), vbErrors.IllegalFuncCall) + Case Else + 'Case TypeCode.Object + If FieldType Is GetType(Object) Then + m_oFile.GetObject(vValue) + ElseIf FieldType Is GetType(System.Exception) Then + Throw VbMakeException(New ArgumentException(GetResourceString(ResID.Argument_UnsupportedFieldType2, field_info.Name, "Exception")), vbErrors.IllegalFuncCall) + ElseIf FieldType Is GetType(System.Reflection.Missing) Then + Throw VbMakeException(New ArgumentException(GetResourceString(ResID.Argument_UnsupportedFieldType2, field_info.Name, "Missing")), vbErrors.IllegalFuncCall) + Else + Throw VbMakeException(New ArgumentException(GetResourceString(ResID.Argument_UnsupportedFieldType2, field_info.Name, FieldType.Name)), vbErrors.IllegalFuncCall) + End If + End Select + End If + + End Function + End Class + + + + '********************************************** + '* + '* VB6File + '* + '* Base for all VB6 compatible file i/o + '* + '********************************************** + _ + Friend MustInherit Class VB6File + Friend m_lCurrentColumn As Integer + Friend m_lWidth As Integer + Friend m_lRecordLen As Integer + Friend m_lRecordStart As Long + Friend m_sFullPath As String + Friend m_share As OpenShare + Friend m_access As OpenAccess + Friend m_eof As Boolean + Friend m_position As Long + Friend m_file As FileStream + Friend m_fAppend As Boolean + Friend m_bPrint As Boolean + Protected m_sw As StreamWriter + Protected m_sr As StreamReader + Protected m_bw As BinaryWriter + Protected m_br As BinaryReader + Protected m_Encoding As Encoding + + Protected Const lchTab As Integer = 9 + Protected Const lchCR As Integer = 13 + Protected Const lchLF As Integer = 10 + Protected Const lchSpace As Integer = 32 + Protected Const lchIntlSpace As Integer = &H3000I + Protected Const lchDoubleQuote As Integer = 34 + Protected Const lchPound As Integer = AscW("#") + Protected Const lchComma As Integer = AscW(",") + Protected Const EOF_INDICATOR As Integer = -1 + Protected Const EOF_CHAR As Integer = &H1A + Protected Const FIN_NUMTERMCHAR As Short = 6 + Protected Const FIN_LINEINP As Short = 0 + Protected Const FIN_QSTRING As Short = 1 + Protected Const FIN_STRING As Short = 2 + Protected Const FIN_NUMBER As Short = 3 + + + + '============================================================================ + ' Construction functions. + '============================================================================ + Protected Sub New() + MyBase.New() + End Sub + + + + Protected Sub New(ByVal sPath As String, ByVal access As OpenAccess, ByVal share As OpenShare, ByVal lRecordLen As Integer) + MyBase.New() + + If access <> OpenAccess.Read AndAlso _ + access <> OpenAccess.ReadWrite AndAlso _ + access <> OpenAccess.Write Then + Throw New ArgumentException(GetResourceString(ResID.Argument_InvalidValue1, "Access")) + End If + m_access = access + + If (share <> OpenShare.Shared AndAlso _ + share <> OpenShare.LockRead AndAlso _ + share <> OpenShare.LockReadWrite AndAlso _ + share <> OpenShare.LockWrite) Then + Throw New ArgumentException(GetResourceString(ResID.Argument_InvalidValue1, "Share")) + End If + + m_share = share + + m_lRecordLen = lRecordLen + m_sFullPath = (New FileInfo(sPath)).FullName + End Sub + + + + '============================================================================ + ' Open/Close/Information functions. + '============================================================================ + Friend Function GetAbsolutePath() As String + Return m_sFullPath + End Function + + + + Friend Overridable Sub OpenFile() + Try + If File.Exists(m_sFullPath) Then + m_file = New FileStream(m_sFullPath, FileMode.Open, CType(m_access, FileAccess), CType(m_share, FileShare)) + Else + m_file = New FileStream(m_sFullPath, FileMode.Create, CType(m_access, FileAccess), CType(m_share, FileShare)) + End If + + ' Catch e1 As FileNotFoundException + ' If e1.ErrorCode = Win32Api.ERROR_SHARING_VIOLATION Then + ' Throw VbMakeException(vbErrors.PermissionDenied) + ' Else + ' Throw VbMakeException(vbErrors.FileNotFound) + ' End If + + Catch e2 As SecurityException + Throw VbMakeException(vbErrors.FileNotFound) + + ' Catch e3 As IOException + ' If e3.ErrorCode = Win32Api.ERROR_NOT_READY Then + ' Throw VbMakeException(vbErrors.DiskNotReady) + ' Else + ' Throw VbMakeException(vbErrors.PathFileAccess) + ' 'Throw VbMakeException(vbErrors.PathNotFound) + ' End If + + End Try + End Sub + + + + Friend Overridable Sub CloseFile() + CloseTheFile() + End Sub + + + + Protected Sub CloseTheFile() + If m_sw Is Nothing Then + 'nothin to do + Else + m_sw.Close() + m_sw = Nothing + End If + + If m_sr Is Nothing Then + 'nothin to do + Else + m_sr.Close() + m_sr = Nothing + End If + + If Not m_file Is Nothing Then + m_file.Close() + m_file = Nothing + End If + End Sub + + + + Friend Function GetColumn() As Integer + Return m_lCurrentColumn + End Function + + + + Friend Sub SetColumn(ByVal lColumn As Integer) + If m_lWidth <> 0 AndAlso m_lCurrentColumn <> 0 AndAlso _ + (lColumn + 14) > m_lWidth Then + WriteLine(Nothing) + Else + SPC(lColumn - m_lCurrentColumn) + End If + End Sub + + + + Friend Function GetWidth() As Integer + Return m_lWidth + End Function + + + + Friend Sub SetWidth(ByVal RecordWidth As Integer) + If RecordWidth < 0 OrElse RecordWidth > 255 Then + Throw VbMakeException(vbErrors.IllegalFuncCall) + End If + + m_lWidth = RecordWidth + End Sub + + + + '============================================================================ + ' Output functions. + '============================================================================ + Friend Overridable Sub WriteLine(ByVal s As String) + Throw VbMakeException(vbErrors.BadFileMode) + End Sub + + + + Friend Overridable Sub WriteString(ByVal s As String) + Throw VbMakeException(vbErrors.BadFileMode) + End Sub + + + + Friend Overridable Function EOF() As Boolean + Return m_eof + End Function + + + + Friend Function LOF() As Long + Return m_file.Length + End Function + + + + Friend Overridable Function LOC() As Long + If (m_lRecordLen = -1) OrElse (GetMode() <> OpenMode.Random) Then + Return (m_position + 1) + End If + + If m_lRecordLen = 0 Then + 'Debug.Assert "HOW DID WE GET HERE" + Throw VbMakeException(vbErrors.InternalError) + Else + Dim pos As Long + pos = m_position + + If pos = 0 Then + Return 0 + End If + + Return (m_position \ m_lRecordLen) + 1 + End If + End Function + + + + Friend Overridable Function GetStreamReader() As StreamReader + Return m_sr + End Function + + + + Friend Sub SetRecord(ByVal RecordNumber As Long) + Dim lSeekPos As Long + + If m_lRecordLen = 0 Then + Exit Sub + End If + + If RecordNumber = 0 Then + Exit Sub + ElseIf m_lRecordLen = -1 Then + If RecordNumber = -1 Then + 'Binary file, leave at current position + Exit Sub + Else + 'No records, use actual byte position + lSeekPos = RecordNumber - 1 + End If + ElseIf RecordNumber = -1 Then + 'Go to next record + lSeekPos = GetPos() + + If lSeekPos = 0 Then + m_lRecordStart = 0 + Exit Sub + End If + + If (lSeekPos Mod m_lRecordLen) = 0 Then + 'Already on record boundary + m_lRecordStart = lSeekPos + Exit Sub + End If + + 'Go to next record + lSeekPos = m_lRecordLen * (lSeekPos \ m_lRecordLen + 1) + ElseIf RecordNumber <> 0 Then + 'Go to specified record + 'lSeekPos = (RecordNumber - 1) * m_lRecordLen + + If m_lRecordLen = -1 Then + lSeekPos = RecordNumber + Else + lSeekPos = (RecordNumber - 1) * m_lRecordLen + End If + End If + + SeekOffset(lSeekPos) + m_lRecordStart = lSeekPos + End Sub + + + + Friend Overridable Overloads Sub Seek(ByVal BaseOnePosition As Long) + If BaseOnePosition <= 0 Then + Throw VbMakeException(vbErrors.BadRecordNum) + End If + + Dim BaseZeroPosition As Long = BaseOnePosition - 1 + + If BaseZeroPosition > m_file.Length Then + m_file.SetLength(BaseZeroPosition) + End If + + m_file.Position = BaseZeroPosition + m_position = BaseZeroPosition + + m_eof = (m_position >= m_file.Length) + + If Not m_sr Is Nothing Then + m_sr.DiscardBufferedData() + End If + + End Sub + + + + 'Function Seek + ' + 'RANDOM MODE - Returns number of next record + 'other modes - Returns the byte position at which the next operation + ' will take place + Friend Overridable Overloads Function Seek() As Long + 'm_position is the last read byte as a zero based offset + 'Seek returns the position of the next byte to read + Return (m_position + 1) + End Function + + + + Friend Sub SeekOffset(ByVal offset As Long) + 'Do not call m_file.SetLength here because that could extend the file length, + 'which shouldn't happen until a subsequent Write or Put operation. + m_position = offset + m_file.Position = offset + + If Not m_sr Is Nothing Then + m_sr.DiscardBufferedData() + End If + + End Sub + + + + Friend Function GetPos() As Long + Return m_position + End Function + + + + Friend Overridable Overloads Sub Lock() + 'Lock the whole file, not just the current size of file, since file could change. + m_file.Lock(0, Int32.MaxValue) 'Win98 doesn't handle Int64 + End Sub + + + + Friend Overridable Overloads Sub Unlock() + m_file.Unlock(0, Int32.MaxValue) + End Sub + + + + Friend Overridable Overloads Sub Lock(ByVal Record As Long) + If m_lRecordLen = -1 Then + m_file.Lock((Record - 1), 1) + Else + m_file.Lock((Record - 1) * m_lRecordLen, m_lRecordLen) + End If + End Sub + + + + Friend Overridable Overloads Sub Unlock(ByVal Record As Long) + If m_lRecordLen = -1 Then + m_file.Unlock((Record - 1), 1) + Else + m_file.Unlock((Record - 1) * m_lRecordLen, m_lRecordLen) + End If + End Sub + + + + Friend Overridable Overloads Sub Lock(ByVal RecordStart As Long, ByVal RecordEnd As Long) + If m_lRecordLen = -1 Then + m_file.Lock((RecordStart - 1), (RecordEnd - RecordStart) + 1) + Else + m_file.Lock((RecordStart - 1) * m_lRecordLen, ((RecordEnd - RecordStart) + 1) * m_lRecordLen) + End If + End Sub + + + + Friend Overridable Overloads Sub Unlock(ByVal RecordStart As Long, ByVal RecordEnd As Long) + If m_lRecordLen = -1 Then + m_file.Unlock((RecordStart - 1), (RecordEnd - RecordStart) + 1) + Else + m_file.Unlock((RecordStart - 1) * m_lRecordLen, ((RecordEnd - RecordStart) + 1) * m_lRecordLen) + End If + End Sub + + + + Friend Function LineInput() As String + ValidateReadable() + Dim Result As String = m_sr.ReadLine() + If Result Is Nothing Then + Result = "" + End If + + Diagnostics.Debug.Assert(Not m_Encoding Is Nothing) + m_position += m_Encoding.GetByteCount(Result) + 2 + m_eof = CheckEOF(m_sr.Peek()) + Return Result + End Function + + + + Friend Overridable Function CanInput() As Boolean + Return False + End Function + + + + Friend Overridable Function CanWrite() As Boolean + Return False + End Function + + + + Protected Overridable Sub InputObject(ByRef Value As Object) + Throw VbMakeException(vbErrors.BadFileMode) + End Sub + + + + Protected Overridable Function InputStr() As String + Dim lChar As Integer + + ValidateReadable() + + ' read past any leading spaces or tabs + 'Skip over leading whitespace + lChar = SkipWhiteSpaceEOF() + + If lChar = lchDoubleQuote Then + lChar = m_sr.Read() + m_position += 1 + InputStr = ReadInField(FIN_QSTRING) + Else + InputStr = ReadInField(FIN_STRING) + End If + + SkipTrailingWhiteSpace() + End Function + + + + Protected Overridable Function InputNum(ByVal vt As VariantType) As Object + Dim sField As String + + ValidateReadable() + + 'Read past any leading spaces or tabs + 'Skip over leading whitespace + SkipWhiteSpaceEOF() + + sField = ReadInField(FIN_NUMBER) + + ' considering adding validity checks for expected varianttype + InputNum = sField + SkipTrailingWhiteSpace() + End Function + + + + Public MustOverride Function GetMode() As OpenMode + + + + Friend Function InputString(ByVal lLen As Integer) As String + Dim sb As StringBuilder + Dim i As Integer + Dim lInput As Integer + Dim FileOpenMode As OpenMode + + ValidateReadable() + + sb = New StringBuilder(lLen) + FileOpenMode = GetMode() + + For i = 1 To lLen + If FileOpenMode = OpenMode.Binary Then + lInput = m_br.Read() + m_position += 1 + + If (lInput = -1) Then 'Binary files don't stop upon reading 26=CTRL-Z + Exit For + End If + ElseIf FileOpenMode = OpenMode.Input Then + lInput = m_sr.Read() + m_position += 1 + + If (lInput = -1) Or (lInput = 26) Then 'Input files do stop upon reading 26=CTRL-Z + m_eof = True + Throw VbMakeException(vbErrors.EndOfFile) + End If + Else + Throw VbMakeException(vbErrors.BadFileMode) + End If + + If lInput <> 0 Then + sb.Append(ChrW(lInput)) + End If + Next i + + If FileOpenMode = OpenMode.Binary Then + m_eof = (m_br.PeekChar() = EOF_INDICATOR) + Else + m_eof = CheckEOF(m_sr.Peek()) + End If + + Return sb.ToString() + End Function + + + + Friend Sub SPC(ByVal iCount As Integer) + Dim lCurPos As Integer + Dim lWidth As Integer + Dim s As String + + If iCount <= 0 Then + ' iCount = 0 + Exit Sub + End If + + lCurPos = GetColumn() + lWidth = GetWidth() + + If lWidth <> 0 Then + ' File output with line length limit + If iCount >= lWidth Then + iCount = iCount Mod lWidth ' Modulo the line length + End If + + If (iCount + lCurPos) > lWidth Then + ' Spaces don't fit on this line. Subtract what fits and put the + ' rest on next line. + iCount -= (lWidth - lCurPos) + GoTo NewLine + End If + End If + + iCount += lCurPos + + ' If tab position is less than current position, + ' goto next line. + If (iCount < lCurPos) Then +NewLine: + WriteLine(Nothing) + 'FileOutString(iodata, FILE_EOL, FILE_EOL_LEN) + lCurPos = 0 + End If + + If (iCount > lCurPos) Then + s = New System.String(" "c, iCount - lCurPos) + [WriteString](s) + End If + End Sub + + + + Friend Sub Tab(ByVal Column As Integer) + Dim lCurPos As Integer + Dim lWidth As Integer + Dim s As String + + If Column < 1 Then + Column = 1 + End If + + 'When tabbing, we go to the space before the column + 'so the next print will be in that column + Column -= 1 + + lCurPos = GetColumn() + lWidth = GetWidth() + + If lWidth <> 0 Then + ' File output with line length limit + If Column >= lWidth Then + Column = Column Mod lWidth ' Modulo the line length + End If + End If + + ' If tab position is less than current position, + ' goto next line. + If (Column < lCurPos) Then + WriteLine(Nothing) + lCurPos = 0 + End If + + If (Column > lCurPos) Then + s = New System.String(" "c, Column - lCurPos) + [WriteString](s) + End If + End Sub + + + + Friend Sub SetPrintMode() + Dim mode As OpenMode + + mode = GetMode() + + If mode = OpenMode.Input OrElse _ + mode = OpenMode.Binary OrElse _ + mode = OpenMode.Random Then + Throw VbMakeException(vbErrors.BadFileMode) + End If + + m_bPrint = True + End Sub + + + + Friend Shared Function VTType(ByVal VarName As Object) As VT + If VarName Is Nothing Then + Return VT.Variant + End If + + Return VTFromComType(VarName.GetType()) + End Function + + + + Friend Shared Function VTFromComType(ByVal typ As System.Type) As VT + If typ Is Nothing Then + Return VT.Variant + End If + + If typ.IsArray() Then + typ = typ.GetElementType() + If typ.IsArray Then + Return CType(VT.Array Or VT.Variant, VT) + End If + + Dim Result As VT = VTFromComType(typ) + If (Result And VT.Array) <> 0 Then + 'Element type is also an array, so just return "array of objects" + Return CType(VT.Array Or VT.Variant, VT) + End If + Return CType(Result Or VT.Array, VT) + + ElseIf typ.IsEnum() Then + typ = System.Enum.GetUnderlyingType(typ) + End If + + If typ Is Nothing Then + Return VT.Empty + End If + + Select Case Type.GetTypeCode(typ) + Case TypeCode.String + Return VT.String + Case TypeCode.Int32 + Return VT.Integer + Case TypeCode.Int16 + Return VT.Short + Case TypeCode.Int64 + Return VT.Long + Case TypeCode.Single + Return VT.Single + Case TypeCode.Double + Return VT.Double + Case TypeCode.DateTime + Return VT.Date + Case TypeCode.Boolean + Return VT.Boolean + Case TypeCode.Decimal + Return VT.Decimal + Case TypeCode.Byte + Return VT.Byte + Case TypeCode.Char + Return VT.Char + Case TypeCode.DBNull + Return VT.DBNull + End Select + + If typ Is GetType(System.Reflection.Missing) Then + Return VT.Error + + ElseIf typ Is GetType(System.Exception) OrElse typ.IsSubclassOf(GetType(System.Exception)) Then + Return VT.Error + + 'Must come after all the Intrinsic types + ElseIf typ.IsValueType() Then + Return VT.Structure + + Else + Return VT.Variant + + End If + End Function + + + + Friend Sub PutFixedArray(ByVal RecordNumber As Long, ByVal arr As System.Array, ByVal ElementType As System.Type, _ + Optional ByVal FixedStringLength As Integer = -1, Optional ByVal FirstBound As Integer = -1, _ + Optional ByVal SecondBound As Integer = -1) + + SetRecord(RecordNumber) + If ElementType Is Nothing Then + ElementType = arr.GetType().GetElementType() + End If + PutArrayData(arr, ElementType, FixedStringLength, FirstBound, SecondBound) + End Sub + + + + Friend Sub PutDynamicArray(ByVal RecordNumber As Long, ByVal arr As System.Array, _ + Optional ByVal ContainedInVariant As Boolean = True, Optional ByVal FixedStringLength As Integer = -1) + + Dim FirstBound As Integer + Dim SecondBound As Integer + Dim cDims As Integer + + If arr Is Nothing Then + cDims = 0 + Else + cDims = arr.Rank() + FirstBound = arr.GetUpperBound(0) + End If + + If cDims = 1 Then + SecondBound = -1 + ElseIf cDims = 2 Then + SecondBound = arr.GetUpperBound(1) + ElseIf cDims <> 0 Then + Throw New ArgumentException(GetResourceString(ResID.Argument_UnsupportedArrayDimensions)) + End If + + SetRecord(RecordNumber) + + If ContainedInVariant Then + Dim vtype As VT + + vtype = VTType(arr) + m_bw.Write(CShort(vtype)) + m_position += 2 + + If (vtype And VT.Array) = 0 Then + Throw VbMakeException(vbErrors.InvalidTypeLibVariable) + End If + End If + + PutArrayDesc(arr) + If cDims <> 0 Then + PutArrayData(arr, arr.GetType().GetElementType(), FixedStringLength, FirstBound, SecondBound) + End If + End Sub + + + + Friend Sub LengthCheck(ByVal Length As Integer) + If m_lRecordLen = -1 Then + Exit Sub + End If + + If Length > m_lRecordLen Then + Throw VbMakeException(vbErrors.BadRecordLen) + Else + If (GetPos() + Length) > (m_lRecordStart + m_lRecordLen) Then + Throw VbMakeException(vbErrors.BadRecordLen) + End If + End If + End Sub + + + + 'Writes a fixed length string member of a structure to the file + Friend Sub PutFixedLengthString(ByVal RecordNumber As Long, ByVal s As String, ByVal lengthToWrite As Integer) + Dim PadChar As Char = " "c + + If s Is Nothing Then + s = "" + End If + + If s = "" Then + PadChar = ChrW(0) + End If + + 'Need to handle double byte chars in s + Diagnostics.Debug.Assert(Not m_Encoding Is Nothing) + Dim ByteLength As Integer = m_Encoding.GetByteCount(s) + + If ByteLength > lengthToWrite Then + If ByteLength = s.Length Then + s = Left(s, lengthToWrite) + Else + 'String contains multi-byte characters. Truncate to 'length' bytes. + 'VSWhidbey 521192: The assumption that if cuts off half a DBCS character, that character is replaced with Chr(0) + ' is nolonger true. See VSWhidbey 521213. + Dim Bytes() As Byte = m_Encoding.GetBytes(s) + s = m_Encoding.GetString(Bytes, 0, lengthToWrite) + + Diagnostics.Debug.Assert(Not m_Encoding Is Nothing) + ByteLength = m_Encoding.GetByteCount(s) + 'VSWhidbey 521192: In case of cutting off half a DBCS character, the encoding may return a default DBCS character, + ' making ByteLenght > Length. Replace the ending bytes with 0 until we got back a string with correct byte count. + If ByteLength > lengthToWrite Then + For i As Integer = lengthToWrite - 1 To 0 Step -1 + Bytes(i) = 0 + s = m_Encoding.GetString(Bytes, 0, lengthToWrite) + ByteLength = m_Encoding.GetByteCount(s) + If ByteLength <= lengthToWrite Then + Exit For + End If + Next + End If + Diagnostics.Debug.Assert(ByteLength <= lengthToWrite) + End If + End If + + If ByteLength < lengthToWrite Then + s = s & StrDup(lengthToWrite - ByteLength, PadChar) + End If + + Diagnostics.Debug.Assert(m_Encoding.GetByteCount(s) = lengthToWrite) + + SetRecord(RecordNumber) + ' CONSIDER: We should call LengthCheck against m_ae.GetByteCount(s) instead of asserting as above? + LengthCheck(lengthToWrite) + m_sw.Write(s) + m_position += lengthToWrite + End Sub + + + + Friend Sub PutVariantString(ByVal RecordNumber As Long, ByVal s As String) + If s Is Nothing Then + s = "" + End If + + Diagnostics.Debug.Assert(Not m_Encoding Is Nothing) + Dim ByteLength As Integer = m_Encoding.GetByteCount(s) + + SetRecord(RecordNumber) + LengthCheck(ByteLength + 2 + 2) 'Add sizeof string length and vartype + m_bw.Write(CShort(VT.String)) + m_bw.Write(CShort(ByteLength)) + + If (ByteLength <> 0) Then + m_sw.Write(s) + End If + + m_position += ByteLength + 2 + 2 + End Sub + + + + Friend Sub PutString(ByVal RecordNumber As Long, ByVal s As String) + If s Is Nothing Then + s = "" + End If + + Diagnostics.Debug.Assert(Not m_Encoding Is Nothing) + Dim ByteLength As Integer = m_Encoding.GetByteCount(s) + + SetRecord(RecordNumber) + LengthCheck(ByteLength) + + If (ByteLength <> 0) Then + m_sw.Write(s) + End If + + m_position += ByteLength + End Sub + + + Friend Sub PutStringWithLength(ByVal RecordNumber As Long, ByVal s As String) + If s Is Nothing Then + s = "" + End If + + Diagnostics.Debug.Assert(Not m_Encoding Is Nothing) + Dim ByteLength As Integer = m_Encoding.GetByteCount(s) + + SetRecord(RecordNumber) + LengthCheck(ByteLength + 2) + m_bw.Write(CShort(ByteLength)) + + If ByteLength <> 0 Then + 'Must use streamwriter to get the unicode/ansi conversion done + m_sw.Write(s) + End If + + m_position += ByteLength + 2 + End Sub + + + + Friend Sub PutDate(ByVal RecordNumber As Long, ByVal dt As Date, Optional ByVal ContainedInVariant As Boolean = False) + Dim RecLength As Integer = 8 + Dim dbl As Double + + If ContainedInVariant Then + RecLength += 2 + End If + + SetRecord(RecordNumber) + LengthCheck(RecLength) + + If ContainedInVariant Then + m_bw.Write(VT.Date) + End If + + dbl = dt.ToOADate() + m_bw.Write(dbl) + m_position += RecLength + End Sub + + + Friend Sub PutShort(ByVal RecordNumber As Long, ByVal i As Short, Optional ByVal ContainedInVariant As Boolean = False) + Dim RecLength As Integer = 2 + + If ContainedInVariant Then + RecLength += 2 + End If + + SetRecord(RecordNumber) + LengthCheck(RecLength) + + If ContainedInVariant Then + m_bw.Write(VT.Short) + End If + + m_bw.Write(i) + m_position += RecLength + End Sub + + + + Friend Sub PutInteger(ByVal RecordNumber As Long, ByVal l As Integer, Optional ByVal ContainedInVariant As Boolean = False) + Dim RecLength As Integer = 4 + + If ContainedInVariant Then + RecLength += 2 + End If + + SetRecord(RecordNumber) + LengthCheck(RecLength) + + If ContainedInVariant Then + m_bw.Write(VT.Integer) + End If + + m_bw.Write(l) + m_position += RecLength + End Sub + + + + Friend Sub PutLong(ByVal RecordNumber As Long, ByVal l As Long, Optional ByVal ContainedInVariant As Boolean = False) + Dim RecLength As Integer = 8 + + If ContainedInVariant Then + RecLength += 2 ' Add length of vartype + End If + + SetRecord(RecordNumber) + LengthCheck(RecLength) + + If ContainedInVariant Then + m_bw.Write(VT.Long) + End If + + m_bw.Write(l) + m_position += RecLength + End Sub + + + + Friend Sub PutByte(ByVal RecordNumber As Long, ByVal byt As Byte, Optional ByVal ContainedInVariant As Boolean = False) + Dim RecLength As Integer = 1 + + If ContainedInVariant Then + RecLength += 2 ' Add length of vartype + End If + + SetRecord(RecordNumber) + LengthCheck(RecLength) + + If ContainedInVariant Then + m_bw.Write(VT.Byte) + End If + + m_bw.Write(byt) + m_position += RecLength + End Sub + + + + Friend Sub PutChar(ByVal RecordNumber As Long, ByVal ch As Char, Optional ByVal ContainedInVariant As Boolean = False) + Dim RecLength As Integer = 2 + + If ContainedInVariant Then + RecLength += 2 ' Add length of vartype + End If + + SetRecord(RecordNumber) + LengthCheck(RecLength) + + If ContainedInVariant Then + m_bw.Write(VT.Char) + End If + + m_bw.Write(ch) + m_position += RecLength + End Sub + + + + Friend Sub PutSingle(ByVal RecordNumber As Long, ByVal sng As Single, Optional ByVal ContainedInVariant As Boolean = False) + Dim RecLength As Integer = 4 + + If ContainedInVariant Then + RecLength += 2 ' Add length of vartype + End If + + SetRecord(RecordNumber) + LengthCheck(RecLength) + + If ContainedInVariant Then + m_bw.Write(VT.Single) + End If + + m_bw.Write(sng) + m_position += RecLength + End Sub + + + + Friend Sub PutDouble(ByVal RecordNumber As Long, ByVal dbl As Double, Optional ByVal ContainedInVariant As Boolean = False) + Dim RecLength As Integer = 8 + + If ContainedInVariant Then + RecLength += 2 ' Add length of vartype + End If + + SetRecord(RecordNumber) + LengthCheck(RecLength) + + If ContainedInVariant Then + m_bw.Write(VT.Double) + End If + + m_bw.Write(dbl) + m_position += RecLength + End Sub + + + + Friend Sub PutEmpty(ByVal RecordNumber As Long) + 'This will always be a Variant + SetRecord(RecordNumber) + LengthCheck(2) + m_bw.Write(VT.Empty) + m_position += 2 + End Sub + + + + Friend Sub PutBoolean(ByVal RecordNumber As Long, ByVal b As Boolean, Optional ByVal ContainedInVariant As Boolean = False) + Dim RecLength As Integer = 2 + + If ContainedInVariant Then + RecLength += 2 ' Add length of vartype + End If + + SetRecord(RecordNumber) + LengthCheck(RecLength) + + If ContainedInVariant Then + m_bw.Write(VT.Boolean) + End If + + If b Then + m_bw.Write(CShort(-1)) + Else + m_bw.Write(CShort(0)) + End If + + m_position += RecLength + End Sub + + + + Friend Sub PutDecimal(ByVal RecordNumber As Long, ByVal dec As Decimal, Optional ByVal ContainedInVariant As Boolean = False) + Dim RecLength As Integer = 16 + + If ContainedInVariant Then + RecLength += 2 ' Add length of vartype + End If + + SetRecord(RecordNumber) + LengthCheck(RecLength) + + If ContainedInVariant Then + m_bw.Write(VT.Decimal) + End If + + Dim lo, mid, hi As Integer + Dim flags As Byte + Dim sign As Byte + Dim bits() As Integer + + bits = System.Decimal.GetBits(dec) + flags = CByte((bits(3) And &H7FFFFFFFI) \ &H10000I) + lo = bits(0) + mid = bits(1) + hi = bits(2) + + If (bits(3) And &H80000000I) <> 0 Then + sign = 128 + End If + + m_bw.Write(CShort(VT.Decimal)) ' Decimal contains the vtype as first 2 bytes + m_bw.Write(flags) + m_bw.Write(sign) + m_bw.Write(hi) + m_bw.Write(lo) + m_bw.Write(mid) + m_position += RecLength + End Sub + + + + Friend Sub PutCurrency(ByVal RecordNumber As Long, ByVal dec As Decimal, Optional ByVal ContainedInVariant As Boolean = False) + Dim RecLength As Integer = 16 + + If ContainedInVariant Then + RecLength += 2 ' Add length of vartype + End If + + SetRecord(RecordNumber) + LengthCheck(RecLength) + + If ContainedInVariant Then + m_bw.Write(VT.Currency) + End If + + m_bw.Write(System.Decimal.ToOACurrency(dec)) + m_position += RecLength + End Sub + + + + Friend Sub PutRecord(ByVal RecordNumber As Long, ByVal o As ValueType) + If o Is Nothing Then + Throw New NullReferenceException + End If + + Dim intf As IRecordEnum + Dim ph As PutHandler + + SetRecord(RecordNumber) + + ph = New PutHandler(Me) + intf = ph + + If intf Is Nothing Then + Throw VbMakeException(vbErrors.IllegalFuncCall) + End If + + EnumerateUDT(o, intf, False) + End Sub + + + + Friend Function ComTypeFromVT(ByVal vtype As VT) As System.Type + Select Case vtype + Case VT.Variant + Return GetType(System.Object) + Case VT.Empty + Return Nothing + Case VT.DBNull + Return GetType(System.DBNull) + Case VT.Short + Return GetType(System.Int16) + Case VT.Integer + Return GetType(System.Int32) + Case VT.Long + Return GetType(System.Int64) + Case VT.Single + Return GetType(System.Single) + Case VT.Double + Return GetType(System.Double) + Case VT.Date + Return GetType(System.DateTime) + Case VT.String + Return GetType(System.String) + Case VT.Error + Return GetType(System.Exception) + Case VT.Boolean + Return GetType(System.Boolean) + Case VT.Decimal + Return GetType(System.Decimal) + Case VT.Byte + Return GetType(System.Byte) + Case VT.Char + Return GetType(System.Char) + 'Case VT.Structure + ' 'Return m_Type + Case Else + Throw VbMakeException(vbErrors.InvalidTypeLibVariable) + End Select + End Function + + + + Friend Sub GetFixedArray(ByVal RecordNumber As Long, ByRef arr As System.Array, _ + ByVal FieldType As System.Type, Optional ByVal FirstBound As Integer = -1, _ + Optional ByVal SecondBound As Integer = -1, Optional ByVal FixedStringLength As Integer = -1) + + If SecondBound = -1 Then + arr = System.Array.CreateInstance(FieldType, FirstBound + 1) + Else + arr = System.Array.CreateInstance(FieldType, FirstBound + 1, SecondBound + 1) + End If + + SetRecord(RecordNumber) + GetArrayData(arr, FieldType, FirstBound, SecondBound, FixedStringLength) + End Sub + + + + Friend Sub GetDynamicArray(ByRef arr As System.Array, ByVal t As System.Type, Optional ByVal FixedStringLength As Integer = -1) + arr = GetArrayDesc(t) + + Dim cDims As Integer = arr.Rank + Dim FirstBound As Integer = arr.GetUpperBound(0) + Dim SecondBound As Integer + + If cDims = 1 Then + SecondBound = -1 + Else + SecondBound = arr.GetUpperBound(1) + End If + + GetArrayData(arr, t, FirstBound, SecondBound, FixedStringLength) + End Sub + + + + Private Sub PutArrayDesc(ByVal arr As System.Array) + Dim cDims As Short + Dim i As Integer + + If arr Is Nothing Then + cDims = 0 + Else + cDims = CShort(arr.Rank()) + End If + m_bw.Write(cDims) + m_position += 2 + + If cDims = 0 Then + Exit Sub + End If + + For i = 0 To cDims - 1 + m_bw.Write(CInt(arr.GetLength(i))) + m_bw.Write(CInt(arr.GetLowerBound(i))) 'Lower bound + m_position += 8 + Next i + End Sub + + + + Friend Function GetArrayDesc(ByVal typ As System.Type) As System.Array + Dim cDims As Integer + Dim lElementCounts() As Integer + Dim lLowerBounds() As Integer + Dim i As Integer + + ' for reading, read cDims, and how many in each, and redim + cDims = m_br.ReadInt16() + m_position += 2 + + If cDims = 0 Then + Return System.Array.CreateInstance(typ, 0) + End If + + ReDim lElementCounts(cDims - 1) + ReDim lLowerBounds(cDims - 1) + + For i = 0 To cDims - 1 + lElementCounts(i) = m_br.ReadInt32() + lLowerBounds(i) = m_br.ReadInt32() + m_position += 8 + Next i + + ' whidbey 43829 - consider support for fixedstring attributes + Return System.Array.CreateInstance(typ, lElementCounts, lLowerBounds) + End Function + + + + Friend Overridable Function GetLengthPrefixedString(ByVal RecordNumber As Long) As String + SetRecord(RecordNumber) + + If EOF() Then + Return "" + End If + + Return ReadString() + End Function + + + + Friend Overridable Function GetFixedLengthString(ByVal RecordNumber As Long, ByVal ByteLength As Integer) As String + SetRecord(RecordNumber) + Return ReadString(ByteLength) + End Function + + + + Protected Overloads Function ReadString(ByVal ByteLength As Integer) As String + Dim byteArray As Byte() + + If ByteLength = 0 Then + Return Nothing + End If + + byteArray = m_br.ReadBytes(ByteLength) + m_position += ByteLength + + Return m_Encoding.GetString(byteArray) + End Function + + + + Protected Overloads Function ReadString() As String + Dim ByteLen As Integer + + ByteLen = m_br.ReadInt16() + m_position += 2 + + If ByteLen = 0 Then + Return Nothing + End If + + LengthCheck(ByteLen) + Return ReadString(ByteLen) + + End Function + + + + Friend Function GetDate(ByVal RecordNumber As Long) As Date + Dim dbl As Double + + SetRecord(RecordNumber) + dbl = m_br.ReadDouble() + m_position += 8 + Return System.DateTime.FromOADate(dbl) + End Function + + + + Friend Function GetShort(ByVal RecordNumber As Long) As Short + Dim s As Short + + SetRecord(RecordNumber) + s = m_br.ReadInt16() + m_position += 2 + Return s + End Function + + + + Friend Function GetInteger(ByVal RecordNumber As Long) As Integer + Dim i As Integer + + SetRecord(RecordNumber) + i = m_br.ReadInt32() + m_position += 4 + Return i + End Function + + + + Friend Function GetLong(ByVal RecordNumber As Long) As Long + Dim l As Long + + SetRecord(RecordNumber) + l = m_br.ReadInt64() + m_position += 8 + Return l + End Function + + + + Friend Function GetByte(ByVal RecordNumber As Long) As Byte + Dim b As Byte + + SetRecord(RecordNumber) + b = m_br.ReadByte() + m_position += 1 + Return b + End Function + + + + Friend Function GetChar(ByVal RecordNumber As Long) As Char + Dim c As Char + + SetRecord(RecordNumber) + c = m_br.ReadChar() + m_position += 1 + Return c + End Function + + + + Friend Function GetSingle(ByVal RecordNumber As Long) As Single + Dim s As Single + + SetRecord(RecordNumber) + s = m_br.ReadSingle() + m_position += 4 + Return s + End Function + + + + Friend Function GetDouble(ByVal RecordNumber As Long) As Double + Dim d As Double + + SetRecord(RecordNumber) + d = m_br.ReadDouble() + m_position += 8 + Return d + End Function + + + + Friend Function GetDecimal(ByVal RecordNumber As Long) As Decimal + Dim vt As Integer + Dim lo, mid, hi As Integer + Dim flags As Byte + Dim negative As Boolean + Dim sign As Byte + + SetRecord(RecordNumber) + vt = m_br.ReadInt16() + flags = m_br.ReadByte() + sign = m_br.ReadByte() + hi = m_br.ReadInt32() + lo = m_br.ReadInt32() + mid = m_br.ReadInt32() + m_position += 16 + + If sign <> 0 Then + negative = True + End If + + Return New Decimal(lo, mid, hi, negative, flags) + End Function + + + + Friend Function GetCurrency(ByVal RecordNumber As Long) As Decimal + Dim i64 As Int64 + + SetRecord(RecordNumber) + i64 = m_br.ReadInt64() + m_position += 8 + Return Decimal.FromOACurrency(i64) + End Function + + + + Friend Function GetBoolean(ByVal RecordNumber As Long) As Boolean + Dim i As Short + + SetRecord(RecordNumber) + i = m_br.ReadInt16() + m_position += 2 + + If i = 0 Then + Return False + Else + Return True + End If + End Function + + + + Friend Sub GetRecord(ByVal RecordNumber As Long, ByRef o As ValueType, Optional ByVal ContainedInVariant As Boolean = False) + Dim intf As IRecordEnum + Dim ph As GetHandler + + If o Is Nothing Then + Throw New NullReferenceException + End If + + SetRecord(RecordNumber) + ph = New GetHandler(Me) + intf = ph + + If intf Is Nothing Then + Throw VbMakeException(vbErrors.IllegalFuncCall) + End If + + EnumerateUDT(o, intf, True) + End Sub + + + + Friend Sub PutArrayData(ByVal arr As System.Array, ByVal typ As System.Type, ByVal FixedStringLength As Integer, _ + ByVal FirstBound As Integer, ByVal SecondBound As Integer) + + Dim vtype As VT + Dim obj As Object + Dim iElementX As Integer + Dim iElementY As Integer + Dim iUpperElementX As Integer + Dim iUpperElementY As Integer + Dim sTemp As String + Dim ArrUBoundX, ArrUBoundY As Integer + Dim FixedBlankString As String = Nothing + Dim FixedCharArray As Char() = Nothing + + If arr Is Nothing Then + ArrUBoundY = -1 + ArrUBoundX = -1 + ElseIf (arr.GetUpperBound(0) > FirstBound) Then + Throw New ArgumentException(GetResourceString(ResID.Argument_ArrayDimensionsDontMatch)) + End If + + If typ Is Nothing Then + typ = arr.GetType().GetElementType() + End If + + vtype = VTFromComType(typ) + + If SecondBound = -1 Then + iUpperElementX = 0 + iUpperElementY = FirstBound + If Not arr Is Nothing Then + ArrUBoundY = arr.GetUpperBound(0) + End If + Else + iUpperElementX = SecondBound + iUpperElementY = FirstBound + If Not arr Is Nothing Then + If arr.Rank <> 2 OrElse arr.GetUpperBound(1) <> SecondBound Then + Throw New ArgumentException(GetResourceString(ResID.Argument_ArrayDimensionsDontMatch)) + End If + ArrUBoundY = arr.GetUpperBound(0) + ArrUBoundX = arr.GetUpperBound(1) + End If + End If + + If vtype = VT.String Then + If FixedStringLength = 0 Then + 'Use length of first String element + If SecondBound = -1 Then + obj = arr.GetValue(0) + Else + obj = arr.GetValue(0, 0) + End If + If Not obj Is Nothing Then + FixedStringLength = obj.ToString().Length + End If + End If + If FixedStringLength = 0 Then + Throw New ArgumentException(GetResourceString(ResID.Argument_InvalidFixedLengthString)) + ElseIf FixedStringLength > 0 Then + FixedBlankString = StrDup(FixedStringLength, " "c) + FixedCharArray = FixedBlankString.ToCharArray() 'Used for padding + End If + End If + + ' VSWhidbey 32415: Improve performance for FilePut. + Dim vtByteLength As Integer = GetByteLength(vtype) + ' Only attempt to write data down as a byte array for improved performance if: + ' 1. 1-Dimension array. + ' 2. Array is of the supported type (see GetByteLength). + ' 3. The given bound (iUpperElement - fixed size array) is the same as real size of the array (ArrUBound). + ' (The first check at the start of the array ensure that iUpperElement (FirstBound) will never < ArrUBound. + If (SecondBound = -1) AndAlso (vtByteLength > 0) AndAlso (iUpperElementY = ArrUBoundY) Then + ' Calculate the total byte length we're writing down. + Dim totalLength As Integer = vtByteLength * (iUpperElementY + 1) + ' The totalLength has to be less than the record length (See LengthCheck). + If GetPos() + totalLength <= m_lRecordStart + m_lRecordLen Then + Dim byteArr(totalLength - 1) As Byte + System.Buffer.BlockCopy(arr, 0, byteArr, 0, totalLength) + m_bw.Write(byteArr) + m_position += totalLength + Return + End If + End If + + For iElementX = 0 To iUpperElementX + For iElementY = 0 To iUpperElementY + Try + If SecondBound = -1 Then + If iElementY > ArrUBoundY Then + obj = Nothing + Else + obj = arr.GetValue(iElementY) + End If + Else + If iElementY > ArrUBoundY OrElse iElementX > ArrUBoundX Then + obj = Nothing + Else + 'These are supposed to be ordered Y, X + ' because of the order VB6 writes out + obj = arr.GetValue(iElementY, iElementX) + End If + End If + Catch Ex As IndexOutOfRangeException + 'The VBFixedArrayAttribute size must be larger than the array, pad it. + obj = 0 + End Try + + Select Case vtype + + Case VT.DBNull, VT.Empty + 'Nothing + + Case VT.Byte '1 byte + LengthCheck(1) + m_bw.Write(ByteType.FromObject(obj)) + m_position += 1 + + Case VT.Short '2 bytes + LengthCheck(2) + m_bw.Write(ShortType.FromObject(obj)) + m_position += 2 + + Case VT.Boolean '2 bytes + LengthCheck(2) + Dim b As Boolean = BooleanType.FromObject(obj) + + If b Then + m_bw.Write(CShort(-1)) + Else + m_bw.Write(CShort(0)) + End If + m_position += 2 + + Case VT.Integer '4 Bytes + LengthCheck(4) + m_bw.Write(IntegerType.FromObject(obj)) + m_position += 4 + + Case VT.Long '8 Bytes + LengthCheck(8) + m_bw.Write(LongType.FromObject(obj)) + m_position += 8 + + Case VT.Single '4 bytes + LengthCheck(4) + m_bw.Write(SingleType.FromObject(obj)) + m_position += 4 + + Case VT.Error '4 bytes + Throw VbMakeException(vbErrors.TypeMismatch) + + Case VT.Double '8 bytes + LengthCheck(8) + m_bw.Write(DoubleType.FromObject(obj)) + m_position += 8 + + Case VT.Date '8 bytes + LengthCheck(8) + m_bw.Write(CDbl(DateType.FromObject(obj).ToOADate())) + m_position += 8 + + Case VT.Decimal '8 bytes + LengthCheck(8) + m_bw.Write(System.Decimal.ToOACurrency(DecimalType.FromObject(obj))) + m_position += 8 + + Case VT.String + Dim ByteLength As Integer + + If obj Is Nothing Then + If FixedStringLength > 0 Then + sTemp = FixedBlankString + ByteLength = FixedStringLength + Debug.Assert(m_Encoding.GetByteCount(sTemp) = ByteLength) + Else + sTemp = "" + ByteLength = 0 + End If + Else + sTemp = obj.ToString() + Diagnostics.Debug.Assert(Not m_Encoding Is Nothing) + ByteLength = m_Encoding.GetByteCount(sTemp) + + If FixedStringLength > 0 AndAlso ByteLength > FixedStringLength Then + 'We need to truncate the string to the fixed string length (in bytes, not characters) + If ByteLength = sTemp.Length Then + 'SBCS or DBCS but the string contains only SBCS characters + sTemp = Microsoft.VisualBasic.Left(sTemp, FixedStringLength) + Debug.Assert(m_Encoding.GetByteCount(sTemp) = FixedStringLength) + ByteLength = FixedStringLength + Else + 'String contains multi-byte characters. Truncate to 'FixedStringLength' + ' bytes (if cuts off half of a DBCS character, that character + ' is replaced with a single Chr(0)) + Dim Bytes() As Byte = m_Encoding.GetBytes(sTemp) + sTemp = m_Encoding.GetString(Bytes, 0, FixedStringLength) + + ByteLength = m_Encoding.GetByteCount(sTemp) + Debug.Assert(ByteLength <= FixedStringLength) + End If + End If + End If + + If ByteLength > System.Int16.MaxValue Then + 'Size for strings is 2 bytes, thus the Short.MaxValue limitation + Throw VbMakeException(New ArgumentException(GetResourceString(ResID.FileIO_StringLengthExceeded)), vbErrors.IllegalFuncCall) + End If + + 'Do a length check and write out the length if not fixed length + If FixedStringLength > 0 Then + LengthCheck(FixedStringLength) + m_sw.Write(sTemp) + Debug.Assert(ByteLength = m_Encoding.GetByteCount(sTemp) AndAlso ByteLength <= FixedStringLength) + If ByteLength < FixedStringLength Then + 'Pad with spaces + m_sw.Write(FixedCharArray, 0, FixedStringLength - ByteLength) + End If + m_position += FixedStringLength + Else + LengthCheck(ByteLength + 2) + m_bw.Write(CShort(ByteLength)) + m_sw.Write(sTemp) + m_position += (2 + ByteLength) + End If + + Case VT.Char '2 bytes + LengthCheck(2) + m_bw.Write(CharType.FromObject(obj)) + m_position += 2 + + Case VT.Variant + PutObject(obj, 0, True) + + Case VT.Structure + PutObject(obj, 0, False) + + Case Else + If (vtype And VT.Array) <> 0 Then + 'Arrays of arrays not supported + Throw VbMakeException(vbErrors.TypeMismatch) + Else + Throw VbMakeException(vbErrors.InvalidTypeLibVariable) + End If + + vtype = vtype Xor VT.Array + + If vtype = VT.Variant Then + Throw VbMakeException(vbErrors.TypeMismatch) + End If + + If vtype > VT.Variant AndAlso (vtype <> VT.Byte AndAlso vtype <> VT.Decimal AndAlso vtype <> VT.Char AndAlso vtype <> VT.Long) Then + Throw VbMakeException(vbErrors.InvalidTypeLibVariable) + End If + End Select + Next iElementY + Next iElementX + End Sub + + Friend Sub GetArrayData(ByVal arr As System.Array, ByVal typ As System.Type, Optional ByVal FirstBound As Integer = -1, _ + Optional ByVal SecondBound As Integer = -1, Optional ByVal FixedStringLength As Integer = -1) + + Dim vtype As VT + Dim obj As Object = Nothing + Dim iElementX As Integer + Dim iElementY As Integer + Dim iUpperElementX As Integer + Dim iUpperElementY As Integer + + If arr Is Nothing Then + Throw New ArgumentException(GetResourceString(ResID.Argument_ArrayNotInitialized)) + End If + + If typ Is Nothing Then + typ = arr.GetType().GetElementType() + End If + vtype = VTFromComType(typ) + + If SecondBound = -1 Then + iUpperElementX = 0 + iUpperElementY = FirstBound + Else + iUpperElementX = SecondBound + iUpperElementY = FirstBound + End If + + ' VSWhidbey 32415: Improve performance for FilePut. + Dim vtByteLength As Integer = GetByteLength(vtype) + ' Only attempt to read data as a byte array for improved performance if: + ' 1. 1-Dimension array. + ' 2. Array is of the supported type (see GetByteLength). + ' 3. The given bound (iUpperElement - fixed size array) is the same as the real size of the array. + If (SecondBound = -1) AndAlso (vtByteLength > 0) AndAlso (iUpperElementY = arr.GetUpperBound(0)) Then + ' Calculate the total byte length we're reading. + Dim totalLength As Integer = vtByteLength * (iUpperElementY + 1) + ' The totalLength has to be less than the length in byte of the array. + If totalLength <= arr.Length * vtByteLength Then + System.Buffer.BlockCopy(m_br.ReadBytes(totalLength), 0, arr, 0, totalLength) + m_position += totalLength + Return + End If + End If + + For iElementX = 0 To iUpperElementX + For iElementY = 0 To iUpperElementY + Select Case vtype + Case VT.DBNull, VT.Empty + 'Nothing + Case VT.Byte '1 byte + obj = m_br.ReadByte() + m_position += 1 + Case VT.Short '2 bytes + obj = m_br.ReadInt16() + m_position += 2 + Case VT.Boolean '2 bytes + obj = CBool(m_br.ReadInt16()) + m_position += 2 + Case VT.Integer '4 Bytes + obj = m_br.ReadInt32() + m_position += 4 + Case VT.Long '8 Bytes + obj = m_br.ReadInt64() + m_position += 8 + Case VT.Single '4 bytes + obj = m_br.ReadSingle() + m_position += 4 + Case VT.Error '4 bytes + 'consider error case + Case VT.Double '8 bytes + obj = m_br.ReadDouble() + m_position += 8 + Case VT.Date '8 bytes + obj = System.DateTime.FromOADate(m_br.ReadDouble()) + m_position += 8 + Case VT.Decimal '8 bytes + Dim l As Long + l = m_br.ReadInt64() + m_position += 8 + obj = System.Decimal.FromOACurrency(l) + Case VT.String + If FixedStringLength >= 0 Then + obj = ReadString(FixedStringLength) + Else + obj = ReadString() + End If + Case VT.Char + obj = m_br.ReadChar() + m_position += 1 + Case VT.Variant + If SecondBound = -1 Then + obj = arr.GetValue(iElementY) + Else + obj = arr.GetValue(iElementY, iElementX) + End If + + GetObject(obj, 0, True) + Case VT.Structure + If SecondBound = -1 Then + obj = arr.GetValue(iElementY) + Else + obj = arr.GetValue(iElementY, iElementX) + End If + + GetObject(obj, 0, False) + Case Else + If (vtype And VT.Array) <> 0 Then + 'OK + Else + Throw VbMakeException(vbErrors.InvalidTypeLibVariable) + End If + + vtype = vtype Xor VT.Array + + If vtype = VT.Variant Then + Throw VbMakeException(vbErrors.TypeMismatch) + End If + + If vtype > VT.Variant AndAlso (vtype <> VT.Byte AndAlso vtype <> VT.Decimal AndAlso vtype <> VT.Char AndAlso vtype <> VT.Long) Then + Throw VbMakeException(vbErrors.InvalidTypeLibVariable) + End If + End Select + + Try + If SecondBound = -1 Then + arr.SetValue(obj, iElementY) + Else + arr.SetValue(obj, iElementY, iElementX) + End If + Catch Ex As IndexOutOfRangeException + Throw New ArgumentException(GetResourceString(ResID.Argument_ArrayDimensionsDontMatch)) + End Try + Next iElementY + Next iElementX + End Sub + + ''' ;GetByteLength + ''' + ''' This function is also used to check if a value type is supported for optimized FilePut in array case. + ''' Given a VT value, determine the byte length of that type. Return -1 if that type is not supported. + ''' + Private Function GetByteLength(ByVal vtype As VT) As Integer + Select Case vtype + Case VT.Byte '1 byte + Return 1 + Case VT.Short '2 bytes + Return 2 + Case VT.Integer '4 Bytes + Return 4 + Case VT.Long '8 Bytes + Return 8 + Case VT.Single '4 bytes + Return 4 + Case VT.Double '8 bytes + Return 8 + Case Else + Return -1 + End Select + End Function + + Private Sub PrintTab(ByVal ti As TabInfo) + If ti.Column = -1 Then + Dim CurColumn As Integer + + CurColumn = GetColumn() + CurColumn += (14 - (CurColumn Mod 14)) + SetColumn(CurColumn) + Else + Tab(ti.Column) + End If + End Sub + + + + Private Function AddSpaces(ByVal s As String) As String + Dim NegativeSign As String + + NegativeSign = Threading.Thread.CurrentThread.CurrentCulture.NumberFormat.NegativeSign + + If NegativeSign.Length = 1 Then + If s.Chars(0) = NegativeSign.Chars(0) Then + 'Append trailing space + Return s & " " + End If + ElseIf Left(s, NegativeSign.Length) = NegativeSign Then + 'Append trailing space + Return s & " " + End If + + 'Append both leading and trailing space + Return System.String.Concat(" ", s, " ") + End Function + + + Friend Sub PrintLine(ByVal ParamArray Output() As Object) + Print(Output) + WriteLine(Nothing) + End Sub + + Friend Sub Print(ByVal ParamArray Output() As Object) + Dim i As Integer + Dim s As String + Dim obj As Object + Dim typ As Type + Dim ParamCount As Integer + Dim LastTabOrSpc As Integer + + SetPrintMode() + + If (Output Is Nothing) OrElse (Output.Length = 0) Then + Exit Sub + End If + + ParamCount = Output.GetUpperBound(0) + LastTabOrSpc = -1 + + For i = 0 To ParamCount + s = Nothing + obj = Output(i) + + If obj Is Nothing Then + typ = Nothing + Else + typ = obj.GetType() + If typ.IsEnum() Then + typ = System.Enum.GetUnderlyingType(typ) + End If + End If + + If obj Is Nothing Then + 'Treat as empty + s = "" + End If + + If typ Is Nothing Then + s = "" + Else + Select Case Type.GetTypeCode(typ) + Case TypeCode.String + s = obj.ToString() + Case TypeCode.Int16 + s = AddSpaces(StringType.FromShort(ShortType.FromObject(obj))) + Case TypeCode.Int32 + s = AddSpaces(StringType.FromInteger(IntegerType.FromObject(obj))) + Case TypeCode.Int64 + s = AddSpaces(StringType.FromLong(LongType.FromObject(obj))) + Case TypeCode.Byte + s = AddSpaces(StringType.FromByte(ByteType.FromObject(obj))) + Case TypeCode.DateTime + s = StringType.FromDate(DateType.FromObject(obj)) & " " + Case TypeCode.Double + s = AddSpaces(StringType.FromDouble(DoubleType.FromObject(obj))) + Case TypeCode.Single + s = AddSpaces(StringType.FromSingle(SingleType.FromObject(obj))) + Case TypeCode.Decimal + s = AddSpaces(StringType.FromDecimal(DecimalType.FromObject(obj))) + Case TypeCode.DBNull + s = "Null" + Case TypeCode.Boolean + s = StringType.FromBoolean(BooleanType.FromObject(obj)) + Case TypeCode.Char + s = StringType.FromChar(CharType.FromObject(obj)) + Case Else + If typ Is GetType(TabInfo) Then + PrintTab(CType(obj, TabInfo)) + LastTabOrSpc = i + Continue For + ElseIf typ Is GetType(SpcInfo) Then + SPC(CType(obj, SpcInfo).Count) + LastTabOrSpc = i + Continue For + ElseIf typ Is GetType(System.Reflection.Missing) Then + s = "Error 448" + Else + Throw New ArgumentException(GetResourceString(ResID.Argument_UnsupportedIOType1, VBFriendlyName(typ))) + End If + End Select + End If + + If LastTabOrSpc <> (i - 1) Then + Dim lCurPos As Integer + lCurPos = GetColumn() + SetColumn(lCurPos + (14 - (lCurPos Mod 14))) + End If + WriteString(s) + Next i + End Sub + + + _ + Friend Sub WriteLineHelper(ByVal ParamArray Output() As Object) + InternalWriteHelper(Output) + WriteLine(Nothing) + End Sub + + + _ + Friend Sub WriteHelper(ByVal ParamArray Output() As Object) + InternalWriteHelper(Output) + WriteString(",") + End Sub + + + _ + Private Sub InternalWriteHelper(ByVal ParamArray Output() As Object) + Dim SpcInfoType As Type = GetType(SpcInfo) + Dim CurrentType As Type = SpcInfoType + Dim value As Object + Dim i As Integer + + 'Always write in invariant format for cross culture compatibility + Dim InvariantNumberFormat As NumberFormatInfo = GetInvariantCultureInfo().NumberFormat + + For i = 0 To Output.GetUpperBound(0) + value = Output(i) + + If value Is Nothing Then + WriteString("#ERROR 448#") + Else + If Not (CurrentType Is SpcInfoType) Then + WriteString(",") + End If + + CurrentType = value.GetType() + + If CurrentType Is SpcInfoType Then + SPC(CType(value, SpcInfo).Count) + ElseIf CurrentType Is GetType(TabInfo) Then + Dim ti As TabInfo = CType(value, TabInfo) + + If ti.Column >= 0 Then + PrintTab(ti) + End If + ElseIf CurrentType Is GetType(System.Reflection.Missing) Then + WriteString("#ERROR 448#") + Else + Select Case Type.GetTypeCode(CurrentType) + Case TypeCode.String + WriteString(GetQuotedString(value.ToString())) + Case TypeCode.Int16 + WriteString(StringType.FromShort(ShortType.FromObject(value))) + Case TypeCode.Int32 + WriteString(StringType.FromInteger(IntegerType.FromObject(value))) + Case TypeCode.Int64 + WriteString(StringType.FromLong(LongType.FromObject(value))) + Case TypeCode.Byte + WriteString(StringType.FromByte(ByteType.FromObject(value))) + Case TypeCode.DateTime + WriteString(FormatUniversalDate(DateType.FromObject(value))) + Case TypeCode.Double + WriteString(IOStrFromDouble(DoubleType.FromObject(value), InvariantNumberFormat)) + Case TypeCode.Single + WriteString(IOStrFromSingle(SingleType.FromObject(value), InvariantNumberFormat)) + Case TypeCode.Decimal + WriteString(IOStrFromDecimal(DecimalType.FromObject(value), InvariantNumberFormat)) + Case TypeCode.DBNull + WriteString("#NULL#") + Case TypeCode.Boolean + If BooleanType.FromObject(value) Then + WriteString("#TRUE#") + Else + WriteString("#FALSE#") + End If + Case TypeCode.Char + WriteString(StringType.FromChar(CharType.FromObject(value))) + Case Else + ' consider support for UDT + If TypeOf value Is Char() AndAlso CType(value, Array).Rank = 1 Then + WriteString(CStr(CharArrayType.FromObject(value))) + Else + Throw VbMakeException(vbErrors.IllegalFuncCall) + End If + End Select + End If + End If + Next + End Sub + + + + Private Function IOStrFromSingle(ByVal Value As Single, ByVal NumberFormat As NumberFormatInfo) As String + Return Value.ToString(Nothing, NumberFormat) + End Function + + + + + Private Function IOStrFromDouble(ByVal Value As Double, ByVal NumberFormat As NumberFormatInfo) As String + Return Value.ToString(Nothing, NumberFormat) + End Function + + + + + Private Function IOStrFromDecimal(ByVal Value As Decimal, ByVal NumberFormat As NumberFormatInfo) As String + Return Value.ToString("G29", NumberFormat) + End Function + + + _ + Friend Function FormatUniversalDate(ByVal dt As Date) As String + Dim bHasDate As Boolean +#If False Then + Dim sb As StringBuilder +#End If + Dim sFormat As String + + 'sb = New StringBuilder("#", 24) + + sFormat = sTimeFormat + + ' only insert date If not at the "start of time" (1/1/0) + + If (dt.Year <> 0 OrElse dt.Month <> 1 OrElse dt.Day <> 1) Then +#If False Then + ' output year, month, day as "yyyy-mm-dd" + sb.Append(Right(CStr(dt.Year + 10000), 4)) + sb.Append("-") + sb.Append(Right(CStr(dt.Month + 100), 2)) + sb.Append("-") + sb.Append(Right(CStr(dt.Day + 100), 2)) +#End If + bHasDate = True + sFormat = sDateFormat + End If + + ' only insert time If not midnight (00:00:00) + If ((dt.Hour + dt.Minute + dt.Second) <> 0) Then + ' insert space separator If date was output + If bHasDate Then + sFormat = sDateTimeFormat + End If +#If False Then + If bHasDate Then + sb.Append(" ") + End If + ' output hour, minute, and second as "hh:mm:ss" + + sb.Append(Right(CStr(dt.Hour + 100), 2)) + sb.Append(":") + sb.Append(Right(CStr(dt.Minute + 100), 2)) + sb.Append(":") + sb.Append(Right(CStr(dt.Second + 100), 2)) +#End If + End If + + Return dt.ToString(sFormat, m_WriteDateFormatInfo) + + ' sb.Append("#") + ' FormatUniversalDate = sb.ToString() + End Function + + + + Protected Function GetQuotedString(ByVal Value As String) As String + 'Wrap Value with quotes, but make sure to escape quotes contained in Value. + Return """" & Value.Replace("""", """""") & """" + End Function + + + + Protected Sub ValidateRec(ByVal RecordNumber As Long) + If RecordNumber < 1 Then + Throw VbMakeException(vbErrors.BadRecordNum) + End If + End Sub + + + + Friend Overridable Sub GetObject(ByRef Value As Object, Optional ByVal RecordNumber As Long = 0, Optional ByVal ContainedInVariant As Boolean = True) + Throw VbMakeException(vbErrors.BadFileMode) + End Sub + + + + Friend Overridable Overloads Sub [Get](ByRef Value As ValueType, Optional ByVal RecordNumber As Long = 0) + Throw VbMakeException(vbErrors.BadFileMode) + End Sub + + + + Friend Overridable Overloads Sub [Get](ByRef Value As System.Array, Optional ByVal RecordNumber As Long = 0, _ + Optional ByVal ArrayIsDynamic As Boolean = False, Optional ByVal StringIsFixedLength As Boolean = False) + + Throw VbMakeException(vbErrors.BadFileMode) + End Sub + + + + Friend Overridable Overloads Sub [Get](ByRef Value As Boolean, Optional ByVal RecordNumber As Long = 0) + Throw VbMakeException(vbErrors.BadFileMode) + End Sub + + + + Friend Overridable Overloads Sub [Get](ByRef Value As Byte, Optional ByVal RecordNumber As Long = 0) + Throw VbMakeException(vbErrors.BadFileMode) + End Sub + + + + Friend Overridable Overloads Sub [Get](ByRef Value As Short, Optional ByVal RecordNumber As Long = 0) + Throw VbMakeException(vbErrors.BadFileMode) + End Sub + + + + Friend Overridable Overloads Sub [Get](ByRef Value As Integer, Optional ByVal RecordNumber As Long = 0) + Throw VbMakeException(vbErrors.BadFileMode) + End Sub + + + + Friend Overridable Overloads Sub [Get](ByRef Value As Long, Optional ByVal RecordNumber As Long = 0) + Throw VbMakeException(vbErrors.BadFileMode) + End Sub + + + + Friend Overridable Overloads Sub [Get](ByRef Value As Char, Optional ByVal RecordNumber As Long = 0) + Throw VbMakeException(vbErrors.BadFileMode) + End Sub + + + + Friend Overridable Overloads Sub [Get](ByRef Value As Single, Optional ByVal RecordNumber As Long = 0) + Throw VbMakeException(vbErrors.BadFileMode) + End Sub + + + + Friend Overridable Overloads Sub [Get](ByRef Value As Double, Optional ByVal RecordNumber As Long = 0) + Throw VbMakeException(vbErrors.BadFileMode) + End Sub + + + + Friend Overridable Overloads Sub [Get](ByRef Value As Decimal, Optional ByVal RecordNumber As Long = 0) + Throw VbMakeException(vbErrors.BadFileMode) + End Sub + + + + Friend Overridable Overloads Sub [Get](ByRef Value As String, Optional ByVal RecordNumber As Long = 0, Optional ByVal StringIsFixedLength As Boolean = False) + Throw VbMakeException(vbErrors.BadFileMode) + End Sub + + + + Friend Overridable Overloads Sub [Get](ByRef Value As Date, Optional ByVal RecordNumber As Long = 0) + Throw VbMakeException(vbErrors.BadFileMode) + End Sub + + + + Friend Overridable Sub PutObject(ByVal Value As Object, Optional ByVal RecordNumber As Long = 0, Optional ByVal ContainedInVariant As Boolean = True) + Throw VbMakeException(vbErrors.BadFileMode) + End Sub + + + + Friend Overridable Overloads Sub Put(ByVal Value As Object, Optional ByVal RecordNumber As Long = 0) + Throw VbMakeException(vbErrors.BadFileMode) + End Sub + + + + Friend Overridable Overloads Sub Put(ByVal Value As ValueType, Optional ByVal RecordNumber As Long = 0) + Throw VbMakeException(vbErrors.BadFileMode) + End Sub + + + + Friend Overridable Overloads Sub Put(ByVal Value As System.Array, Optional ByVal RecordNumber As Long = 0, _ + Optional ByVal ArrayIsDynamic As Boolean = False, Optional ByVal StringIsFixedLength As Boolean = False) + + Throw VbMakeException(vbErrors.BadFileMode) + End Sub + + + + Friend Overridable Overloads Sub Put(ByVal Value As Boolean, Optional ByVal RecordNumber As Long = 0) + Throw VbMakeException(vbErrors.BadFileMode) + End Sub + + + + Friend Overridable Overloads Sub Put(ByVal Value As Byte, Optional ByVal RecordNumber As Long = 0) + Throw VbMakeException(vbErrors.BadFileMode) + End Sub + + + + Friend Overridable Overloads Sub Put(ByVal Value As Short, Optional ByVal RecordNumber As Long = 0) + Throw VbMakeException(vbErrors.BadFileMode) + End Sub + + + + Friend Overridable Overloads Sub Put(ByVal Value As Integer, Optional ByVal RecordNumber As Long = 0) + Throw VbMakeException(vbErrors.BadFileMode) + End Sub + + + + Friend Overridable Overloads Sub Put(ByVal Value As Long, Optional ByVal RecordNumber As Long = 0) + Throw VbMakeException(vbErrors.BadFileMode) + End Sub + + + + Friend Overridable Overloads Sub Put(ByVal Value As Char, Optional ByVal RecordNumber As Long = 0) + Throw VbMakeException(vbErrors.BadFileMode) + End Sub + + + + Friend Overridable Overloads Sub Put(ByVal Value As Single, Optional ByVal RecordNumber As Long = 0) + Throw VbMakeException(vbErrors.BadFileMode) + End Sub + + + + Friend Overridable Overloads Sub Put(ByVal Value As Double, Optional ByVal RecordNumber As Long = 0) + Throw VbMakeException(vbErrors.BadFileMode) + End Sub + + + + Friend Overridable Overloads Sub Put(ByVal Value As Decimal, Optional ByVal RecordNumber As Long = 0) + Throw VbMakeException(vbErrors.BadFileMode) + End Sub + + + + Friend Overridable Overloads Sub Put(ByVal Value As String, Optional ByVal RecordNumber As Long = 0, Optional ByVal StringIsFixedLength As Boolean = False) + Throw VbMakeException(vbErrors.BadFileMode) + End Sub + + + + Friend Overridable Overloads Sub Put(ByVal Value As Date, Optional ByVal RecordNumber As Long = 0) + Throw VbMakeException(vbErrors.BadFileMode) + End Sub + + + + '====================================== + ' Input + '====================================== + _ + Friend Overridable Overloads Sub Input(ByRef obj As Object) + Throw VbMakeException(vbErrors.BadFileMode) + End Sub + + + + Friend Overridable Overloads Sub Input(ByRef Value As Boolean) + Throw VbMakeException(vbErrors.BadFileMode) + End Sub + + + + Friend Overridable Overloads Sub Input(ByRef Value As Byte) + Throw VbMakeException(vbErrors.BadFileMode) + End Sub + + + + Friend Overridable Overloads Sub Input(ByRef Value As Short) + Throw VbMakeException(vbErrors.BadFileMode) + End Sub + + + + Friend Overridable Overloads Sub Input(ByRef Value As Integer) + Throw VbMakeException(vbErrors.BadFileMode) + End Sub + + + + Friend Overridable Overloads Sub Input(ByRef Value As Long) + Throw VbMakeException(vbErrors.BadFileMode) + End Sub + + + + Friend Overridable Overloads Sub Input(ByRef Value As Char) + Throw VbMakeException(vbErrors.BadFileMode) + End Sub + + + + Friend Overridable Overloads Sub Input(ByRef Value As Single) + Throw VbMakeException(vbErrors.BadFileMode) + End Sub + + + + Friend Overridable Overloads Sub Input(ByRef Value As Double) + Throw VbMakeException(vbErrors.BadFileMode) + End Sub + + + + Friend Overridable Overloads Sub Input(ByRef Value As Decimal) + Throw VbMakeException(vbErrors.BadFileMode) + End Sub + + + + Friend Overridable Overloads Sub Input(ByRef Value As String) + Throw VbMakeException(vbErrors.BadFileMode) + End Sub + + + + Friend Overridable Overloads Sub Input(ByRef Value As Date) + Throw VbMakeException(vbErrors.BadFileMode) + End Sub + + + + Protected Function SkipWhiteSpace() As Integer + Dim lChar As Integer = m_sr.Peek() + + If CheckEOF(lChar) Then + m_eof = True + GoTo SkipWhiteSpaceExit + End If + + Do While (IntlIsSpace(lChar) OrElse (lChar = lchTab)) + m_sr.Read() + m_position += 1 + lChar = m_sr.Peek() + + If CheckEOF(lChar) Then + m_eof = True + Exit Do + End If + Loop + +SkipWhiteSpaceExit: + Return lChar + End Function + + + + Private Function GetFileInTerm(ByVal iTermType As Short) As String + Select Case iTermType + Case FIN_NUMTERMCHAR + GetFileInTerm = " ," & ControlChars.Tab & ControlChars.Cr + Case FIN_LINEINP + GetFileInTerm = ControlChars.Cr + Case FIN_QSTRING + GetFileInTerm = chDblQuote + Case FIN_STRING + GetFileInTerm = "," & ControlChars.Cr + Case FIN_NUMBER + GetFileInTerm = " ," & ControlChars.Tab & ControlChars.Cr + Case Else + Throw VbMakeException(vbErrors.IllegalFuncCall) + End Select + End Function + + + + Protected Function IntlIsSpace(ByVal lch As Integer) As Boolean + ' consider testing for intl spaces + Return (lch = lchSpace) Or (lch = lchIntlSpace) + End Function + + + + Protected Function IntlIsDoubleQuote(ByVal lch As Integer) As Boolean + ' consider testing for intl double quotes + Return (lch = lchDoubleQuote) + End Function + + + + Protected Function IntlIsComma(ByVal lch As Integer) As Boolean + ' consider testing for intl commas + Return (lch = lchComma) + End Function + + + + Protected Function SkipWhiteSpaceEOF() As Integer + Dim retValue As Integer = SkipWhiteSpace() + + If CheckEOF(retValue) Then + Throw VbMakeException(vbErrors.EndOfFile) + End If + Return retValue + End Function + + + + Protected Sub SkipTrailingWhiteSpace() + Dim lChar As Integer + + ' get the field termination character + lChar = m_sr.Peek() + If CheckEOF(lChar) Then + m_eof = True + Exit Sub + End If + + ' If field was teminated by space/tab (numeric) or quote + ' quoted-string, scan ahead over any further spaces/tabs + If (IntlIsSpace(lChar) OrElse IntlIsDoubleQuote(lChar) OrElse lChar = lchTab) Then + lChar = m_sr.Read() 'Remove it + m_position += 1 + + 'Remove any remaining whitespace + lChar = m_sr.Peek() + If CheckEOF(lChar) Then + m_eof = True + Exit Sub + End If + + Do While (IntlIsSpace(lChar) OrElse (lChar = lchTab)) + m_sr.Read() 'Remove it + m_position += 1 + lChar = m_sr.Peek() 'Look at next char + + If CheckEOF(lChar) Then + m_eof = True + Exit Sub + End If + Loop + End If + + ' If a carriage-return terminates the field, scan over + ' a following line-feed If there + If (lChar = lchCR) Then + lChar = m_sr.Read() + m_position += 1 + + If CheckEOF(lChar) Then + m_eof = True + Exit Sub + End If + + If (m_sr.Peek() = lchLF) Then + lChar = m_sr.Read() + m_position += 1 + End If + ElseIf IntlIsComma(lChar) Then + ' Go past the comma + lChar = m_sr.Read() + m_position += 1 + End If + + lChar = m_sr.Peek() + If CheckEOF(lChar) Then + m_eof = True + Exit Sub + End If + End Sub + + + + Protected Function ReadInField(ByVal iTermType As Short) As String + Dim sTermChars As String + Dim lChar As Integer + Dim sb As StringBuilder + + sb = New StringBuilder + sTermChars = GetFileInTerm(iTermType) + + ' Peek at the first character + lChar = m_sr.Peek() + If CheckEOF(lChar) Then + m_eof = True + Else + Do While (sTermChars.IndexOf(ChrW(lChar)) = -1) + lChar = m_sr.Read() + m_position += 1 + + If lChar <> 0 Then + sb.Append(ChrW(lChar)) + End If + + lChar = m_sr.Peek() + + If CheckEOF(lChar) Then + m_eof = True + Exit Do + End If + Loop + End If + + ' if no error, finish up + ' if reading a string, or field string exists, + ' append buffer to string. + ' if the string is not quoted, and we are not + ' in line-input mode, then RTrim the string. + If (iTermType = FIN_STRING OrElse iTermType = FIN_NUMBER) Then + ReadInField = RTrim(sb.ToString()) + Else + ReadInField = sb.ToString() + End If + End Function + + Protected Function CheckEOF(ByVal lChar As Integer) As Boolean + Return (lChar = EOF_INDICATOR OrElse lChar = EOF_CHAR) + End Function + + + ': The deal here is that this function was moved from vb6randomfile.vb to fix VSWhidbey 32408. The problem is, it introduced a breaking change. The bug this fix + 'originally addressed was that we'd get a NullReference exception when you did a read of any kind on a file that was write-only. So Huy made a better error here so the user knows + 'what is going on. But that is a breaking change from Everett behavior. So I'm taking his function, throwing the original NullReference exception, but putting Huy's better + 'exception in there as the inner-exception. Since we have to go back to Everett behavior, at least having the inner exception helps a little. + ': ValidateWriteable is not used anywhere - FxCop violation. + 'Private Sub ValidateWriteable() + ' If (m_access <> OpenAccess.ReadWrite) AndAlso (m_access <> OpenAccess.Write) Then + ' Dim JustNeedTheMessage As New NullReferenceException 'Hack. I don't have access to the localized resources for this string, and I can't skip providing it if I want to supply the inner exception, so I'll get the string this way. + ' Throw New NullReferenceException(JustNeedTheMessage.Message, New IO.IOException(GetResourceString(ResID.FileOpenedNoWrite))) + ' End If + 'End Sub + + ': The deal here is that this function was moved from vb6randomfile.vb to fix VSWhidbey 32408. The problem is, it introduced a breaking change. The bug this fix + 'originally addressed was that we'd get a NullReference exception when you did a read of any kind on a file that was write-only. So Huy made a better error here so the user knows + 'what is going on. But that is a breaking change from Everett behavior. So I'm taking his function, throwing the original NullReference exception, but putting Huy's better + 'exception in there as the inner-exception. Since we have to go back to Everett behavior, at least having the inner exception helps a little. + Private Sub ValidateReadable() + If (m_access <> OpenAccess.ReadWrite) AndAlso (m_access <> OpenAccess.Read) Then + Dim JustNeedTheMessage As New NullReferenceException 'Hack. I don't have access to the localized resources for this string, and I can't skip providing it if I want to supply the inner exception, so I'll get the string this way. + Throw New NullReferenceException(JustNeedTheMessage.Message, New IO.IOException(GetResourceString(ResID.FileOpenedNoRead))) + End If + End Sub + + + End Class + +#End Region + +End Namespace diff --git a/Microsoft.VisualBasic/runtime/msvbalib/Helpers/VB6InputFile.vb b/Microsoft.VisualBasic/runtime/msvbalib/Helpers/VB6InputFile.vb new file mode 100644 index 000000000..32a2856eb --- /dev/null +++ b/Microsoft.VisualBasic/runtime/msvbalib/Helpers/VB6InputFile.vb @@ -0,0 +1,262 @@ +' Copyright (c) Microsoft Corporation. All rights reserved. + +Imports System +Imports System.Security +Imports System.Globalization +Imports System.IO +Imports System.Text + +Imports Microsoft.VisualBasic.CompilerServices.ExceptionUtils +Imports Microsoft.VisualBasic.CompilerServices.Utils + +Namespace Microsoft.VisualBasic.CompilerServices + +#Region " BACKWARDS COMPATIBILITY " + + 'WARNING WARNING WARNING WARNING WARNING + 'This code exists to support Everett compiled applications. Make sure you understand + 'the backwards compatibility ramifications of any edit you make in this region. + 'WARNING WARNING WARNING WARNING WARNING + + _ + Friend Class VB6InputFile + + '============================================================================ + ' Declarations + '============================================================================ + + Inherits VB6File + + '============================================================================ + ' Constructor + '============================================================================ + Public Sub New(ByVal FileName As String, ByVal share As OpenShare) + MyBase.New(FileName, OpenAccess.Read, share, -1) + End Sub + + + + '============================================================================ + ' Operations + '============================================================================ + Friend Overrides Sub OpenFile() + Try + m_file = New FileStream(m_sFullPath, FileMode.Open, CType(m_access, FileAccess), CType(m_share, FileShare)) + Catch ex As FileNotFoundException + Throw VbMakeException(ex, vbErrors.FileNotFound) + Catch ex As SecurityException + Throw VbMakeException(vbErrors.FileNotFound) + Catch ex As DirectoryNotFoundException + Throw VbMakeException(ex, vbErrors.PathNotFound) + Catch ex As IOException + Throw VbMakeException(ex, vbErrors.PathFileAccess) + Catch ex As StackOverflowException + Throw ex + Catch ex As OutOfMemoryException + Throw ex + Catch ex As System.Threading.ThreadAbortException + Throw ex + Catch ex As Exception + Throw VbMakeException(ex, vbErrors.PathNotFound) + End Try + + m_Encoding = GetFileIOEncoding() + m_sr = New StreamReader(m_file, m_Encoding, False, 128) + m_eof = (m_file.Length = 0) 'Don't do a Peek here or it will buffer data, causing side-effects with the Lock function. + End Sub + + + + Public Function ReadLine() As String + Dim s As String + s = m_sr.ReadLine() + Diagnostics.Debug.Assert(Not m_Encoding Is Nothing) + m_position += m_Encoding.GetByteCount(s) + 2 + 'It appears that no one is calling this function. It has returned nothing + ' since it was created, so keep it that way for compatibility reasons. + Return Nothing + End Function + + + + Friend Overrides Function CanInput() As Boolean + Return True + End Function + + + + Friend Overrides Function EOF() As Boolean + Return m_eof + End Function + + + + Public Overrides Function GetMode() As OpenMode + Return OpenMode.Input + End Function + + + + Friend Function ParseInputString(ByRef sInput As String) As Object + ParseInputString = sInput + + ' variant must have last character as a pound sign + ' that is different than the first + If sInput.Chars(0) = CChar("#") AndAlso sInput.Length <> 1 Then + ' isolate the string between the pound signs + sInput = sInput.Substring(1, sInput.Length - 2) + + ' test for fixed string values first + ' VT_EMPTY is not converted + If sInput = "NULL" Then + ParseInputString = DBNull.Value + ElseIf sInput = "TRUE" Then + ParseInputString = CObj(True) + ElseIf sInput = "FALSE" Then + ParseInputString = CObj(False) + ElseIf Left(sInput, 6) = "ERROR " Then + ' parse I4 value after "ERROR " string + Dim errValue As Integer + + If sInput.Length > 6 Then + errValue = IntegerType.FromString(Mid(sInput, 7)) + End If + + ' error value is assigned to the input string for now + ParseInputString = errValue + + ' test for date variant. Note, input always uses the + ' universal date format; so use english LCID (0x40) for + ' coercion. + ' CALENDAR_SUPPORT + Else + Try + ParseInputString = System.DateTime.Parse(ToHalfwidthNumbers(sInput, GetCultureInfo())) + Catch ex As StackOverflowException + Throw ex + Catch ex As OutOfMemoryException + Throw ex + Catch ex As System.Threading.ThreadAbortException + Throw ex + Catch e As Exception + End Try + End If + End If + End Function + + + + '====================================== + ' Input + '====================================== + _ + Friend Overloads Overrides Sub Input(ByRef obj As Object) + Dim lChar As Integer + Dim sField As String + + lChar = SkipWhiteSpaceEOF() 'Skip over leading whitespace + + If lChar = lchDoubleQuote Then + lChar = m_sr.Read() + m_position += 1 + + obj = ReadInField(FIN_QSTRING) + SkipTrailingWhiteSpace() + ElseIf lChar = lchPound Then + obj = ParseInputString(InputStr()) + Else + sField = ReadInField(FIN_NUMBER) + obj = ParseInputField(sField, VariantType.Empty) + SkipTrailingWhiteSpace() + End If + End Sub + + + + Friend Overloads Overrides Sub Input(ByRef Value As Boolean) + Value = BooleanType.FromObject(ParseInputString(InputStr())) + End Sub + + + + Friend Overloads Overrides Sub Input(ByRef Value As Byte) + Value = ByteType.FromObject(InputNum(VariantType.Byte)) + End Sub + + + + Friend Overloads Overrides Sub Input(ByRef Value As Short) + Value = ShortType.FromObject(InputNum(VariantType.Short)) + End Sub + + + + Friend Overloads Overrides Sub Input(ByRef Value As Integer) + Value = IntegerType.FromObject(InputNum(VariantType.Integer)) + End Sub + + + + Friend Overloads Overrides Sub Input(ByRef Value As Long) + Value = LongType.FromObject(InputNum(VariantType.Long)) + End Sub + + + + Friend Overloads Overrides Sub Input(ByRef Value As Char) + Dim s As String = InputStr() + + If s.Length > 0 Then + Value = s.Chars(0) + Else + Value = ControlChars.NullChar + End If + End Sub + + + + Friend Overloads Overrides Sub Input(ByRef Value As Single) + Value = SingleType.FromObject(InputNum(VariantType.Single), GetInvariantCultureInfo().NumberFormat) + End Sub + + + + Friend Overloads Overrides Sub Input(ByRef Value As Double) + Value = DoubleType.FromObject(InputNum(VariantType.Double), GetInvariantCultureInfo().NumberFormat) + End Sub + + + + Friend Overloads Overrides Sub Input(ByRef Value As Decimal) + Value = DecimalType.FromObject(InputNum(VariantType.Decimal), GetInvariantCultureInfo().NumberFormat) + End Sub + + + + Friend Overloads Overrides Sub Input(ByRef Value As String) + Value = InputStr() + End Sub + + + + Friend Overloads Overrides Sub Input(ByRef Value As Date) + Value = DateType.FromObject(ParseInputString(InputStr())) + End Sub + + + + Friend Overrides Function LOC() As Long + 'This calculation depends on the buffersize of the FileStream + 'object, any changes in the urt classes could mess this up + ' The FileStream is used by the StreamReader, which reads ahead + ' into the 128 byte buffer specified when the StreamReader was created + ' The m_file.Position is where the reader has read to, not the vb user + 'm_position tracks where the vb user has read to. + Return ((m_position + 127) \ 128) + End Function + + End Class + +#End Region + +End Namespace diff --git a/Microsoft.VisualBasic/runtime/msvbalib/Helpers/VB6OutputFile.vb b/Microsoft.VisualBasic/runtime/msvbalib/Helpers/VB6OutputFile.vb new file mode 100644 index 000000000..95e21da16 --- /dev/null +++ b/Microsoft.VisualBasic/runtime/msvbalib/Helpers/VB6OutputFile.vb @@ -0,0 +1,168 @@ +' Copyright (c) Microsoft Corporation. All rights reserved. + + +Imports System +Imports System.Text +Imports System.Globalization +Imports System.IO +Imports System.Security + +Imports Microsoft.VisualBasic.CompilerServices.ExceptionUtils +Imports Microsoft.VisualBasic.CompilerServices.Utils + +Namespace Microsoft.VisualBasic.CompilerServices + +#Region " BACKWARDS COMPATIBILITY " + + 'WARNING WARNING WARNING WARNING WARNING + 'This code exists to support Everett compiled applications. Make sure you understand + 'the backwards compatibility ramifications of any edit you make in this region. + 'WARNING WARNING WARNING WARNING WARNING + + _ + Friend Class VB6OutputFile + + '============================================================================ + ' Declarations + '============================================================================ + Inherits VB6File + + + + '============================================================================ + ' Constructor + '============================================================================ + Friend Sub New() + MyBase.New() + End Sub + + + + Friend Sub New(ByVal FileName As String, ByVal share As OpenShare, ByVal fAppend As Boolean) + MyBase.New(FileName, OpenAccess.Write, share, -1) + m_fAppend = fAppend + End Sub + + + + '============================================================================ + ' Operations + '============================================================================ + Friend Overrides Sub OpenFile() + 'MyBase.OpenFile() + + Try + If m_fAppend Then + 'consider checking WRITE if cannot open READWRITE + 'Note: COM+BUG - OpenOrCreate is working like create, so we need to make a temporary workaround + If File.Exists(m_sFullPath) Then + m_file = New FileStream(m_sFullPath, FileMode.Open, CType(m_access, FileAccess), CType(m_share, FileShare)) + Else + m_file = New FileStream(m_sFullPath, FileMode.Create, CType(m_access, FileAccess), CType(m_share, FileShare)) + End If + Else + m_file = New FileStream(m_sFullPath, FileMode.Create, CType(m_access, FileAccess), CType(m_share, FileShare)) + End If + Catch ex As FileNotFoundException + Throw VbMakeException(ex, vbErrors.FileNotFound) + Catch ex As SecurityException + Throw VbMakeException(ex, vbErrors.FileNotFound) + Catch ex As DirectoryNotFoundException + Throw VbMakeException(ex, vbErrors.PathNotFound) + Catch ex As IOException + Throw VbMakeException(ex, vbErrors.PathFileAccess) + End Try + + m_Encoding = GetFileIOEncoding() + m_sw = New StreamWriter(m_file, m_Encoding) + m_sw.AutoFlush = True + + If m_fAppend Then + 'Now position at end of file + Dim lEndOfFile As Long + lEndOfFile = m_file.Length + m_file.Position = lEndOfFile + m_position = lEndOfFile + End If + End Sub + + + + Friend Overrides Sub WriteLine(ByVal s As String) + If s Is Nothing Then + m_sw.WriteLine() + m_position += 2 + Else + If m_bPrint AndAlso (m_lWidth <> 0) Then + If m_lCurrentColumn >= m_lWidth Then + m_sw.WriteLine() + m_position += 2 + End If + End If + + m_sw.WriteLine(s) + Diagnostics.Debug.Assert(Not m_Encoding Is Nothing) + m_position += m_Encoding.GetByteCount(s) + 2 + End If + + m_lCurrentColumn = 0 + End Sub + + + + Friend Overrides Sub WriteString(ByVal s As String) + If (s Is Nothing) OrElse (s.Length = 0) Then + Exit Sub + End If + + If m_bPrint AndAlso (m_lWidth <> 0) Then + If (m_lCurrentColumn >= m_lWidth) OrElse _ + (m_lCurrentColumn <> 0 AndAlso (m_lCurrentColumn + s.Length) > m_lWidth) Then + m_sw.WriteLine() + m_position += 2 + m_lCurrentColumn = 0 + End If + End If + + m_sw.Write(s) + Diagnostics.Debug.Assert(Not m_Encoding Is Nothing) + Dim ByteLength As Integer = m_Encoding.GetByteCount(s) + m_position += ByteLength + m_lCurrentColumn += s.Length + End Sub + + + + Friend Overrides Function CanWrite() As Boolean + CanWrite = True + End Function + + + + Public Overrides Function GetMode() As OpenMode + If m_fAppend Then + GetMode = OpenMode.Append + Else + GetMode = OpenMode.Output + End If + End Function + + + + Friend Overrides Function EOF() As Boolean + EOF = True + End Function + + + + Friend Overrides Function LOC() As Long + Return ((m_position + 127) \ 128) + End Function + + + + End Class + +#End Region + +End Namespace diff --git a/Microsoft.VisualBasic/runtime/msvbalib/Helpers/VB6RandomFile.vb b/Microsoft.VisualBasic/runtime/msvbalib/Helpers/VB6RandomFile.vb new file mode 100644 index 000000000..c5af5a4aa --- /dev/null +++ b/Microsoft.VisualBasic/runtime/msvbalib/Helpers/VB6RandomFile.vb @@ -0,0 +1,702 @@ +' Copyright (c) Microsoft Corporation. All rights reserved. + +Imports System +Imports System.Text +Imports System.IO +Imports System.Globalization + +Imports Microsoft.VisualBasic.CompilerServices.ExceptionUtils +Imports Microsoft.VisualBasic.CompilerServices.Utils + +Namespace Microsoft.VisualBasic.CompilerServices + +#Region " BACKWARDS COMPATIBILITY " + + 'WARNING WARNING WARNING WARNING WARNING + 'This code exists to support Everett compiled applications. Make sure you understand + 'the backwards compatibility ramifications of any edit you make in this region. + 'WARNING WARNING WARNING WARNING WARNING + + _ + Friend Class VB6RandomFile + + '============================================================================ + ' Declarations + '============================================================================ + + Inherits VB6File + + '============================================================================ + ' Constructor + '============================================================================ + Public Sub New(ByVal FileName As String, ByVal access As OpenAccess, ByVal share As OpenShare, ByVal lRecordLen As Integer) + MyBase.New(FileName, access, share, lRecordLen) + End Sub + + + '============================================================================ + ' Operations + '============================================================================ + Private Sub OpenFileHelper(ByVal fm As FileMode, ByVal fa As OpenAccess) + Try + m_file = New FileStream(m_sFullPath, fm, CType(fa, FileAccess), CType(m_share, FileShare)) + Catch ex As FileNotFoundException + Throw VbMakeException(ex, vbErrors.FileNotFound) + Catch ex As DirectoryNotFoundException + Throw VbMakeException(ex, vbErrors.PathNotFound) + Catch ex As Security.SecurityException + Throw VbMakeException(ex, vbErrors.FileNotFound) + Catch ex As IOException + Throw VbMakeException(ex, vbErrors.PathFileAccess) + Catch ex As UnauthorizedAccessException + Throw VbMakeException(ex, vbErrors.PathFileAccess) + Catch ex As ArgumentException 'Invalid combination of FileMode and OpenAccess + Throw VbMakeException(ex, vbErrors.PathFileAccess) + Catch ex As StackOverflowException + Throw ex + Catch ex As OutOfMemoryException + Throw ex + Catch ex As System.Threading.ThreadAbortException + Throw ex + Catch ex As Exception + 'UNDONE : what error? + Throw VbMakeException(vbErrors.InternalError) + End Try + End Sub + + + + Friend Overrides Sub OpenFile() + Dim fm As FileMode + Dim stm As Stream + + 'Attempt the following + If File.Exists(m_sFullPath) Then + fm = FileMode.Open + ElseIf m_access = OpenAccess.Read Then + fm = FileMode.OpenOrCreate + Else + fm = FileMode.Create + End If + + If m_access = OpenAccess.Default Then + 'Must try ReadWrite/Write then Read + m_access = OpenAccess.ReadWrite + + Try + OpenFileHelper(fm, m_access) + Catch ex As StackOverflowException + Throw ex + Catch ex As OutOfMemoryException + Throw ex + Catch ex As System.Threading.ThreadAbortException + Throw ex + Catch + 'Try Write access + m_access = OpenAccess.Write + Try + OpenFileHelper(fm, m_access) + Catch ex As StackOverflowException + Throw ex + Catch ex As OutOfMemoryException + Throw ex + Catch ex As System.Threading.ThreadAbortException + Throw ex + Catch + 'If that failed, try read access + m_access = OpenAccess.Read + OpenFileHelper(fm, m_access) + End Try + End Try + Else + OpenFileHelper(fm, m_access) + End If + + m_Encoding = GetFileIOEncoding() + stm = m_file + + If (m_access = OpenAccess.Write) OrElse (m_access = OpenAccess.ReadWrite) Then + m_sw = New StreamWriter(stm, m_Encoding) + m_sw.AutoFlush = True + m_bw = New BinaryWriter(stm, m_Encoding) + End If + + If (m_access = OpenAccess.Read) OrElse (m_access = OpenAccess.ReadWrite) Then + m_br = New BinaryReader(stm, m_Encoding) + + If GetMode() = OpenMode.Binary Then + ' pass false to prevent detection of encoding marks + m_sr = New StreamReader(stm, m_Encoding, False, 128) + End If + End If + End Sub + + + + Friend Overrides Sub CloseFile() + If Not m_sw Is Nothing Then + m_sw.Flush() + End If + CloseTheFile() + End Sub + + + + Friend Overloads Overrides Sub Lock(ByVal lStart As Long, ByVal lEnd As Long) + If lStart > lEnd Then + Throw New ArgumentException(GetResourceString(ResID.Argument_InvalidValue1, "Start")) + End If + + Dim lStartByte As Long + Dim lLength As Long + + lStartByte = (lStart - 1) * m_lRecordLen + lLength = (lEnd - lStart + 1) * m_lRecordLen + + m_file.Lock(lStartByte, lLength) + End Sub + + + + Friend Overloads Overrides Sub Unlock(ByVal lStart As Long, ByVal lEnd As Long) + If lStart > lEnd Then + Throw New ArgumentException(GetResourceString(ResID.Argument_InvalidValue1, "Start")) + End If + + Dim lStartByte As Long + Dim lLength As Long + + lStartByte = (lStart - 1) * m_lRecordLen + lLength = (lEnd - lStart + 1) * m_lRecordLen + m_file.Unlock(lStartByte, lLength) + End Sub + + + + Public Overrides Function GetMode() As OpenMode + GetMode = OpenMode.Random + End Function + + + + Friend Overrides Function GetStreamReader() As StreamReader + GetStreamReader = New StreamReader(m_file, m_Encoding) + End Function + + + + Friend Overrides Function EOF() As Boolean + 'COM+INTEG REVIEW: this may not be replacement equivalent + 'If Not m_file.DataAvailable Then ' consider what is this supposed to be replaced with + m_eof = (m_position >= m_file.Length) + Return m_eof + End Function + + + + Friend Overrides Function LOC() As Long + If m_lRecordLen = 0 Then + Throw VbMakeException(vbErrors.InternalError) + Else + Dim pos As Long + pos = m_position + Return (pos + m_lRecordLen - 1) \ m_lRecordLen + End If + End Function + + + + Friend Overloads Overrides Sub Seek(ByVal Position As Long) + SetRecord(Position) + End Sub + + + + Friend Overloads Overrides Function Seek() As Long + Return (LOC() + 1) + End Function + + + + '====================================== + ' Get + '====================================== + Friend Overrides Sub GetObject(ByRef Value As Object, Optional ByVal RecordNumber As Long = 0, _ + Optional ByVal ContainedInVariant As Boolean = True) + + Dim typ As System.Type = Nothing + Dim vtype As VT + + ValidateReadable() + SetRecord(RecordNumber) + + If ContainedInVariant Then + vtype = CType(m_br.ReadInt16(), VT) + m_position += 2 + Else + typ = Value.GetType + + Select Case Type.GetTypeCode(typ) + Case TypeCode.String + vtype = VT.String + Case TypeCode.Int16 + vtype = VT.Short + Case TypeCode.Int32 + vtype = VT.Integer + Case TypeCode.Int64 + vtype = VT.Long + Case TypeCode.Byte + vtype = VT.Byte + Case TypeCode.DateTime + vtype = VT.Date + Case TypeCode.Double + vtype = VT.Double + Case TypeCode.Single + vtype = VT.Single + Case TypeCode.Decimal + vtype = VT.Decimal + Case TypeCode.Boolean + vtype = VT.Boolean + Case TypeCode.Char + vtype = VT.Char + Case TypeCode.Object + If typ.IsValueType Then + vtype = VT.Structure + Else + vtype = VT.Variant 'To force an exception later + End If + Case Else + vtype = VT.Variant 'To force an exception later + End Select + End If + + If (vtype And VT.Array) <> 0 Then + Dim arr As System.Array = Nothing + Dim v As VT = vtype Xor VT.Array + GetDynamicArray(arr, ComTypeFromVT(v)) + Value = arr + Else + If vtype = VT.String Then + Value = GetLengthPrefixedString(0) + ElseIf vtype = VT.Short Then + Value = GetShort(0) + ElseIf vtype = VT.Integer Then + Value = GetInteger(0) + ElseIf vtype = VT.Long Then + Value = GetLong(0) + ElseIf vtype = VT.Byte Then + Value = GetByte(0) + ElseIf vtype = VT.Date Then + Value = GetDate(0) + ElseIf vtype = VT.Double Then + Value = GetDouble(0) + ElseIf vtype = VT.Single Then + Value = GetSingle(0) + ElseIf vtype = VT.Currency Then + Value = GetCurrency(0) + ElseIf vtype = VT.Decimal Then + Value = GetDecimal(0) + ElseIf vtype = VT.Boolean Then + Value = GetBoolean(0) + ElseIf vtype = VT.Char Then + Value = GetChar(0) + ElseIf vtype = VT.Structure Then + Dim valType As ValueType + valType = CType(Value, ValueType) + GetRecord(0, valType, False) + Value = valType + ElseIf vtype = VT.DBNull AndAlso ContainedInVariant Then + Value = DBNull.Value + ElseIf vtype = VT.DBNull Then + Throw VbMakeException(New ArgumentException(GetResourceString(ResID.Argument_UnsupportedIOType1, "DBNull")), vbErrors.IllegalFuncCall) + ElseIf vtype = VT.Empty Then + Value = Nothing + ElseIf vtype = VT.Currency Then + Throw VbMakeException(New ArgumentException(GetResourceString(ResID.Argument_UnsupportedIOType1, "Currency")), vbErrors.IllegalFuncCall) + Else + Throw VbMakeException(New ArgumentException(GetResourceString(ResId.Argument_UnsupportedIOType1, typ.FullName)), VbErrors.IllegalFuncCall) + End If + End If + End Sub + + + + Friend Overloads Overrides Sub [Get](ByRef Value As ValueType, Optional ByVal RecordNumber As Long = 0) + ValidateReadable() + GetRecord(RecordNumber, Value, False) + End Sub + + + + Friend Overloads Overrides Sub [Get](ByRef Value As System.Array, Optional ByVal RecordNumber As Long = 0, _ + Optional ByVal ArrayIsDynamic As Boolean = False, Optional ByVal StringIsFixedLength As Boolean = False) + + ValidateReadable() + + If (Value Is Nothing) Then + Throw New ArgumentException(GetResourceString(ResID.Argument_ArrayNotInitialized)) + End If + + Dim typ As Type = Value.GetType().GetElementType + Dim len As Integer = -1 + Dim obj As Object + Dim cDims As Integer = Value.Rank() + Dim FirstBound As Integer = -1 + Dim SecondBound As Integer = -1 + SetRecord(RecordNumber) + + If m_file.Position >= m_file.Length Then + Return + End If + + If StringIsFixedLength AndAlso (typ Is GetType(String)) Then + 'Use first element to determine fixed length + If cDims = 1 Then + obj = Value.GetValue(0) + ElseIf cDims = 2 Then + obj = Value.GetValue(0, 0) + Else '0 or > 2 + Throw New ArgumentException(GetResourceString(ResID.Argument_UnsupportedArrayDimensions)) + End If + + If obj Is Nothing Then + len = 0 + Else + len = DirectCast(obj, String).Length + End If + + If len = 0 Then + Throw New ArgumentException(GetResourceString(ResID.Argument_InvalidFixedLengthString)) + End If + End If + + If ArrayIsDynamic Then + Value = GetArrayDesc(typ) + cDims = Value.Rank() + End If + + FirstBound = Value.GetUpperBound(0) + + If cDims = 1 Then + 'nothing to do + ElseIf cDims = 2 Then + SecondBound = Value.GetUpperBound(1) + Else '0 or > 2 + Throw New ArgumentException(GetResourceString(ResID.Argument_UnsupportedArrayDimensions)) + End If + + If ArrayIsDynamic Then + GetArrayData(Value, typ, FirstBound, SecondBound, len) + Else + GetFixedArray(RecordNumber, Value, typ, FirstBound, SecondBound, len) + End If + End Sub + + + + Friend Overloads Overrides Sub [Get](ByRef Value As Boolean, Optional ByVal RecordNumber As Long = 0) + ValidateReadable() + Value = GetBoolean(RecordNumber) + End Sub + + + + Friend Overloads Overrides Sub [Get](ByRef Value As Byte, Optional ByVal RecordNumber As Long = 0) + ValidateReadable() + Value = GetByte(RecordNumber) + End Sub + + + + Friend Overloads Overrides Sub [Get](ByRef Value As Short, Optional ByVal RecordNumber As Long = 0) + ValidateReadable() + Value = GetShort(RecordNumber) + End Sub + + + + Friend Overloads Overrides Sub [Get](ByRef Value As Integer, Optional ByVal RecordNumber As Long = 0) + ValidateReadable() + Value = GetInteger(RecordNumber) + End Sub + + + + Friend Overloads Overrides Sub [Get](ByRef Value As Long, Optional ByVal RecordNumber As Long = 0) + ValidateReadable() + Value = GetLong(RecordNumber) + End Sub + + + + Friend Overloads Overrides Sub [Get](ByRef Value As Char, Optional ByVal RecordNumber As Long = 0) + ValidateReadable() + Value = GetChar(RecordNumber) + End Sub + + + + Friend Overloads Overrides Sub [Get](ByRef Value As Single, Optional ByVal RecordNumber As Long = 0) + ValidateReadable() + Value = GetSingle(RecordNumber) + End Sub + + + + Friend Overloads Overrides Sub [Get](ByRef Value As Double, Optional ByVal RecordNumber As Long = 0) + ValidateReadable() + Value = GetDouble(RecordNumber) + End Sub + + + + Friend Overloads Overrides Sub [Get](ByRef Value As Decimal, Optional ByVal RecordNumber As Long = 0) + ValidateReadable() + Value = GetCurrency(RecordNumber) + End Sub + + + + Friend Overloads Overrides Sub [Get](ByRef Value As String, Optional ByVal RecordNumber As Long = 0, _ + Optional ByVal StringIsFixedLength As Boolean = False) + + ValidateReadable() + + If StringIsFixedLength Then + Dim Length As Integer + If Value Is Nothing Then + Length = 0 + Else + Diagnostics.Debug.Assert(Not m_Encoding Is Nothing) + Length = m_Encoding.GetByteCount(Value) + End If + Value = GetFixedLengthString(RecordNumber, Length) + Else + Value = GetLengthPrefixedString(RecordNumber) + End If + End Sub + + + + Friend Overloads Overrides Sub [Get](ByRef Value As Date, Optional ByVal RecordNumber As Long = 0) + ValidateReadable() + Value = GetDate(RecordNumber) + End Sub + + + + Friend Overrides Sub PutObject(ByVal Value As Object, Optional ByVal RecordNumber As Long = 0, _ + Optional ByVal ContainedInVariant As Boolean = True) + + Dim typ As Type + + ValidateWriteable() + + If Value Is Nothing Then + 'Put a VT_EMPTY + PutEmpty(RecordNumber) + Exit Sub + End If + + typ = Value.GetType + + If typ Is Nothing Then + Throw VbMakeException(New ArgumentException(GetResourceString(ResID.Argument_UnsupportedIOType1, "Empty")), vbErrors.IllegalFuncCall) + ElseIf typ.IsArray Then + PutDynamicArray(RecordNumber, CType(Value, System.Array)) + Exit Sub + ElseIf typ.IsEnum Then + typ = System.Enum.GetUnderlyingType(typ) + End If + + Select Case Type.GetTypeCode(typ) + Case TypeCode.String + PutVariantString(RecordNumber, Value.ToString()) + Return + Case TypeCode.Int16 + PutShort(RecordNumber, ShortType.FromObject(Value), ContainedInVariant) + Return + Case TypeCode.Int32 + PutInteger(RecordNumber, IntegerType.FromObject(Value), ContainedInVariant) + Return + Case TypeCode.Int64 + PutLong(RecordNumber, LongType.FromObject(Value), ContainedInVariant) + Return + Case TypeCode.Byte + PutByte(RecordNumber, ByteType.FromObject(Value), ContainedInVariant) + Return + Case TypeCode.DateTime + PutDate(RecordNumber, DateType.FromObject(Value), ContainedInVariant) + Return + Case TypeCode.Double + PutDouble(RecordNumber, DoubleType.FromObject(Value), ContainedInVariant) + Return + Case TypeCode.Single + PutSingle(RecordNumber, SingleType.FromObject(Value), ContainedInVariant) + Return + Case TypeCode.Decimal + PutDecimal(RecordNumber, DecimalType.FromObject(Value), ContainedInVariant) + Return + Case TypeCode.Boolean + PutBoolean(RecordNumber, BooleanType.FromObject(Value), ContainedInVariant) + Return + Case TypeCode.Char + PutChar(RecordNumber, CharType.FromObject(Value), ContainedInVariant) + Return + Case TypeCode.DBNull + 'Use PutShort since DBNull is only a two-byte vartype with no data + PutShort(RecordNumber, VT.DBNull, False) + Return + End Select + + If typ Is GetType(System.Reflection.Missing) Then + Throw VbMakeException(New ArgumentException(GetResourceString(ResID.Argument_UnsupportedIOType1, "Missing")), vbErrors.IllegalFuncCall) + + ElseIf typ.IsValueType() AndAlso Not ContainedInVariant Then + PutRecord(RecordNumber, CType(Value, ValueType)) + + ElseIf ContainedInVariant AndAlso typ.IsValueType Then + Throw VbMakeException(New ArgumentException(GetResourceString(ResID.Argument_PutObjectOfValueType1, VBFriendlyName(typ, Value))), vbErrors.IllegalFuncCall) + + Else + Throw VbMakeException(New ArgumentException(GetResourceString(ResID.Argument_UnsupportedIOType1, VBFriendlyName(typ, Value))), vbErrors.IllegalFuncCall) + End If + End Sub + + + + Friend Overloads Overrides Sub Put(ByVal Value As ValueType, Optional ByVal RecordNumber As Long = 0) + ValidateWriteable() + PutRecord(RecordNumber, Value) + End Sub + + + + Friend Overloads Overrides Sub Put(ByVal Value As System.Array, Optional ByVal RecordNumber As Long = 0, _ + Optional ByVal ArrayIsDynamic As Boolean = False, Optional ByVal StringIsFixedLength As Boolean = False) + + ValidateWriteable() + + If Value Is Nothing Then + PutEmpty(RecordNumber) + Return + End If + + Dim FirstBound As Integer = Value.GetUpperBound(0) + Dim SecondBound As Integer = -1 + Dim FixedStringLength As Integer = -1 + Dim typ As System.Type + + If Value.Rank = 2 Then + SecondBound = Value.GetUpperBound(1) + End If + If StringIsFixedLength Then + FixedStringLength = 0 'Fixed length string, but length calculated by Put function + End If + + typ = Value.GetType().GetElementType() + + If ArrayIsDynamic Then + PutDynamicArray(RecordNumber, Value, False, FixedStringLength) + Else + PutFixedArray(RecordNumber, Value, typ, FixedStringLength, FirstBound, SecondBound) + End If + End Sub + + + + Friend Overloads Overrides Sub Put(ByVal Value As Boolean, Optional ByVal RecordNumber As Long = 0) + ValidateWriteable() + PutBoolean(RecordNumber, Value) + End Sub + + + + Friend Overloads Overrides Sub Put(ByVal Value As Byte, Optional ByVal RecordNumber As Long = 0) + ValidateWriteable() + PutByte(RecordNumber, Value) + End Sub + + + + Friend Overloads Overrides Sub Put(ByVal Value As Short, Optional ByVal RecordNumber As Long = 0) + ValidateWriteable() + PutShort(RecordNumber, Value) + End Sub + + + + Friend Overloads Overrides Sub Put(ByVal Value As Integer, Optional ByVal RecordNumber As Long = 0) + ValidateWriteable() + PutInteger(RecordNumber, Value) + End Sub + + + + Friend Overloads Overrides Sub Put(ByVal Value As Long, Optional ByVal RecordNumber As Long = 0) + ValidateWriteable() + PutLong(RecordNumber, Value) + End Sub + + + + Friend Overloads Overrides Sub Put(ByVal Value As Char, Optional ByVal RecordNumber As Long = 0) + ValidateWriteable() + PutChar(RecordNumber, Value) + End Sub + + + + Friend Overloads Overrides Sub Put(ByVal Value As Single, Optional ByVal RecordNumber As Long = 0) + ValidateWriteable() + PutSingle(RecordNumber, Value) + End Sub + + + + Friend Overloads Overrides Sub Put(ByVal Value As Double, Optional ByVal RecordNumber As Long = 0) + ValidateWriteable() + PutDouble(RecordNumber, Value) + End Sub + + + + Friend Overloads Overrides Sub Put(ByVal Value As Decimal, Optional ByVal RecordNumber As Long = 0) + ValidateWriteable() + PutCurrency(RecordNumber, Value) + End Sub + + + + Friend Overloads Overrides Sub Put(ByVal Value As String, Optional ByVal RecordNumber As Long = 0, Optional ByVal StringIsFixedLength As Boolean = False) + ValidateWriteable() + + If StringIsFixedLength Then + PutString(RecordNumber, Value) + Else + PutStringWithLength(RecordNumber, Value) + End If + End Sub + + Friend Overloads Overrides Sub Put(ByVal Value As Date, Optional ByVal RecordNumber As Long = 0) + ValidateWriteable() + PutDate(RecordNumber, Value) + End Sub + + Protected Sub ValidateWriteable() + If (m_access <> OpenAccess.ReadWrite) AndAlso (m_access <> OpenAccess.Write) Then + Throw VbMakeExceptionEx(vbErrors.PathFileAccess, GetResourceString(ResID.FileOpenedNoWrite)) + End If + End Sub + + Protected Sub ValidateReadable() + If (m_access <> OpenAccess.ReadWrite) AndAlso (m_access <> OpenAccess.Read) Then + Throw VbMakeExceptionEx(vbErrors.PathFileAccess, GetResourceString(ResID.FileOpenedNoRead)) + End If + End Sub + + End Class + +#End Region + +End Namespace diff --git a/Microsoft.VisualBasic/runtime/msvbalib/Helpers/VBBinder.vb b/Microsoft.VisualBasic/runtime/msvbalib/Helpers/VBBinder.vb new file mode 100644 index 000000000..2b63f1e4d --- /dev/null +++ b/Microsoft.VisualBasic/runtime/msvbalib/Helpers/VBBinder.vb @@ -0,0 +1,2357 @@ +' Copyright (c) Microsoft Corporation. All rights reserved. + +Imports System +Imports System.Diagnostics +Imports System.Reflection +Imports System.Globalization +Imports System.Security +Imports System.Security.Permissions + +Imports Microsoft.VisualBasic.CompilerServices.LateBinding +Imports Microsoft.VisualBasic.CompilerServices.ExceptionUtils +Imports Microsoft.VisualBasic.CompilerServices.Utils + +Namespace Microsoft.VisualBasic.CompilerServices + +#Region " BACKWARDS COMPATIBILITY " + + 'WARNING WARNING WARNING WARNING WARNING + 'This code exists to support Everett compiled applications. Make sure you understand + 'the backwards compatibility ramifications of any edit you make in this region. + 'WARNING WARNING WARNING WARNING WARNING + + Friend NotInheritable Class VBBinder + + Inherits Binder + + Const PARAMARRAY_NONE As Integer = -1 + Const ARG_MISSING As Integer = -1 + + _ + Friend NotInheritable Class VBBinderState + Friend m_OriginalArgs() As Object + Friend m_ByRefFlags() As Boolean + Friend m_OriginalByRefFlags() As Boolean + Friend m_OriginalParamOrder() As Integer + + Friend Sub New() + End Sub + End Class + + _ + Enum BindScore + Exact = 0 + Widening0 = 1 + Widening1 = 2 + [Narrowing] = 3 + Unknown = 4 + End Enum + + Friend m_BindToName As String + Friend m_objType As System.Type + Private m_state As VBBinderState + Private m_CachedMember As MemberInfo + + Private Sub ThrowInvalidCast(ByVal ArgType As System.Type, ByVal ParmType As System.Type, ByVal ParmIndex As Integer) + Throw New InvalidCastException(GetResourceString(ResID.InvalidCast_FromToArg4, CalledMethodName(), CStr(ParmIndex + 1), VBFriendlyName(ArgType), VBFriendlyName(ParmType))) + End Sub + + 'Flags to indicate if parameter was passed + Private m_ByRefFlags As Boolean() + + Sub New(ByVal CopyBack As Boolean()) + m_ByRefFlags = CopyBack + End Sub + + ' + ' * This method allows us to reorder the arguments back to the original caller order + ' * if necessary + ' + Public Overrides Sub ReorderArgumentArray(ByRef args() As Object, ByVal objState As Object) + + Dim i, j As Integer + Dim state As VBBinderState = CType(objState, VBBinderState) + Dim IsByRef As Boolean + + If (args Is Nothing) OrElse (state Is Nothing) Then + GoTo CleanupAndExit + End If + + If Not state.m_OriginalParamOrder Is Nothing Then + + 'The arguments have been reordered, lets put them back + If (Not m_ByRefFlags Is Nothing) Then + + If state.m_ByRefFlags Is Nothing Then + 'Clear all flags, nothing was byref + For i = 0 To m_ByRefFlags.GetUpperBound(0) + m_ByRefFlags(i) = False + Next i + + Else + 'Make temporary array to reorder arguments + For i = 0 To state.m_OriginalParamOrder.GetUpperBound(0) + + j = state.m_OriginalParamOrder(i) + If j >= 0 AndAlso j <= args.GetUpperBound(0) Then + m_ByRefFlags(j) = state.m_ByRefFlags(j) + state.m_OriginalArgs(j) = args(i) + End If + + Next i + + End If + + End If + + Else + + If Not m_ByRefFlags Is Nothing Then + + If state.m_ByRefFlags Is Nothing Then + 'Clear all flags, nothing was byref + For i = 0 To m_ByRefFlags.GetUpperBound(0) + m_ByRefFlags(i) = False + Next i + Else + For i = 0 To m_ByRefFlags.GetUpperBound(0) + If m_ByRefFlags(i) Then + 'Argument was passed to us as a byref candidate + 'so we need to reflect it's true "ByRef"ness + IsByRef = state.m_ByRefFlags(i) + m_ByRefFlags(i) = IsByRef + If IsByRef Then + state.m_OriginalArgs(i) = args(i) + End If + End If + Next i + End If + + End If + + End If + + +CleanupAndExit: + If Not state Is Nothing Then + state.m_OriginalParamOrder = Nothing + state.m_ByRefFlags = Nothing + End If + + End Sub + + + '/** + ' * This method is passed a of methods and must choose the best fit. + ' * + ' * @exception MissingMethodException + ' * @exception ArgumentException + ' * @exception AmbiguousMatchException + ' * be invoked. + '*/ + + Public Overrides Function BindToMethod(ByVal bindingAttr As BindingFlags, ByVal match() As MethodBase, ByRef args() As Object, ByVal modifiers() As ParameterModifier, ByVal culture As CultureInfo, ByVal names() As String, ByRef ObjState As Object) As MethodBase + + Dim HasByRefArgs As Boolean + Dim ThisMatchHasByRefs As Boolean + Dim SelectedCount, SelectedIndex As Integer + Dim SelectedMatch As MethodBase + Dim ThisMethod As MethodBase + Dim LastParam As ParameterInfo + Dim state As VBBinderState + Dim MethodIndex, NameIndex, ParmIndex, ArgIndex As Integer + Dim Parameters() As ParameterInfo + Dim ArgTypes() As Type + Dim ArgType As Type = Nothing + Dim ParmType As Type = Nothing + Dim ParamArrayElementType As Type = Nothing + Dim ParamArrayIndex As Integer + Dim LastArgIndexToCheck, LastParmIndexToCheck As Integer + Dim IsPropertySet As Boolean + Dim InitialMemberCount As Integer ' Member count minus shadowed members + Dim MostSpecific As Integer + + If (match Is Nothing) OrElse (match.Length = 0) Then + Throw VbMakeException(vbErrors.OLENoPropOrMethod) + End If + + If (Not m_CachedMember Is Nothing) AndAlso _ + (m_CachedMember.MemberType = MemberTypes.Method) AndAlso _ + (Not match(0) Is Nothing) AndAlso _ + (match(0).Name = m_CachedMember.Name) Then + + Return DirectCast(m_CachedMember, MethodBase) + End If + + IsPropertySet = ((bindingAttr And BindingFlags.SetProperty) <> 0) + + If Not names Is Nothing AndAlso names.Length = 0 Then + ' simplify the checkin down below + names = Nothing + End If + + ' There is a minor difference in behavior of early vs. late binding + ' with regard to Shadows + ' Shadows can apply to protected methods overriding a public + ' method of the same name on a base class + ' but we do not know about these methods, because only asked for + ' public members. To get early bound binding semantics, we need + ' to query for all methods and then remove all private/protected members + ' from the match list after we remove the methods that are shadowed + ' on the base class, since they should not be visible in the derived class + + 'STEP 1 - Remove Shadowed members + 'Iterate through the methods and see if any Shadowed members + 'need to be removed from the list + 'MethodIndex = 0 + + SelectedCount = match.Length + + If SelectedCount > 1 Then + For MethodIndex = 0 To match.GetUpperBound(0) + + ThisMethod = match(MethodIndex) + + If ThisMethod Is Nothing Then + 'Skip this one + + ElseIf ThisMethod.IsHideBySig Then + 'Hide-by-sig - shadows exact name and sig on base types + ' + 'Don't bother filtering here, this will get done below for this case + + ElseIf ThisMethod.IsVirtual Then + 'Virtual methods only shadow if NewSlot set + If (ThisMethod.Attributes And MethodAttributes.NewSlot) <> 0 Then + ' + 'Run through the list and remove all the inherited members of this type + ' + Dim j As Integer + For j = 0 To match.GetUpperBound(0) + + If MethodIndex <> j AndAlso (Not match(j) Is Nothing) AndAlso _ + ThisMethod.DeclaringType.IsSubclassOf(match(j).DeclaringType) Then + ' ThisMethod Shadows the baseclass and ThatMethod should not be accessible + ' to the caller + match(j) = Nothing + SelectedCount -= 1 + End If + + Next j + End If + + Else + ' + 'Run through the list and remove all the inherited members of this type + ' + Dim j As Integer + For j = 0 To match.GetUpperBound(0) + + If MethodIndex <> j AndAlso (Not match(j) Is Nothing) AndAlso _ + ThisMethod.DeclaringType.IsSubclassOf(match(j).DeclaringType) Then + ' ThisMethod Shadows the baseclass and ThatMethod should not be accessible + ' to the caller + match(j) = Nothing + SelectedCount -= 1 + End If + + Next j + + End If + + Next + End If + + InitialMemberCount = SelectedCount + + 'STEP 2 - Remove all Private and Protected members + ' + ' TBD IF NEEDED : see note above on shadows + + + 'STEP 3 - Remove functions that don't have matching argument names + ' + If Not names Is Nothing Then + + For MethodIndex = 0 To match.GetUpperBound(0) + + ThisMethod = match(MethodIndex) + + If Not ThisMethod Is Nothing Then + + Parameters = ThisMethod.GetParameters() + + 'Check for the last argument being a ParamArray + LastParmIndexToCheck = Parameters.GetUpperBound(0) + If IsPropertySet Then + LastParmIndexToCheck -= 1 + End If + + If LastParmIndexToCheck >= 0 Then + LastParam = Parameters(LastParmIndexToCheck) + + ParamArrayIndex = PARAMARRAY_NONE + + If LastParam.ParameterType.IsArray() Then + 'Check for ParamArray attribute + Dim ca() As Object + ca = LastParam.GetCustomAttributes(GetType(ParamArrayAttribute), False) + If (Not ca Is Nothing) AndAlso (ca.Length > 0) Then + ParamArrayIndex = LastParmIndexToCheck + Else + ParamArrayIndex = PARAMARRAY_NONE + End If + End If + End If + + For NameIndex = 0 To names.GetUpperBound(0) + + For ParmIndex = 0 To LastParmIndexToCheck + + If StrComp(names(NameIndex), Parameters(ParmIndex).Name, CompareMethod.Text) = 0 Then + If ParmIndex = ParamArrayIndex AndAlso SelectedCount = 1 Then + Throw VbMakeExceptionEx(vbErrors.NamedArgsNotSupported, GetResourceString(ResID.NamedArgumentOnParamArray)) + Else + If ParmIndex = ParamArrayIndex Then + 'Matched against a paramarray, force into the removal code below + ParmIndex = LastParmIndexToCheck + 1 + Else + 'Found it, so look at the next name + End If + Exit For + End If + End If + + Next ParmIndex + + ' This is an error condition. The name was not found. This + ' method must not match what we sent. + If (ParmIndex > LastParmIndexToCheck) Then + + If SelectedCount = 1 Then + ' i.e. MissingMethod, MissingField, MissingMember + 'This is the last possible matching member + ' so throw an exception that the name doesn't match + Throw New MissingMemberException(GetResourceString(ResID.Argument_InvalidNamedArg2, names(NameIndex), CalledMethodName())) + End If + + match(MethodIndex) = Nothing + SelectedCount -= 1 + Exit For + + End If + + Next NameIndex + + End If + + Next MethodIndex + + End If + + 'STEP 4 - Rearrange the arguments + ' Named arguments and ParamArrays affect the ordering, + ' so we have to move them around a bit, + ' but also keep track of what order they + ' were in so we can reorder them on the way out + + ' We are creating a paramOrder array to act as a mapping + ' between the order of the args and the actual order of the + ' parameters in the method. This order may differ because + ' named parameters (names) may change the order. If names + ' is not provided, then we assume the default mapping (0,1,...) + + Dim ParamArrayIndexList() As Integer + + 'Create a list of flags marking those having a ParamArray + ParamArrayIndexList = New Integer(match.Length - 1) {} + + For MethodIndex = 0 To match.GetUpperBound(0) + + ThisMethod = match(MethodIndex) + + If Not ThisMethod Is Nothing Then + + ParamArrayIndex = PARAMARRAY_NONE + + Parameters = ThisMethod.GetParameters() + LastParmIndexToCheck = Parameters.GetUpperBound(0) + If IsPropertySet Then + LastParmIndexToCheck -= 1 + End If + + 'Check for the last argument being a ParamArray + If LastParmIndexToCheck >= 0 Then + + LastParam = Parameters(LastParmIndexToCheck) + + If LastParam.ParameterType.IsArray() Then + 'Check for ParamArray attribute + Dim ca() As Object + ca = LastParam.GetCustomAttributes(GetType(ParamArrayAttribute), False) + If (Not ca Is Nothing) AndAlso (ca.Length > 0) Then + ParamArrayIndex = LastParmIndexToCheck + End If + End If + + End If + + ParamArrayIndexList(MethodIndex) = ParamArrayIndex + + If (ParamArrayIndex = PARAMARRAY_NONE) AndAlso (args.Length > Parameters.Length) Then + 'If we have too many arguments, we don't match any function + If SelectedCount = 1 Then + Throw New MissingMemberException(GetResourceString(ResID.NoMethodTakingXArguments2, CalledMethodName(), CStr(GetPropArgCount(args, IsPropertySet)))) + End If + + 'Clear this entry + match(MethodIndex) = Nothing + SelectedCount -= 1 + + End If + + Dim LengthOfNonParamArrayArguments As Integer = LastParmIndexToCheck + If ParamArrayIndex <> PARAMARRAY_NONE Then + LengthOfNonParamArrayArguments -= 1 + End If + + If (args.Length < LengthOfNonParamArrayArguments) Then + + ' If the number of parameters is greater than the number + ' of args then we are in the situation were we must + ' be using default values. + Dim j As Integer + For j = args.Length To LengthOfNonParamArrayArguments - 1 + 'DBNull indicates no default value + If (Parameters(j).DefaultValue Is System.DBNull.Value) Then + Exit For + End If + Next j + + If (j <> LengthOfNonParamArrayArguments) Then + 'Not enough arguments to call this method, so remove it + If SelectedCount = 1 Then + Throw New MissingMemberException(GetResourceString(ResID.NoMethodTakingXArguments2, CalledMethodName(), CStr(GetPropArgCount(args, IsPropertySet)))) + End If + match(MethodIndex) = Nothing + SelectedCount -= 1 + + End If + + End If + + End If + + Next MethodIndex + + + 'STEP 5 - Create mapping table for argument reordering + ' + Dim paramOrder As Object() = New Object(match.Length - 1) {} + Dim ArgIndexes() As Integer + + For MethodIndex = 0 To match.GetUpperBound(0) + + ThisMethod = match(MethodIndex) + + If Not ThisMethod Is Nothing Then + + Parameters = ThisMethod.GetParameters() + + If args.Length > Parameters.Length Then + ArgIndexes = New Integer(args.Length - 1) {} + Else + ArgIndexes = New Integer(Parameters.Length - 1) {} + End If + + paramOrder(MethodIndex) = ArgIndexes + + If (names Is Nothing) Then + ' Default mapping + + Dim TmpLastIndex As Integer + ' Mark which parameters have not been found in the names list + TmpLastIndex = args.GetUpperBound(0) + If IsPropertySet Then + TmpLastIndex -= 1 + End If + For ArgIndex = 0 To TmpLastIndex + If TypeOf args(ArgIndex) Is System.Reflection.Missing AndAlso (ArgIndex > Parameters.GetUpperBound(0) OrElse Parameters(ArgIndex).IsOptional) Then + ArgIndexes(ArgIndex) = ARG_MISSING + Else + ArgIndexes(ArgIndex) = ArgIndex + End If + Next ArgIndex + + + TmpLastIndex = ArgIndexes.GetUpperBound(0) + ' Any extra arguments must be optional + For ArgIndex = ArgIndex To TmpLastIndex + ArgIndexes(ArgIndex) = ARG_MISSING + Next ArgIndex + + If IsPropertySet Then + 'Last index or args array is the Set value + 'we might have optional arguments before the new value + ArgIndexes(TmpLastIndex) = args.GetUpperBound(0) + End If + Else + ' Named parameters, reorder the mapping. If + ' CreateParamOrder fails, it means that the method + ' doesn't have a name that matchs one of the named + ' parameters so we don't consider it any further. + + Dim ex As Exception + + ex = CreateParamOrder(IsPropertySet, _ + ArgIndexes, _ + ThisMethod.GetParameters(), _ + args, _ + names) + If (Not ex Is Nothing) Then + If SelectedCount = 1 Then + 'Just throw the exception + Throw ex + Else + match(MethodIndex) = Nothing + SelectedCount -= 1 + End If + End If + End If + + End If + + Next MethodIndex + + + 'STEP 6 - Save the types of the arguments passed in + + ' objects that contain a null are treated as + ' if they were typeless (but match either object references + ' or value classes). We mark this condition by + ' placing a null in the argTypes array. + ArgTypes = New Type(args.Length - 1) {} + + For ArgIndex = 0 To args.GetUpperBound(0) + If Not args(ArgIndex) Is Nothing Then + ArgTypes(ArgIndex) = args(ArgIndex).GetType() + End If + Next + + + 'STEP 7 - Eliminate methods that have types that cannot be called + ' (i.e. no widening or narrowing posibilities) + ' + + For MethodIndex = 0 To match.GetUpperBound(0) + + ThisMethod = match(MethodIndex) + + If Not ThisMethod Is Nothing Then + + Parameters = ThisMethod.GetParameters() + ArgIndexes = CType(paramOrder(MethodIndex), Integer()) + LastParmIndexToCheck = ArgIndexes.GetUpperBound(0) + If IsPropertySet Then + LastParmIndexToCheck -= 1 + End If + + ParamArrayIndex = ParamArrayIndexList(MethodIndex) + If ParamArrayIndex <> PARAMARRAY_NONE Then + ParamArrayElementType = Parameters(ParamArrayIndex).ParameterType.GetElementType() + Else + 'No ParamArray involved, eleminate methods with insufficient arguments + If ArgIndexes.Length > Parameters.Length Then + GoTo ClearMethod7 + End If + End If + + For ParmIndex = 0 To LastParmIndexToCheck + + ArgIndex = ArgIndexes(ParmIndex) + + 'Do we need this check now? Will it already have been removed? + If (ArgIndex = ARG_MISSING) Then + If Parameters(ParmIndex).IsOptional OrElse (ParmIndex = ParamArrayIndexList(MethodIndex)) Then + 'Argument not supplied for this argument + GoTo NextParm7 + Else + If SelectedCount = 1 Then + Throw New MissingMemberException(GetResourceString(ResID.NoMethodTakingXArguments2, CalledMethodName(), CStr(GetPropArgCount(args, IsPropertySet)))) + End If + GoTo ClearMethod7 + End If + End If + + ArgType = ArgTypes(ArgIndex) + 'If ArgType Is Nothing Then + ' ArgType = GetType(Object) + 'End If + If (ArgType Is Nothing) Then + 'Nothing matches anything + GoTo NextParm7 + End If + + If (ParamArrayIndex <> PARAMARRAY_NONE) AndAlso (ParmIndex > ParamArrayIndex) Then + ParmType = Parameters(ParamArrayIndex).ParameterType.GetElementType() + Else + ParmType = Parameters(ParmIndex).ParameterType + + If ParmType.IsByRef Then + ParmType = ParmType.GetElementType() + End If + + If (ParmIndex = ParamArrayIndex) Then + If (ParmType.IsInstanceOfType(args(ArgIndex)) AndAlso ParmIndex = LastParmIndexToCheck) Then + 'Arg can be cast to the Param type + GoTo NextParm7 + End If + ParmType = ParamArrayElementType + End If + + End If + + If ParmType Is ArgType Then + GoTo NextParm7 + End If + + If (ArgType Is Type.Missing) AndAlso Parameters(ParmIndex).IsOptional Then + 'Missing matches any optional + GoTo NextParm7 + End If + + If args(ArgIndex) Is Missing.Value Then + GoTo ClearMethod7 + End If + + If (ParmType Is GetType(Object)) Then + 'Param type is Object, so anything goes + GoTo NextParm7 + End If + + If ParmType.IsInstanceOfType(args(ArgIndex)) Then + 'Arg can be cast to the Param type + GoTo NextParm7 + End If + + 'Check if this can be converted + Dim ParmTypeCode As TypeCode + Dim ArgTypeCode As TypeCode + + ParmTypeCode = System.Type.GetTypeCode(ParmType) + + If ArgType Is Nothing Then + ArgTypeCode = TypeCode.Empty + Else + ArgTypeCode = System.Type.GetTypeCode(ArgType) + End If + + Select Case ParmTypeCode + + Case TypeCode.Boolean, _ + TypeCode.Byte, _ + TypeCode.Int16, _ + TypeCode.Int32, _ + TypeCode.Int64, _ + TypeCode.Decimal, _ + TypeCode.Single, _ + TypeCode.Double + 'Allowed coercions + + Select Case ArgTypeCode + + Case TypeCode.Boolean, _ + TypeCode.Byte, _ + TypeCode.Int16, _ + TypeCode.Int32, _ + TypeCode.Int64, _ + TypeCode.Decimal, _ + TypeCode.Single, _ + TypeCode.Double, _ + TypeCode.String + 'Allowed coercions + + Case Else + 'Case TypeCode.DateTime, TypeCode.Char, TypeCode.Object + GoTo ClearMethod7 + + End Select + + Case TypeCode.Char + + Select Case ArgTypeCode + + Case TypeCode.String + 'This can be converted + + Case Else + GoTo ClearMethod7 + + End Select + + Case TypeCode.String + Select Case ArgTypeCode + Case TypeCode.Boolean, _ + TypeCode.Byte, _ + TypeCode.Int16, _ + TypeCode.Int32, _ + TypeCode.Int64, _ + TypeCode.Decimal, _ + TypeCode.Double, _ + TypeCode.Single, _ + TypeCode.Char, _ + TypeCode.String, _ + TypeCode.Empty + 'Accept These + + 'Case TypeCode.Object, TypeCode.UInt16, TypeCode.UInt32, TypeCode.UInt64 + Case Else + If ArgType Is GetType(Char()) Then + 'Accept as if type String + Else + GoTo ClearMethod7 + End If + End Select + + Case TypeCode.DateTime + Select Case ArgTypeCode + Case TypeCode.String + 'Accept String-to-Date + + Case Else + GoTo ClearMethod7 + End Select + + 'Case TypeCode.Object, TypeCode.UInt16, TypeCode.UInt32, TypeCode.UInt64 + Case Else + If ParmType Is GetType(Char()) Then + Select Case ArgTypeCode + Case TypeCode.String + + Case TypeCode.Object + If Not ArgType Is GetType(Char()) Then + GoTo ClearMethod7 + End If + + Case Else + GoTo ClearMethod7 + End Select + + Else + 'If we're here, then the Param + ' is a reference type, but NOT System.Object + ' and cannot be cast to the expected type + ' + GoTo ClearMethod7 + End If + + End Select +NextParm7: + Next ParmIndex + + End If + GoTo NextMethod7 +ClearMethod7: + If SelectedCount = 1 Then + 'Removing the only remaining member + If InitialMemberCount = 1 Then + ThrowInvalidCast(ArgType, ParmType, ParmIndex) + Else + Throw New AmbiguousMatchException(GetResourceString(ResID.AmbiguousMatch_NarrowingConversion1, CalledMethodName())) + End If + End If + match(MethodIndex) = Nothing + SelectedCount -= 1 + +NextMethod7: + + Next MethodIndex + + + SelectedCount = 0 + + 'STEP 8 + ' + ' Find the methods that match... + For MethodIndex = 0 To match.GetUpperBound(0) + + ThisMethod = match(MethodIndex) + + ' If we have named parameters then we may + ' have hole in the match array. + If (ThisMethod Is Nothing) Then + GoTo NextMethod8 + End If + + ArgIndexes = CType(paramOrder(MethodIndex), Integer()) + + ' Validate the parameters. + Parameters = ThisMethod.GetParameters() + + ThisMatchHasByRefs = False + + LastParmIndexToCheck = Parameters.GetUpperBound(0) + If IsPropertySet Then + LastParmIndexToCheck -= 1 + End If + + LastArgIndexToCheck = args.GetUpperBound(0) + If IsPropertySet Then + LastArgIndexToCheck -= 1 + End If + ParamArrayIndex = ParamArrayIndexList(MethodIndex) + If ParamArrayIndex <> PARAMARRAY_NONE Then + ParamArrayElementType = Parameters(LastParmIndexToCheck).ParameterType.GetElementType() + End If + + For ParmIndex = 0 To LastParmIndexToCheck + + If ParmIndex = ParamArrayIndex Then + ParmType = ParamArrayElementType + Else + ParmType = Parameters(ParmIndex).ParameterType + End If + + If ParmType.IsByRef Then + ThisMatchHasByRefs = True + ParmType = ParmType.GetElementType() + End If + + ArgIndex = ArgIndexes(ParmIndex) + + If (ArgIndex = ARG_MISSING) AndAlso Parameters(ParmIndex).IsOptional _ + OrElse (ParmIndex = ParamArrayIndexList(MethodIndex)) Then + 'Argument not supplied for this argument + GoTo NextParm8 + End If + + ArgType = ArgTypes(ArgIndex) + + If (ArgType Is Nothing) Then + 'Nothing passed, will cast to anything + GoTo NextParm8 + End If + + If (ArgType Is Type.Missing) AndAlso Parameters(ParmIndex).IsOptional Then + GoTo NextParm8 + End If + + If ParmType Is ArgType Then + GoTo NextParm8 + End If + + If (ParmType Is GetType(Object)) Then + GoTo NextParm8 + End If + + 'Check if this can be converted + Dim ParmTypeCode As TypeCode + Dim ArgTypeCode As TypeCode + + ParmTypeCode = System.Type.GetTypeCode(ParmType) + + If ArgType Is Nothing Then + ArgTypeCode = TypeCode.Empty + Else + ArgTypeCode = System.Type.GetTypeCode(ArgType) + End If + + Select Case ParmTypeCode + + Case TypeCode.Boolean, _ + TypeCode.Byte, _ + TypeCode.Int16, _ + TypeCode.Int32, _ + TypeCode.Int64, _ + TypeCode.Decimal, _ + TypeCode.Single, _ + TypeCode.Double + 'Allowed coercions + + Select Case ArgTypeCode + + Case TypeCode.Boolean, _ + TypeCode.Byte, _ + TypeCode.Int16, _ + TypeCode.Int32, _ + TypeCode.Int64, _ + TypeCode.Decimal, _ + TypeCode.Single, _ + TypeCode.Double, _ + TypeCode.String + 'Allowed coercions + + Case Else 'Case TypeCode.DateTime, TypeCode.Char + If SelectedCount = 0 Then + ThrowInvalidCast(ArgType, ParmType, ParmIndex) + End If + + End Select + + Case TypeCode.Char + Case TypeCode.String + Case TypeCode.DateTime + + Case Else + + End Select + +NextParm8: + Next + + + 'If we went through all the args, and they matched + ' then j will be > the args upper bound + If (ParmIndex > LastParmIndexToCheck) Then + 'WE FOUND ONE! + If MethodIndex <> SelectedCount Then + match(SelectedCount) = match(MethodIndex) + paramOrder(SelectedCount) = paramOrder(MethodIndex) + ParamArrayIndexList(SelectedCount) = ParamArrayIndexList(MethodIndex) + match(MethodIndex) = Nothing + End If + SelectedCount += 1 + If ThisMatchHasByRefs Then + HasByRefArgs = True + End If + Else + match(MethodIndex) = Nothing + End If +NextMethod8: + Next + + If (SelectedCount = 0) Then + Throw New MissingMemberException(GetResourceString(ResID.NoMethodTakingXArguments2, CalledMethodName(), CStr(GetPropArgCount(args, IsPropertySet)))) + End If + + state = New VBBinderState + m_state = state + + 'Store in OUT param for caller to pass back to us + ObjState = state + + state.m_OriginalArgs = args + + If (SelectedCount = 1) Then + + 'All matches are pushed to the front of the list + SelectedIndex = 0 + + Else + + ' Walk all of the methods looking the most specific method to invoke + Dim AmbiguousCount As Integer + Dim Score, LowestScore As BindScore + + SelectedIndex = 0 + LowestScore = BindScore.Unknown + AmbiguousCount = 0 + + ' Score each method + ' 0 ==> Exact match + ' 1 ==> Casting down conversion required + ' 2 ==> Intrinsic widening conversion required + ' 3 ==> Requires narrowing conversion + + For MethodIndex = 0 To SelectedCount - 1 'match.GetUpperBound(0) + + ThisMethod = match(MethodIndex) + + If ThisMethod Is Nothing Then + 'Skip it + Else + + ArgIndexes = CType(paramOrder(MethodIndex), Integer()) + Score = BindingScore(ThisMethod.GetParameters(), ArgIndexes, ArgTypes, IsPropertySet, ParamArrayIndexList(MethodIndex)) + + If Score < LowestScore Then + If MethodIndex <> 0 Then + match(0) = match(MethodIndex) + paramOrder(0) = paramOrder(MethodIndex) + ParamArrayIndexList(0) = ParamArrayIndexList(MethodIndex) + match(MethodIndex) = Nothing + End If + AmbiguousCount = 1 + LowestScore = Score + + ElseIf Score = LowestScore Then + + If Score = BindScore.Exact OrElse Score = BindScore.Widening1 Then + + MostSpecific = GetMostSpecific(match(0), ThisMethod, ArgIndexes, paramOrder, IsPropertySet, ParamArrayIndexList(0), ParamArrayIndexList(MethodIndex), args) + + If MostSpecific = -1 Then + If AmbiguousCount <> MethodIndex Then + match(AmbiguousCount) = match(MethodIndex) + paramOrder(AmbiguousCount) = paramOrder(MethodIndex) + ParamArrayIndexList(AmbiguousCount) = ParamArrayIndexList(MethodIndex) + match(MethodIndex) = Nothing + End If + AmbiguousCount += 1 + + ElseIf MostSpecific = 0 Then + 'AmbiguousCount remains unchanged + 'because we could have already had multiple matches + + Else 'If MostSpecific = 1 Then + + ' VSW 370803: For MethodIndex to be the new best match, it + ' needs to be most specific than all the current ambiguous + ' matches. + Dim MoreSpecificThanAllMatches As Boolean = True + + For AmbiguousIndex As Integer = 1 To AmbiguousCount - 1 + If GetMostSpecific(match(AmbiguousIndex), ThisMethod, ArgIndexes, paramOrder, IsPropertySet, ParamArrayIndexList(AmbiguousIndex), ParamArrayIndexList(MethodIndex), args) <> 1 Then + MoreSpecificThanAllMatches = False + Exit For + End If + Next + + If MoreSpecificThanAllMatches Then + AmbiguousCount = 0 + End If + + If MethodIndex <> AmbiguousCount Then + match(AmbiguousCount) = match(MethodIndex) + paramOrder(AmbiguousCount) = paramOrder(MethodIndex) + ParamArrayIndexList(AmbiguousCount) = ParamArrayIndexList(MethodIndex) + match(MethodIndex) = Nothing + End If + AmbiguousCount += 1 + End If + + Else + If AmbiguousCount <> MethodIndex Then + match(AmbiguousCount) = match(MethodIndex) + paramOrder(AmbiguousCount) = paramOrder(MethodIndex) + ParamArrayIndexList(AmbiguousCount) = ParamArrayIndexList(MethodIndex) + match(MethodIndex) = Nothing + End If + AmbiguousCount += 1 + End If + + Else + ' We don't care it is less specific + match(MethodIndex) = Nothing + End If + + End If + + Next MethodIndex + + If (AmbiguousCount > 1) Then + 'We have an ambiguous match, run through and remove + 'shadowed members + For MethodIndex = 0 To match.GetUpperBound(0) + + ThisMethod = match(MethodIndex) + If Not ThisMethod Is Nothing Then + ' + 'Run through the list and remove all the inherited members of this type + ' + Dim j As Integer + For j = 0 To match.GetUpperBound(0) + + If MethodIndex <> j AndAlso (Not match(j) Is Nothing) AndAlso _ + (ThisMethod Is match(j) OrElse _ + (ThisMethod.DeclaringType.IsSubclassOf(match(j).DeclaringType) AndAlso _ + MethodsDifferOnlyByReturnType(ThisMethod, match(j)))) Then + 'If (Not ThisMethod.IsHideBySig) OrElse MethodsDifferOnlyByReturnType(ThisMethod, match(j)) Then + ' ThisMethod Shadows the baseclass and match(j) should not be accessible + ' to the caller + match(j) = Nothing + AmbiguousCount -= 1 + End If + + Next j + End If + Next + Diagnostics.Debug.Assert(AmbiguousCount > 0) + 'Iterate through to force them to the top of the list + For MethodIndex = 0 To match.GetUpperBound(0) + If match(MethodIndex) Is Nothing Then + Dim j As Integer + Dim TmpMatch As MethodBase + For j = MethodIndex + 1 To match.GetUpperBound(0) + TmpMatch = match(j) + If Not TmpMatch Is Nothing Then + match(MethodIndex) = TmpMatch + paramOrder(MethodIndex) = paramOrder(j) + ParamArrayIndexList(MethodIndex) = ParamArrayIndexList(j) + match(j) = Nothing + End If + Next j + End If + Next MethodIndex + End If + + If (AmbiguousCount > 1) Then + Dim Msg As String = ControlChars.CrLf & " " & MethodToString(match(0)) + For MethodIndex = 1 To AmbiguousCount - 1 + Msg = Msg & ControlChars.CrLf & " " & MethodToString(match(MethodIndex)) + Next MethodIndex + + Select Case LowestScore + Case BindScore.Exact + Throw New AmbiguousMatchException(GetResourceString(ResID.AmbiguousCall_ExactMatch2, CalledMethodName(), Msg)) + + Case BindScore.Widening0, BindScore.Widening1 + Throw New AmbiguousMatchException(GetResourceString(ResID.AmbiguousCall_WideningConversion2, CalledMethodName(), Msg)) + + Case Else + 'BindScore.Narrowing + 'BindScore.Unknown + Throw New AmbiguousMatchException(GetResourceString(ResID.AmbiguousCall2, CalledMethodName(), Msg)) + End Select + End If + + End If + + SelectedMatch = match(SelectedIndex) + + ArgIndexes = CType(paramOrder(SelectedIndex), Integer()) + + If (Not names Is Nothing) Then + ReorderParams(ArgIndexes, args, state) + End If + + Dim parms() As ParameterInfo = SelectedMatch.GetParameters() + + If args.Length > 0 Then + state.m_ByRefFlags = New Boolean(args.GetUpperBound(0)) {} + + 'Could have been multiple matches, so check the + 'selected match + HasByRefArgs = False + + For ParmIndex = 0 To parms.GetUpperBound(0) + If parms(ParmIndex).ParameterType.IsByRef Then + If state.m_OriginalParamOrder Is Nothing Then + If ParmIndex < state.m_ByRefFlags.Length Then + state.m_ByRefFlags(ParmIndex) = True + End If + Else + If ParmIndex < state.m_OriginalParamOrder.Length Then + Dim OriginalParmIndex As Integer = state.m_OriginalParamOrder(ParmIndex) + If OriginalParmIndex >= 0 Then + state.m_ByRefFlags(OriginalParmIndex) = True + End If + End If + End If + HasByRefArgs = True + End If + Next + + If Not HasByRefArgs Then + state.m_ByRefFlags = Nothing + End If + + Else + state.m_ByRefFlags = Nothing + End If + + ' If the parameters and the args are not the same length + ' then we need to create an argument array. + + ParamArrayIndex = ParamArrayIndexList(SelectedIndex) + If ParamArrayIndex <> PARAMARRAY_NONE Then + + LastParmIndexToCheck = parms.GetUpperBound(0) + If IsPropertySet Then + LastParmIndexToCheck -= 1 + End If + + LastArgIndexToCheck = args.GetUpperBound(0) + If IsPropertySet Then + LastArgIndexToCheck -= 1 + End If + + 'Fill in the non-paramarray arguments + Dim objs() As Object = New Object(parms.Length - 1) {} + + 'Assign arguments before the paramarray + For ParmIndex = 0 To Math.Min(LastArgIndexToCheck, ParamArrayIndex) - 1 + objs(ParmIndex) = ObjectType.CTypeHelper(args(ParmIndex), parms(ParmIndex).ParameterType) + Next ParmIndex + + 'Assign default values of missing arguments + If LastArgIndexToCheck < ParamArrayIndex Then + For ParmIndex = LastArgIndexToCheck + 1 To ParamArrayIndex - 1 + objs(ParmIndex) = ObjectType.CTypeHelper(parms(ParmIndex).DefaultValue, parms(ParmIndex).ParameterType) + Next ParmIndex + End If + + 'Fill in the Set value if we are doing a property or field set + If IsPropertySet Then + Dim SetValueIndex As Integer = objs.GetUpperBound(0) + objs(SetValueIndex) = ObjectType.CTypeHelper(args(args.GetUpperBound(0)), parms(SetValueIndex).ParameterType) + End If + + If LastArgIndexToCheck = -1 Then + 'No arguments, just pack the empty paramarray + + 'Stuff the Object array into the last (non-setvalue) argument + objs(ParamArrayIndex) = System.Array.CreateInstance(ParamArrayElementType, 0) + Else + ParamArrayElementType = parms(LastParmIndexToCheck).ParameterType.GetElementType() + + Dim ParamArrayLength As Integer = args.Length - parms.Length + 1 + + ParmType = parms(LastParmIndexToCheck).ParameterType + If ParamArrayLength = 1 AndAlso ParmType.IsArray AndAlso (args(ParamArrayIndex) Is Nothing OrElse ParmType.IsInstanceOfType(args(ParamArrayIndex))) Then + objs(ParamArrayIndex) = args(ParamArrayIndex) + + Else + + If ParamArrayElementType Is GetType(Object) Then + 'Special handling for Object() paramarray + Dim ObjArray() As Object = New Object(ParamArrayLength - 1) {} + For ArgIndex = 0 To ParamArrayLength - 1 + ObjArray(ArgIndex) = ObjectType.CTypeHelper(args(ArgIndex + ParamArrayIndex), ParamArrayElementType) + Next ArgIndex + 'Stuff the Object array into the last argument + objs(ParamArrayIndex) = ObjArray + Else + 'Special handling for non-Object() paramarray + Dim TypeArray As System.Array = System.Array.CreateInstance(ParamArrayElementType, ParamArrayLength) + For ArgIndex = 0 To ParamArrayLength - 1 + TypeArray.SetValue(ObjectType.CTypeHelper(args(ArgIndex + ParamArrayIndex), ParamArrayElementType), ArgIndex) + Next ArgIndex + 'Stuff the Object array into the last argument + objs(ParamArrayIndex) = TypeArray + End If + + End If + + End If + args = objs + Else + Dim objs() As Object = New Object(parms.Length - 1) {} + Dim MappedArgIndex As Integer + + For ArgIndex = 0 To objs.GetUpperBound(0) + MappedArgIndex = ArgIndexes(ArgIndex) + If MappedArgIndex >= 0 AndAlso MappedArgIndex <= args.GetUpperBound(0) Then + objs(ArgIndex) = ObjectType.CTypeHelper(args(MappedArgIndex), parms(ArgIndex).ParameterType) + Else + objs(ArgIndex) = ObjectType.CTypeHelper(parms(ArgIndex).DefaultValue, parms(ArgIndex).ParameterType) + End If + Next ArgIndex + + For ParmIndex = ArgIndex To parms.GetUpperBound(0) + objs(ParmIndex) = ObjectType.CTypeHelper(parms(ParmIndex).DefaultValue, parms(ParmIndex).ParameterType) + Next + args = objs + End If + + 'Step XX - Change arguments to correct type for calling method + ' + Debug.Assert(Not SelectedMatch Is Nothing, "Should have already thrown an exception") + If SelectedMatch Is Nothing Then + Throw New MissingMemberException(GetResourceString(ResID.NoMethodTakingXArguments2, CalledMethodName(), CStr(GetPropArgCount(args, IsPropertySet)))) + End If + Return SelectedMatch + + End Function + + Private Function GetPropArgCount(ByVal args As Object(), ByVal IsPropertySet As Boolean) As Integer + If IsPropertySet Then + Return args.Length - 1 + Else + Return args.Length + End If + End Function + + + + Private Function GetMostSpecific(ByVal match0 As MethodBase, ByVal ThisMethod As MethodBase, ByVal ArgIndexes() As Integer, ByVal ParamOrder As Object(), ByVal IsPropertySet As Boolean, ByVal ParamArrayIndex0 As Integer, ByVal ParamArrayIndex1 As Integer, ByVal args As Object()) As Integer + + Dim AmbigParams() As ParameterInfo + Dim Parameters() As ParameterInfo + Dim Type0, Type1 As Type + Dim MostSpecific As Integer = -1 + Dim AmbigArgIndexes() As Integer + Dim ParmIndex As Integer + Dim Index0, Index1 As Integer + Dim ParamArrayElementType0 As Type = Nothing + Dim ParamArrayElementType1 As Type = Nothing + Dim LastNonSetValueIndex0, LastNonSetValueIndex1, LastNonSetValueIndexArgs As Integer + Dim ParamArrayExpanded0, ParamArrayExpanded1 As Boolean + Dim ArgCountUpperBound As Integer = args.GetUpperBound(0) + + Parameters = ThisMethod.GetParameters() + + AmbigParams = match0.GetParameters() + AmbigArgIndexes = CType(ParamOrder(0), Integer()) + MostSpecific = -1 + + LastNonSetValueIndexArgs = args.GetUpperBound(0) + LastNonSetValueIndex0 = AmbigParams.GetUpperBound(0) + LastNonSetValueIndex1 = Parameters.GetUpperBound(0) + If IsPropertySet Then + LastNonSetValueIndex0 -= 1 + LastNonSetValueIndex1 -= 1 + LastNonSetValueIndexArgs -= 1 + ArgCountUpperBound -= 1 + End If + + If ParamArrayIndex0 = PARAMARRAY_NONE Then + ParamArrayExpanded0 = False + Else + ParamArrayElementType0 = AmbigParams(ParamArrayIndex0).ParameterType.GetElementType() + ParamArrayExpanded0 = True + If (LastNonSetValueIndexArgs <> PARAMARRAY_NONE) AndAlso (LastNonSetValueIndexArgs = LastNonSetValueIndex0) Then + Dim objTmp As Object = args(LastNonSetValueIndexArgs) + If (objTmp Is Nothing) OrElse (AmbigParams(LastNonSetValueIndex0).ParameterType.IsInstanceOfType(objTmp)) Then + ParamArrayExpanded0 = False + End If + End If + End If + + If ParamArrayIndex1 = PARAMARRAY_NONE Then + ParamArrayExpanded1 = False + Else + ParamArrayElementType1 = Parameters(ParamArrayIndex1).ParameterType.GetElementType() + ParamArrayExpanded1 = True + If (LastNonSetValueIndexArgs <> PARAMARRAY_NONE) AndAlso (LastNonSetValueIndexArgs = LastNonSetValueIndex1) Then + Dim objTmp As Object = args(LastNonSetValueIndexArgs) + If (objTmp Is Nothing) OrElse (Parameters(LastNonSetValueIndex1).ParameterType.IsInstanceOfType(objTmp)) Then + ParamArrayExpanded1 = False + End If + End If + End If + + + For ParmIndex = 0 To Math.Min(ArgCountUpperBound, Math.Max(LastNonSetValueIndex0, LastNonSetValueIndex1)) + + If ParmIndex <= LastNonSetValueIndex0 Then + Index0 = AmbigArgIndexes(ParmIndex) + Else + Index0 = -1 + End If + + If ParmIndex <= LastNonSetValueIndex1 Then + Index1 = ArgIndexes(ParmIndex) + Else + Index1 = -1 + End If + + If Index0 = -1 AndAlso Index1 = -1 Then + 'Both are optional and thus equal + + ElseIf (ParamArrayExpanded1 AndAlso ParamArrayIndex1 <> PARAMARRAY_NONE AndAlso ParmIndex >= ParamArrayIndex1) Then + + 'Paramarray, so everything else must be equal or could be basetype diffs + If ParamArrayExpanded0 AndAlso ParamArrayIndex0 <> PARAMARRAY_NONE AndAlso ParmIndex >= ParamArrayIndex0 Then + Type0 = ParamArrayElementType0 + Else + Type0 = AmbigParams(Index0).ParameterType + If Type0.IsByRef Then + Type0 = Type0.GetElementType() + End If + End If + + If ParamArrayElementType1 Is Type0 Then + 'Identical + If MostSpecific = -1 AndAlso ParamArrayIndex0 = PARAMARRAY_NONE AndAlso ParmIndex = LastNonSetValueIndex0 AndAlso _ + (Not args(LastNonSetValueIndex0) Is Nothing) Then + MostSpecific = 0 + End If + + ElseIf ObjectType.IsWideningConversion(Type0, ParamArrayElementType1) Then + 'match(0) is a less widening conversion + 'up to this argument + If MostSpecific <> 1 Then + MostSpecific = 0 + Else + MostSpecific = -1 + Exit For + End If + + ElseIf ObjectType.IsWideningConversion(ParamArrayElementType1, Type0) Then + 'match(MethodIndex) is a less widening conversion + 'up to this argument + If MostSpecific <> 0 Then + MostSpecific = 1 + Else + MostSpecific = -1 + Exit For + End If + End If + + ElseIf (ParamArrayExpanded0 AndAlso ParamArrayIndex0 <> PARAMARRAY_NONE AndAlso ParmIndex >= ParamArrayIndex0) Then + + 'Paramarray, so everything else must be equal or could be basetype diffs + If ParamArrayExpanded1 AndAlso ParamArrayIndex1 <> PARAMARRAY_NONE AndAlso ParmIndex >= ParamArrayIndex1 Then + Type1 = ParamArrayElementType1 + Else + Type1 = Parameters(Index1).ParameterType + If Type1.IsByRef Then + Type1 = Type1.GetElementType() + End If + End If + + If ParamArrayElementType0 Is Type1 Then + 'Identical + If MostSpecific = -1 AndAlso ParamArrayIndex1 = PARAMARRAY_NONE AndAlso ParmIndex = LastNonSetValueIndex1 AndAlso _ + (Not args(LastNonSetValueIndex1) Is Nothing) Then + MostSpecific = 1 + End If + + ElseIf ObjectType.IsWideningConversion(ParamArrayElementType0, Type1) Then + 'match(0) is a less widening conversion + 'up to this argument + If MostSpecific <> 1 Then + MostSpecific = 0 + Else + MostSpecific = -1 + Exit For + End If + + ElseIf ObjectType.IsWideningConversion(Type1, ParamArrayElementType0) Then + 'match(MethodIndex) is a less widening conversion + 'up to this argument + If MostSpecific <> 0 Then + MostSpecific = 1 + Else + MostSpecific = -1 + Exit For + End If + End If + + Else + Type0 = AmbigParams(AmbigArgIndexes(ParmIndex)).ParameterType + Type1 = Parameters(ArgIndexes(ParmIndex)).ParameterType + + If Type0 Is Type1 Then + 'Neither is more specific + + ElseIf ObjectType.IsWideningConversion(Type0, Type1) Then + 'match(0) is a more derived class than match(MethodIndex) + 'up to this argument + If MostSpecific <> 1 Then + MostSpecific = 0 + Else + MostSpecific = -1 + Exit For + End If + ElseIf ObjectType.IsWideningConversion(Type1, Type0) Then + 'match(MethodIndex) is a more derived class than match(0) + 'up to this argument + If MostSpecific <> 0 Then + MostSpecific = 1 + Else + MostSpecific = -1 + Exit For + End If + ElseIf ObjectType.IsWiderNumeric(Type0, Type1) Then + 'match(MethodIndex) is a more derived class than match(0) + 'up to this argument + If MostSpecific <> 0 Then + MostSpecific = 1 + Else + MostSpecific = -1 + Exit For + End If + ElseIf ObjectType.IsWiderNumeric(Type1, Type0) Then + 'match(0) is a more derived class than match(MethodIndex) + 'up to this argument + If MostSpecific <> 1 Then + MostSpecific = 0 + Else + MostSpecific = -1 + Exit For + End If + Else + MostSpecific = -1 + End If + End If + Next ParmIndex + + If MostSpecific = -1 Then + If (ParamArrayIndex0 = PARAMARRAY_NONE OrElse Not ParamArrayExpanded0) AndAlso ParamArrayIndex1 <> PARAMARRAY_NONE Then + If ParamArrayExpanded1 AndAlso MatchesParamArraySignature(AmbigParams, Parameters, ParamArrayIndex1, IsPropertySet, ArgCountUpperBound) Then + MostSpecific = 0 + End If + + ElseIf (ParamArrayIndex1 = PARAMARRAY_NONE OrElse Not ParamArrayExpanded1) AndAlso ParamArrayIndex0 <> PARAMARRAY_NONE Then + If ParamArrayExpanded0 AndAlso MatchesParamArraySignature(Parameters, AmbigParams, ParamArrayIndex0, IsPropertySet, ArgCountUpperBound) Then + MostSpecific = 1 + End If + + End If + + End If + + Return MostSpecific + End Function + + + Private Function MatchesParamArraySignature(ByVal param0 As ParameterInfo(), ByVal param1 As ParameterInfo(), ByVal ParamArrayIndex1 As Integer, ByVal IsPropertySet As Boolean, ByVal ArgCountUpperBound As Integer) As Boolean + + Dim i As Integer + Dim paramType0, paramType1 As Type + Dim UpperBound As Integer + + UpperBound = param0.GetUpperBound(0) + If IsPropertySet Then + UpperBound -= 1 + End If + UpperBound = System.Math.Min(UpperBound, ArgCountUpperBound) + + For i = 0 To UpperBound + + paramType0 = param0(i).ParameterType + If paramType0.IsByRef Then + paramType0 = paramType0.GetElementType() + End If + + If i >= ParamArrayIndex1 Then + paramType1 = param1(ParamArrayIndex1).ParameterType + paramType1 = paramType1.GetElementType() + Else + paramType1 = param1(i).ParameterType + If paramType1.IsByRef Then + paramType1 = paramType1.GetElementType() + End If + End If + + If Not paramType0 Is paramType1 Then + Return False + End If + Next i + + Return True + End Function + + + Private Function MethodsDifferOnlyByReturnType(ByVal match1 As MethodBase, ByVal match2 As MethodBase) As Boolean + Dim p1(), p2() As ParameterInfo + + If match1 Is match2 Then + 'Both are nothing + + End If + p1 = match1.GetParameters() + p2 = match2.GetParameters() + + + Dim i As Integer + Dim paramType1, paramType2 As Type + Dim UpperBound As Integer + + UpperBound = System.Math.Min(p1.GetUpperBound(0), p2.GetUpperBound(0)) + + For i = 0 To UpperBound + paramType1 = p1(i).ParameterType + If paramType1.IsByRef Then + paramType1 = paramType1.GetElementType() + End If + paramType2 = p2(i).ParameterType + If paramType2.IsByRef Then + paramType2 = paramType2.GetElementType() + End If + If Not paramType1 Is paramType2 Then + Return False + End If + Next i + + If p1.Length > p2.Length Then + 'Optional arguments could also cause a match + For i = UpperBound + 1 To p2.GetUpperBound(0) + If Not p1(i).IsOptional Then + Return False + End If + Next i + + ElseIf p2.Length > p1.Length Then + 'Optional arguments could also cause a match + For i = UpperBound + 1 To p1.GetUpperBound(0) + If Not p2(i).IsOptional Then + Return False + End If + Next i + + End If + + Return True + + End Function + + ' * + ' * Fields can have no arguments, so just choose the method on the outermost type + ' * + Public Overrides Function BindToField(ByVal bindingAttr As BindingFlags, ByVal match() As FieldInfo, ByVal value As Object, ByVal culture As CultureInfo) As FieldInfo + 'Only the outermost definition can be called + Dim i As Integer + + If (Not m_CachedMember Is Nothing) AndAlso (m_CachedMember.MemberType = MemberTypes.Field) AndAlso (Not match(0) Is Nothing) AndAlso _ + (match(0).Name = m_CachedMember.Name) Then + Return DirectCast(m_CachedMember, FieldInfo) + End If + + BindToField = match(0) + For i = 1 To match.GetUpperBound(0) + If match(i).DeclaringType.IsSubclassOf(BindToField.DeclaringType) Then + BindToField = match(i) + End If + Next i + + End Function + + ' * + ' * Given a of methods that match the base criteria, select a method based + ' * upon an array of types. This method should return null If no method matchs + ' * the criteria. + ' * + Public Overrides Function SelectMethod(ByVal bindingAttr As BindingFlags, ByVal match() As MethodBase, ByVal types() As Type, ByVal modifiers() As ParameterModifier) As MethodBase + Throw New NotSupportedException + End Function + + '/** + ' * Given a of propreties that match the base criteria, select one. + ' */ + Public Overrides Function SelectProperty(ByVal bindingAttr As BindingFlags, ByVal match() As PropertyInfo, ByVal returnType As Type, ByVal indexes() As Type, ByVal modifiers() As ParameterModifier) As PropertyInfo + + ' Walk all of the methods looking the most specific method based on the index types + Dim AmbiguousCount As Integer + Dim Score, LowestScore As BindScore + Dim ThisProperty As PropertyInfo + Dim PropertyIndex, ParmIndex As Integer + Dim Parameters, AmbigParams As ParameterInfo() + Dim Type0, Type1 As Type + + LowestScore = BindScore.Unknown + AmbiguousCount = 0 + + ' Score each method + ' 0 ==> Exact match + ' 1 ==> Casting down conversion required + ' 2 ==> Intrinsic widening conversion required + ' 3 ==> Requires narrowing conversion + + For PropertyIndex = 0 To match.GetUpperBound(0) + + ThisProperty = match(PropertyIndex) + + If ThisProperty Is Nothing Then + 'Skip it + Else + + Score = BindingScore(ThisProperty.GetIndexParameters(), Nothing, indexes, False, PARAMARRAY_NONE) + + If Score < LowestScore Then + If PropertyIndex <> 0 Then + match(0) = match(PropertyIndex) + match(PropertyIndex) = Nothing + End If + AmbiguousCount = 1 + LowestScore = Score + + ElseIf Score = LowestScore Then + + If Score = BindScore.Widening1 Then + + 'It is possible that one is more + ' precise than another + Dim MostSpecific As Integer = -1 + Dim Index0, Index1 As Integer + + Parameters = ThisProperty.GetIndexParameters() + + AmbigParams = match(0).GetIndexParameters() + + MostSpecific = -1 + + For ParmIndex = 0 To Parameters.GetUpperBound(0) + Index0 = ParmIndex + Index1 = ParmIndex + If Index0 = -1 OrElse Index1 = -1 Then + 'Both are optional and thus equal + Else + Type0 = AmbigParams(Index0).ParameterType + Type1 = Parameters(Index1).ParameterType + + If ObjectType.IsWideningConversion(Type0, Type1) Then + 'match(0) is a less widening conversion + 'up to this argument + If MostSpecific <> 1 Then + MostSpecific = 0 + Else + MostSpecific = -1 + Exit For + End If + ElseIf ObjectType.IsWideningConversion(Type1, Type0) Then + 'match(PropertyIndex) is a less widening conversion + 'up to this argument + If MostSpecific <> 0 Then + MostSpecific = 1 + Else + MostSpecific = -1 + Exit For + End If + End If + End If + + Next ParmIndex + + If MostSpecific = -1 Then + If AmbiguousCount <> PropertyIndex Then + match(AmbiguousCount) = match(PropertyIndex) + match(PropertyIndex) = Nothing + End If + AmbiguousCount += 1 + + ElseIf MostSpecific = 0 Then + AmbiguousCount = 1 + + Else 'If MostSpecific = 1 Then + If PropertyIndex <> 0 Then + match(0) = match(PropertyIndex) + 'paramOrder(0) = paramOrder(PropertyIndex) + 'ParamArrayIndexList(0) = ParamArrayIndexList(PropertyIndex) + match(PropertyIndex) = Nothing + End If + AmbiguousCount = 1 + End If + + ElseIf Score = BindScore.Exact Then + + 'Must be a shadowed member + If ThisProperty.DeclaringType.IsSubclassOf(match(0).DeclaringType) Then + If PropertyIndex <> 0 Then + match(0) = match(PropertyIndex) + match(PropertyIndex) = Nothing + End If + AmbiguousCount = 1 + + ElseIf match(0).DeclaringType.IsSubclassOf(ThisProperty.DeclaringType) Then + 'Keep the first match + + Else + If AmbiguousCount <> PropertyIndex Then + match(AmbiguousCount) = match(PropertyIndex) + match(PropertyIndex) = Nothing + End If + AmbiguousCount += 1 + + End If + + Else + If AmbiguousCount <> PropertyIndex Then + match(AmbiguousCount) = match(PropertyIndex) + match(PropertyIndex) = Nothing + End If + AmbiguousCount += 1 + End If + + Else + ' We don't care it is less specific + match(PropertyIndex) = Nothing + End If + + End If + + Next PropertyIndex + + If AmbiguousCount = 1 Then + Return match(0) + Else 'If AmbiguousCount = 0 Then + Return Nothing + End If + + End Function + + '/** + ' * ChangeType + ' * The default binder doesn't support any change type functionality. + ' * This is because the default is built into the low level invoke code. + ' */ + Public Overrides Function ChangeType(ByVal value As Object, ByVal typ As Type, ByVal culture As CultureInfo) As Object + + Try + If (typ Is GetType(System.Object)) OrElse (typ.IsByRef AndAlso typ.GetElementType() Is GetType(System.Object)) Then + Return value + Else + Return ObjectType.CTypeHelper(value, typ) + End If + Catch ex As Exception + Throw New InvalidCastException(GetResourceString(ResID.InvalidCast_FromTo, VBFriendlyName(value), VBFriendlyName(typ))) + End Try + End Function + + Private Function BindingScore(ByVal Parameters() As ParameterInfo, ByVal paramOrder() As Integer, ByVal ArgTypes() As Type, ByVal IsPropertySet As Boolean, ByVal ParamArrayIndex As Integer) As BindScore + + Dim Score As BindScore + Dim ArgType, ParmType As Type + Dim ArgIndex, ParmIndex As Integer + Dim LastArgNonSetValueIndex, LastParamNonSetValueIndex As Integer + + Score = BindScore.Exact + + LastArgNonSetValueIndex = ArgTypes.GetUpperBound(0) + LastParamNonSetValueIndex = Parameters.GetUpperBound(0) + If IsPropertySet Then + LastParamNonSetValueIndex -= 1 + LastArgNonSetValueIndex -= 1 + End If + + For ParmIndex = 0 To Math.Max(LastArgNonSetValueIndex, LastParamNonSetValueIndex) + + If paramOrder Is Nothing Then + ArgIndex = ParmIndex + Else + ArgIndex = paramOrder(ParmIndex) + End If + + If ArgIndex = -1 Then + ArgType = Nothing + Else + ArgType = ArgTypes(ArgIndex) + End If + + If ArgType Is Nothing Then + 'Treat as zero + + Else + If ParmIndex > LastParamNonSetValueIndex Then + ParmType = Parameters(ParamArrayIndex).ParameterType + Else + ParmType = Parameters(ParmIndex).ParameterType + End If + + If ParmIndex = ParamArrayIndex AndAlso ArgType.IsArray() AndAlso ParmType Is ArgType Then + 'BindScore.Exact - default, don't overwrite current value + ElseIf ParmIndex = ParamArrayIndex AndAlso ArgType.IsArray() AndAlso _ + (m_state.m_OriginalArgs Is Nothing OrElse m_state.m_OriginalArgs(ArgIndex) Is Nothing OrElse ParmType.IsInstanceOfType(m_state.m_OriginalArgs(ArgIndex))) Then + If Score < BindScore.Widening1 Then + Score = BindScore.Widening1 + End If + Else + If ParamArrayIndex <> ARG_MISSING AndAlso ParmIndex >= ParamArrayIndex OrElse ParmType.IsByRef Then + ParmType = ParmType.GetElementType() + End If + + ' If the two types are exact move on... + If (ArgType Is ParmType) Then + 'BindScore.Exact - default, don't overwrite current value + + ElseIf ObjectType.IsWideningConversion(ArgType, ParmType) Then + If Score < BindScore.Widening1 Then + Score = BindScore.Widening1 + End If + + 'This ElseIf is most likely covered by the above IsWidening call + ElseIf ArgType.IsArray() AndAlso _ + (m_state.m_OriginalArgs Is Nothing OrElse m_state.m_OriginalArgs(ArgIndex) Is Nothing OrElse ParmType.IsInstanceOfType(m_state.m_OriginalArgs(ArgIndex))) Then + If Score < BindScore.Widening1 Then + Score = BindScore.Widening1 + End If + + Else + Score = BindScore.Narrowing + + End If + End If + + + End If + + Next ParmIndex + + Return Score + + End Function + + ' This method will sort the vars array into the mapping order stored + ' in the paramOrder array. + Private Sub ReorderParams(ByVal paramOrder() As Integer, ByVal vars() As Object, ByVal state As VBBinderState) + + 'CONSIDER: write more efficient code for this + Dim i As Integer + 'paramOrder.GetUpperBound(0) should always be the MAX + Dim ArrayUBound As Integer = Math.Max(vars.GetUpperBound(0), paramOrder.GetUpperBound(0)) + + state.m_OriginalParamOrder = New Integer(ArrayUBound) {} + + For i = 0 To ArrayUBound + + state.m_OriginalParamOrder(i) = paramOrder(i) + + Next i + + End Sub + + ' This method will create the mapping between the Parameters and the underlying + ' data based upon the names array. The names array is stored in the same order + ' as the values and maps to the parameters of the method. We store the mapping + ' from the parameters to the names in the paramOrder array. All parameters that + ' don't have matching names are then stored in the array in order. + Private Function CreateParamOrder(ByVal SetProp As Boolean, ByVal paramOrder() As Integer, ByVal pars() As ParameterInfo, ByVal args() As Object, ByVal names() As String) As Exception + + Dim used() As Boolean = New Boolean(pars.Length - 1) {} + Dim i, j As Integer + Dim LastUnnamedIndex As Integer = (args.Length - names.Length - 1) + Dim LastNonSetIndex As Integer = pars.GetUpperBound(0) + + ' Mark which parameters have not been found in the names list + For i = 0 To pars.GetUpperBound(0) + paramOrder(i) = ARG_MISSING + Next i + + If SetProp Then + 'The last unnamed argument is the Set value + ' and cannot be moved from that spot + paramOrder(pars.GetUpperBound(0)) = args.GetUpperBound(0) + LastUnnamedIndex -= 1 + LastNonSetIndex -= 1 + End If + + 'Unnamed parameters must be used as the first arguments + For i = 0 To LastUnnamedIndex + paramOrder(i) = names.Length + i + Next i + + ' Find the parameters with names. + For i = 0 To names.GetUpperBound(0) + + For j = 0 To LastNonSetIndex + + If StrComp(names(i), pars(j).Name, CompareMethod.Text) = 0 Then + + If paramOrder(j) <> -1 Then + Return New ArgumentException(GetResourceString(ResID.NamedArgumentAlreadyUsed1, pars(j).Name)) + End If + + paramOrder(j) = i + used(i) = True + Exit For + + End If + + Next j + + ' This is an error condition. The name was not found. This + ' method must not match what we sent. + If (j > LastNonSetIndex) Then + 'Should no longer hit this, since we removed all these cases in a previous step + Return New MissingMemberException(GetResourceString(ResID.Argument_InvalidNamedArg2, names(i), CalledMethodName())) + End If + + Next i + + Return Nothing + + End Function + + _ + _ + Friend Function InvokeMember(ByVal name As String, _ + ByVal invokeAttr As BindingFlags, _ + ByVal objType As System.Type, _ + ByVal objIReflect As IReflect, _ + ByVal target As Object, _ + ByVal args As Object(), _ + ByVal namedParameters As String()) As Object + + Dim i As Integer + + If objType.IsCOMObject() Then + Dim modifiers As ParameterModifier() = Nothing + + If (Not m_ByRefFlags Is Nothing) AndAlso (Not target Is Nothing) AndAlso _ + (Not System.Runtime.Remoting.RemotingServices.IsTransparentProxy(target)) Then + + Dim pmTemp As Reflection.ParameterModifier = New Reflection.ParameterModifier(args.Length) + modifiers = New Reflection.ParameterModifier() {pmTemp} + 'Set all flags to ByRef + Dim MissingValue As Object = System.Reflection.Missing.Value + For i = 0 To args.GetUpperBound(0) + If args(i) Is MissingValue Then + 'Missing type, don't set as byref + Else + pmTemp.Item(i) = m_ByRefFlags(i) + End If + Next i + End If + + Try + ' + 'BEGIN: SECURITY SECURITY SECURITY SECURITY SECURITY SECURITY SECURITY SECURITY + ' + Call (New SecurityPermission(PermissionState.Unrestricted)).Demand() + Return objIReflect.InvokeMember(name, invokeAttr, Nothing, target, args, modifiers, Nothing, namedParameters) + + ' + 'END: SECURITY SECURITY SECURITY SECURITY SECURITY SECURITY SECURITY SECURITY + ' + Catch ex As MissingMemberException + Throw New MissingMemberException(GetResourceString(ResID.MissingMember_MemberNotFoundOnType2, name, VBFriendlyName(objType))) + End Try + End If + + m_BindToName = name + m_objType = objType + +#If DEBUG Then + If (Not namedParameters Is Nothing) Then + For i = 0 To namedParameters.GetUpperBound(0) + If (namedParameters(i) Is Nothing) Then + Diagnostics.Debug.Assert(False, "Should never be reached") + Throw New ArgumentException + End If + Next i + End If +#End If + + Debug.Assert((invokeAttr And BindingFlags.CreateInstance) = 0, "CreateInstance not supported") + ' For fields, methods and properties the name must be specified. + Debug.Assert(Not name Is Nothing, "Invalid argument") + + ' if we are looking for the default member, find it... + If (name.Length = 0) Then + If (objType Is objIReflect) Then + name = GetDefaultMemberName(objType) + If (name Is Nothing) Then + Throw New MissingMemberException(GetResourceString(ResID.MissingMember_NoDefaultMemberFound1, VBFriendlyName(objType))) + End If + Else + ' IReflect case, we pass in empty string so that user implementation can return default members determined at run time + name = "" + End If + End If + + Dim p As MethodBase() + Dim invokeMethod As MethodBase + + p = GetMethodsByName(objType, objIReflect, name, invokeAttr) + If (args Is Nothing) Then + args = New Object() {} + End If + + Dim binderState As Object = Nothing + invokeMethod = Me.BindToMethod(invokeAttr, p, args, Nothing, Nothing, namedParameters, binderState) + If (invokeMethod Is Nothing) Then + Throw New MissingMemberException(GetResourceString(ResID.NoMethodTakingXArguments2, CalledMethodName(), CStr(GetPropArgCount(args, (invokeAttr And BindingFlags.SetProperty) <> 0)))) + End If + + ' + 'BEGIN: SECURITY SECURITY SECURITY SECURITY SECURITY SECURITY SECURITY SECURITY + ' + SecurityCheckForLateboundCalls(invokeMethod, objType, objIReflect) + + Dim Method As MethodInfo = DirectCast(invokeMethod, MethodInfo) + Dim res As Object + If objType Is objIReflect OrElse Method.IsStatic OrElse _ + DoesTargetObjectMatch(target, Method) Then + + VerifyObjRefPresentForInstanceCall(target, Method) + + res = Method.Invoke(target, args) + + Else + res = InvokeMemberOnIReflect(objIReflect, Method, BindingFlags.InvokeMethod, target, args) + End If + + ' + 'END: SECURITY SECURITY SECURITY SECURITY SECURITY SECURITY SECURITY SECURITY + ' + + If (Not binderState Is Nothing) Then + Me.ReorderArgumentArray(args, binderState) + End If + Return res + + End Function + + + + Private Function GetDefaultMemberName(ByVal typ As Type) As String + + Dim attributeList As Object() + + Do + attributeList = typ.GetCustomAttributes(GetType(DefaultMemberAttribute), False) + If (Not attributeList Is Nothing) AndAlso (attributeList.Length <> 0) Then + Return CType(attributeList(0), DefaultMemberAttribute).MemberName + End If + typ = typ.BaseType + Loop While (Not typ Is Nothing) + + Return Nothing + + End Function + + + Private Function GetMethodsByName(ByVal objType As System.Type, ByVal objIReflect As IReflect, ByVal name As String, ByVal invokeAttr As BindingFlags) As MethodBase() + + Dim mi As MemberInfo() + Dim mb As MethodBase() + Dim ThisMember As MemberInfo + Dim MemberIndex As Integer + Dim ThisMethod As MethodInfo + Dim ThisProperty As PropertyInfo + Dim DeclaringType As System.Type + Dim RemovedCount As Integer + + mi = objIReflect.GetMember(name, invokeAttr) + + ' Filter out generic methods for compatibility with the Whidbey framework + mi = GetNonGenericMembers(mi) + + If mi Is Nothing Then + Return Nothing + End If + + For MemberIndex = 0 To mi.GetUpperBound(0) + + ThisMember = mi(MemberIndex) + + If ThisMember Is Nothing Then + 'Skip this one + + ElseIf ThisMember.MemberType = MemberTypes.Field Then + 'Filter all subclass members + ' + 'Run through the list and remove all the inherited members of this type + ' + DeclaringType = ThisMember.DeclaringType + + Dim j As Integer + + For j = 0 To mi.GetUpperBound(0) + + If MemberIndex <> j AndAlso (Not mi(j) Is Nothing) AndAlso _ + DeclaringType.IsSubclassOf(mi(j).DeclaringType) Then + ' ThisMember Shadows the baseclass and ThatMethod should not be accessible + ' to the caller + mi(j) = Nothing + RemovedCount += 1 + End If + + Next j + + ElseIf ThisMember.MemberType = MemberTypes.Method Then + + 'Filter all subclass members + ThisMethod = CType(ThisMember, MethodInfo) + + If ThisMethod.IsHideBySig Then + 'Hide-by-sig - shadows exact name and sig on base types + ' + 'Don't bother filtering here, this will get done below for this case + + + 'Non-virtual members shadow baseclass methods + 'Virtual members with newslot flag shadow baseclass methods ' + 'Virtual members whose base definition has the newslot attribute shadow baseclass methods + ElseIf Not ThisMethod.IsVirtual OrElse _ + (ThisMethod.IsVirtual AndAlso ((ThisMethod.Attributes And MethodAttributes.NewSlot) <> 0)) OrElse _ + (ThisMethod.IsVirtual AndAlso ((ThisMethod.GetBaseDefinition().Attributes And MethodAttributes.NewSlot) <> 0)) Then + ' + 'Run through the list and remove all the inherited members of this type + ' + Dim j As Integer + + DeclaringType = ThisMember.DeclaringType + + For j = 0 To mi.GetUpperBound(0) + + If MemberIndex <> j AndAlso (Not mi(j) Is Nothing) AndAlso _ + DeclaringType.IsSubclassOf(mi(j).DeclaringType) Then + ' ThisMember Shadows the baseclass and ThatMethod should not be accessible + ' to the caller + mi(j) = Nothing + RemovedCount += 1 + End If + + Next j + + End If + + ElseIf ThisMember.MemberType = MemberTypes.Property Then + + ThisProperty = CType(ThisMember, PropertyInfo) + + + 'Filter out all shadowed members first + Dim i As Integer + + For i = 1 To 2 + + If i = 1 Then + ThisMethod = ThisProperty.GetGetMethod() + Else + ThisMethod = ThisProperty.GetSetMethod() + End If + + If ThisMethod Is Nothing Then + + ElseIf ThisMethod.IsHideBySig Then + 'Hide-by-sig - shadows exact name and sig on base types + ' + 'Don't bother filtering here, this will get done below for this case + + ElseIf Not ThisMethod.IsVirtual OrElse _ + (ThisMethod.IsVirtual AndAlso ((ThisMethod.Attributes And MethodAttributes.NewSlot) <> 0)) Then + ' + 'Run through the list and remove all the inherited members of this type + ' + Dim j As Integer + + DeclaringType = ThisMember.DeclaringType + + For j = 0 To mi.GetUpperBound(0) + + If MemberIndex <> j AndAlso (Not mi(j) Is Nothing) AndAlso _ + DeclaringType.IsSubclassOf(mi(j).DeclaringType) Then + ' ThisMember Shadows the baseclass and ThatMethod should not be accessible + ' to the caller + mi(j) = Nothing + RemovedCount += 1 + End If + + Next j + + End If + Next i + + If (invokeAttr And BindingFlags.GetProperty) <> 0 Then + ThisMethod = ThisProperty.GetGetMethod() + + ElseIf (invokeAttr And BindingFlags.SetProperty) <> 0 Then + ThisMethod = ThisProperty.GetSetMethod() + + Else + ThisMethod = Nothing + End If + + If ThisMethod Is Nothing Then + RemovedCount += 1 + End If + mi(MemberIndex) = ThisMethod + + ElseIf ThisMember.MemberType = MemberTypes.NestedType Then + + 'Remove all shadowed members base types + Dim j As Integer + + DeclaringType = ThisMember.DeclaringType + + For j = 0 To mi.GetUpperBound(0) + + If MemberIndex <> j AndAlso (Not mi(j) Is Nothing) AndAlso _ + DeclaringType.IsSubclassOf(mi(j).DeclaringType) Then + ' ThisMember Shadows the baseclass and ThatMethod should not be accessible + ' to the caller + mi(j) = Nothing + RemovedCount += 1 + End If + + Next j + + If RemovedCount = mi.Length - 1 Then + Throw New ArgumentException(GetResourceString(ResID.Argument_IllegalNestedType2, name, VBFriendlyName(objType))) + End If + mi(MemberIndex) = Nothing 'Remove the nested class, since we cannot use it + RemovedCount += 1 + + End If + Next + + 'Compact the list + Dim NewSize As Integer = mi.Length - RemovedCount + + mb = New MethodBase(NewSize - 1) {} + Dim TargetIndex As Integer = 0 + For Index As Integer = 0 To mi.Length - 1 + If Not mi(Index) Is Nothing Then + mb(TargetIndex) = CType(mi(Index), MethodBase) + TargetIndex += 1 + End If + Next + + Return mb + + End Function + + + Friend Function CalledMethodName() As String + Debug.Assert((Not m_objType Is Nothing) AndAlso (Not m_BindToName Is Nothing)) + Return m_objType.Name & "." & m_BindToName + End Function + + ' + 'BEGIN: SECURITY SECURITY SECURITY SECURITY SECURITY SECURITY SECURITY SECURITY + ' + Friend Shared Sub SecurityCheckForLateboundCalls(ByVal member As MemberInfo, ByVal objType As Type, ByVal objIReflect As IReflect) + + Dim declaringType As System.Type + + ' If we are using User provided IReflect Implementation instead of System.Type's IReflect Implementation and + ' the Member Info is not public, then throw exception - VB latebinding only supports access of public members + If (Not objType Is objIReflect) AndAlso (Not IsMemberPublic(member)) Then + 'No message text intentional - will get rethrown with more informative message + Throw New MissingMethodException + End If + + declaringType = member.DeclaringType + + + ' VSW#430608: For nested types IsNotPublic doesn't return the right value so + ' we need to use Not IsPublic. + ' + ' The following code will only allow calls to members of top level public types + ' in the runtime library. Read the reflection documentation and test with + ' nested types before changing this code. + + If Not declaringType.IsPublic Then + 'Disallow latebound calls to internal Microsoft.VisualBasic types + If declaringType.Assembly Is Utils.VBRuntimeAssembly Then + 'No message text intentional - will get rethrown with more informative message + Throw New MissingMethodException + End If + End If + + End Sub + + Private Shared Function IsMemberPublic(ByVal Member As MemberInfo) As Boolean + Debug.Assert(Not Member Is Nothing, "How can this be Nothing ?") + + Select Case Member.MemberType + Case MemberTypes.Method + Return DirectCast(Member, MethodInfo).IsPublic + + Case MemberTypes.Field + Return DirectCast(Member, FieldInfo).IsPublic + + Case MemberTypes.Constructor + Return DirectCast(Member, ConstructorInfo).IsPublic + + Case MemberTypes.Property + Debug.Assert(False, "How can a property get here ?") + ' We always decided based on the context and use the get or the set accessor + ' appropriately. So by the time this method is invoked, we should just see + ' the MethodInfos of the Getter or the Setter + Return False + + Case Else + ' No Assert here because users implementation of IReflect could return some bad stuff + ' return False here because VB Latebinding only supports Fields, Properties and Methods + Return False + End Select + End Function + + ' + 'END: SECURITY SECURITY SECURITY SECURITY SECURITY SECURITY SECURITY SECURITY + ' + + Friend Sub CacheMember(ByVal member As MemberInfo) + m_CachedMember = member + End Sub + End Class + +#End Region + +End Namespace + diff --git a/Microsoft.VisualBasic/runtime/msvbalib/Helpers/VBInputBox.resx b/Microsoft.VisualBasic/runtime/msvbalib/Helpers/VBInputBox.resx new file mode 100644 index 000000000..d88d19b31 --- /dev/null +++ b/Microsoft.VisualBasic/runtime/msvbalib/Helpers/VBInputBox.resx @@ -0,0 +1,261 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Assembly + + + + + 290, 12 + + + + 60, 22 + + + + + 2 + + + + OK + + + OKButton + + + System.Windows.Forms.Button, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + $this + + + 2 + + + Assembly + + + + 290, 40 + + + + 60, 22 + + + + 3 + + + + Cancel + + + MyCancelButton + + + System.Windows.Forms.Button, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + $this + + + 3 + + + Assembly + + + + 10, 90 + + + + 335, 20 + + + + 0 + + + + TextBox + + + System.Windows.Forms.TextBox, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + $this + + + 0 + + + Assembly + + + + 10, 10 + + + + 250, 70 + + + + 1 + + + + Label + + + System.Windows.Forms.Label, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + $this + + + 1 + + + True + + + + 5, 13 + + + + 353, 120 + + + + VBInputBox + + + System.Windows.Forms.Form, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + # + + + # + + \ No newline at end of file diff --git a/Microsoft.VisualBasic/runtime/msvbalib/Helpers/VBInputBox.vb b/Microsoft.VisualBasic/runtime/msvbalib/Helpers/VBInputBox.vb new file mode 100644 index 000000000..72019be2c --- /dev/null +++ b/Microsoft.VisualBasic/runtime/msvbalib/Helpers/VBInputBox.vb @@ -0,0 +1,147 @@ +' Copyright (c) Microsoft Corporation. All rights reserved. + +Imports Microsoft.VisualBasic +Imports System +Imports System.Security +Imports System.Security.Permissions +Imports System.Globalization +Imports System.Text +Imports System.Collections +Imports System.Threading +Imports System.Runtime.InteropServices +Imports System.Diagnostics + +Imports System.ComponentModel +Imports System.Drawing +Imports System.Windows.Forms + +Imports Microsoft.Win32 +Imports Microsoft.VisualBasic.CompilerServices + +Namespace Microsoft.VisualBasic.CompilerServices + + Friend NotInheritable Class VBInputBox + Inherits System.Windows.Forms.Form + + Private components As System.ComponentModel.Container + Private TextBox As System.Windows.Forms.TextBox + Private Label As System.Windows.Forms.Label + Private OKButton As System.Windows.Forms.Button + Private MyCancelButton As System.Windows.Forms.Button + Public Output As String = "" + + 'This constructor needed to be able to show the designer at designtime. + Friend Sub New() + MyBase.New() + InitializeComponent() + End Sub + + Friend Sub New(ByVal Prompt As String, ByVal Title As String, ByVal DefaultResponse As String, ByVal XPos As Integer, ByVal YPos As Integer) + MyBase.New() + InitializeComponent() + InitializeInputBox(Prompt, Title, DefaultResponse, XPos, YPos) + End Sub + + + Protected Overloads Overrides Sub Dispose(ByVal disposing As Boolean) + If disposing Then + If Not (components Is Nothing) Then + components.Dispose() + End If + End If + MyBase.Dispose(disposing) + End Sub + + + Private Sub InitializeComponent() + Dim resources As System.ComponentModel.ComponentResourceManager = New System.ComponentModel.ComponentResourceManager(GetType(VBInputBox)) + Me.OKButton = New System.Windows.Forms.Button + Me.MyCancelButton = New System.Windows.Forms.Button + Me.TextBox = New System.Windows.Forms.TextBox + Me.Label = New System.Windows.Forms.Label + Me.SuspendLayout() + ' + 'OKButton + ' + resources.ApplyResources(Me.OKButton, "OKButton", CultureInfo.CurrentUICulture) + Me.OKButton.Name = "OKButton" + ' + 'MyCancelButton + ' + Me.MyCancelButton.DialogResult = System.Windows.Forms.DialogResult.Cancel + resources.ApplyResources(Me.MyCancelButton, "MyCancelButton", CultureInfo.CurrentUICulture) + Me.MyCancelButton.Name = "MyCancelButton" + ' + 'TextBox + ' + resources.ApplyResources(Me.TextBox, "TextBox", CultureInfo.CurrentUICulture) + Me.TextBox.Name = "TextBox" + ' + 'Label + ' + resources.ApplyResources(Me.Label, "Label", CultureInfo.CurrentUICulture) + Me.Label.Name = "Label" + ' + 'VBInputBox + ' + Me.AcceptButton = Me.OKButton + resources.ApplyResources(Me, "$this", CultureInfo.CurrentUICulture) + Me.CancelButton = Me.MyCancelButton + Me.Controls.Add(Me.TextBox) + Me.Controls.Add(Me.Label) + Me.Controls.Add(Me.OKButton) + Me.Controls.Add(Me.MyCancelButton) + Me.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedDialog + Me.MaximizeBox = False + Me.MinimizeBox = False + Me.Name = "VBInputBox" + Me.ResumeLayout(False) + Me.PerformLayout() + + End Sub + + 'Initialize labels etc from the args passed in to InputBox() + Private Sub InitializeInputBox(ByVal Prompt As String, ByVal Title As String, ByVal DefaultResponse As String, ByVal XPos As Integer, ByVal YPos As Integer) + Me.Text = Title + Label.Text = Prompt + TextBox.Text = DefaultResponse + AddHandler OKButton.Click, AddressOf Me.OKButton_Click + AddHandler MyCancelButton.Click, AddressOf Me.MyCancelButton_Click + + 'Re-size the dialog if the prompt is too large + Dim LabelGraphics As Graphics = Label.CreateGraphics + Dim LabelSizeNeeded As SizeF = LabelGraphics.MeasureString(Prompt, Label.Font, Label.Width) + LabelGraphics.Dispose() + If LabelSizeNeeded.Height > Label.Height Then + 'The current label size is not large enough to accommodate the prompt. We need + ' to expand the label and the dialog, and move the textbox to make room. + Dim DialogHeightChange As Integer = CInt(LabelSizeNeeded.Height) - Label.Height + Label.Height += DialogHeightChange + TextBox.Top += DialogHeightChange + Me.Height += DialogHeightChange + End If + + 'Position the form + If (XPos = -1) AndAlso (YPos = -1) Then + Me.StartPosition = FormStartPosition.CenterScreen + Else + If (XPos = -1) Then XPos = 600 + If (YPos = -1) Then YPos = 350 + Me.StartPosition = FormStartPosition.Manual + Me.DesktopLocation = New Point(XPos, YPos) + End If + End Sub + + + Private Sub OKButton_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) + Output = TextBox.Text + Me.Close() + End Sub + + + Private Sub MyCancelButton_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) + Me.Close() + End Sub + End Class + +End Namespace diff --git a/Microsoft.VisualBasic/runtime/msvbalib/Helpers/VBResourceID.vb b/Microsoft.VisualBasic/runtime/msvbalib/Helpers/VBResourceID.vb new file mode 100644 index 000000000..a4fe1f6ba --- /dev/null +++ b/Microsoft.VisualBasic/runtime/msvbalib/Helpers/VBResourceID.vb @@ -0,0 +1,304 @@ +' Copyright (c) Microsoft Corporation. All rights reserved. + +Namespace Microsoft.VisualBasic.CompilerServices + '************************************************************************** + ';ResID + ' + 'Remarks: + ' This class is use internally inside this Dll. It contains the constant + ' strings matching the resource name defined in Microsoft.VisualBasic.txt. + ' The purpose is to reduce typing errors. Everytime you add a new string + ' resource to Microsoft.VisualBasic.txt, add a constant with the same name + ' of your string resource name to this class. For example, if your + ' string resource name is YourResourceName, add a constant like this + ' Const YourResourceName As String = "YourResourceName" + ' Then you can access the resource using + ' ResourceLoader.GetString(ResourceID.YourResourceName) + ' Note that this class is divided into two sections: The runtime ids and + ' the My.Net ids. All My.Net ids go in the nested MY class + '************************************************************************** +#If TELESTO Then + 'FIXME: _ + Friend NotInheritable Class ResID +#Else + _ + Friend NotInheritable Class ResID +#End If + +#If Not TELESTO Then + Friend Const Argument_InvalidVbStrConv As String = "Argument_InvalidVbStrConv" + Friend Const Argument_StrConvSCandTC As String = "Argument_StrConvSCandTC" + Friend Const Argument_SCNotSupported As String = "Argument_SCNotSupported" + Friend Const Argument_TCNotSupported As String = "Argument_TCNotSupported" + Friend Const Argument_JPNNotSupported As String = "Argument_JPNNotSupported" + Friend Const Argument_IllegalWideNarrow As String = "Argument_IllegalWideNarrow" + Friend Const Argument_LocalNotSupported As String = "Argument_LocalNotSupported" + Friend Const Argument_WideNarrowNotApplicable As String = "Argument_WideNarrowNotApplicable" + Friend Const Argument_IllegalKataHira As String = "Argument_IllegalKataHira" + Friend Const Argument_PathNullOrEmpty As String = "Argument_PathNullOrEmpty" + Friend Const Argument_PathNullOrEmpty1 As String = "Argument_PathNullOrEmpty1" + Friend Const Argument_InvalidPathChars1 As String = "Argument_InvalidPathChars1" + Friend Const FileSystem_IllegalInputAccess As String = "FileSystem_IllegalInputAccess" + Friend Const FileSystem_IllegalOutputAccess As String = "FileSystem_IllegalOutputAccess" + Friend Const FileSystem_IllegalAppendAccess As String = "FileSystem_IllegalAppendAccess" + Friend Const FileSystem_FileAlreadyOpen1 As String = "FileSystem_FileAlreadyOpen1" + Friend Const DIR_IllegalCall As String = "DIR_IllegalCall" + Friend Const KILL_NoFilesFound1 As String = "KILL_NoFilesFound1" + Friend Const FileSystem_DriveNotFound1 As String = "FileSystem_DriveNotFound1" + Friend Const FileSystem_FileNotFound1 As String = "FileSystem_FileNotFound1" + Friend Const FileSystem_PathNotFound1 As String = "FileSystem_PathNotFound1" + Friend Const Financial_CalcDivByZero As String = "Financial_CalcDivByZero" + Friend Const Financial_CannotCalculateNPer As String = "Financial_CannotCalculateNPer" + Friend Const Financial_CannotCalculateRate As String = "Financial_CannotCalculateRate" + Friend Const Rate_NPerMustBeGTZero As String = "Rate_NPerMustBeGTZero" + Friend Const PPMT_PerGT0AndLTNPer As String = "PPMT_PerGT0AndLTNPer" + Friend Const Financial_LifeNEZero As String = "Financial_LifeNEZero" + Friend Const Financial_ArgGEZero1 As String = "Financial_ArgGEZero1" + Friend Const Financial_ArgGTZero1 As String = "Financial_ArgGTZero1" + Friend Const Financial_PeriodLELife As String = "Financial_PeriodLELife" + Friend Const Argument_Range1toFF1 As String = "Argument_Range1toFF1" + Friend Const Interaction_ResKeyNotCreated1 As String = "Interaction_ResKeyNotCreated1" + Friend Const Argument_LCIDNotSupported1 As String = "Argument_LCIDNotSupported1" + Friend Const ProcessNotFound As String = "ProcessNotFound" + Friend Const SetLocalDateFailure As String = "SetLocalDateFailure" + Friend Const SetLocalTimeFailure As String = "SetLocalTimeFailure" + Friend Const Argument_UnsupportedFieldType2 As String = "Argument_UnsupportedFieldType2" + Friend Const Argument_UnsupportedIOType1 As String = "Argument_UnsupportedIOType1" + Friend Const UseFilePutObject As String = "UseFilePutObject" + Friend Const FileIO_StringLengthExceeded As String = "FileIO_StringLengthExceeded" + Friend Const MissingMember_MemberSetNotFoundOnType2 As String = "MissingMember_MemberSetNotFoundOnType2" + Friend Const MissingMember_MemberLetNotFoundOnType2 As String = "MissingMember_MemberLetNotFoundOnType2" + Friend Const Argument_InvalidNamedArg2 As String = "Argument_InvalidNamedArg2" + Friend Const NoMethodTakingXArguments2 As String = "NoMethodTakingXArguments2" + Friend Const AmbiguousCall2 As String = "AmbiguousCall2" + Friend Const AmbiguousCall_WideningConversion2 As String = "AmbiguousCall_WideningConversion2" + Friend Const NamedArgumentAlreadyUsed1 As String = "NamedArgumentAlreadyUsed1" + Friend Const NamedArgumentOnParamArray As String = "NamedArgumentOnParamArray" + Friend Const LinguisticRequirements As String = "LinguisticRequirements" + Friend Const Argument_ArrayNotInitialized As String = "Argument_ArrayNotInitialized" + Friend Const InvalidCast_FromToArg4 As String = "InvalidCast_FromToArg4" + Friend Const Argument_ArrayDimensionsDontMatch As String = "Argument_ArrayDimensionsDontMatch" + Friend Const AmbiguousMatch_NarrowingConversion1 As String = "AmbiguousMatch_NarrowingConversion1" + Friend Const AmbiguousCall_ExactMatch2 As String = "AmbiguousCall_ExactMatch2" + Friend Const Invalid_VBFixedArray As String = "Invalid_VBFixedArray" + Friend Const Invalid_VBFixedString As String = "Invalid_VBFixedString" + Friend Const Argument_UnsupportedArrayDimensions As String = "Argument_UnsupportedArrayDimensions" + Friend Const Argument_InvalidFixedLengthString As String = "Argument_InvalidFixedLengthString" + Friend Const Argument_IllegalNestedType2 As String = "Argument_IllegalNestedType2" + Friend Const Argument_PutObjectOfValueType1 As String = "Argument_PutObjectOfValueType1" + Friend Const FileOpenedNoRead As String = "FileOpenedNoRead" + Friend Const FileOpenedNoWrite As String = "FileOpenedNoWrite" + Friend Const Security_LateBoundCallsNotPermitted As String = "Security_LateBoundCallsNotPermitted" + Friend Const Serialization_MissingCultureInfo As String = "Serialization_MissingCultureInfo" + Friend Const Serialization_MissingKeys As String = "Serialization_MissingKeys" + Friend Const Serialization_MissingValues As String = "Serialization_MissingValues" + Friend Const Serialization_KeyValueDifferentSizes As String = "Serialization_KeyValueDifferentSizes" + Friend Const NoValidOperator_OneOperand As String = "NoValidOperator_OneOperand" + Friend Const NoValidOperator_TwoOperands As String = "NoValidOperator_TwoOperands" +#End If + + Friend Const [False] As String = "False" + Friend Const [True] As String = "True" + Friend Const Argument_GEZero1 As String = "Argument_GEZero1" + Friend Const Argument_GTZero1 As String = "Argument_GTZero1" + Friend Const Argument_LengthGTZero1 As String = "Argument_LengthGTZero1" + Friend Const Argument_RangeTwoBytes1 As String = "Argument_RangeTwoBytes1" + Friend Const Argument_MinusOneOrGTZero1 As String = "Argument_MinusOneOrGTZero1" + Friend Const Argument_GEMinusOne1 As String = "Argument_GEMinusOne1" + Friend Const Argument_GEOne1 As String = "Argument_GEOne1" + Friend Const Argument_RankEQOne1 As String = "Argument_RankEQOne1" + Friend Const Argument_IComparable2 As String = "Argument_IComparable2" + Friend Const Argument_NotNumericType2 As String = "Argument_NotNumericType2" + Friend Const Argument_InvalidValue1 As String = "Argument_InvalidValue1" + Friend Const Argument_InvalidValueType2 As String = "Argument_InvalidValueType2" + Friend Const Argument_InvalidValue As String = "Argument_InvalidValue" + Friend Const Collection_BeforeAfterExclusive As String = "Collection_BeforeAfterExclusive" + Friend Const Collection_DuplicateKey As String = "Collection_DuplicateKey" + Friend Const ForLoop_CommonType2 As String = "ForLoop_CommonType2" + Friend Const ForLoop_CommonType3 As String = "ForLoop_CommonType3" + Friend Const ForLoop_ConvertToType3 As String = "ForLoop_ConvertToType3" + Friend Const ForLoop_OperatorRequired2 As String = "ForLoop_OperatorRequired2" + Friend Const ForLoop_UnacceptableOperator2 As String = "ForLoop_UnacceptableOperator2" + Friend Const ForLoop_UnacceptableRelOperator2 As String = "ForLoop_UnacceptableRelOperator2" + Friend Const InternalError As String = "InternalError" + Friend Const MaxErrNumber As String = "MaxErrNumber" + Friend Const Argument_InvalidNullValue1 As String = "Argument_InvalidNullValue1" + Friend Const Argument_InvalidRank1 As String = "Argument_InvalidRank1" + Friend Const Argument_Range0to99_1 As String = "Argument_Range0to99_1" + Friend Const Array_RankMismatch As String = "Array_RankMismatch" + Friend Const Array_TypeMismatch As String = "Array_TypeMismatch" + Friend Const InvalidCast_FromTo As String = "InvalidCast_FromTo" + Friend Const InvalidCast_FromStringTo As String = "InvalidCast_FromStringTo" + Friend Const Argument_InvalidDateValue1 As String = "Argument_InvalidDateValue1" + Friend Const ArgumentNotNumeric1 As String = "ArgumentNotNumeric1" + Friend Const Argument_IndexLELength2 As String = "Argument_IndexLELength2" + Friend Const MissingMember_NoDefaultMemberFound1 As String = "MissingMember_NoDefaultMemberFound1" + Friend Const MissingMember_MemberNotFoundOnType2 As String = "MissingMember_MemberNotFoundOnType2" + Friend Const IntermediateLateBoundNothingResult1 As String = "IntermediateLateBoundNothingResult1" + Friend Const OnOffFormatStyle As String = "OnOffFormatStyle" + Friend Const YesNoFormatStyle As String = "YesNoFormatStyle" + Friend Const TrueFalseFormatStyle As String = "TrueFalseFormatStyle" + Friend Const Argument_CollectionIndex As String = "Argument_CollectionIndex" + Friend Const RValueBaseForValueType As String = "RValueBaseForValueType" + Friend Const ExpressionNotProcedure As String = "ExpressionNotProcedure" + Friend Const LateboundCallToInheritedComClass As String = "LateboundCallToInheritedComClass" + Friend Const MissingMember_ReadOnlyField2 As String = "MissingMember_ReadOnlyField2" + Friend Const Argument_InvalidNamedArgs As String = "Argument_InvalidNamedArgs" + Friend Const SyncLockRequiresReferenceType1 As String = "SyncLockRequiresReferenceType1" + Friend Const NullReference_InstanceReqToAccessMember1 As String = "NullReference_InstanceReqToAccessMember1" + Friend Const MatchArgumentFailure2 As String = "MatchArgumentFailure2" + Friend Const NoGetProperty1 As String = "NoGetProperty1" + Friend Const NoSetProperty1 As String = "NoSetProperty1" + Friend Const MethodAssignment1 As String = "MethodAssignment1" + + Friend Const NoViableOverloadCandidates1 As String = "NoViableOverloadCandidates1" + Friend Const NoArgumentCountOverloadCandidates1 As String = "NoArgumentCountOverloadCandidates1" + Friend Const NoTypeArgumentCountOverloadCandidates1 As String = "NoTypeArgumentCountOverloadCandidates1" + Friend Const NoCallableOverloadCandidates2 As String = "NoCallableOverloadCandidates2" + Friend Const NoNonNarrowingOverloadCandidates2 As String = "NoNonNarrowingOverloadCandidates2" + Friend Const NoMostSpecificOverload2 As String = "NoMostSpecificOverload2" + Friend Const AmbiguousCast2 As String = "AmbiguousCast2" + + Friend Const NotMostSpecificOverload As String = "NotMostSpecificOverload" + + Friend Const NamedParamNotFound2 As String = "NamedParamNotFound2" + Friend Const NamedParamArrayArgument1 As String = "NamedParamArrayArgument1" + Friend Const NamedArgUsedTwice2 As String = "NamedArgUsedTwice2" + Friend Const OmittedArgument1 As String = "OmittedArgument1" + Friend Const OmittedParamArrayArgument As String = "OmittedParamArrayArgument" + + Friend Const ArgumentMismatch3 As String = "ArgumentMismatch3" + Friend Const ArgumentMismatchAmbiguous3 As String = "ArgumentMismatchAmbiguous3" + Friend Const ArgumentNarrowing3 As String = "ArgumentNarrowing3" + Friend Const ArgumentMismatchCopyBack3 As String = "ArgumentMismatchCopyBack3" + Friend Const ArgumentMismatchAmbiguousCopyBack3 As String = "ArgumentMismatchAmbiguousCopyBack3" + Friend Const ArgumentNarrowingCopyBack3 As String = "ArgumentNarrowingCopyBack3" + + Friend Const UnboundTypeParam1 As String = "UnboundTypeParam1" + Friend Const TypeInferenceFails1 As String = "TypeInferenceFails1" + Friend Const FailedTypeArgumentBinding As String = "FailedTypeArgumentBinding" + Friend Const UnaryOperand2 As String = "UnaryOperand2" + Friend Const BinaryOperands3 As String = "BinaryOperands3" + Friend Const NoValidOperator_StringType1 As String = "NoValidOperator_StringType1" + Friend Const NoValidOperator_NonStringType1 As String = "NoValidOperator_NonStringType1" + + Friend Const PropertySetMissingArgument1 As String = "PropertySetMissingArgument1" + Friend Const EmptyPlaceHolderMessage As String = "EmptyPlaceHolderMessage" + + Friend Const WebNotSupportedOnThisSKU As String = "WebNotSupportedOnThisSKU" + + '======================= MY.NET IDs GO HERE ========================== +#If Not TELESTO Then + Friend NotInheritable Class MyID + + ' Mouse errors. + Friend Const Mouse_NoMouseIsPresent As String = "Mouse_NoMouseIsPresent" + Friend Const Mouse_NoWheelIsPresent As String = "Mouse_NoWheelIsPresent" + + ' FileSystem exceptions. + Friend Const IO_SpecialDirectoryNotExist As String = "IO_SpecialDirectoryNotExist" + Friend Const IO_SpecialDirectory_MyDocuments As String = "IO_SpecialDirectory_MyDocuments" + Friend Const IO_SpecialDirectory_MyMusic As String = "IO_SpecialDirectory_MyMusic" + Friend Const IO_SpecialDirectory_MyPictures As String = "IO_SpecialDirectory_MyPictures" + Friend Const IO_SpecialDirectory_Desktop As String = "IO_SpecialDirectory_Desktop" + Friend Const IO_SpecialDirectory_Programs As String = "IO_SpecialDirectory_Programs" + Friend Const IO_SpecialDirectory_ProgramFiles As String = "IO_SpecialDirectory_ProgramFiles" + Friend Const IO_SpecialDirectory_Temp As String = "IO_SpecialDirectory_Temp" + Friend Const IO_SpecialDirectory_AllUserAppData As String = "IO_SpecialDirectory_AllUserAppData" + Friend Const IO_SpecialDirectory_UserAppData As String = "IO_SpecialDirectory_UserAppData" + + Friend Const IO_FileExists_Path As String = "IO_FileExists_Path" + Friend Const IO_FileNotFound_Path As String = "IO_FileNotFound_Path" + Friend Const IO_DirectoryExists_Path As String = "IO_DirectoryExists_Path" + Friend Const IO_DirectoryIsRoot_Path As String = "IO_DirectoryIsRoot_Path" + Friend Const IO_DirectoryNotFound_Path As String = "IO_DirectoryNotFound_Path" + Friend Const IO_GetParentPathIsRoot_Path As String = "IO_GetParentPathIsRoot_Path" + + Friend Const IO_ArgumentIsPath_Name_Path As String = "IO_ArgumentIsPath_Name_Path" + + Friend Const IO_CopyMoveRecursive As String = "IO_CopyMoveRecursive" + Friend Const IO_CyclicOperation As String = "IO_CyclicOperation" + Friend Const IO_SourceEqualsTargetDirectory As String = "IO_SourceEqualsTargetDirectory" + Friend Const IO_GetFiles_NullPattern As String = "IO_GetFiles_NullPattern" + Friend Const IO_DevicePath As String = "IO_DevicePath" + Friend Const IO_FilePathException As String = "IO_FilePathException" + + 'General errors + Friend Const General_ArgumentNullException As String = "General_ArgumentNullException" + Friend Const General_ArgumentEmptyOrNothing_Name As String = "General_ArgumentEmptyOrNothing_Name" + Friend Const General_PropertyNothing As String = "General_PropertyNothing" + + 'Application Log errors + Friend Const ApplicationLog_FreeSpaceError As String = "ApplicationLog_FreeSpaceError" + Friend Const ApplicationLog_FileExceedsMaximumSize As String = "ApplicationLog_FileExceedsMaximumSize" + Friend Const ApplicationLog_ReservedSpaceEncroached As String = "ApplicationLog_ReservedSpaceEncroached" + Friend Const ApplicationLog_NegativeNumber As String = "ApplicationLog_NegativeNumber" + + Friend Const ApplicationLogNumberTooSmall As String = "ApplicationLogNumberTooSmall" + Friend Const ApplicationLogBaseNameNull As String = "ApplicationLogBaseNameNull" + Friend Const ApplicationLog_ExhaustedPossibleStreamNames As String = "ApplicationLog_ExhaustedPossibleStreamNames" + + 'Network Strings + Friend Const Network_InvalidUriString As String = "Network_InvalidUriString" + Friend Const Network_BadConnectionTimeout As String = "Network_BadConnectionTimeout" + Friend Const Network_NetworkNotAvailable As String = "Network_NetworkNotAvailable" + Friend Const Network_UploadAddressNeedsFilename As String = "Network_UploadAddressNeedsFilename" + Friend Const Network_DownloadNeedsFilename As String = "Network_DownloadNeedsFilename" + + 'Progress Dialog + Friend Const ProgressDialogDownloadingTitle As String = "ProgressDialogDownloadingTitle" + Friend Const ProgressDialogUploadingTitle As String = "ProgressDialogUploadingTitle" + Friend Const ProgressDialogDownloadingLabel As String = "ProgressDialogDownloadingLabel" + Friend Const ProgressDialogUploadingLabel As String = "ProgressDialogUploadingLabel" + + 'Diagnostic Information errors. + Friend Const DiagnosticInfo_Memory As String = "DiagnosticInfo_Memory" + Friend Const DiagnosticInfo_FullOSName As String = "DiagnosticInfo_FullOSName" + + ' Parser Errors + Friend Const TextFieldParser_StreamNotReadable As String = "TextFieldParser_StreamNotReadable" + Friend Const TextFieldParser_NumberOfCharsMustBePositive As String = "TextFieldParser_NumberOfCharsMustBePositive" + Friend Const TextFieldParser_BufferExceededMaxSize As String = "TextFieldParser_BufferExceededMaxSize" + Friend Const TextFieldParser_MaxLineSizeExceeded As String = "TextFieldParser_MaxLineSizeExceeded" + Friend Const TextFieldParser_FieldWidthsNothing As String = "TextFieldParser_FieldWidthsNothing" + Friend Const TextFieldParser_FieldWidthsMustPositive As String = "TextFieldParser_FieldWidthsMustPositive" + Friend Const TextFieldParser_DelimitersNothing As String = "TextFieldParser_DelimitersNothing" + Friend Const TextFieldParser_IllegalDelimiter As String = "TextFieldParser_IllegalDelimiter" + Friend Const TextFieldParser_DelimiterNothing As String = "TextFieldParser_DelimiterNothing" + Friend Const TextFieldParser_InvalidComment As String = "TextFieldParser_InvalidComment" + Friend Const TextFieldParser_MalFormedDelimitedLine As String = "TextFieldParser_MalFormedDelimitedLine" + Friend Const TextFieldParser_MalFormedFixedWidthLine As String = "TextFieldParser_MalFormedFixedWidthLine" + Friend Const TextFieldParser_MalformedExtraData As String = "TextFieldParser_MalformedExtraData" + Friend Const TextFieldParser_WhitespaceInToken As String = "TextFieldParser_WhitespaceInToken" + Friend Const TextFieldParser_EndCharsInDelimiter As String = "TextFieldParser_EndCharsInDelimiter" + + 'Application Model errors. + Friend Const AppModel_CantGetMemoryMappedFile As String = "AppModel_CantGetMemoryMappedFile" + Friend Const AppModel_NoStartupForm As String = "AppModel_NoStartupForm" + Friend Const AppModel_SingleInstanceCantConnect As String = "AppModel_SingleInstanceCantConnect" + Friend Const AppModel_SplashAndMainFormTheSame As String = "AppModel_SplashAndMainFormTheSame" + + ' Other exceptions + Friend Const EnvVarNotFound_Name As String = "EnvVarNotFound_Name" + + '''************************************************************************* + ''' ;New + ''' + ''' FxCop violation: Avoid uninstantiated internal class. + ''' Adding a private constructor to prevent the compiler from generating a default constructor. + ''' + Private Sub New() + End Sub + End Class 'MyID +#End If 'Not TELESTO + + '''************************************************************************* + ''' ;New + ''' + ''' FxCop violation: Avoid uninstantiated internal class. + ''' Adding a private constructor to prevent the compiler from generating a default constructor. + ''' + Private Sub New() + End Sub + End Class +End Namespace diff --git a/Microsoft.VisualBasic/runtime/msvbalib/Helpers/Versioned.vb b/Microsoft.VisualBasic/runtime/msvbalib/Helpers/Versioned.vb new file mode 100644 index 000000000..26cd27f43 --- /dev/null +++ b/Microsoft.VisualBasic/runtime/msvbalib/Helpers/Versioned.vb @@ -0,0 +1,204 @@ +' Copyright (c) Microsoft Corporation. All rights reserved. + +Imports System +Imports System.Dynamic + +Imports Microsoft.VisualBasic.CompilerServices.Utils + +Namespace Microsoft.VisualBasic.CompilerServices + +#If TELESTO Then + 'FIXME: _ + Public NotInheritable Class Versioned +#Else + _ + Public NotInheritable Class Versioned +#End If + ' Prevent creation. + Private Sub New() + End Sub + + Public Shared Function CallByName(ByVal Instance As System.Object, ByVal MethodName As String, ByVal UseCallType As CallType, ByVal ParamArray Arguments() As Object) As Object + + Select Case UseCallType + + Case CallType.Method + 'Need to use LateGet, because we are returning a value + Return CompilerServices.NewLateBinding.LateCall(Instance, Nothing, MethodName, Arguments, Nothing, Nothing, Nothing, False) + + Case CallType.Get + Return CompilerServices.NewLateBinding.LateGet(Instance, Nothing, MethodName, Arguments, Nothing, Nothing, Nothing) + + Case CallType.Let, _ + CallType.Set + Dim idmop As IDynamicMetaObjectProvider = IDOUtils.TryCastToIDMOP(Instance) + If idmop IsNot Nothing Then + ' UseCallType is used in the late binder to affect the binding behavior for COM Object, but COM Objects + ' don't implement IDynamicMetaObjectProvider. Therefore it is safe not to pass on UseCallType here. + IDOBinder.IDOSet(idmop, MethodName, Nothing, Arguments) + Else + CompilerServices.NewLateBinding.LateSet(Instance, Nothing, MethodName, Arguments, Nothing, Nothing, False, False, UseCallType) + End If + Return Nothing + + Case Else + Throw New ArgumentException(GetResourceString(ResID.Argument_InvalidValue1, "CallType")) + End Select + + End Function + + '* + '* IsNumeric - + '* + '* + '* NOTE: Code changes here MUST BE PERFORMANCE TESTED + '* + Public Shared Function IsNumeric(ByVal Expression As Object) As Boolean + + Dim ValueInterface As IConvertible = TryCast(Expression, IConvertible) + + If ValueInterface Is Nothing Then + Return False + End If + + Select Case ValueInterface.GetTypeCode() + + Case TypeCode.SByte, _ + TypeCode.Byte, _ + TypeCode.Int16, _ + TypeCode.UInt16, _ + TypeCode.Int32, _ + TypeCode.UInt32, _ + TypeCode.Int64, _ + TypeCode.UInt64, _ + TypeCode.Decimal, _ + TypeCode.Single, _ + TypeCode.Double + + Return True + + Case TypeCode.Boolean + Return True + + Case TypeCode.Char, _ + TypeCode.String + + 'Convert to double, exception thrown if not a number + Dim Value As String = ValueInterface.ToString(Nothing) + + Try + 'CONSIDER: Optimize out this exception case + Dim i64Value As Int64 + If IsHexOrOctValue(Value, i64Value) Then + Return True + End If + Catch ex As FormatException + Return False + End Try + + Dim dbl As Double + Return Conversions.TryParseDouble(Value, dbl) + + Case TypeCode.Empty, _ + TypeCode.Object, _ + TypeCode.DBNull, _ + TypeCode.DateTime + + 'fall through to end + + End Select + + Return False + + End Function + + Public Shared Function TypeName(ByVal Expression As Object) As String + + Dim Result As String + Dim typ As System.Type + + If Expression Is Nothing Then + Return "Nothing" + End If + + typ = Expression.GetType() +#If Not TELESTO Then + If (typ.IsCOMObject AndAlso (System.String.CompareOrdinal(typ.Name, COMObjectName) = 0)) Then + Result = TypeNameOfCOMObject(Expression, True) + Else + Result = VBFriendlyNameOfType(typ) + End If +#Else + Result = VBFriendlyNameOfType(typ) +#End If + Return Result + End Function + + Public Shared Function SystemTypeName(ByVal VbName As String) As String +#If TELESTO Then + Select Case Trim(VbName).ToUpper(Globalization.CultureInfo.InvariantCulture) 'Using instead of ToUpperInvariant because ToUpperInvariant isn't available on Telesto and this is equivilant on both platforms +#Else + Select Case Trim(VbName).ToUpperInvariant() +#End If + Case "BOOLEAN" : Return "System.Boolean" + Case "SBYTE" : Return "System.SByte" + Case "BYTE" : Return "System.Byte" + Case "SHORT" : Return "System.Int16" + Case "USHORT" : Return "System.UInt16" + Case "INTEGER" : Return "System.Int32" + Case "UINTEGER" : Return "System.UInt32" + Case "LONG" : Return "System.Int64" + Case "ULONG" : Return "System.UInt64" + Case "DECIMAL" : Return "System.Decimal" + Case "SINGLE" : Return "System.Single" + Case "DOUBLE" : Return "System.Double" + Case "DATE" : Return "System.DateTime" + Case "CHAR" : Return "System.Char" + Case "STRING" : Return "System.String" + Case "OBJECT" : Return "System.Object" + + Case Else + Return Nothing + + End Select + End Function + + Public Shared Function VbTypeName(ByVal SystemName As String) As String +#If TELESTO Then + SystemName = Trim(SystemName).ToUpper(Globalization.CultureInfo.InvariantCulture) 'Using instead of ToUpperInvariant because ToUpperInvariant isn't available on Telesto and this is equivilant on both platforms +#Else + SystemName = Trim(SystemName).ToUpperInvariant() +#End If + If Left(SystemName, 7) = "SYSTEM." Then + SystemName = Mid(SystemName, 8) + End If + + Select Case SystemName + + Case "BOOLEAN" : Return "Boolean" + Case "SBYTE" : Return "SByte" + Case "BYTE" : Return "Byte" + Case "INT16" : Return "Short" + Case "UINT16" : Return "UShort" + Case "INT32" : Return "Integer" + Case "UINT32" : Return "UInteger" + Case "INT64" : Return "Long" + Case "UINT64" : Return "ULong" + Case "DECIMAL" : Return "Decimal" + Case "SINGLE" : Return "Single" + Case "DOUBLE" : Return "Double" + Case "DATETIME" : Return "Date" + Case "CHAR" : Return "Char" + Case "STRING" : Return "String" + Case "OBJECT" : Return "Object" + + Case Else + Return Nothing + + End Select + End Function + + End Class + +End Namespace + diff --git a/Microsoft.VisualBasic/runtime/msvbalib/Information.vb b/Microsoft.VisualBasic/runtime/msvbalib/Information.vb new file mode 100644 index 000000000..9bd313aff --- /dev/null +++ b/Microsoft.VisualBasic/runtime/msvbalib/Information.vb @@ -0,0 +1,679 @@ +' Copyright (c) Microsoft Corporation. All rights reserved. + +Imports System +Imports System.Globalization +Imports System.Security +Imports System.Security.Permissions + +Imports Microsoft.VisualBasic.CompilerServices +Imports Microsoft.VisualBasic.CompilerServices.ExceptionUtils +Imports Microsoft.VisualBasic.CompilerServices.Utils + + + + +Namespace Microsoft.VisualBasic + + Public Module Information + +#If Not TELESTO Then + 'QBColorTable below consists of : + '&H0I, ' 0 - black + '&H800000I, ' 1 - blue + '&H8000I, ' 2 - green + '&H808000I, ' 3 - cyan + '&H80I, ' 4 - red + '&H800080I, ' 5 - magenta + '&H8080I, ' 6 - yellow + '&HC0C0C0I, ' 7 - white + '&H808080I, ' 8 - gray + '&HFF0000I, ' 9 - light blue + '&HFF00I, ' 10 - light green + '&HFFFF00I, ' 11 - light cyan + '&HFFI, ' 12 - light red + '&HFF00FFI, ' 13 - light magenta + '&HFFFFI, ' 14 - light yellow + '&HFFFFFFI, ' 15 - bright white + Private ReadOnly QBColorTable() As Integer = {&H0I, &H800000I, &H8000I, &H808000I, _ + &H80I, &H800080I, &H8080I, _ + &HC0C0C0I, &H808080I, &HFF0000I, _ + &HFF00I, &HFFFF00I, &HFFI, _ + &HFF00FFI, &HFFFFI, &HFFFFFFI} + Friend Const COMObjectName As String = "__ComObject" +#End If + + '============================================================================ + ' Error functions. + '============================================================================ + Public Function Err() As ErrObject + + Dim oProj As ProjectData + oProj = ProjectData.GetProjectData() + + If oProj.m_Err Is Nothing Then + oProj.m_Err = New ErrObject + End If + Err = oProj.m_Err + + End Function + +#If Not TELESTO Then + ' UNDONE This should be hidden + _ + Public Function Erl() As Integer +#Else + ' _ + Public Function Erl() As Integer +#End If + 'UNDONE: THREADING REVIEW + Dim oProj As ProjectData + oProj = ProjectData.GetProjectData() + Erl = oProj.m_Err.Erl + End Function + + '============================================================================ + ' Is... functions. + '============================================================================ + Public Function IsArray(ByVal VarName As Object) As Boolean + + If VarName Is Nothing Then + Return False + End If + + Return (TypeOf VarName Is System.Array) + + End Function + + Public Function IsDate(ByVal Expression As Object) As Boolean + + If Expression Is Nothing Then + Return False + End If + + If TypeOf Expression Is Date Then + + Return True + + Else + Dim StringExpression As String = TryCast(Expression, String) + + If StringExpression IsNot Nothing Then + Dim ConvertedDate As DateTime + + Return Conversions.TryParseDate(StringExpression, ConvertedDate) + End If + End If + + Return False + + End Function + + Public Function IsDBNull(ByVal Expression As Object) As Boolean + + If Expression Is Nothing Then + Return False + + ElseIf TypeOf Expression Is System.DBNull Then + Return True + + Else + Return False + + End If + + End Function + + Public Function IsNothing(ByVal Expression As Object) As Boolean + + Return (Expression Is Nothing) + + End Function + + Public Function IsError(ByVal Expression As Object) As Boolean + + If Expression Is Nothing Then + Return False + End If + + Return (TypeOf Expression Is Exception) + + End Function + + Public Function IsReference(ByVal Expression As Object) As Boolean + + Return Not (TypeOf Expression Is System.ValueType) + + End Function + + Public Function LBound(ByVal Array As System.Array, Optional ByVal Rank As Integer = 1) As Integer + + If (Array Is Nothing) Then + Throw VbMakeException(New ArgumentNullException(GetResourceString(ResID.Argument_InvalidNullValue1, "Array")), vbErrors.OutOfBounds) + + ElseIf (Rank < 1) OrElse (Rank > Array.Rank) Then + Throw New RankException(GetResourceString(ResID.Argument_InvalidRank1, "Rank")) + + End If + + Return Array.GetLowerBound(Rank - 1) + + End Function + + Public Function UBound(ByVal Array As System.Array, Optional ByVal Rank As Integer = 1) As Integer + + If (Array Is Nothing) Then + Throw VbMakeException(New ArgumentNullException(GetResourceString(ResID.Argument_InvalidNullValue1, "Array")), vbErrors.OutOfBounds) + + ElseIf (Rank < 1) OrElse (Rank > Array.Rank) Then + Throw New RankException(GetResourceString(ResID.Argument_InvalidRank1, "Rank")) + + End If + + Return Array.GetUpperBound(Rank - 1) + + End Function + +#If Not TELESTO Then + '============================================================================ + ' Object type functions. + '============================================================================ + _ + Friend Function TypeNameOfCOMObject(ByVal VarName As Object, ByVal bThrowException As Boolean) As String + + Dim Result As String = COMObjectName + + Try + Call (New SecurityPermission(SecurityPermissionFlag.UnmanagedCode)).Demand() + Catch ex As StackOverflowException + Throw ex + Catch ex As OutOfMemoryException + Throw ex + Catch ex As System.Threading.ThreadAbortException + Throw ex + Catch e As Exception + If bThrowException Then + Throw e + Else + GoTo CleanupTypeName + End If + End Try + + Dim pTypeInfo As UnsafeNativeMethods.ITypeInfo = Nothing + Dim hr As Integer + Dim ClassName As String = Nothing + Dim DocString As String = Nothing + Dim HelpContext As Integer + Dim HelpFile As String = Nothing + + + Do + Dim pProvideClassInfo As UnsafeNativeMethods.IProvideClassInfo = TryCast(VarName, UnsafeNativeMethods.IProvideClassInfo) + + If pProvideClassInfo IsNot Nothing Then + Try + pTypeInfo = pProvideClassInfo.GetClassInfo() + hr = pTypeInfo.GetDocumentation(-1, ClassName, DocString, HelpContext, HelpFile) + If hr >= 0 Then + Result = ClassName + Exit Do + End If + pTypeInfo = Nothing + Catch ex As StackOverflowException + Throw ex + Catch ex As OutOfMemoryException + Throw ex + Catch ex As System.Threading.ThreadAbortException + Throw ex + Catch + 'Ignore the error + End Try + End If + + Dim pDispatch As UnsafeNativeMethods.IDispatch = TryCast(VarName, UnsafeNativeMethods.IDispatch) + + If pDispatch IsNot Nothing Then + ' Try using IDispatch + hr = pDispatch.GetTypeInfo(0, UnsafeNativeMethods.LCID_US_ENGLISH, pTypeInfo) + If hr >= 0 Then + hr = pTypeInfo.GetDocumentation(-1, ClassName, DocString, HelpContext, HelpFile) + If hr >= 0 Then + Result = ClassName + Exit Do + End If + End If + End If + + Loop While (False) + + +CleanupTypeName: + + If Result.Chars(0) = "_"c Then + Result = Result.Substring(1) + End If + + Return Result + End Function + + '============================================================================ + ' Color functions. + '============================================================================ + Public Function QBColor(ByVal Color As Integer) As Integer + If (Color And &HFFF0I) <> 0 Then + Throw New ArgumentException(GetResourceString(ResID.Argument_InvalidValue1, "Color")) + End If + + QBColor = QBColorTable(Color) + End Function + + Public Function RGB(ByVal Red As Integer, ByVal Green As Integer, ByVal Blue As Integer) As Integer + If (Red And &H80000000I) <> 0 Then + Throw New ArgumentException(GetResourceString(ResID.Argument_InvalidValue1, "Red")) + ElseIf (Green And &H80000000I) <> 0 Then + Throw New ArgumentException(GetResourceString(ResID.Argument_InvalidValue1, "Green")) + ElseIf (Blue And &H80000000I) <> 0 Then + Throw New ArgumentException(GetResourceString(ResID.Argument_InvalidValue1, "Blue")) + End If + + ' VB2 treats any value > 255 as 255 + + If (Red > 255) Then + Red = &HFFI + End If + + If (Green > 255) Then + Green = &HFFI + End If + + If (Blue > 255) Then + Blue = &HFFI + End If + + Return ((Blue * &H10000I) + (Green * &H100I) + Red) + End Function +#End If 'NOT TELESTO + + Public Function VarType(ByVal VarName As Object) As VariantType + If VarName Is Nothing Then + Return VariantType.Object + End If + + Return VarTypeFromComType(VarName.GetType()) + End Function + + Friend Function VarTypeFromComType(ByVal typ As System.Type) As VariantType + If typ Is Nothing Then + Return VariantType.Object + End If + + If typ.IsArray() Then + + typ = typ.GetElementType() + If typ.IsArray Then + Return CType(VariantType.Array Or VariantType.Object, VariantType) + End If + + Dim Result As VariantType = VarTypeFromComType(typ) + If (Result And VariantType.Array) <> 0 Then + 'Element type is also an array, so just return "array of objects" + Return CType(VariantType.Array Or VariantType.Object, VariantType) + End If + Return CType(Result Or VariantType.Array, VariantType) + + ElseIf typ.IsEnum() Then + typ = System.Enum.GetUnderlyingType(typ) + End If + + If typ Is Nothing Then + Return VariantType.Empty + End If + + Select Case Type.GetTypeCode(typ) + + Case TypeCode.String + Return VariantType.String + Case TypeCode.Int32 + Return VariantType.Integer + Case TypeCode.Int16 + Return VariantType.Short + Case TypeCode.Int64 + Return VariantType.Long + Case TypeCode.Single + Return VariantType.Single + Case TypeCode.Double + Return VariantType.Double + Case TypeCode.DateTime + Return VariantType.Date + Case TypeCode.Boolean + Return VariantType.Boolean + Case TypeCode.Decimal + Return VariantType.Decimal + Case TypeCode.Byte + Return VariantType.Byte + Case TypeCode.Char + Return VariantType.Char + Case TypeCode.DBNull + Return VariantType.Null + + End Select + + If (typ Is GetType(System.Reflection.Missing)) OrElse _ + (typ Is GetType(System.Exception)) OrElse _ + (typ.IsSubclassOf(GetType(System.Exception))) Then + Return VariantType.Error + ElseIf typ.IsValueType() Then + Return VariantType.UserDefinedType + Else + Return VariantType.Object + End If + + End Function + +#If Not TELESTO Then +#Region " BACKWARDS COMPATIBILITY. These functions (IsNumeric, TypeName, SystemTypeName, VbTypeName) have been superceded by the versions in Versioned.vb " + + 'WARNING WARNING WARNING WARNING WARNING + 'This code exists to support Everett compiled applications. Make sure you understand + 'the backwards compatibility ramifications of any edit you make in this region. + 'The compiler loads references to these functions (see CompilerHost::PrepareRemappingTable()) so + 'they need to exist. The Orcas compiler will be made not to die if they are missing + 'WARNING WARNING WARNING WARNING WARNING + + Friend Function IsOldNumericTypeCode(ByVal TypCode As System.TypeCode) As Boolean + + Select Case TypCode + + Case TypeCode.Int16, _ + TypeCode.Int32, _ + TypeCode.Int64, _ + TypeCode.Single, _ + TypeCode.Double, _ + TypeCode.Boolean, _ + TypeCode.Decimal, _ + TypeCode.Byte + Return True + + Case Else + Return False + + End Select + + End Function + + '* + '* IsNumeric - + '* + '* + '* NOTE: Code changes here MUST BE PERFORMANCE TESTED + '* + Public Function IsNumeric(ByVal Expression As Object) As Boolean + + Dim ValueInterface As IConvertible + Dim ValueTypeCode As TypeCode + + ValueInterface = TryCast(Expression, IConvertible) + + If ValueInterface Is Nothing Then + Dim CharArray As Char() = TryCast(Expression, Char()) + + If CharArray IsNot Nothing Then + Expression = CStr(CharArray) + Else + Return False + End If + End If + + ValueTypeCode = ValueInterface.GetTypeCode() + + If (ValueTypeCode = TypeCode.String) OrElse (ValueTypeCode = TypeCode.Char) Then + + 'Convert to double, exception thrown if not a number + Dim dbl As Double + + Dim i64Value As Int64 + Dim Value As String + + Value = ValueInterface.ToString(Nothing) + + Try + If IsHexOrOctValue(Value, i64Value) Then + Return True + End If + Catch ex As StackOverflowException + Throw ex + Catch ex As OutOfMemoryException + Throw ex + Catch ex As System.Threading.ThreadAbortException + Throw ex + Catch + Return False + End Try + + Return DoubleType.TryParse(Value, dbl) + + End If + + Return IsOldNumericTypeCode(ValueTypeCode) + + End Function + + Friend Function OldVBFriendlyNameOfTypeName(ByVal typename As String) As String + Dim ArraySuffix As String = Nothing + Dim Name As String + Dim LastChar As Integer = typename.Length - 1 + + If typename.Chars(LastChar) = "]"c Then + Dim pos As Integer + pos = typename.IndexOf("["c) + If pos + 1 = LastChar Then + ArraySuffix = "()" + Else + ArraySuffix = typename.Substring(pos, LastChar - pos + 1).Replace("["c, "("c).Replace("]"c, ")"c) + End If + typename = typename.Substring(0, pos) + End If + + Name = OldVbTypeName(typename) + If Name Is Nothing Then + Name = typename + End If + + If ArraySuffix Is Nothing Then + Return Name + End If + Return Name & AdjustArraySuffix(ArraySuffix) + + End Function + + Public Function TypeName(ByVal VarName As Object) As String + + Dim Result As String + Dim bIsArray As Boolean + Dim typ As System.Type + Dim ArrayType As System.Type + + If VarName Is Nothing Then + Return "Nothing" + End If + + typ = VarName.GetType() + + If typ.IsArray Then + bIsArray = True + ArrayType = typ + typ = ArrayType.GetElementType() + End If + + If typ.IsEnum() Then + + Result = typ.Name + GoTo UnmangleName + + Else + Dim tc As TypeCode + + tc = Type.GetTypeCode(typ) + + Select Case tc + + Case TypeCode.DBNull : Result = "DBNull" + Case TypeCode.Int16 : Result = "Short" + Case TypeCode.Int32 : Result = "Integer" + Case TypeCode.Single : Result = "Single" + Case TypeCode.Double : Result = "Double" + Case TypeCode.DateTime : Result = "Date" + Case TypeCode.String : Result = "String" + Case TypeCode.Boolean : Result = "Boolean" + Case TypeCode.Decimal : Result = "Decimal" + Case TypeCode.Byte : Result = "Byte" + Case TypeCode.Char : Result = "Char" + Case TypeCode.Int64 : Result = "Long" + + Case Else + + Result = typ.Name + + If (typ.IsCOMObject AndAlso (System.String.CompareOrdinal(Result, COMObjectName) = 0)) Then + Result = LegacyTypeNameOfCOMObject(VarName, True) + End If + +UnmangleName: + 'REVIEW: Will managled names go away for Beta2? + ' They don't seem to be as of 11/6/2000 + Dim i As Integer + i = Result.IndexOf("+"c) + If i >= 0 Then + Result = Result.Substring(i + 1) + End If + + End Select + + End If + + If bIsArray Then + + Dim ary As Array + ary = CType(VarName, Array) + If ary.Rank = 1 Then + Result = Result & "[]" + Else + Result = Result & "[" & (New String(","c, ary.Rank - 1)) & "]" + End If + + Result = OldVBFriendlyNameOfTypeName(Result) + + End If + + Return Result + End Function + + Public Function SystemTypeName(ByVal VbName As String) As String + + Select Case Trim(VbName).ToUpperInvariant() + Case "OBJECT" : Return "System.Object" + Case "SHORT" : Return "System.Int16" + Case "INTEGER" : Return "System.Int32" + Case "SINGLE" : Return "System.Single" + Case "DOUBLE" : Return "System.Double" + Case "DATE" : Return "System.DateTime" + Case "STRING" : Return "System.String" + Case "BOOLEAN" : Return "System.Boolean" + Case "DECIMAL" : Return "System.Decimal" + Case "BYTE" : Return "System.Byte" + Case "CHAR" : Return "System.Char" + Case "LONG" : Return "System.Int64" + Case Else : Return Nothing + End Select + + End Function + + Public Function VbTypeName(ByVal UrtName As String) As String + Return OldVbTypeName(UrtName) + End Function + + Friend Function OldVbTypeName(ByVal UrtName As String) As String + + UrtName = Trim(UrtName).ToUpperInvariant() + If Left(UrtName, 7) = "SYSTEM." Then + UrtName = Mid(UrtName, 8) + End If + + Select Case UrtName + Case "OBJECT" : Return "Object" + Case "INT16" : Return "Short" + Case "INT32" : Return "Integer" + Case "SINGLE" : Return "Single" + Case "DOUBLE" : Return "Double" + Case "DATETIME" : Return "Date" + Case "STRING" : Return "String" + Case "BOOLEAN" : Return "Boolean" + Case "DECIMAL" : Return "Decimal" + Case "BYTE" : Return "Byte" + Case "CHAR" : Return "Char" + Case "INT64" : Return "Long" + Case Else + Return Nothing + End Select + + End Function + + '============================================================================ + ' Object type functions. + '============================================================================ + _ + Friend Function LegacyTypeNameOfCOMObject(ByVal VarName As Object, ByVal bThrowException As Boolean) As String + + Dim Result As String = COMObjectName + + Try + Call (New SecurityPermission(SecurityPermissionFlag.UnmanagedCode)).Demand() + Catch ex As StackOverflowException + Throw ex + Catch ex As OutOfMemoryException + Throw ex + Catch ex As System.Threading.ThreadAbortException + Throw ex + Catch e As Exception + If bThrowException Then + Throw e + Else + GoTo CleanupTypeName + End If + End Try + + Dim pTypeInfo As UnsafeNativeMethods.ITypeInfo = Nothing + Dim hr As Integer + Dim ClassName As String = Nothing + Dim DocString As String = Nothing + Dim HelpContext As Integer + Dim HelpFile As String = Nothing + + Dim pDispatch As UnsafeNativeMethods.IDispatch = TryCast(VarName, UnsafeNativeMethods.IDispatch) + + If pDispatch IsNot Nothing Then + hr = pDispatch.GetTypeInfo(0, UnsafeNativeMethods.LCID_US_ENGLISH, pTypeInfo) + If hr >= 0 Then + hr = pTypeInfo.GetDocumentation(-1, ClassName, DocString, HelpContext, HelpFile) + If hr >= 0 Then + Result = ClassName + End If + End If + End If + +CleanupTypeName: + + If Result.Chars(0) = "_"c Then + Result = Result.Substring(1) + End If + + Return Result + End Function + +#End Region +#End If 'Not TELESTO + + End Module + +End Namespace + + diff --git a/Microsoft.VisualBasic/runtime/msvbalib/Interaction.vb b/Microsoft.VisualBasic/runtime/msvbalib/Interaction.vb new file mode 100644 index 000000000..9e21ac7ef --- /dev/null +++ b/Microsoft.VisualBasic/runtime/msvbalib/Interaction.vb @@ -0,0 +1,1150 @@ +' Copyright (c) Microsoft Corporation. All rights reserved. + +Imports System +Imports System.Security +Imports System.Security.Permissions +Imports System.Globalization +Imports System.Text +Imports System.Collections +Imports System.Threading +Imports System.Runtime.CompilerServices +Imports System.Runtime.InteropServices +Imports System.Runtime.Versioning +Imports System.Diagnostics + +#If Not TELESTO Then +Imports System.ComponentModel +Imports System.Drawing +Imports System.Windows.Forms +Imports Microsoft.Win32 +#End If + +Imports Microsoft.VisualBasic.CompilerServices +Imports Microsoft.VisualBasic.CompilerServices.ExceptionUtils +Imports Microsoft.VisualBasic.CompilerServices.Utils + +Namespace Microsoft.VisualBasic + + Public Module Interaction +#If Not LATEBINDING Then +#If Not TELESTO Then + Private m_SortedEnvList As System.Collections.SortedList 'SECURITY! We asserted to get this list. Whenever you hand out portions of it you must demand the appropriate EnvironmentPermission + + '============================================================================ + ' Application/system interaction functions. + '============================================================================ + + 'No HostProtection attribute because the code has a demand for UnmanagedCode. + _ + _ + _ + _ + Public Function Shell(ByVal PathName As String, Optional ByVal Style As AppWinStyle = AppWinStyle.MinimizedFocus, Optional ByVal Wait As Boolean = False, Optional ByVal Timeout As Integer = -1) As Integer + Dim StartupInfo As New NativeTypes.STARTUPINFO + Dim ProcessInfo As New NativeTypes.PROCESS_INFORMATION + Dim ok As Integer + Dim safeProcessHandle As New NativeTypes.LateInitSafeHandleZeroOrMinusOneIsInvalid() + Dim safeThreadHandle As New NativeTypes.LateInitSafeHandleZeroOrMinusOneIsInvalid() + Dim ErrorCode As Integer = 0 + + ' UNDONE:M3: + ' - use WaitOne or do DoEvents during the waiting infinite case below so that entire app-domain + ' is not unloaded in abort cases. Suggested by Christopher Brummer. Will wait for FX + ' + ' M2 - we are okay for now with the ExtermalProcessMgmt demand. This demand should always + ' be present. + + Try 'We demand UI because we don't want to allow shelling out UI in a server or non-UI app + Call (New UIPermission(UIPermissionWindow.AllWindows)).Demand() + Catch e As Exception + Throw e + End Try + + If (PathName Is Nothing) Then + Throw New NullReferenceException(GetResourceString(ResID.Argument_InvalidNullValue1, "Pathname")) + End If + + If (Style < 0 OrElse Style > 9) Then + Throw New ArgumentException(GetResourceString(ResID.Argument_InvalidValue1, "Style")) + End If + + NativeMethods.GetStartupInfo(StartupInfo) + Try + StartupInfo.dwFlags = NativeTypes.STARTF_USESHOWWINDOW ' we want to specify the initial window style (minimized, etc) so set this bit. + StartupInfo.wShowWindow = Style + + 'We have to have unmanaged permissions to do this, so asking for path permissions would be redundant + 'Note: We are using the StartupInfo (defined in nativeTypes.StartupInfo) in CreateProcess() even though this version + 'of the StartupInfo type uses Intptr instead of String because GetStartupInfo() above requires that version so we don't + 'free the string fields since the API manages it instead. But its ok here because we are just passing along the memory + 'that GetStartupInfo() allocated along to CreateProcess() which just reads the string fields. + + RuntimeHelpers.PrepareConstrainedRegions() + Try + Finally + ok = NativeMethods.CreateProcess(Nothing, PathName, Nothing, Nothing, False, NativeTypes.NORMAL_PRIORITY_CLASS, Nothing, Nothing, StartupInfo, ProcessInfo) + If ok = 0 Then + ErrorCode = Marshal.GetLastWin32Error() + End If + If ProcessInfo.hProcess <> IntPtr.Zero AndAlso ProcessInfo.hProcess <> NativeTypes.INVALID_HANDLE Then + safeProcessHandle.InitialSetHandle(ProcessInfo.hProcess) + End If + If ProcessInfo.hThread <> IntPtr.Zero AndAlso ProcessInfo.hThread <> NativeTypes.INVALID_HANDLE Then + safeThreadHandle.InitialSetHandle(ProcessInfo.hThread) + End If + End Try + + Try + If (ok <> 0) Then + If Wait Then + ' Is infinite wait okay here ? + ' This is okay since this is marked as requiring the HostPermission with ExternalProcessMgmt rights + ok = NativeMethods.WaitForSingleObject(safeProcessHandle, Timeout) + + If ok = 0 Then 'succeeded + 'Process ran to completion + Shell = 0 + Else + 'Wait timedout + Shell = ProcessInfo.dwProcessId + End If + Else + NativeMethods.WaitForInputIdle(safeProcessHandle, 10000) + Shell = ProcessInfo.dwProcessId + End If + Else + 'Check for a win32 error access denied. If it is, make and throw the exception. + 'If not, throw FileNotFound + Const ERROR_ACCESS_DENIED As Integer = 5 + If ErrorCode = ERROR_ACCESS_DENIED Then + Throw VbMakeException(vbErrors.PermissionDenied) + End If + + Throw VbMakeException(vbErrors.FileNotFound) + End If + Finally + safeProcessHandle.Close() ' Close the process handle will not cause the process to stop. + safeThreadHandle.Close() + End Try + Finally + StartupInfo.Dispose() + End Try + End Function + + + 'No HostProtection attribute because the code a demand for UnmanagedCode. + _ + _ + _ + _ + Public Sub AppActivate(ByVal ProcessId As Integer) + 'As an optimization, we will only check the UI permission once we actually know we found the app to activate - we'll do that in AppActivateHelper + + Dim ProcessIdOwningWindow As Integer + 'Note, a process can have multiple windows. What we want to do is dig through to find one + 'that we can actually activate. So first ignore all the ones that are not visible and don't support mouse + 'or keyboard input + Dim WindowHandle As IntPtr = NativeMethods.GetWindow(NativeMethods.GetDesktopWindow(), NativeTypes.GW_CHILD) + + Do While (IntPtr.op_Inequality(WindowHandle, IntPtr.Zero)) + SafeNativeMethods.GetWindowThreadProcessId(WindowHandle, ProcessIdOwningWindow) + If (ProcessIdOwningWindow = ProcessId) AndAlso SafeNativeMethods.IsWindowEnabled(WindowHandle) AndAlso SafeNativeMethods.IsWindowVisible(WindowHandle) Then + Exit Do 'We found a window belonging to the desired process that we can actually activate and will support user input + End If + + 'keep rummaging through windows looking for one that belongs to the process we are after + WindowHandle = NativeMethods.GetWindow(WindowHandle, NativeTypes.GW_HWNDNEXT) + Loop + + 'If we didn't find a window during the pass above, try the less desirable route of finding any window that belongs to the process + If IntPtr.op_Equality(WindowHandle, IntPtr.Zero) Then + WindowHandle = NativeMethods.GetWindow(NativeMethods.GetDesktopWindow(), NativeTypes.GW_CHILD) + + Do While IntPtr.op_Inequality(WindowHandle, IntPtr.Zero) + SafeNativeMethods.GetWindowThreadProcessId(WindowHandle, ProcessIdOwningWindow) + If (ProcessIdOwningWindow = ProcessId) Then + Exit Do + End If + + 'keep rummaging through windows looking for one that belongs to the process we are after + WindowHandle = NativeMethods.GetWindow(WindowHandle, NativeTypes.GW_HWNDNEXT) + Loop + End If + + If IntPtr.op_Equality(WindowHandle, IntPtr.Zero) Then 'we never found a window belonging to the desired process + Throw New ArgumentException(GetResourceString(ResID.ProcessNotFound, CStr(ProcessId))) + Else + AppActivateHelper(WindowHandle) + End If + End Sub + + + 'No HostProtection attribute because the code a demand for UnmanagedCode. + _ + _ + _ + _ + Public Sub AppActivate(ByVal Title As String) + 'As an optimization, we will only check the UI permission once we actually know we found the app to activate - we'll do that in AppActivateHelper + Dim WindowHandle As IntPtr = NativeMethods.FindWindow(Nothing, Title) 'see if we can find the window using an exact match on the title + Const MAX_TITLE_LENGTH As Integer = 511 'CONSIDER: - this is bizarre. Eventually might want to do the trick in c:\vs\ndp\Designer\CompMod\System\ComponentModel\Design\MultilineStringEditor.cs (search for getWindowText) + + ' if no match, search through all parent windows + If IntPtr.op_Equality(WindowHandle, IntPtr.Zero) Then + Dim AppTitle As String = String.Empty + ' Old implementation uses MAX_TITLE_LENGTH characters, INCLUDING NULL character. + ' Interop code will extend string builder to handle NULL character. + Dim AppTitleBuilder As New StringBuilder(MAX_TITLE_LENGTH) + Dim AppTitleLength As Integer + Dim TitleLength As Integer = Len(Title) + + 'Loop through all children of the desktop + WindowHandle = NativeMethods.GetWindow(NativeMethods.GetDesktopWindow(), NativeTypes.GW_CHILD) + Do While IntPtr.op_Inequality(WindowHandle, IntPtr.Zero) + ' get the window caption and test for a left-aligned substring + AppTitleLength = NativeMethods.GetWindowText(WindowHandle, AppTitleBuilder, AppTitleBuilder.Capacity) + AppTitle = AppTitleBuilder.ToString() + + If AppTitleLength >= TitleLength Then + If String.Compare(AppTitle, 0, Title, 0, TitleLength, StringComparison.OrdinalIgnoreCase) = 0 Then + Exit Do 'found one + End If + End If + + 'keep looking + WindowHandle = NativeMethods.GetWindow(WindowHandle, NativeTypes.GW_HWNDNEXT) + Loop + + If IntPtr.op_Equality(WindowHandle, IntPtr.Zero) Then + ' We didn't find it so try right aligned + WindowHandle = NativeMethods.GetWindow(NativeMethods.GetDesktopWindow(), NativeTypes.GW_CHILD) + + Do While IntPtr.op_Inequality(WindowHandle, IntPtr.Zero) + ' get the window caption and test for a right-aligned substring + AppTitleLength = NativeMethods.GetWindowText(WindowHandle, AppTitleBuilder, AppTitleBuilder.Capacity) + AppTitle = AppTitleBuilder.ToString() + + If AppTitleLength >= TitleLength Then + If String.Compare(Right(AppTitle, TitleLength), 0, Title, 0, TitleLength, StringComparison.OrdinalIgnoreCase) = 0 Then + Exit Do 'found a match + End If + End If + + 'keep looking + WindowHandle = NativeMethods.GetWindow(WindowHandle, NativeTypes.GW_HWNDNEXT) + Loop + End If + End If + + If IntPtr.op_Equality(WindowHandle, IntPtr.Zero) Then 'no match + Throw New ArgumentException(GetResourceString(ResID.ProcessNotFound, Title)) + Else + AppActivateHelper(WindowHandle) + End If + End Sub + + _ + _ + _ + Private Sub AppActivateHelper(ByVal hwndApp As IntPtr) + Try + Call (New UIPermission(UIPermissionWindow.AllWindows)).Demand() 'We only check the UI permission once we actually know we found the app to activate - we'll do that in AppActivateHelper + Catch e As Exception + Throw e + End Try + + ' if no window with name (full or truncated) or task id, return an error + ' if the window is not enabled or not visible, get the first window owned by it that is not enabled or not visible + Dim hwndOwned As IntPtr + If (Not SafeNativeMethods.IsWindowEnabled(hwndApp) OrElse Not SafeNativeMethods.IsWindowVisible(hwndApp)) Then + ' scan to the next window until failure + hwndOwned = NativeMethods.GetWindow(hwndApp, NativeTypes.GW_HWNDFIRST) + Do While IntPtr.op_Inequality(hwndOwned, IntPtr.Zero) + If IntPtr.op_Equality(NativeMethods.GetWindow(hwndOwned, NativeTypes.GW_OWNER), hwndApp) Then + If (Not SafeNativeMethods.IsWindowEnabled(hwndOwned) OrElse Not SafeNativeMethods.IsWindowVisible(hwndOwned)) Then + hwndApp = hwndOwned + hwndOwned = NativeMethods.GetWindow(hwndApp, NativeTypes.GW_HWNDFIRST) + Else + Exit Do + End If + End If + hwndOwned = NativeMethods.GetWindow(hwndOwned, NativeTypes.GW_HWNDNEXT) + Loop + + ' if scan failed, return an error + If IntPtr.op_Equality(hwndOwned, IntPtr.Zero) Then + Throw New ArgumentException(GetResourceString(ResID.ProcessNotFound)) + End If + + ' set active window to the owned one + hwndApp = hwndOwned + End If + + ' SetActiveWindow on Win32 only activates the Window - it does + ' not bring to to the foreground unless the window belongs to + ' the current thread. NativeMethods.SetForegroundWindow() activates the + ' window, moves it to the foreground, and bumps the priority + ' of the thread which owns the window. + + Dim dwDummy As Integer ' dummy arg for SafeNativeMethods.GetWindowThreadProcessId + + ' Attach ourselves to the window we want to set focus to + NativeMethods.AttachThreadInput(0, SafeNativeMethods.GetWindowThreadProcessId(hwndApp, dwDummy), 1) + ' Make him foreground and give him focus, this will occur + ' synchronously because we are attached. + NativeMethods.SetForegroundWindow(hwndApp) + NativeMethods.SetFocus(hwndApp) + ' Unattach ourselves from the window + NativeMethods.AttachThreadInput(0, SafeNativeMethods.GetWindowThreadProcessId(hwndApp, dwDummy), 0) + End Sub + + + + + Private m_CommandLine As String + + + Public Function Command() As String + + 'Do this demand so we don't have access to cached m_CommandLine when we go through here without permissions + Call (New EnvironmentPermission(EnvironmentPermissionAccess.Read, "Path")).Demand() + + If m_CommandLine Is Nothing Then + Dim s As String = Environment.CommandLine + + 'The first element of the array is the .exe name + ' we must remove this when building the return value + If (s Is Nothing) OrElse (s.Length = 0) Then + Return "" + End If + + 'The following code must remove the application name from the command line + ' without disturbing the arguments (trailing and embedded spaces) + ' + 'We also need to handle embedded spaces in the application name + ' as well as skipping over quotations used around embedded spaces within + ' the application name + ' examples: + ' f:\"Program Files"\Microsoft\foo.exe a b d e f + ' "f:\"Program Files"\Microsoft\foo.exe" a b d e f + ' f:\Program Files\Microsoft\foo.exe a b d e f + Dim LengthOfAppName, j As Integer + + 'Remove the app name from the arguments + LengthOfAppName = Environment.GetCommandLineArgs(0).Length + + Do + j = s.IndexOf(ChrW(34), j) + If j >= 0 AndAlso j <= LengthOfAppName Then + s = s.Remove(j, 1) + End If + Loop While (j >= 0 AndAlso j <= LengthOfAppName) + + If j = 0 OrElse j > s.Length Then + m_CommandLine = "" + Else + m_CommandLine = LTrim(s.Substring(LengthOfAppName)) + End If + End If + Return m_CommandLine + End Function + + + 'No HostProtection attribute because the code already directly or indirectly has a demand. + _ + Public Function Environ(ByVal Expression As Integer) As String + + 'Validate index - Note that unlike the fx, this is a legacy VB function and the index is 1 based. + If Expression <= 0 OrElse Expression > 255 Then + Throw New ArgumentException(GetResourceString(ResID.Argument_Range1toFF1, "Expression")) + End If + + If m_SortedEnvList Is Nothing Then + SyncLock m_EnvironSyncObject + If m_SortedEnvList Is Nothing Then + 'Constructing the sorted environment list is extremely slow, so we keep a copy around. This list must be alphabetized to match vb5/vb6 behavior + Call New System.Security.Permissions.EnvironmentPermission(PermissionState.Unrestricted).Assert() 'We control to whom we allow access to m_SortedEnvList + m_SortedEnvList = New System.Collections.SortedList(Environment.GetEnvironmentVariables()) + System.Security.PermissionSet.RevertAssert() + End If + End SyncLock + End If + + If Expression > m_SortedEnvList.Count Then + Return "" + End If + + Dim EnvVarName As String = m_SortedEnvList.GetKey(Expression - 1).ToString() + Dim EnvVarValue As String = m_SortedEnvList.GetByIndex(Expression - 1).ToString() + Call New EnvironmentPermission(EnvironmentPermissionAccess.Read, EnvVarName).Demand() 'make sure they have permission to read it. + Return (EnvVarName & "=" & EnvVarValue) + End Function + + Private m_EnvironSyncObject As New Object + + 'No security demand in this version because the frameworks will do a demand on GetEnvironmentVariable + 'No HostProtection attribute because the code already directly or indirectly has a demand. + Public Function Environ(ByVal Expression As String) As String + Expression = Trim(Expression) + + If Expression.Length = 0 Then + Throw New ArgumentException(GetResourceString(ResID.Argument_InvalidValue1, "Expression")) + End If + + Return Environment.GetEnvironmentVariable(Expression) + End Function + + + '============================================================================ + ' User interaction functions. + '============================================================================ + + 'No HostProtection attribute because the code already directly or indirectly has a demand. + _ + Public Sub Beep() + + Try + '*** SECURITY CHECK - SECURITY CHECK - SECURITY CHECK - SECURITY CHECK - SECURITY CHECK - SECURITY CHECK *** + 'Note that if you were to call system.Media.MessageBeep.Play you wouldn't get the security check. They don't + 'check anything at all. + Call (New UIPermission(UIPermissionWindow.SafeSubWindows)).Demand() + Catch ex1 As System.Security.SecurityException + Try + Call (New UIPermission(UIPermissionWindow.SafeTopLevelWindows)).Demand() + Catch ex2 As System.Security.SecurityException + 'Just ignore if not sufficient rights + Exit Sub + End Try + End Try + + UnsafeNativeMethods.MessageBeep(0) + End Sub + + + Private NotInheritable Class InputBoxHandler + Private m_Prompt As String + Private m_Title As String + Private m_DefaultResponse As String + Private m_XPos As Integer + Private m_YPos As Integer + Private m_Result As String + Private m_ParentWindow As System.Windows.Forms.IWin32Window + Private m_Exception As Exception + + Sub New(ByVal Prompt As String, ByVal Title As String, ByVal DefaultResponse As String, ByVal XPos As Integer, ByVal YPos As Integer, ByVal ParentWindow As System.Windows.Forms.IWin32Window) + m_Prompt = Prompt + m_Title = Title + m_DefaultResponse = DefaultResponse + m_XPos = XPos + m_YPos = YPos + m_ParentWindow = ParentWindow + End Sub + + Public Sub StartHere() + Try + m_Result = InternalInputBox(m_Prompt, m_Title, m_DefaultResponse, m_XPos, m_YPos, m_ParentWindow) + Catch ex As Exception + m_Exception = ex + End Try + End Sub + + Public ReadOnly Property Result() As String + Get + Return m_Result + End Get + End Property + + Friend ReadOnly Property Exception As Exception + Get + Return m_Exception + End Get + End Property + End Class + + + _ + Public Function InputBox(ByVal Prompt As String, Optional ByVal Title As String = "", Optional ByVal DefaultResponse As String = "", Optional ByVal XPos As Integer = -1, Optional ByVal YPos As Integer = -1) As String + Dim vbhost As CompilerServices.IVbHost + Dim ParentWindow As System.Windows.Forms.IWin32Window = Nothing + + vbhost = CompilerServices.HostServices.VBHost + If vbhost IsNot Nothing Then 'If we are hosted then we want to use the host as the parent window. If no parent window that's fine. + ParentWindow = vbhost.GetParentWindow() + End If + + If Title.Length = 0 Then + If vbhost Is Nothing Then + Title = GetTitleFromAssembly(System.Reflection.Assembly.GetCallingAssembly()) + Else + Title = vbhost.GetWindowTitle() + End If + End If + + 'Threading state can only be set once, and will most often be already set + 'but set to STA and check if it isn't STA, then we need to start another thread + 'to display the InputBox + 'CONSIDER : use the threadpool for performance? + If System.Threading.Thread.CurrentThread.GetApartmentState() <> Threading.ApartmentState.STA Then + Dim InputHandler As New InputBoxHandler(Prompt, Title, DefaultResponse, XPos, YPos, ParentWindow) + Dim thread As New Threading.Thread(New Threading.ThreadStart(AddressOf InputHandler.StartHere)) + thread.Start() + Thread.Join() + + If InputHandler.Exception IsNot Nothing Then + Throw InputHandler.Exception + End If + + Return InputHandler.Result + Else + Return InternalInputBox(Prompt, Title, DefaultResponse, XPos, YPos, ParentWindow) + End If + End Function + + Private Function GetTitleFromAssembly(ByVal CallingAssembly As System.Reflection.Assembly) As String + + Dim Title As String + + 'Get the Assembly name of the calling assembly + 'Assembly.GetName requires PathDiscovery permission so we try this first + 'and if it throws we catch the security exception and parse the name + 'from the full assembly name + Try + Title = CallingAssembly.GetName().Name + Catch ex As SecurityException + Dim FullName As String = CallingAssembly.FullName + + 'Find the text up to the first comma. Note, this fails if the assembly has + 'a comma in its name + Dim FirstCommaLocation As Integer = FullName.IndexOf(","c) + If FirstCommaLocation >= 0 Then + Title = FullName.Substring(0, FirstCommaLocation) + Else + 'The name is not in the format we're expecting so return an empty string + Title = "" + End If + End Try + + Return Title + + End Function + + Private Function InternalInputBox(ByVal Prompt As String, ByVal Title As String, ByVal DefaultResponse As String, ByVal XPos As Integer, ByVal YPos As Integer, ByVal ParentWindow As System.Windows.Forms.IWin32Window) As String + Dim Box As VBInputBox = New VBInputBox(Prompt, Title, DefaultResponse, XPos, YPos) + Box.ShowDialog(ParentWindow) + + ' Fix FxCop violation DisposeObjectsBeforeLosingScope + InternalInputBox = Box.Output + Box.Dispose() + End Function + + _ + Public Function MsgBox(ByVal Prompt As Object, Optional ByVal Buttons As MsgBoxStyle = MsgBoxStyle.OKOnly, Optional ByVal Title As Object = Nothing) As MsgBoxResult + Dim sPrompt As String = Nothing + Dim sTitle As String + Dim vbhost As CompilerServices.IVbHost + Dim ParentWindow As System.Windows.Forms.IWin32Window = Nothing + + vbhost = CompilerServices.HostServices.VBHost + If Not vbhost Is Nothing Then + ParentWindow = vbhost.GetParentWindow() + End If + + 'Only allow legal button combinations to be set, one choice from each group + 'These bit constants are defined in System.Windows.Forms.MessageBox + 'Low-order 4 bits (0x000f), legal values: 0, 1, 2, 3, 4, 5 + ' next 4 bits (0x00f0), legal values: 0, &H10, &H20, &H30, &H40 + ' next 4 bits (0x0f00), legal values: 0, &H100, &H200 + If ((Buttons And &HFI) > MsgBoxStyle.RetryCancel) OrElse ((Buttons And &HF0I) > MsgBoxStyle.Information) _ + OrElse ((Buttons And &HF00I) > MsgBoxStyle.DefaultButton3) Then + Buttons = MsgBoxStyle.OKOnly + End If + + Try + If Not Prompt Is Nothing Then + sPrompt = DirectCast(Conversions.ChangeType(Prompt, GetType(String)), String) + End If + Catch ex As StackOverflowException + Throw ex + Catch ex As OutOfMemoryException + Throw ex + Catch ex As System.Threading.ThreadAbortException + Throw ex + Catch + Throw New ArgumentException(GetResourceString(ResID.Argument_InvalidValueType2, "Prompt", "String")) + End Try + + Try + If Title Is Nothing Then + If vbhost Is Nothing Then + sTitle = GetTitleFromAssembly(System.Reflection.Assembly.GetCallingAssembly()) + Else + sTitle = vbhost.GetWindowTitle() + End If + Else + sTitle = CStr(Title) 'allows the title to be an expression, e.g. msgbox(prompt, Title:=1+5) + End If + Catch ex As StackOverflowException + Throw ex + Catch ex As OutOfMemoryException + Throw ex + Catch ex As System.Threading.ThreadAbortException + Throw ex + Catch + Throw New ArgumentException(GetResourceString(ResID.Argument_InvalidValueType2, "Title", "String")) + End Try + + Return CType(System.Windows.Forms.MessageBox.Show(ParentWindow, sPrompt, sTitle, _ + CType(Buttons And &HF, System.Windows.Forms.MessageBoxButtons), _ + CType(Buttons And &HF0, System.Windows.Forms.MessageBoxIcon), _ + CType(Buttons And &HF00, System.Windows.Forms.MessageBoxDefaultButton), _ + CType(Buttons And &HFFFFF000, System.Windows.Forms.MessageBoxOptions)), _ + MsgBoxResult) + End Function + +#End If ' Not TELESTO + + '============================================================================ + ' String functions. + '============================================================================ + Public Function Choose(ByVal Index As Double, ByVal ParamArray Choice() As Object) As Object + + Dim FixedIndex As Integer = CInt(Fix(Index) - 1) 'ParamArray is 0 based, but Choose assumes 1 based + + If Choice.Rank <> 1 Then + Throw New ArgumentException(GetResourceString(ResID.Argument_RankEQOne1, "Choice")) + ElseIf FixedIndex < 0 OrElse FixedIndex > Choice.GetUpperBound(0) Then + Return Nothing + End If + + Return Choice(FixedIndex) + End Function + + Public Function IIf(ByVal Expression As Boolean, ByVal TruePart As Object, ByVal FalsePart As Object) As Object + If Expression Then + Return TruePart + End If + + Return FalsePart + End Function +#End If 'Not LATEBINDING + + + Friend Function IIf(Of T)(ByVal Condition As Boolean, ByVal TruePart As T, ByVal FalsePart As T) As T + If Condition Then + Return TruePart + End If + + Return FalsePart + End Function + +#If Not LATEBINDING Then + + Public Function Partition(ByVal Number As Long, ByVal Start As Long, ByVal [Stop] As Long, ByVal Interval As Long) As String + 'CONSIDER: Change to use StringBuilder + Dim Lower As Long + Dim Upper As Long + Dim NoUpper As Boolean + Dim NoLower As Boolean + Dim Buffer As String = Nothing + Dim Buffer1 As String + Dim Buffer2 As String + Dim Spaces As Long + + 'Validate arguments + If (Start < 0) Then + Throw New ArgumentException(GetResourceString(ResID.Argument_InvalidValue1, "Start")) + End If + + If ([Stop] <= Start) Then + Throw New ArgumentException(GetResourceString(ResID.Argument_InvalidValue1, "Stop")) + End If + + If (Interval < 1) Then + Throw New ArgumentException(GetResourceString(ResID.Argument_InvalidValue1, "Interval")) + End If + + 'Check for before-first and after-last ranges + If Number < Start Then + Upper = Start - 1 + NoLower = True + ElseIf Number > [Stop] Then + Lower = [Stop] + 1 + NoUpper = True + ElseIf Interval = 1 Then 'This is a special case + Lower = Number + Upper = Number + Else + 'Calculate the upper and lower ranges + 'Note the use of Integer division "\" which truncates to whole number + Lower = ((Number - Start) \ Interval) * Interval + Start + Upper = Lower + Interval - 1 + + 'Adjust for first and last ranges + If Upper > [Stop] Then + Upper = [Stop] + End If + + If Lower < Start Then + Lower = Start + End If + End If + + 'Build-up the string. Calculate number of spaces needed: VB3 uses Stop + 1. + 'This may seem bogus but it has to be this way for VB3 compatibilty. + Buffer1 = CStr([Stop] + 1) + Buffer2 = CStr([Start] - 1) + + If Len(Buffer1) > Len(Buffer2) Then + Spaces = Len(Buffer1) + Else + Spaces = Len(Buffer2) + End If + + 'Handle case where Upper is -1 and Stop < 9 + If NoLower Then + Buffer1 = CStr(Upper) + If Spaces < Len(Buffer1) Then + Spaces = Len(Buffer1) + End If + End If + + 'Insert lower-end of partition range. + If NoLower Then + InsertSpaces(Buffer, Spaces) + Else + InsertNumber(Buffer, Lower, Spaces) + End If + + 'Insert the partition + Buffer = Buffer & ":" + + 'Insert upper-end of partition range + If NoUpper Then + InsertSpaces(Buffer, Spaces) + Else + InsertNumber(Buffer, Upper, Spaces) + End If + + Return Buffer + End Function + + + Private Sub InsertSpaces(ByRef Buffer As String, ByVal Spaces As Long) + Do While Spaces > 0 'consider: - use stringbuilder + Buffer = Buffer & " " + Spaces = Spaces - 1 + Loop + End Sub + + + Private Sub InsertNumber(ByRef Buffer As String, ByVal Num As Long, ByVal Spaces As Long) + Dim Buffer1 As String 'consider: - use stringbuilder + + 'Convert number to a string + Buffer1 = CStr(Num) + + 'Insert leading spaces + InsertSpaces(Buffer, Spaces - Len(Buffer1)) + + 'Append string + Buffer = Buffer & Buffer1 + End Sub + + + Public Function Switch(ByVal ParamArray VarExpr() As Object) As Object + Dim Elements As Integer + Dim Index As Integer + + If VarExpr Is Nothing Then + Return Nothing + End If + + Elements = VarExpr.Length + Index = 0 + + 'Ensure we have an even number of arguments (0 based) + If (Elements Mod 2) <> 0 Then + Throw New ArgumentException(GetResourceString(ResID.Argument_InvalidValue1, "VarExpr")) + End If + + Do While Elements > 0 + If CBool(VarExpr(Index)) Then + Return VarExpr(Index + 1) + End If + + Index += 2 + Elements -= 2 + Loop + + Return Nothing 'If nothing matched above + End Function + +#If Not TELESTO Then + + '============================================================================ + ' Registry functions. + '============================================================================ + + _ + Public Sub DeleteSetting(ByVal AppName As String, Optional ByVal Section As String = Nothing, Optional ByVal Key As String = Nothing) + Dim AppSection As String + Dim UserKey As RegistryKey + Dim AppSectionKey As RegistryKey = Nothing + + CheckPathComponent(AppName) + AppSection = FormRegKey(AppName, Section) + + Try + UserKey = Registry.CurrentUser + + If IsNothing(Key) OrElse (Key.Length = 0) Then + UserKey.DeleteSubKeyTree(AppSection) + Else + AppSectionKey = UserKey.OpenSubKey(AppSection, True) + If AppSectionKey Is Nothing Then + Throw New ArgumentException(GetResourceString(ResID.Argument_InvalidValue1, "Section")) + End If + + AppSectionKey.DeleteValue(Key) + End If + + Catch ex As Exception + Throw ex + Finally + If AppSectionKey IsNot Nothing Then + AppSectionKey.Close() + End If + End Try + End Sub + + + 'No HostProtection attribute because the code already directly or indirectly has a demand. + Public Function GetAllSettings(ByVal AppName As String, ByVal Section As String) As String(,) + Dim rk As RegistryKey + Dim sAppSect As String + Dim i As Integer + Dim lUpperBound As Integer + Dim sValueNames() As String + Dim sValues(,) As String + Dim o As Object + Dim sName As String + + ' Check for empty string in path + CheckPathComponent(AppName) + CheckPathComponent(Section) + sAppSect = FormRegKey(AppName, Section) + rk = Registry.CurrentUser.OpenSubKey(sAppSect) + + + If rk Is Nothing Then + Return Nothing + End If + + GetAllSettings = Nothing + Try + If rk.ValueCount <> 0 Then + sValueNames = rk.GetValueNames() + lUpperBound = sValueNames.GetUpperBound(0) + ReDim sValues(lUpperBound, 1) + + For i = 0 To lUpperBound + sName = sValueNames(i) + + 'Assign name + sValues(i, 0) = sName + + 'Assign value + o = rk.GetValue(sName) + + If (Not o Is Nothing) AndAlso (TypeOf o Is String) Then + sValues(i, 1) = o.ToString() + End If + Next i + + GetAllSettings = sValues + End If + + Catch ex As StackOverflowException + Throw ex + Catch ex As OutOfMemoryException + Throw ex + Catch ex As System.Threading.ThreadAbortException + Throw ex + + Catch ex As Exception + 'Consume the exception + + Finally + rk.Close() + End Try + End Function + + + Public Function GetSetting(ByVal AppName As String, ByVal Section As String, ByVal Key As String, Optional ByVal [Default] As String = "") As String + Dim rk As RegistryKey = Nothing + Dim sAppSect As String + Dim o As Object + + 'Check for empty strings + CheckPathComponent(AppName) + CheckPathComponent(Section) + CheckPathComponent(Key) + If [Default] Is Nothing Then + [Default] = "" + End If + + 'Open the sub key + sAppSect = FormRegKey(AppName, Section) + Try + rk = Registry.CurrentUser.OpenSubKey(sAppSect) 'By default, does not request write permission + + 'Get the key's value + If rk Is Nothing Then + Return [Default] + End If + + o = rk.GetValue(Key, [Default]) + Finally + If rk IsNot Nothing Then + rk.Close() + End If + End Try + + If o Is Nothing Then + Return Nothing + ElseIf TypeOf o Is String Then ' - odd that this is required to be a string when it isn't in GetAllSettings() above... + Return DirectCast(o, String) + Else + Throw New ArgumentException(GetResourceString(ResID.Argument_InvalidValue)) + End If + End Function + + + 'No HostProtection attribute because the code already directly or indirectly has a demand. + Public Sub SaveSetting(ByVal AppName As String, ByVal Section As String, ByVal Key As String, ByVal Setting As String) + Dim rk As RegistryKey + Dim sIniSect As String + + ' Check for empty string in path + CheckPathComponent(AppName) + CheckPathComponent(Section) + CheckPathComponent(Key) + + sIniSect = FormRegKey(AppName, Section) + rk = Registry.CurrentUser.CreateSubKey(sIniSect) + + If rk Is Nothing Then + 'Subkey could not be created + Throw New ArgumentException(GetResourceString(ResID.Interaction_ResKeyNotCreated1, sIniSect)) + End If + + Try + rk.SetValue(Key, Setting) + Catch ex As Exception + 'CONSIDER: Should we throw a different exception? + Throw ex + Finally + rk.Close() + End Try + End Sub + + '============================================================================ + ' Private functions. + '============================================================================ + Private Function FormRegKey(ByVal sApp As String, ByVal sSect As String) As String + Const REGISTRY_INI_ROOT As String = "Software\VB and VBA Program Settings" + 'Forms the string for the key value + If IsNothing(sApp) OrElse (sApp.Length = 0) Then + FormRegKey = REGISTRY_INI_ROOT + ElseIf IsNothing(sSect) OrElse (sSect.Length = 0) Then + FormRegKey = REGISTRY_INI_ROOT & "\" & sApp + Else + FormRegKey = REGISTRY_INI_ROOT & "\" & sApp & "\" & sSect + End If + End Function + + + Private Sub CheckPathComponent(ByVal s As String) + If (s Is Nothing) OrElse (s.Length = 0) Then + Throw New ArgumentException(GetResourceString(ResID.Argument_PathNullOrEmpty)) + End If + End Sub + + + _ + Private Interface IPersistFile + + Sub GetClassID(ByRef pClassID As Guid) + Sub IsDirty() + Sub Load(ByVal pszFileName As String, ByVal dwMode As Integer) + Sub Save(ByVal pszFileName As String, ByVal fRemember As Integer) + Sub SaveCompleted(ByVal pszFileName As String) + Function GetCurFile() As String + End Interface + + _ + _ + _ + Public Function CreateObject(ByVal ProgId As String, Optional ByVal ServerName As String = "") As Object + 'Creates local or remote COM2 objects. Should not be used to create COM+ objects. + 'Applications that need to be STA should set STA either on their Sub Main via STAThreadAttribute + 'or through Thread.CurrentThread.ApartmentState - the VB runtime will not change this. + 'DO NOT SET THREAD STATE - Thread.CurrentThread.ApartmentState = ApartmentState.STA + + Dim t As Type + + If ProgId.Length = 0 Then + Throw VbMakeException(vbErrors.CantCreateObject) + End If + + If ServerName Is Nothing OrElse ServerName.Length = 0 Then + ServerName = Nothing + Else + 'Does the ServerName match the MachineName? + If String.Compare(Environment.MachineName, ServerName, StringComparison.OrdinalIgnoreCase) = 0 Then + ServerName = Nothing + End If + End If + + Try + If ServerName Is Nothing Then + t = Type.GetTypeFromProgID(ProgId) + Else + t = Type.GetTypeFromProgID(ProgId, ServerName, True) + End If + + Return System.Activator.CreateInstance(t) + Catch e As COMException + If e.ErrorCode = &H800706BA Then '&H800706BA = The RPC Server is unavailable + Throw VbMakeException(vbErrors.ServerNotFound) + Else + Throw VbMakeException(vbErrors.CantCreateObject) + End If + Catch ex As StackOverflowException + Throw ex + Catch ex As OutOfMemoryException + Throw ex + Catch ex As System.Threading.ThreadAbortException + Throw ex + Catch e As Exception + Throw VbMakeException(vbErrors.CantCreateObject) + End Try + End Function + + _ + _ + _ + Public Function GetObject(Optional ByVal PathName As String = Nothing, Optional ByVal [Class] As String = Nothing) As Object + 'Only works for Com2 objects, not for COM+ objects. + Dim o As Object + Dim t As Type + Dim Persist As IPersistFile + + If Len([Class]) = 0 Then + Try + Return Marshal.BindToMoniker([PathName]) + Catch ex As StackOverflowException + Throw ex + Catch ex As OutOfMemoryException + Throw ex + Catch ex As System.Threading.ThreadAbortException + Throw ex + Catch + Throw VbMakeException(vbErrors.CantCreateObject) + End Try + Else + If PathName Is Nothing Then + Try + Return Marshal.GetActiveObject([Class]) + Catch ex As StackOverflowException + Throw ex + Catch ex As OutOfMemoryException + Throw ex + Catch ex As System.Threading.ThreadAbortException + Throw ex + Catch + Throw VbMakeException(vbErrors.CantCreateObject) + End Try + ElseIf Len(PathName) = 0 Then + Try + t = Type.GetTypeFromProgID([Class]) + Return System.Activator.CreateInstance(t) + Catch ex As StackOverflowException + Throw ex + Catch ex As OutOfMemoryException + Throw ex + Catch ex As System.Threading.ThreadAbortException + Throw ex + Catch + Throw VbMakeException(vbErrors.CantCreateObject) + End Try + Else + Try + o = Marshal.GetActiveObject([Class]) + Persist = CType(o, IPersistFile) + Catch ex As StackOverflowException + Throw ex + Catch ex As OutOfMemoryException + Throw ex + Catch ex As System.Threading.ThreadAbortException + Throw ex + Catch + Throw VbMakeException(vbErrors.OLEFileNotFound) + End Try + + Try + Persist.Load([PathName], 0) + Catch ex As StackOverflowException + Throw ex + Catch ex As OutOfMemoryException + Throw ex + Catch ex As System.Threading.ThreadAbortException + Throw ex + Catch + Throw VbMakeException(vbErrors.CantCreateObject) + End Try + + Return Persist + End If + End If + End Function + +#Region " BACKWARDS COMPATIBILITY " + + 'WARNING WARNING WARNING WARNING WARNING + 'This code exists to support Everett compiled applications. Make sure you understand + 'the backwards compatibility ramifications of any edit you make in this region. + 'WARNING WARNING WARNING WARNING WARNING + + '============================================================================ + ' Object/latebound functions. + '============================================================================ + Public Function CallByName(ByVal ObjectRef As System.Object, ByVal ProcName As String, ByVal UseCallType As CallType, ByVal ParamArray Args() As Object) As Object + Select Case UseCallType + + Case CallType.Method + 'Need to use LateGet, because we are returning a value + Return CompilerServices.LateBinding.InternalLateCall(ObjectRef, Nothing, ProcName, Args, Nothing, Nothing, False) + + Case CallType.Get + Return CompilerServices.LateBinding.LateGet(ObjectRef, Nothing, ProcName, Args, Nothing, Nothing) + + Case CallType.Let, _ + CallType.Set + CompilerServices.LateBinding.InternalLateSet(ObjectRef, Nothing, ProcName, Args, Nothing, False, UseCallType) + Return Nothing + + Case Else + Throw New ArgumentException(GetResourceString(ResID.Argument_InvalidValue1, "CallType")) + End Select + End Function +#End Region + +#End If 'Not TELESTO +#End If 'Not LATEBINDING Then + End Module + +End Namespace + diff --git a/Microsoft.VisualBasic/runtime/msvbalib/Logging/FileLogTraceListener.vb b/Microsoft.VisualBasic/runtime/msvbalib/Logging/FileLogTraceListener.vb new file mode 100644 index 000000000..91b13275e --- /dev/null +++ b/Microsoft.VisualBasic/runtime/msvbalib/Logging/FileLogTraceListener.vb @@ -0,0 +1,1489 @@ +' Copyright (c) Microsoft Corporation. All rights reserved. +Option Explicit On +Option Strict On + +Imports System +Imports System.Collections.Generic +Imports System.ComponentModel +Imports System.Diagnostics +Imports System.Globalization +Imports System.IO +Imports System.Reflection +Imports System.Security +Imports System.Security.Permissions +Imports System.Windows.Forms +Imports System.Text +Imports Microsoft.VisualBasic +Imports Microsoft.VisualBasic.CompilerServices +Imports Microsoft.VisualBasic.CompilerServices.ExceptionUtils +Imports Microsoft.VisualBasic.CompilerServices.Utils + +Namespace Microsoft.VisualBasic.Logging + + '''*************************************************************************************** + ''';LogFileLocation + ''' + ''' Options for the location of a log's directory + ''' + ''' + Public Enum LogFileLocation As Integer + '!!!!!!!!!!!! Changes to this enum must be reflected in ValidateLogfileLocationEnumValue() + TempDirectory + LocalUserApplicationDirectory + CommonApplicationDirectory + ExecutableDirectory + Custom + End Enum + + '''*************************************************************************************** + ''';LogFileCreationScheduleOption + ''' + ''' Options for the date stamp in the name of a log file + ''' + ''' + Public Enum LogFileCreationScheduleOption As Integer + None '(default) + Daily 'YYYY-MM-DD for today + Weekly 'YYYY-MM-DD for first day of this week + End Enum + + '''*************************************************************************************** + ''';DiskSpaceExhaustedOption + ''' + ''' Options for behavior when resources are exhausted + ''' + ''' + Public Enum DiskSpaceExhaustedOption As Integer + ThrowException + DiscardMessages + End Enum + + '''**************************************************************************** + ''';FileLogTraceListener + ''' + ''' Class for logging to a text file + ''' + ''' + ''' TraceListener is ComVisible(False), Microsoft.VisualBasic.dll is ComVisible(True). + ''' Therefore, mark FileLogTraceListener as ComVisible(False). + ''' + _ + Public Class FileLogTraceListener + Inherits TraceListener + + '==PUBLIC****************************************************************** + + '''************************************************************************ + ''';New + ''' + ''' Creates a FileLogTraceListener with the passed in name + ''' + ''' The name of the listener + ''' + _ + Public Sub New(ByVal name As String) + MyBase.New(name) + End Sub + + '''************************************************************************ + ''';New + ''' + ''' Creates a FileLogTraceListener with default name + ''' + ''' + _ + Public Sub New() + Me.New(DEFAULT_NAME) + End Sub + + '''************************************************************************ + ''';Location + ''' + ''' Indicates the log's directory + ''' + ''' An enum which can indicate one of several logical locations for the log + ''' + Public Property Location() As LogFileLocation + Get + If Not m_PropertiesSet(LOCATION_INDEX) Then + If Attributes.ContainsKey(KEY_LOCATION) Then + Dim converter As TypeConverter = TypeDescriptor.GetConverter(GetType(LogFileLocation)) + Me.Location = DirectCast(converter.ConvertFromInvariantString(Attributes(KEY_LOCATION)), LogFileLocation) + End If + End If + Return m_Location + End Get + Set(ByVal value As LogFileLocation) + ValidateLogFileLocationEnumValue(value, "value") + + ' If the location is changing we need to close the current file + If m_Location <> value Then + CloseCurrentStream() + End If + m_Location = value + m_PropertiesSet(LOCATION_INDEX) = True + End Set + End Property + + '''*********************************************************************** + ''';AutoFlush + ''' + ''' Indicates whether or not the stream should be flushed after every write + ''' + ''' True if the stream should be flushed after every write, otherwise False + ''' + Public Property AutoFlush() As Boolean + Get + If Not m_PropertiesSet(AUTOFLUSH_INDEX) Then + If Attributes.ContainsKey(KEY_AUTOFLUSH) Then + Me.AutoFlush = Convert.ToBoolean(Attributes(KEY_AUTOFLUSH), CultureInfo.InvariantCulture) + End If + End If + + Return m_AutoFlush + End Get + _ + Set(ByVal value As Boolean) + DemandWritePermission() + m_AutoFlush = value + m_PropertiesSet(AUTOFLUSH_INDEX) = True + End Set + End Property + + '''*********************************************************************** + ''';IncludeHostName + ''' + ''' Indicates whether or not the the host name of the logging machine should + ''' be included in the output. + ''' + ''' True if the HostId should be included, otherwise False + ''' + Public Property IncludeHostName() As Boolean + Get + If Not m_PropertiesSet(INCLUDEHOSTNAME_INDEX) Then + If Attributes.ContainsKey(KEY_INCLUDEHOSTNAME) Then + Me.IncludeHostName = Convert.ToBoolean(Attributes(KEY_INCLUDEHOSTNAME), CultureInfo.InvariantCulture) + End If + End If + Return m_IncludeHostName + End Get + _ + Set(ByVal value As Boolean) + DemandWritePermission() + m_IncludeHostName = value + m_PropertiesSet(INCLUDEHOSTNAME_INDEX) = True + End Set + End Property + + '''*********************************************************************** + ''';Append + ''' + ''' Indicates whether or not the file should be appended to or overwritten + ''' + ''' True if the file should be appended to, otherwise False + ''' + Public Property Append() As Boolean + Get + If Not m_PropertiesSet(APPEND_INDEX) Then + If Attributes.ContainsKey(KEY_APPEND) Then + Me.Append = Convert.ToBoolean(Attributes(KEY_APPEND), CultureInfo.InvariantCulture) + End If + End If + + Return m_Append + End Get + _ + Set(ByVal value As Boolean) + + DemandWritePermission() + + ' If this property is changing, we need to close the current file + If value <> m_Append Then + CloseCurrentStream() + End If + m_Append = value + m_PropertiesSet(APPEND_INDEX) = True + End Set + End Property + + '''*********************************************************************** + ''';DiskSpaceExhaustedBehavior + ''' + ''' Indicates what to do when the size of the log trespasses on the MaxFileSize + ''' or the ReserveDiskSpace set by the user + ''' + ''' An enum indicating the desired behavior (do nothing, throw) + ''' + Public Property DiskSpaceExhaustedBehavior() As DiskSpaceExhaustedOption + Get + If Not m_PropertiesSet(DISKSPACEEXHAUSTEDBEHAVIOR_INDEX) Then + If Attributes.ContainsKey(KEY_DISKSPACEEXHAUSTEDBEHAVIOR) Then + Dim converter As TypeConverter = TypeDescriptor.GetConverter(GetType(DiskSpaceExhaustedOption)) + Me.DiskSpaceExhaustedBehavior = DirectCast(converter.ConvertFromInvariantString(Attributes(KEY_DISKSPACEEXHAUSTEDBEHAVIOR)), DiskSpaceExhaustedOption) + End If + End If + Return m_DiskSpaceExhaustedBehavior + End Get + _ + Set(ByVal value As DiskSpaceExhaustedOption) + DemandWritePermission() + ValidateDiskSpaceExhaustedOptionEnumValue(value, "value") + m_DiskSpaceExhaustedBehavior = value + m_PropertiesSet(DISKSPACEEXHAUSTEDBEHAVIOR_INDEX) = True + End Set + End Property + + '''*********************************************************************** + ''';BaseFileName + ''' + ''' The name of the log file not including DateStamp, file number, Path or extension + ''' + ''' The name of the log file + ''' + Public Property BaseFileName() As String + Get + If Not m_PropertiesSet(BASEFILENAME_INDEX) Then + If Attributes.ContainsKey(KEY_BASEFILENAME) Then + Me.BaseFileName = Attributes(KEY_BASEFILENAME) + End If + End If + Return m_BaseFileName + End Get + Set(ByVal value As String) + If value = "" Then + Throw GetArgumentNullException("value", ResID.MyID.ApplicationLogBaseNameNull) + End If + + ' Test the file name. This will throw if the name is invalid. + Path.GetFullPath(value) + + If String.Compare(value, m_BaseFileName, StringComparison.OrdinalIgnoreCase) <> 0 Then + CloseCurrentStream() + m_BaseFileName = value + End If + + m_PropertiesSet(BASEFILENAME_INDEX) = True + End Set + End Property + + '''*********************************************************************** + ''';FullLogFileName + ''' + ''' The fullname and path of the actual log file including DateStamp and file number + ''' + ''' The full name and path + ''' Calling this method will open the log file if it's not already open + Public ReadOnly Property FullLogFileName() As String + _ + Get + ' The only way to reliably know the file name is to open the file. If we + ' don't have a stream, get one (this will open the file) + EnsureStreamIsOpen() + + ' We shouldn't use fields for demands so we use a local variable + Dim returnPath As String = m_FullFileName + Dim filePermission As New FileIOPermission(FileIOPermissionAccess.PathDiscovery, returnPath) + filePermission.Demand() + + Return returnPath + End Get + End Property + + '''********************************************************************** + ''';LogFileCreationSchedule + ''' + ''' Indicates what Date to stamp the log file with (none, first day of week, day) + ''' + ''' An enum indicating how to stamp the file + ''' + Public Property LogFileCreationSchedule() As LogFileCreationScheduleOption + Get + If Not m_PropertiesSet(LOGFILECREATIONSCHEDULE_INDEX) Then + If Attributes.ContainsKey(KEY_LOGFILECREATIONSCHEDULE) Then + Dim converter As TypeConverter = TypeDescriptor.GetConverter(GetType(LogFileCreationScheduleOption)) + Me.LogFileCreationSchedule = DirectCast(converter.ConvertFromInvariantString(Attributes(KEY_LOGFILECREATIONSCHEDULE)), LogFileCreationScheduleOption) + End If + End If + Return m_LogFileDateStamp + End Get + Set(ByVal value As LogFileCreationScheduleOption) + ValidateLogFileCreationScheduleOptionEnumValue(value, "value") + + If value <> m_LogFileDateStamp Then + CloseCurrentStream() + m_LogFileDateStamp = value + End If + + m_PropertiesSet(LOGFILECREATIONSCHEDULE_INDEX) = True + End Set + End Property + + '''********************************************************************** + ''';MaxFileSize + ''' + ''' The maximum size in bytes the log file is allowed to grow to + ''' + ''' The maximum size + ''' + Public Property MaxFileSize() As Long + Get + If Not m_PropertiesSet(MAXFILESIZE_INDEX) Then + If Attributes.ContainsKey(KEY_MAXFILESIZE) Then + Me.MaxFileSize = Convert.ToInt64(Attributes(KEY_MAXFILESIZE), CultureInfo.InvariantCulture) + End If + End If + Return m_MaxFileSize + End Get + _ + Set(ByVal value As Long) + DemandWritePermission() + If value < MIN_FILE_SIZE Then + Throw GetArgumentExceptionWithArgName("value", ResID.MyID.ApplicationLogNumberTooSmall, "MaxFileSize") + End If + m_MaxFileSize = value + m_PropertiesSet(MAXFILESIZE_INDEX) = True + End Set + End Property + + '''********************************************************************** + ''';ReserveDiskSpace + ''' + ''' The amount of disk space, in bytes, that must be available after a write + ''' + ''' The reserved disk space + ''' + Public Property ReserveDiskSpace() As Long + Get + If Not m_PropertiesSet(RESERVEDISKSPACE_INDEX) Then + If Attributes.ContainsKey(KEY_RESERVEDISKSPACE) Then + Me.ReserveDiskSpace = Convert.ToInt64(Attributes(KEY_RESERVEDISKSPACE), CultureInfo.InvariantCulture) + End If + End If + Return m_ReserveDiskSpace + End Get + _ + Set(ByVal value As Long) + DemandWritePermission() + If value < 0 Then + Throw GetArgumentExceptionWithArgName("value", ResID.MyID.ApplicationLog_NegativeNumber, "ReserveDiskSpace") + End If + m_ReserveDiskSpace = value + m_PropertiesSet(RESERVEDISKSPACE_INDEX) = True + End Set + End Property + + '''********************************************************************* + ''';Delimiter + ''' + ''' The delimiter to be used to delimit fields in a line of output + ''' + ''' The delimiter + ''' + Public Property Delimiter() As String + Get + If Not m_PropertiesSet(DELIMITER_INDEX) Then + If Attributes.ContainsKey(KEY_DELIMITER) Then + Me.Delimiter = Attributes(KEY_DELIMITER) + End If + End If + Return m_Delimiter + End Get + Set(ByVal value As String) + m_Delimiter = value + m_PropertiesSet(DELIMITER_INDEX) = True + End Set + End Property + + '''********************************************************************* + ''';Encoding + ''' + ''' The encoding to try when opening a file. + ''' + ''' The encoding + ''' + ''' If Append is true then this value will be trumped by the actual encoding value + ''' of the file + ''' + Public Property Encoding() As Encoding + Get + If Not m_PropertiesSet(ENCODING_INDEX) Then + If Attributes.ContainsKey(KEY_ENCODING) Then + Me.Encoding = System.Text.Encoding.GetEncoding(Attributes(KEY_ENCODING)) + End If + End If + Return m_Encoding + End Get + Set(ByVal value As Encoding) + If value Is Nothing Then + Throw GetArgumentNullException("value") + End If + m_Encoding = value + m_PropertiesSet(ENCODING_INDEX) = True + End Set + End Property + + '''********************************************************************* + ''';CustomLocation + ''' + ''' The directory to be used if Location is set to Custom + ''' + ''' The name of the directory + ''' This will throw if the path cannot be resolved + Public Property CustomLocation() As String + _ + Get + If Not m_PropertiesSet(CUSTOMLOCATION_INDEX) Then + If Attributes.ContainsKey(KEY_CUSTOMLOCATION) Then + Me.CustomLocation = Attributes(KEY_CUSTOMLOCATION) + End If + End If + + Dim fileName As String = Path.GetFullPath(m_CustomLocation) + Dim filePermission As New FileIOPermission(FileIOPermissionAccess.PathDiscovery, fileName) + filePermission.Demand() + Return fileName + End Get + Set(ByVal value As String) + + ' Validate the path + Dim tempPath As String = Path.GetFullPath(value) + + If Not Directory.Exists(tempPath) Then + Directory.CreateDirectory(tempPath) + End If + + ' If we're using custom location and the value is changing we need to + ' close the stream + If Me.Location = LogFileLocation.Custom And String.Compare(tempPath, m_CustomLocation, StringComparison.OrdinalIgnoreCase) <> 0 Then + CloseCurrentStream() + End If + + ' Since the user is setting a custom path, set Location to custom + Me.Location = LogFileLocation.Custom + + m_CustomLocation = tempPath + m_PropertiesSet(CUSTOMLOCATION_INDEX) = True + + End Set + End Property + + '''******************************************************************** + ''';Write + ''' + ''' Writes the message to the log + ''' + ''' The message to be written + ''' + _ + Public Overloads Overrides Sub Write(ByVal message As String) + + ' Use Try block to attempt to close stream if an exception is thrown + Try + HandleDateChange() + + ' Check resources + Dim NewEntrySize As Int64 = Me.Encoding.GetByteCount(message) + + If ResourcesAvailable(NewEntrySize) Then + ListenerStream.Write(message) + If Me.AutoFlush Then + ListenerStream.Flush() + End If + End If + Catch + CloseCurrentStream() + Throw + End Try + + End Sub + + '''******************************************************************** + ''';WriteLine + ''' + ''' Writes the message to the log as a line + ''' + ''' The message to be written + ''' + _ + Public Overloads Overrides Sub WriteLine(ByVal message As String) + + ' Use Try block to attempt to close stream if an exception is thrown + Try + HandleDateChange() + + ' Check resources + Dim NewEntrySize As Int64 = Me.Encoding.GetByteCount(message & vbCrLf) + + If ResourcesAvailable(NewEntrySize) Then + ListenerStream.WriteLine(message) + If Me.AutoFlush Then + ListenerStream.Flush() + End If + End If + Catch + CloseCurrentStream() + Throw + End Try + End Sub + + '''********************************************************************* + ''';TraceEvent + ''' + ''' Event fired by TraceSourceListener resulting in writing to the log + ''' + ''' Cache of information + ''' The name of the TraceSourceListener + ''' The eventType of the message + ''' The id of the message + ''' The message + ''' + _ + Public Overrides Sub TraceEvent(ByVal eventCache As TraceEventCache, ByVal source As String, ByVal eventType As TraceEventType, ByVal id As Integer, ByVal message As String) + + If Me.Filter IsNot Nothing Then + If Not Me.Filter.ShouldTrace(eventCache, source, eventType, id, message, Nothing, Nothing, Nothing) Then + Return + End If + End If + Dim outBuilder As New StringBuilder + + ' Add fields that always appear (source, eventType, id, message) + ' source + outBuilder.Append(source & Me.Delimiter) + + ' eventType + outBuilder.Append([Enum].GetName(GetType(TraceEventType), eventType) & Me.Delimiter) + + ' id + outBuilder.Append(id.ToString(CultureInfo.InvariantCulture) & Me.Delimiter) + + ' message + outBuilder.Append(message) + + ' Add optional fields + ' Callstack + If (Me.TraceOutputOptions And TraceOptions.Callstack) = TraceOptions.Callstack Then + outBuilder.Append(Me.Delimiter & eventCache.Callstack) + End If + + ' LogicalOperationStack + If (Me.TraceOutputOptions And TraceOptions.LogicalOperationStack) = TraceOptions.LogicalOperationStack Then + outBuilder.Append(Me.Delimiter & StackToString(eventCache.LogicalOperationStack)) + End If + + ' DateTime + If (Me.TraceOutputOptions And TraceOptions.DateTime) = TraceOptions.DateTime Then + ' Add datetime. Time will be in GMT. + outBuilder.Append(Me.Delimiter & eventCache.DateTime.ToString("u", CultureInfo.InvariantCulture)) + End If + + ' ProcessId + If (Me.TraceOutputOptions And TraceOptions.ProcessId) = TraceOptions.ProcessId Then + outBuilder.Append(Me.Delimiter & eventCache.ProcessId.ToString(CultureInfo.InvariantCulture)) + End If + + ' ThreadId + If (Me.TraceOutputOptions And TraceOptions.ThreadId) = TraceOptions.ThreadId Then + outBuilder.Append(Me.Delimiter & eventCache.ThreadId) + End If + + ' Timestamp + If (Me.TraceOutputOptions And TraceOptions.Timestamp) = TraceOptions.Timestamp Then + outBuilder.Append(Me.Delimiter & eventCache.Timestamp.ToString(CultureInfo.InvariantCulture)) + End If + + ' HostName + If Me.IncludeHostName Then + outBuilder.Append(Me.Delimiter & HostName) + End If + + WriteLine(outBuilder.ToString()) + + End Sub + + '''********************************************************************* + ''';TraceEvent + ''' + ''' Event fired by TraceSourceListener resulting in writing to the log + ''' + ''' Cache of information + ''' The name of the TraceSourceListener + ''' The eventType of the message + ''' The id of the message + ''' A string with placeholders that serves as a format for the message + ''' The values for the placeholders in format + ''' + _ + Public Overrides Sub TraceEvent(ByVal eventCache As TraceEventCache, ByVal source As String, ByVal eventType As TraceEventType, ByVal id As Integer, ByVal format As String, ByVal ParamArray args() As Object) + + ' Create the message + Dim message As String = Nothing + If args IsNot Nothing Then + message = String.Format(CultureInfo.InvariantCulture, format, args) + Else + message = format + End If + + TraceEvent(eventCache, source, eventType, id, message) + End Sub + + '''********************************************************************* + ''';TraceData + ''' + ''' Method of the base class we override to keep message format consistent + ''' + ''' Cache of information + ''' The name of the TraceSourceListener + ''' The eventType of the message + ''' The id of the message + ''' An object containing the message to be logged + ''' + _ + Public Overrides Sub TraceData(ByVal eventCache As TraceEventCache, ByVal source As String, ByVal eventType As TraceEventType, ByVal id As Integer, ByVal data As Object) + + Dim message As String = "" + If data IsNot Nothing Then + message = data.ToString() + End If + + TraceEvent(eventCache, source, eventType, id, message) + End Sub + + '''********************************************************************* + ''';TraceData + ''' + ''' Method of the base class we override to keep message format consistent + ''' + ''' Cache of information + ''' The name of the TraceSourceListener + ''' The eventType of the message + ''' The id of the message + ''' A list of objects making up the message to be logged + ''' + _ + Public Overrides Sub TraceData(ByVal eventCache As TraceEventCache, ByVal source As String, ByVal eventType As TraceEventType, ByVal id As Integer, ByVal ParamArray data As Object()) + + Dim messageBuilder As New StringBuilder() + If data IsNot Nothing Then + Dim bound As Integer = data.Length - 1 + For i As Integer = 0 To bound + messageBuilder.Append(data(i).ToString()) + If i <> bound Then + messageBuilder.Append(Me.Delimiter) + End If + Next i + End If + + TraceEvent(eventCache, source, eventType, id, messageBuilder.ToString()) + End Sub + + '''************************************************************************ + ''';Flush + ''' + ''' Flushes the underlying stream + ''' + ''' + _ + Public Overrides Sub Flush() + If m_Stream IsNot Nothing Then + m_Stream.Flush() + End If + End Sub + + '''************************************************************************ + ''';Close + ''' + ''' Closes the underlying stream + ''' + ''' + _ + Public Overrides Sub Close() + Dispose(True) + End Sub + + '==PROTECTED*************************************************************** + + '''************************************************************************ + ''';GetSupportedAttributes + ''' + ''' Gets a list of all the attributes recognized by the this listener. Trying to use an item not in this list + ''' in a config file will cause a configuration exception + ''' + ''' An array of attribute names + ''' + _ + Protected Overrides Function GetSupportedAttributes() As String() + Return m_SupportedAttributes + End Function + + '''************************************************************************ + ''';Dispose + ''' + ''' Makes sure stream is flushed + ''' + ''' + ''' + _ + Protected Overrides Sub Dispose(ByVal disposing As Boolean) + If disposing Then + CloseCurrentStream() + End If + End Sub + + '==PRIVATE***************************************************************** + + '''************************************************************************ + ''';LogFileName + ''' + ''' Gets the log file name under the current configuration. + ''' + ''' The log file name + ''' + ''' Includes the full path and the datestamp, but does not include the + ''' file number or the extension. + ''' + Private ReadOnly Property LogFileName() As String + Get + Dim basePath As String + + ' Get the directory + Select Case Me.Location + Case LogFileLocation.CommonApplicationDirectory + basePath = Application.CommonAppDataPath + Case LogFileLocation.ExecutableDirectory + basePath = Path.GetDirectoryName(Application.ExecutablePath) + Case LogFileLocation.LocalUserApplicationDirectory + basePath = Application.UserAppDataPath + Case LogFileLocation.TempDirectory + basePath = Path.GetTempPath() + Case LogFileLocation.Custom + If Me.CustomLocation = "" Then + basePath = Application.UserAppDataPath + Else + basePath = Me.CustomLocation + End If + Case Else + Debug.Fail("Unrecognized location") + basePath = Application.UserAppDataPath + End Select + + ' Add the base name + Dim fileName As String = Me.BaseFileName + + ' Add DateTime Stamp + Select Case Me.LogFileCreationSchedule + Case LogFileCreationScheduleOption.Daily + fileName += "-" & Now.Date.ToString(DATE_FORMAT, CultureInfo.InvariantCulture) + Case LogFileCreationScheduleOption.Weekly + ' Get first day of week + m_FirstDayOfWeek = Now.AddDays(-Now.DayOfWeek) + fileName += "-" & m_FirstDayOfWeek.Date.ToString(DATE_FORMAT, CultureInfo.InvariantCulture) + Case LogFileCreationScheduleOption.None + Case Else + Debug.Fail("Unrecognized LogFileCreationSchedule") + End Select + + Return Path.Combine(basePath, fileName) + End Get + End Property + + '''************************************************************************ + ''';ListenerStream + ''' + ''' Gets the stream to use for writing to the log + ''' + ''' The stream + ''' + Private ReadOnly Property ListenerStream() As ReferencedStream + Get + EnsureStreamIsOpen() + + Debug.Assert(m_Stream IsNot Nothing, "Unable to get stream") + Return m_Stream + End Get + End Property + + '''************************************************************************ + ''';GetStream + ''' + ''' Gets or creates the stream used for writing to the log + ''' + ''' The stream + ''' + _ + Private Function GetStream() As ReferencedStream + + ' Check the hash table to see if this file is already opened by another + ' FileLogTraceListener in the same process + Dim i As Integer = 0 + Dim refStream As ReferencedStream = Nothing + Dim BaseStreamName As String = Path.GetFullPath(LogFileName & FILE_EXTENSION) + + While refStream Is Nothing AndAlso i < MAX_OPEN_ATTEMPTS + ' This should only be true if processes outside our process have + ' MAX_OPEN_ATTEMPTS files open using the naming schema (file-1.log, file-2.log ... file-MAX_OPEN_ATTEMPTS.log) + + Dim fileName As String + If i = 0 Then + fileName = Path.GetFullPath(LogFileName & FILE_EXTENSION) + Else + fileName = Path.GetFullPath(LogFileName & "-" & i.ToString(CultureInfo.InvariantCulture) & FILE_EXTENSION) + End If + + Dim caseInsensitiveKey As String = fileName.ToUpper(CultureInfo.InvariantCulture) + SyncLock m_Streams + + If m_Streams.ContainsKey(caseInsensitiveKey) Then + refStream = m_Streams(caseInsensitiveKey) + If Not refStream.IsInUse Then + ' This means that the referenced stream has somehow entered an invalid state so remove it + Debug.Fail("Referenced stream is in invalid state") + m_Streams.Remove(caseInsensitiveKey) + refStream = Nothing + Else + If Me.Append Then + ' We are handing off an already existing stream, so we need to make sure the caller has permissions to write to this stream + Dim filePermission As New FileIOPermission(FileIOPermissionAccess.Write, fileName) + filePermission.Demand() + + refStream.AddReference() + m_FullFileName = fileName + Return refStream + Else + ' The user wants to overwrite, so we need to open a new stream + i += 1 + refStream = Nothing + Continue While + End If + End If + End If + + ' Try to open the file + Dim fileEncoding As Encoding = Me.Encoding + Try + If Me.Append Then + ' Try to get the file's actual encoding. If we get it, that trumps + ' the user specified value + fileEncoding = GetFileEncoding(fileName) + If fileEncoding Is Nothing Then + fileEncoding = Me.Encoding + End If + End If + + Dim baseStreamWriter As New StreamWriter(fileName, Me.Append, fileEncoding) + refStream = New ReferencedStream(baseStreamWriter) + refStream.AddReference() + m_Streams.Add(caseInsensitiveKey, refStream) + m_FullFileName = fileName + Return refStream + Catch ex As IOException + End Try + + i += 1 + End SyncLock + End While + 'If we fall out the loop, we have failed to obtain a valid stream name. This occurs if there are files on your system + 'ranging from BaseStreamName0..BaseStreamName{integer.MaxValue} which is pretty unlikely but hey. + Throw GetInvalidOperationException(ResID.MyID.ApplicationLog_ExhaustedPossibleStreamNames, BaseStreamName) + End Function + + '''************************************************************************ + ''';EnsureStreamIsOpen + ''' + ''' Makes sure we have an open stream + ''' + ''' + Private Sub EnsureStreamIsOpen() + If m_Stream Is Nothing Then + m_Stream = GetStream() + End If + End Sub + + '''************************************************************************ + ''';CloseCurrentStream + ''' + ''' Closes the stream. + ''' + ''' This method should be safe to call whether or not there is a stream + Private Sub CloseCurrentStream() + If m_Stream IsNot Nothing Then + SyncLock m_Streams + m_Stream.CloseStream() + If Not m_Stream.IsInUse Then + m_Streams.Remove(m_FullFileName.ToUpper(CultureInfo.InvariantCulture)) + End If + m_Stream = Nothing + End SyncLock + End If + End Sub + + '''************************************************************************ + ''';DayChanged + ''' + ''' Indicates whether or not the current date has changed to new day + ''' + ''' True if the date has changed, otherwise False + ''' + Private Function DayChanged() As Boolean + Return m_Day.Date <> Now.Date + End Function + + '''************************************************************************ + ''';WeekChanged + ''' + ''' Indicates whether or not the date has changed to a new week + ''' + ''' True if the date has changed, otherwise False + ''' + Private Function WeekChanged() As Boolean + Return m_FirstDayOfWeek.Date <> GetFirstDayOfWeek(Now.Date) + End Function + + '''************************************************************************ + ''';GetFirstDayOfWeek + ''' + ''' Utility to get the date of the first day of the week from the passed in date + ''' + ''' The date being checked + ''' + ''' + Private Shared Function GetFirstDayOfWeek(ByVal checkDate As Date) As Date + Return checkDate.AddDays(-checkDate.DayOfWeek).Date + End Function + + '''************************************************************************ + ''';HandleDateChange + ''' + ''' Checks for date changes and carries out appropriate actions + ''' + ''' + ''' If the user has selected a DateStamp option then a change of + ''' date means we need to open a new file. + ''' + Private Sub HandleDateChange() + If Me.LogFileCreationSchedule = LogFileCreationScheduleOption.Daily Then + If DayChanged() Then + CloseCurrentStream() + End If + ElseIf Me.LogFileCreationSchedule = LogFileCreationScheduleOption.Weekly Then + If WeekChanged() Then + CloseCurrentStream() + End If + End If + End Sub + + '''*********************************************************************** + ''';ResourcesAvailable + ''' + ''' Checks the size of the current log plus the new entry and the free disk space against + ''' the user's limits. + ''' + ''' The size of what's about to be written to the file + ''' True if the limits aren't trespassed, otherwise False + ''' This method is not 100% accurate if AutoFlush is False + Private Function ResourcesAvailable(ByVal newEntrySize As Long) As Boolean + + If ListenerStream.FileSize + newEntrySize > Me.MaxFileSize Then + If Me.DiskSpaceExhaustedBehavior = DiskSpaceExhaustedOption.ThrowException Then + Throw New InvalidOperationException(GetResourceString(ResID.MyID.ApplicationLog_FileExceedsMaximumSize)) + End If + Return False + End If + + If Me.GetFreeDiskSpace() - newEntrySize < Me.ReserveDiskSpace Then + If Me.DiskSpaceExhaustedBehavior = DiskSpaceExhaustedOption.ThrowException Then + Throw New InvalidOperationException(GetResourceString(ResID.MyID.ApplicationLog_ReservedSpaceEncroached)) + End If + Return False + End If + + Return True + End Function + + '''************************************************************************ + ''';GetFreeDiskSpace + ''' + ''' Returns the total amount of free disk space available to the current user + ''' + ''' The total amount, in bytes, of free disk space available to the current user + ''' Throws an exception if API fails + _ + Private Function GetFreeDiskSpace() As Long + Dim PathName As String = Path.GetPathRoot(Path.GetFullPath(FullLogFileName)) + + 'Initialize FreeUserSpace so we can determine if its value is changed by the API call + Dim FreeUserSpace As Long = -1 + Dim TotalUserSpace As Long + Dim TotalFreeSpace As Long + + Dim discoveryPermission As New FileIOPermission(FileIOPermissionAccess.PathDiscovery, PathName) + discoveryPermission.Demand() + + If UnsafeNativeMethods.GetDiskFreeSpaceEx(PathName, FreeUserSpace, TotalUserSpace, TotalFreeSpace) Then + If FreeUserSpace > -1 Then + Return FreeUserSpace + End If + End If + + Throw GetWin32Exception(ResID.MyID.ApplicationLog_FreeSpaceError) + End Function + + '''************************************************************************ + ''';GetFileEncoding + ''' + ''' Opens a file and attempts to determine the file's encoding + ''' + ''' The encoding or Nothing + ''' + Private Function GetFileEncoding(ByVal fileName As String) As Encoding + + If File.Exists(fileName) Then + Dim Reader As StreamReader = Nothing + Try + + 'Attempt to determine the encodoing of the file. The call to Reader.ReadLine + 'will change the current encoding of Reader to that of the file. + Reader = New StreamReader(fileName, Me.Encoding, True) + + 'Ignore 0 length file + If Reader.BaseStream.Length > 0 Then + Reader.ReadLine() + + Return Reader.CurrentEncoding + End If + Finally + If Reader IsNot Nothing Then + Reader.Close() + End If + End Try + End If + + Return Nothing + End Function + + '''*************************************************************************** + ''';HostName + ''' + ''' Gets the host name + ''' + ''' The host name + ''' We use the machine name because we can get that even if not hooked up to a network + Private ReadOnly Property HostName() As String + Get + If m_HostName = "" Then + ' Use the machine name + m_HostName = System.Environment.MachineName + End If + Return m_HostName + End Get + End Property + + '''*************************************************************************** + ''';DemandWritePermission + ''' + ''' Demands a FileIO write permission. + ''' + ''' This method should be called by public API that doesn't map to TraceListener. + ''' This ensures these API cannot be used to circumvent CAS + ''' + _ + Private Sub DemandWritePermission() + Debug.Assert(Path.GetDirectoryName(Me.LogFileName) <> "", "The log directory shouldn't be empty.") + Dim fileName As String = Path.GetDirectoryName(Me.LogFileName) + Dim filePermission As New FileIOPermission(FileIOPermissionAccess.Write, fileName) + filePermission.Demand() + End Sub + + '''************************************************************************** + ''' ;ValidateLogFileLocationEnumValue + ''' + ''' Validates that the value being passed as an LogFileLocation enum is a legal value + ''' + ''' + ''' + Private Sub ValidateLogFileLocationEnumValue(ByVal value As LogFileLocation, ByVal paramName As String) + If value < LogFileLocation.TempDirectory OrElse value > LogFileLocation.Custom Then + Throw New System.ComponentModel.InvalidEnumArgumentException(paramName, DirectCast(value, Integer), GetType(LogFileLocation)) + End If + End Sub + + '''************************************************************************** + ''' ;ValidateDiskSpaceExhaustedOptionEnumValue + ''' + ''' Validates that the value being passed as an DiskSpaceExhaustedOption enum is a legal value + ''' + ''' + ''' + Private Sub ValidateDiskSpaceExhaustedOptionEnumValue(ByVal value As DiskSpaceExhaustedOption, ByVal paramName As String) + If value < DiskSpaceExhaustedOption.ThrowException OrElse value > DiskSpaceExhaustedOption.DiscardMessages Then + Throw New System.ComponentModel.InvalidEnumArgumentException(paramName, DirectCast(value, Integer), GetType(DiskSpaceExhaustedOption)) + End If + End Sub + + '''************************************************************************** + ''' ;ValidateLogFileCreationScheduleOptionEnumValue + ''' + ''' Validates that the value being passed as an LogFileCreationScheduleOption enum is a legal value + ''' + ''' + ''' + Private Sub ValidateLogFileCreationScheduleOptionEnumValue(ByVal value As LogFileCreationScheduleOption, ByVal paramName As String) + If value < LogFileCreationScheduleOption.None OrElse value > LogFileCreationScheduleOption.Weekly Then + Throw New System.ComponentModel.InvalidEnumArgumentException(paramName, DirectCast(value, Integer), GetType(LogFileCreationScheduleOption)) + End If + End Sub + + '''************************************************************************* + ''';StackToString + ''' + ''' Convert a stack into a string + ''' + ''' + ''' Returns the stack as a .csv string + ''' + Private Shared Function StackToString(ByVal stack As System.Collections.Stack) As String + Debug.Assert(stack IsNot Nothing, "Stack wasn't created.") + + Dim length As Integer = STACK_DELIMITER.Length + Dim sb As New StringBuilder() + + For Each obj As Object In stack + sb.Append(obj.ToString() & STACK_DELIMITER) + Next + + ' Escape the quotes + sb.Replace("""", """""") + + ' Remove trailing delimiter + If sb.Length >= length Then + sb.Remove(sb.Length - length, length) + End If + + Return """" & sb.ToString() & """" + + End Function + + ' Indicates the location of the log's directory + Private m_Location As LogFileLocation = LogFileLocation.LocalUserApplicationDirectory + + ' Indicates whether or not to flush after every write + Private m_AutoFlush As Boolean = False + + ' Indicates whether to append to or overwrite the log file + Private m_Append As Boolean = True + + ' Indicates whether or not to include the host id in the output + Private m_IncludeHostName As Boolean = False + + ' Indicates what behavior should take place when a resource level has been passed + Private m_DiskSpaceExhaustedBehavior As DiskSpaceExhaustedOption = DiskSpaceExhaustedOption.DiscardMessages + + ' Stores the name of the file minus the path, date stamp, and file number + Private m_BaseFileName As String = Path.GetFileNameWithoutExtension(Application.ExecutablePath) + + ' Indicate which date stamp should be used in the log file name + Private m_LogFileDateStamp As LogFileCreationScheduleOption = LogFileCreationScheduleOption.None + + ' The maximum size of the log file + Private m_MaxFileSize As Long = 5000000L + + ' The amount of free disk space there needs to be on the drive of the log file + Private m_ReserveDiskSpace As Long = 10000000L + + ' The delimiter to be used to separate fields in a line of output + Private m_Delimiter As String = vbTab + + ' The encoding of the log file + Private m_Encoding As Encoding = System.Text.Encoding.UTF8 + + ' The full name and path of the log file + Private m_FullFileName As String + + ' Directory to be used for the log file if Location is set to Custom + Private m_CustomLocation As String = Application.UserAppDataPath + + ' Reference counted stream used for writing to the log file + Private m_Stream As ReferencedStream + + Private m_Day As DateTime = Now.Date + + Private m_FirstDayOfWeek As DateTime = GetFirstDayOfWeek(Now.Date) + + Private m_HostName As String + + ' Indicates whether or not properties have been set + ' Note: Properties that use m_PropertiesSet to track whether or not + ' they've been set should always be set through the property setter and not + ' by directly changing the corresponding private field. + Private m_PropertiesSet As New System.Collections.BitArray(PROPERTY_COUNT, False) + + ' Table of all of the files opened by any FileLogTraceListener in the current process + Private Shared m_Streams As New Dictionary(Of String, ReferencedStream) + + ' A list of supported attributes + Private m_SupportedAttributes() As String = New String() {KEY_APPEND, KEY_APPEND_PASCAL, KEY_AUTOFLUSH, KEY_AUTOFLUSH_PASCAL, KEY_AUTOFLUSH_CAMEL, _ + KEY_BASEFILENAME, KEY_BASEFILENAME_PASCAL, KEY_BASEFILENAME_CAMEL, KEY_BASEFILENAME_PASCAL_ALT, KEY_BASEFILENAME_CAMEL_ALT, _ + KEY_CUSTOMLOCATION, KEY_CUSTOMLOCATION_PASCAL, KEY_CUSTOMLOCATION_CAMEL, KEY_DELIMITER, KEY_DELIMITER_PASCAL, _ + KEY_DISKSPACEEXHAUSTEDBEHAVIOR, KEY_DISKSPACEEXHAUSTEDBEHAVIOR_PASCAL, KEY_DISKSPACEEXHAUSTEDBEHAVIOR_CAMEL, _ + KEY_ENCODING, KEY_ENCODING_PASCAL, KEY_INCLUDEHOSTNAME, KEY_INCLUDEHOSTNAME_PASCAL, KEY_INCLUDEHOSTNAME_CAMEL, KEY_LOCATION, KEY_LOCATION_PASCAL, _ + KEY_LOGFILECREATIONSCHEDULE, KEY_LOGFILECREATIONSCHEDULE_PASCAL, KEY_LOGFILECREATIONSCHEDULE_CAMEL, _ + KEY_MAXFILESIZE, KEY_MAXFILESIZE_PASCAL, KEY_MAXFILESIZE_CAMEL, KEY_RESERVEDISKSPACE, KEY_RESERVEDISKSPACE_PASCAL, KEY_RESERVEDISKSPACE_CAMEL} + + ' Identifies properties in the bitarray + Private Const PROPERTY_COUNT As Integer = 12 + Private Const APPEND_INDEX As Integer = 0 + Private Const AUTOFLUSH_INDEX As Integer = 1 + Private Const BASEFILENAME_INDEX As Integer = 2 + Private Const CUSTOMLOCATION_INDEX As Integer = 3 + Private Const DELIMITER_INDEX As Integer = 4 + Private Const DISKSPACEEXHAUSTEDBEHAVIOR_INDEX As Integer = 5 + Private Const ENCODING_INDEX As Integer = 6 + Private Const INCLUDEHOSTNAME_INDEX As Integer = 7 + Private Const LOCATION_INDEX As Integer = 8 + Private Const LOGFILECREATIONSCHEDULE_INDEX As Integer = 9 + Private Const MAXFILESIZE_INDEX As Integer = 10 + Private Const RESERVEDISKSPACE_INDEX As Integer = 11 + + + Private Const DATE_FORMAT As String = "yyyy-MM-dd" + Private Const FILE_EXTENSION As String = ".log" + Private Const MAX_OPEN_ATTEMPTS As Integer = Integer.MaxValue + + ' Name to be used when parameterless constructor is called + Private Const DEFAULT_NAME As String = "FileLogTraceListener" + + ' The minimum setting allowed for maximum file size + Private Const MIN_FILE_SIZE As Integer = 1000 + + ' Attribute keys used to access properties set in the config file + Private Const KEY_APPEND As String = "append" + Private Const KEY_APPEND_PASCAL As String = "Append" + + Private Const KEY_AUTOFLUSH As String = "autoflush" + Private Const KEY_AUTOFLUSH_PASCAL As String = "AutoFlush" + Private Const KEY_AUTOFLUSH_CAMEL As String = "autoFlush" + + Private Const KEY_BASEFILENAME As String = "basefilename" + Private Const KEY_BASEFILENAME_PASCAL As String = "BaseFilename" + Private Const KEY_BASEFILENAME_CAMEL As String = "baseFilename" + Private Const KEY_BASEFILENAME_PASCAL_ALT As String = "BaseFileName" + Private Const KEY_BASEFILENAME_CAMEL_ALT As String = "baseFileName" + + Private Const KEY_CUSTOMLOCATION As String = "customlocation" + Private Const KEY_CUSTOMLOCATION_PASCAL As String = "CustomLocation" + Private Const KEY_CUSTOMLOCATION_CAMEL As String = "customLocation" + + Private Const KEY_DELIMITER As String = "delimiter" + Private Const KEY_DELIMITER_PASCAL As String = "Delimiter" + + Private Const KEY_DISKSPACEEXHAUSTEDBEHAVIOR As String = "diskspaceexhaustedbehavior" + Private Const KEY_DISKSPACEEXHAUSTEDBEHAVIOR_PASCAL As String = "DiskSpaceExhaustedBehavior" + Private Const KEY_DISKSPACEEXHAUSTEDBEHAVIOR_CAMEL As String = "diskSpaceExhaustedBehavior" + + Private Const KEY_ENCODING As String = "encoding" + Private Const KEY_ENCODING_PASCAL As String = "Encoding" + + Private Const KEY_INCLUDEHOSTNAME As String = "includehostname" + Private Const KEY_INCLUDEHOSTNAME_PASCAL As String = "IncludeHostName" + Private Const KEY_INCLUDEHOSTNAME_CAMEL As String = "includeHostName" + + Private Const KEY_LOCATION As String = "location" + Private Const KEY_LOCATION_PASCAL As String = "Location" + + Private Const KEY_LOGFILECREATIONSCHEDULE As String = "logfilecreationschedule" + Private Const KEY_LOGFILECREATIONSCHEDULE_PASCAL As String = "LogFileCreationSchedule" + Private Const KEY_LOGFILECREATIONSCHEDULE_CAMEL As String = "logFileCreationSchedule" + + Private Const KEY_MAXFILESIZE As String = "maxfilesize" + Private Const KEY_MAXFILESIZE_PASCAL As String = "MaxFileSize" + Private Const KEY_MAXFILESIZE_CAMEL As String = "maxFileSize" + + Private Const KEY_RESERVEDISKSPACE As String = "reservediskspace" + Private Const KEY_RESERVEDISKSPACE_PASCAL As String = "ReserveDiskSpace" + Private Const KEY_RESERVEDISKSPACE_CAMEL As String = "reserveDiskSpace" + + ' Delimiter used when converting a stack to a string + Private Const STACK_DELIMITER As String = ", " + + '''*********************************************************************************** + ''';ReferencedStream + ''' + ''' Wraps a StreamWriter and keeps a reference count. This enables multiple + ''' FileLogTraceListeners on multiple threads to access the same file. + ''' + ''' + Friend Class ReferencedStream + Implements IDisposable + + + '''******************************************************************************* + ''';New + ''' + ''' Creates a new referenced stream + ''' + ''' The stream that does the actual writing + ''' + Friend Sub New(ByVal stream As StreamWriter) + m_Stream = stream + End Sub + + + '''******************************************************************************* + ''';Write + ''' + ''' Writes a message to the stream + ''' + ''' The message to write + ''' + Friend Sub Write(ByVal message As String) + SyncLock m_SyncObject + m_Stream.Write(message) + End SyncLock + End Sub + + '''******************************************************************************* + ''';WriteLine + ''' + ''' Writes a message to the stream as a line + ''' + ''' The message to write + ''' + Friend Sub WriteLine(ByVal message As String) + SyncLock m_SyncObject + m_Stream.WriteLine(message) + End SyncLock + End Sub + + '''******************************************************************************* + ''';AddReference + ''' + ''' Increments the reference count for the stream + ''' + ''' + Friend Sub AddReference() + SyncLock m_SyncObject + m_ReferenceCount += 1 + End SyncLock + End Sub + + '''******************************************************************************* + ''';Flush + ''' + ''' Flushes the stream + ''' + ''' + Friend Sub Flush() + SyncLock m_SyncObject + m_Stream.Flush() + End SyncLock + End Sub + + '''******************************************************************************* + ''';Close + ''' + ''' Decrements the reference count to the stream and closes the stream if the reference count + ''' is zero + ''' + ''' + Friend Sub CloseStream() + SyncLock m_SyncObject + Try + m_ReferenceCount -= 1 + m_Stream.Flush() + Debug.Assert(m_ReferenceCount >= 0, "Ref count is below 0") + Finally + If m_ReferenceCount <= 0 Then + m_Stream.Close() + m_Stream = Nothing + End If + End Try + End SyncLock + End Sub + + '''******************************************************************************* + ''';IsInUse + ''' + ''' Indicates whether or not the stream is still in use by a FileLogTraceListener + ''' + ''' True if the stream is being used, otherwise False + ''' + Friend ReadOnly Property IsInUse() As Boolean + Get + Return m_Stream IsNot Nothing + End Get + End Property + + '''******************************************************************************* + ''';FileSize + ''' + ''' The size of the log file + ''' + ''' The size + ''' + Friend ReadOnly Property FileSize() As Long + Get + Return m_Stream.BaseStream.Length + End Get + End Property + + '==PRIVATE************************************************************************ + + '''******************************************************************************* + ''';Dispose + ''' + ''' Ensures the stream is closed (flushed) no matter how we are closed + ''' + ''' Indicates who called dispose + ''' + Private Overloads Sub Dispose(ByVal disposing As Boolean) + If disposing Then + If Not m_Disposed Then + If m_Stream IsNot Nothing Then + m_Stream.Close() + End If + m_Disposed = True + End If + End If + End Sub + + '''******************************************************************************* + ''';Dispose + ''' + ''' Standard implementation of IDisposable + ''' + ''' + Public Overloads Sub Dispose() Implements IDisposable.Dispose + ' Do not change this code. Put cleanup code in Dispose(ByVal disposing As Boolean) above. + Dispose(True) + GC.SuppressFinalize(Me) + End Sub + + '''******************************************************************************* + ''';Finalize + ''' + ''' Ensures stream is closed at GC + ''' + ''' + Protected Overrides Sub Finalize() + ' Do not change this code. Put cleanup code in Dispose(ByVal disposing As Boolean) above. + Dispose(False) + MyBase.Finalize() + End Sub + + ' The stream that does the writing + Private m_Stream As StreamWriter + + ' The number of FileLogTraceListeners using the stream + Private m_ReferenceCount As Integer = 0 + + ' Used for synchronizing writing and reference counting + Private m_SyncObject As Object = New Object + + ' Indicates whether or not the object has been disposed + Private m_Disposed As Boolean = False + + End Class 'ReferencedStream + + End Class 'FileLogTraceListener + +End Namespace diff --git a/Microsoft.VisualBasic/runtime/msvbalib/Logging/Log.vb b/Microsoft.VisualBasic/runtime/msvbalib/Logging/Log.vb new file mode 100644 index 000000000..04ad79377 --- /dev/null +++ b/Microsoft.VisualBasic/runtime/msvbalib/Logging/Log.vb @@ -0,0 +1,389 @@ +' Copyright (c) Microsoft Corporation. All rights reserved. +Option Explicit On +Option Strict On + +Imports System +Imports System.Collections.Generic +Imports System.Collections.Specialized +Imports System.ComponentModel +Imports System.Diagnostics +Imports System.Globalization +Imports System.Security +Imports System.Security.Permissions +Imports System.Security.Principal +Imports System.Text +Imports Microsoft.VisualBasic.CompilerServices +Imports Microsoft.VisualBasic.CompilerServices.ExceptionUtils + +Namespace Microsoft.VisualBasic.Logging + + '''********************************************************************************** + ''';Log + ''' + ''' Enables logging to configured TraceListeners + ''' + ''' + _ + Public Class Log + + '= PUBLIC ============================================================= + + '''******************************************************************************* + ''';New + ''' + ''' Creates a Log and the underlying TraceSource based on the platform + ''' + ''' Right now we only support Winapp as an application platform + _ + Public Sub New() + ' Set trace source for platform. Right now we only support WinApp + m_TraceSource = New DefaultTraceSource(WINAPP_SOURCE_NAME) + If Not m_TraceSource.HasBeenConfigured Then + InitializeWithDefaultsSinceNoConfigExists() + End If + ' Make sure to flush the log when the application closes + AddHandler System.AppDomain.CurrentDomain.ProcessExit, AddressOf CloseOnProcessExit + End Sub + + '''****************************************************************************** + ''';New + ''' + ''' Creates a Log and the underlying TraceSource based on the passed in name + ''' + ''' The name of the TraceSource to be created + ''' + _ + Public Sub New(ByVal name As String) + m_TraceSource = New DefaultTraceSource(name) + If Not m_TraceSource.HasBeenConfigured Then + InitializeWithDefaultsSinceNoConfigExists() + End If + End Sub + + '''***************************************************************************** + ''';WriteEntry + ''' + ''' Has the TraceSource fire a TraceEvent for all of its listeners + ''' + ''' The message to be logged + ''' + Public Sub WriteEntry(ByVal message As String) + WriteEntry(message, TraceEventType.Information, TraceEventTypeToId(TraceEventType.Information)) + End Sub + + '''***************************************************************************** + ''';WriteEntry + ''' + ''' Has the TraceSource fire a TraceEvent for all of its listeners + ''' + ''' The message to be logged + ''' The type of message (error, info, etc...) + ''' + Public Sub WriteEntry(ByVal message As String, ByVal severity As TraceEventType) + WriteEntry(message, severity, TraceEventTypeToId(severity)) + End Sub + + '''***************************************************************************** + ''';WriteEntry + ''' + ''' Has the TraceSource fire a TraceEvent for all of its listeners + ''' + ''' The message to be logged + ''' The type of message (error, info, etc...) + ''' An id for the message (used for corelation) + ''' + Public Sub WriteEntry(ByVal message As String, ByVal severity As TraceEventType, ByVal id As Integer) + If message Is Nothing Then + message = "" + End If + m_TraceSource.TraceEvent(severity, id, message) + End Sub + + '''***************************************************************************** + ''';WriteException + ''' + ''' Has the TraceSource fire a TraceEvent for all listeners using information in an exception to form the message + ''' + ''' The exception being logged + ''' + Public Sub WriteException(ByVal ex As Exception) + WriteException(ex, TraceEventType.Error, "", TraceEventTypeToId(TraceEventType.Error)) + End Sub + + '''***************************************************************************** + ''';WriteException + ''' + ''' Has the TraceSource fire a TraceEvent for all listeners using information in an exception to form the message + ''' and appending additional info + ''' + ''' The exception being logged + ''' The type of message (error, info, etc...) + ''' Extra information to append to the message + ''' + Public Sub WriteException(ByVal ex As Exception, ByVal severity As TraceEventType, ByVal additionalInfo As String) + WriteException(ex, severity, additionalInfo, TraceEventTypeToId(severity)) + End Sub + + '''***************************************************************************** + ''';WriteException + ''' + ''' Has the TraceSource fire a TraceEvent for all listeners using information in an exception to form the message + ''' and appending additional info + ''' + ''' The exception being logged + ''' The type of message (error, info, etc...) + ''' Extra information to append to the message + ''' An id for the message (used for corelation) + ''' + Public Sub WriteException(ByVal ex As Exception, ByVal severity As TraceEventType, ByVal additionalInfo As String, ByVal id As Integer) + + If ex Is Nothing Then + Throw GetArgumentNullException("ex") + End If + + Dim builder As New StringBuilder() + builder.Append(ex.Message) + + If additionalInfo <> "" Then + builder.Append(" ") + builder.Append(additionalInfo) + End If + + m_TraceSource.TraceEvent(severity, id, builder.ToString()) + + End Sub + + '''****************************************************************************** + ''';TraceSource + ''' + ''' Gives access to the log's underlying TraceSource + ''' + ''' The log's underlying TraceSource + ''' + _ + Public ReadOnly Property TraceSource() As TraceSource + Get + Return m_TraceSource 'Note, this is a downcast from the DefaultTraceSource class we are using + End Get + End Property + + '''****************************************************************************** + ''';DefaultFileLogWriter + ''' + ''' Returns the file log trace listener we create for the Log + ''' + ''' The file log trace listener + ''' + Public ReadOnly Property DefaultFileLogWriter() As FileLogTraceListener + _ + Get + Return CType(TraceSource.Listeners(DEFAULT_FILE_LOG_TRACE_LISTENER_NAME), FileLogTraceListener) + End Get + End Property + + '= FRIEND ============================================================= + + '''********************************************************************************** + ''';DefaultTraceSource + ''' + ''' Encapsulates a System.Diagnostics.TraceSource. The value add is that it knows if it was initialized + ''' using a config file or not. + ''' + ''' + Friend NotInheritable Class DefaultTraceSource + Inherits TraceSource + + '''****************************************************************************** + ''';New + ''' + ''' TraceSource has other constructors, this is the only one we care about for this internal class + ''' + ''' + ''' + Sub New(ByVal name As String) + MyBase.New(name) + End Sub + + '''****************************************************************************** + ''';HasBeenConfigured + ''' + ''' Tells us whether this TraceSource found a config file to configure itself from + ''' + ''' True - The TraceSource was configured from a config file + ''' + Public ReadOnly Property HasBeenConfigured() As Boolean + Get + ' This forces initialization of the attributes list + If listenerAttributes Is Nothing Then + listenerAttributes = Me.Attributes + End If + Return m_HasBeenInitializedFromConfigFile + End Get + End Property + + '''****************************************************************************** + ''';GetSupportedAttributes + ''' + ''' Overriding this function is the trick that tells us whether this trace source was configured + ''' from a config file or not. It only gets called if a config file was found. + ''' + ''' + ''' + Protected Overrides Function GetSupportedAttributes() As String() + m_HasBeenInitializedFromConfigFile = True + Return MyBase.GetSupportedAttributes() + End Function + + Private m_HasBeenInitializedFromConfigFile As Boolean 'True if we this TraceSource is initialized from a config file. False if somebody just news one up. + Private listenerAttributes As StringDictionary + + End Class + + '= PROTECTED FRIEND ====================================================== + + '''***************************************************************************** + ''';InitializeWithDefaultsSinceNoConfigExists + ''' + ''' When there is no config file to configure the trace source, this function is called in order to + ''' configure the trace source according to the defaults they would have had in a default AppConfig + ''' + ''' This shouldn't have been called from the ctor because when you call overridable + ''' methods from the ctor you have the problem that the derived class ctor doesn't get run + ''' before you call the overridden method in the derived class. + ''' Also - this must remain Friend. We will definetly have to refactor if it ever + ''' needs to become public so that we don't have a it doesn't get called from the ctor. + _ + Protected Friend Overridable Sub InitializeWithDefaultsSinceNoConfigExists() + 'By default, you get a file log listener that picks everything from level Information on up. + m_TraceSource.Listeners.Add(New FileLogTraceListener(DEFAULT_FILE_LOG_TRACE_LISTENER_NAME)) + m_TraceSource.Switch.Level = SourceLevels.Information + End Sub + + '= PRIVATE ============================================================= + + '''******************************************************************************* + ''';CloseOnProcessExit + ''' + ''' Make sure we flush the log on exit + ''' + ''' + _ + Private Sub CloseOnProcessExit(ByVal sender As Object, ByVal e As System.EventArgs) + RemoveHandler System.AppDomain.CurrentDomain.ProcessExit, AddressOf CloseOnProcessExit + Me.TraceSource.Close() + End Sub + + '''******************************************************************************* + ''';InitializeIDHash + ''' + ''' Adds the default id values + ''' + ''' Fix FxCop violation InitializeReferenceTypeStaticFieldsInline + Private Shared Function InitializeIDHash() As Dictionary(Of TraceEventType, Integer) + Dim result As New Dictionary(Of TraceEventType, Integer)(10) + + ' Populate table with the fx pre defined ids + With result + .Add(TraceEventType.Information, 0) + .Add(TraceEventType.Warning, 1) + .Add(TraceEventType.Error, 2) + .Add(TraceEventType.Critical, 3) + .Add(TraceEventType.Start, 4) + .Add(TraceEventType.Stop, 5) + .Add(TraceEventType.Suspend, 6) + .Add(TraceEventType.Resume, 7) + .Add(TraceEventType.Verbose, 8) + .Add(TraceEventType.Transfer, 9) + End With + + Return result + End Function + + '''******************************************************************************* + ''';TraceEventTypeToId + ''' + ''' Converts a TraceEventType to an Id + ''' + ''' + ''' The Id + ''' + Private Function TraceEventTypeToId(ByVal traceEventValue As TraceEventType) As Integer + If m_IdHash.ContainsKey(traceEventValue) Then + Return m_IdHash(traceEventValue) + End If + + Return 0 + End Function + + ' The underlying TraceSource for the log + Private m_TraceSource As DefaultTraceSource + + ' A table of default id values + Private Shared m_IdHash As Dictionary(Of TraceEventType, Integer) = InitializeIDHash() + + ' Names of TraceSources + Private Const WINAPP_SOURCE_NAME As String = "DefaultSource" + Private Const DEFAULT_FILE_LOG_TRACE_LISTENER_NAME As String = "FileLog" 'taken from appconfig + + End Class + + '''********************************************************************************** + ''';AspLog + ''' + ''' Enables logging to the ASP log + ''' + ''' + _ + Public Class AspLog + Inherits Log + + '''******************************************************************************* + ''';New + ''' + ''' Creates a Log and the underlying TraceSource based on the platform + ''' + ''' Right now we only support Winapp as an application platform + Sub New() + MyBase.New() + End Sub + + '''****************************************************************************** + ''';New + ''' + ''' Creates a Log and the underlying TraceSource based on the passed in name + ''' + ''' The name of the TraceSource to be created + ''' + _ + Public Sub New(ByVal name As String) + MyBase.New(name) + End Sub + + '''***************************************************************************** + ''';InitializeWithDefaultsIfNoConfigExists + ''' + ''' When there is no config file to configure the trace source, this function is called in order to + ''' configure the trace source according to the defaults they would have had in a default AppConfig + ''' + ''' This gets called from the base constructor, which means that the constructor + ''' for this derived class has not run yet. So don't access anything in here that depends on + ''' the AspLog class constructor running first. + ''' Also, this must remain Friend. If it ever becomes public we will definetly need + ''' to refactor first + _ + Protected Friend Overrides Sub InitializeWithDefaultsSinceNoConfigExists() + 'By default, you get a Web page listener that picks everything from level Information on up. + + 'The [COR_*] things are substituted by a Perl script launched from Microsoft.VisualBasic.Build.vbproj + + Dim ListenerType As System.Type + ListenerType = System.Type.GetType("System.Web.WebPageTraceListener, System.Web, Version=[COR_BUILD_MAJOR].[COR_BUILD_MINOR].[CLR_OFFICIAL_ASSEMBLY_NUMBER].0, Culture=neutral, PublicKeyToken=B03F5F7F11D50A3A") + If ListenerType IsNot Nothing Then + TraceSource.Listeners.Add(DirectCast(Activator.CreateInstance(ListenerType), System.Diagnostics.TraceListener)) + End If + TraceSource.Switch.Level = SourceLevels.Information + End Sub + + End Class + +End Namespace diff --git a/Microsoft.VisualBasic/runtime/msvbalib/Math.vb b/Microsoft.VisualBasic/runtime/msvbalib/Math.vb new file mode 100644 index 000000000..d9227d278 --- /dev/null +++ b/Microsoft.VisualBasic/runtime/msvbalib/Math.vb @@ -0,0 +1,112 @@ +' Copyright (c) Microsoft Corporation. All rights reserved. + +Imports System +Imports System.Security +Imports System.Security.Permissions +Imports System.Text +Imports System.Globalization +Imports Microsoft.VisualBasic.CompilerServices + +Namespace Microsoft.VisualBasic + + Public Module VBMath + + ' Equivalent to calling VB6 rtRandomNext(1.0) + Public Function Rnd() As Single + Return Rnd(CSng(1)) + End Function + + ' Equivalent to VB6 rtRandomNext function + Public Function Rnd(ByVal Number As Single) As Single + Dim oProj As ProjectData = ProjectData.GetProjectData() + Dim rndSeed As Integer = oProj.m_rndSeed + + ' if parameter is zero, generate float from present seed + If (Number <> 0.0) Then + ' if parameter is negative, use to create new seed + If (Number < 0.0) Then + 'Original C++ code + 'rndSeed = *(ULONG *) & fltVal; + 'rndSeed = (rndSeed + (rndSeed >> 24)) & 0xffffffL; + + rndSeed = BitConverter.ToInt32(BitConverter.GetBytes(Number), 0) + + Dim i64 As Int64 = rndSeed + i64 = (i64 And &HFFFFFFFFL) + rndSeed = CInt((i64 + (i64 >> 24)) And &HFFFFFFI) + End If + + ' if parameter is positive or zero, generate a new seed + rndSeed = CInt((CLng(rndSeed) * &H43FD43FDL + &HC39EC3L) And &HFFFFFFL) + End If + + ' copy back seed value to per-project structure + oProj.m_rndSeed = rndSeed + + ' normalize seed to floating value from 0.0 up to 1.0 + Return CSng(rndSeed) / CSng(16777216.0) + End Function + + 'Equivalent to RandomizeTimer in the VB6 codebase + Public Sub Randomize() + Dim oProj As ProjectData = ProjectData.GetProjectData() + Dim sngTimer As Single = GetTimer() + Dim rndSeed As Int32 = oProj.m_rndSeed + Dim lValue As Int32 + + ' treat Single as a long Integer + lValue = BitConverter.ToInt32(BitConverter.GetBytes(sngTimer), 0) + + ' xor the upper and lower words of the long and put in + ' the middle two bytes + lValue = ((lValue And &HFFFFI) Xor (lValue >> 16)) << 8 + + ' replace the middle two bytes of the seed with lValue + rndSeed = (rndSeed And &HFF0000FFI) Or lValue + + ' copy back seed value to per-project structure + oProj.m_rndSeed = rndSeed + End Sub + + 'Equivalent to RandomizeValue in the VB6 codebase + Public Sub Randomize(ByVal Number As Double) + Dim rndSeed As Integer + Dim lValue As Integer + Dim oProj As ProjectData + + oProj = ProjectData.GetProjectData() + rndSeed = oProj.m_rndSeed + + ' for little-endian R8, the high-order Integer is second half + If BitConverter.IsLittleEndian Then + lValue = BitConverter.ToInt32(BitConverter.GetBytes(Number), 4) + Else + lValue = BitConverter.ToInt32(BitConverter.GetBytes(Number), 0) + End If + + ' xor the upper and lower words of the Integer and put in + ' the middle two bytes + ' Original C++ line + ' lValue = ((lValue & 0xffff) ^ (lValue >> 16)) << 8; + 'lValue = ShiftLeft(((lValue And &H0000ffffI) XOr (lValue \ &H10000)), 8) + lValue = ((lValue And &HFFFFI) Xor (lValue >> 16)) << 8 + + ' replace the middle two bytes of the seed with lValue + 'Original C++ line + ' rndSeed = (rndSeed & 0xff0000ff) | lValue; + rndSeed = (rndSeed And &HFF0000FFI) Or lValue + + ' copy back seed value to per-project structure + oProj.m_rndSeed = rndSeed + End Sub + + Private Function GetTimer() As Single + Dim dt As Date + + dt = System.DateTime.Now + Return CSng((60 * dt.Hour + dt.Minute) * 60 + dt.Second + (dt.Millisecond / 1000)) + End Function + + End Module + +End Namespace diff --git a/Microsoft.VisualBasic/runtime/msvbalib/Microsoft.VisualBasic.restext b/Microsoft.VisualBasic/runtime/msvbalib/Microsoft.VisualBasic.restext new file mode 100644 index 000000000..44afda16b --- /dev/null +++ b/Microsoft.VisualBasic/runtime/msvbalib/Microsoft.VisualBasic.restext @@ -0,0 +1,405 @@ +# Microsoft.VisualBasic.Dll string resources. +# +# This file contains the localizable string resources for Microsoft.VisualBasic.dll +# When you add a string resource to this file, add the name of the resource to +# helpers\VbResourceID.vb as a constant. This file is split into two sections - the top part +# are those strings that the runtime used before My.Net was integrated into the runtime, +# and the bottom portion contains those that were in My.Net. We kept the distinction +# because there are some benefits for those who work on the My.Net portion of the runtime. +# +# VbResourceId.vb contains a class named ResID which provides contants that represent +# your string resources which you then use programmatically to refer to your strings +# when you want them loaded from the resources. The runtime now contains what was +# historically considered the VB runtime in earlier versions, but now it also contains +# the My.Net RAD fx. Simply to make it easier to sort through the noise in intellisense, +# there is a nested class defined within ResID where the string contants for My.Net go. +# There is no engineering benefit from doing this - it simply is convenient for those +# who work on My.Net portion of the runtime to reduce the amount of noise in intellisense. +# +# For example, if the name of your resource is YourResourceName add the following to +# VbResourceID.vb in the Class ResID section: +# Const YourResourceName As String = "YourResourceName" +# Then you can access your resource using +# GetResourceString(ResourceID.YourResourceName) +# +# If your resource is used in the My.Net portion of the runtime, add the following to +# VbResourceId.Vb in class MY which is nested within Class ResID: +# +# Const YourResourceName As String = "YourResourceName" +# Then you can access your resource using +# GetResourceString.GetString(ResourceID.My.YourResourceName) +# + +ID3=This Error number is obsolete and no longer used. +ID5=Procedure call or argument is not valid. +ID6=Overflow. +ID7=Out of memory. +ID9=Subscript out of range. +ID10=This array is fixed or temporarily locked. +ID11=Division by zero. +ID13=Type mismatch. +ID14=Out of string space. +ID16=Expression too complex. +ID17=Can't perform requested operation. +ID18=User interrupt occurred. +ID20=Resume without error. +ID28=Out of stack space. +ID35=Sub or Function not defined. +ID47=Too many DLL application clients. +ID48=Error in loading DLL. +ID49=Bad DLL calling convention. +ID51=Internal error. +ID52=Bad file name or number. +ID53=File not found. +ID54=Bad file mode. +ID55=File already open. +ID57=Device I/O error. +ID58=File already exists. +ID59=Bad record length. +ID61=Disk full. +ID62=Input past end of file. +ID63=Bad record number. +ID67=Too many files. +ID68=Device unavailable. +ID70=Permission denied. +ID71=Disk not ready. +ID74=Cannot rename with different drive. +ID75=Path/File access error. +ID76=Path not found. +ID91=Object variable or With block variable not set. +ID92=For loop not initialized. +ID93=Pattern string is not valid. +ID94=This Error number is obsolete and no longer used. +ID95=Application-defined or object-defined error. +ID96=Unable to sink events of object because the object is already firing events to the maximum number of event receivers that it supports. +ID97=Cannot call friend function on object that is not an instance of defining class. +ID98=A property or method call cannot include a reference to a private object, either as an argument or as a return value. +ID100=Class '{0}' does not implement the System.Collections.ICollection interface. +ID321=File format is not valid. +ID322=Cannot create necessary temporary file. +ID325=Format in resource file is not valid. +ID380=Property value is not valid. +ID381=Property array index is not valid. +ID382=Set not supported at runtime. +ID383=Set not supported (read-only property). +ID385=Need property array index. +ID387=Set not permitted. +ID393=Get not supported at runtime. +ID394=Get not supported (write-only property). +ID422=Property not found. +ID423=Property or method not found. +ID424=Object required. +ID429=Cannot create ActiveX component. +ID430=Class does not support Automation or does not support expected interface. +ID432=File name or class name not found during Automation operation. +ID438=Object does not support this property or method. +ID440=Automation error. +ID442=Connection to type library or object library for remote process has been lost. Press OK for dialog to remove reference. +ID443=Automation object does not have a default value. +ID445=Object does not support this action. +ID446=Object does not support named arguments. +ID447=Object does not support current locale setting. +ID448=Named argument not found. +ID449=Argument not optional. +ID450=Wrong number of arguments or property assignment was not valid. +ID451=Property let procedure not defined and property get procedure did not return an object. +ID452=Ordinal is not valid. +ID453=Specified DLL function not found. +ID454=Code resource not found. +ID455=Code resource lock error. +ID457=This key is already associated with an element of this collection. +ID458=Variable uses an Automation type not supported in Visual Basic. +ID459=Object or class does not support the set of events. +ID460=Clipboard format is not valid. +ID461=Method or data member not found. +ID462=The remote server machine does not exist or is unavailable. +ID463=Class not registered on local machine. +ID481=Picture is not valid. +ID482=Printer error. +ID735=Cannot save file to TEMP. +ID744=Search text not found. +ID746=Replacements too long. +ID999=Stop statement encountered. +ID32768=Feature not yet implemented. +False=False +True=True +Argument_GEZero1=Argument '{0}' must be greater or equal to zero. +Argument_GTZero1=Argument '{0}' must be greater than zero. +Argument_InvalidVbStrConv=Argument 'Conversion' is not valid. +Argument_StrConvSCandTC=VbStrConv.SimplifiedChinese and VbStrConv.TraditionalChinese cannot be combined. +Argument_SCNotSupported=This system does not contain support for the Simplified Chinese locale. +Argument_TCNotSupported=This system does not contain support for the Traditional Chinese locale. +Argument_JPNNotSupported=This system does not contain support for the Japanese locale. +Argument_IllegalWideNarrow=VbStrConv.Wide and VbStrConv.Narrow cannot be combined. +Argument_LocalNotSupported=This system does not contain support for the Locale specified. +Argument_WideNarrowNotApplicable=VbStrConv.Wide and VbStrConv.Narrow are not applicable to the locale specified. +Argument_IllegalKataHira=VbStrConv.Katakana and VbStrConv.Hiragana cannot be combined. +Argument_LengthGTZero1=Length of argument '{0}' must be greater than zero. +Argument_RangeTwoBytes1=Argument '{0}' must be within the range of -32768 to 65535. +Argument_MinusOneOrGTZero1=Argument '{0}' must be greater than 0 or equal to -1. +Argument_GEMinusOne1=Argument '{0}' must be greater than or equal to -1. +Argument_GEOne1=Argument '{0}' must be greater than or equal to 1. +Argument_RankEQOne1=Argument '{0}' cannot be a multi-dimensional array. +Argument_IComparable2=Loop control variable of type '{1}' does not implement the 'System.IComparable' interface. +Argument_NotNumericType2=Type of argument '{0}' is '{1}', which is not numeric. +Argument_InvalidValue1=Argument '{0}' is not a valid value. +Argument_InvalidValueType2=Argument '{0}' cannot be converted to type '{1}'. +Argument_PathNullOrEmpty=Argument 'Path' is Nothing or empty. +Argument_PathNullOrEmpty1=Argument '{0}' is Nothing or empty. +Argument_InvalidPathChars1=Argument value '{0}' contains characters that are not valid in a path name. +Argument_InvalidValue=Arguments are not valid. +Collection_BeforeAfterExclusive='Before' and 'After' arguments cannot be combined. +Collection_DuplicateKey=Add failed. Duplicate key value supplied. +FileSystem_IllegalInputAccess=Argument 'Access' is not valid. Valid values for Input mode are 'OpenAccess.Read' and 'OpenAccess.Default'. +FileSystem_IllegalOutputAccess=Argument 'Access' is not valid. Valid values for Output mode are 'OpenAccess.Write' and 'OpenAccess.Default'. +FileSystem_IllegalAppendAccess=Argument 'Access' is not valid. Valid values for Append mode are 'OpenAccess.Write' and 'OpenAccess.Default'. +FileSystem_FileAlreadyOpen1=File '{0}' cannot be deleted because it is open. +ForLoop_CommonType2=Cannot convert start value of type '{0}' and step value of type '{1}' to a common numeric type. +ForLoop_CommonType3=Cannot convert start value of type '{0}', limit value of type '{1}', and step value of type '{2}' to a common numeric type. +ForLoop_ConvertToType3=Cannot convert argument '{0}' of type '{1}' to type '{2}'. +ForLoop_OperatorRequired2=Type '{0}' must define an operator '{1}', with parameters of type '{0}', to be used in a 'For' statement. +ForLoop_UnacceptableOperator2=Return and parameter types of '{0}' must be of type '{1}' to be used in a 'For' statement. +ForLoop_UnacceptableRelOperator2=Parameter types of '{0}' must be of type '{1}' to be used in a 'For' statement. +InternalError=Internal error in the Microsoft Visual Basic runtime. +DIR_IllegalCall='Dir' function must first be called with a 'PathName' argument. +KILL_NoFilesFound1=No files found matching '{0}'. +MaxErrNumber=Error number must be within the range 0 to 65535. +FileSystem_DriveNotFound1=Drive '{0}' not found. +FileSystem_FileNotFound1=File '{0}' not found. +FileSystem_PathNotFound1=Path '{0}' not found. +Financial_CalcDivByZero=Division by zero. +Financial_CannotCalculateNPer=Cannot calculate number of periods using the arguments provided. +Financial_CannotCalculateRate=Cannot calculate rate using the arguments provided. +Argument_InvalidNullValue1=Argument '{0}' is Nothing. +Rate_NPerMustBeGTZero=Argument 'NPer' must be greater than zero. +PPMT_PerGT0AndLTNPer=Argument 'Per' is not valid. +Financial_LifeNEZero=Argument 'Life' cannot be zero. +Financial_ArgGEZero1=Argument '{0}' must be greater than or equal to zero. +Financial_ArgGTZero1=Argument '{0}' must be greater than zero. +Financial_PeriodLELife=Argument 'Period' must be less than or equal to argument 'Life'. +Argument_InvalidRank1=Argument '{0}' is not valid for the array. +Argument_Range1toFF1=Argument '{0}' must be within the range 1 to 255. +Argument_Range0to99_1=Argument '{0}' must be within the range 0 to 99. +Interaction_ResKeyNotCreated1=Registry key '{0}' could not be created. +Argument_LCIDNotSupported1=Locale id '{0}' is not supported on this system. +ProcessNotFound=Process '{0}' was not found. +Array_RankMismatch='ReDim' cannot change the number of dimensions. +Array_TypeMismatch='ReDim' can only change the rightmost dimension. +InvalidCast_FromTo=Conversion from type '{0}' to type '{1}' is not valid. +InvalidCast_FromStringTo=Conversion from string "{0}" to type '{1}' is not valid. +SetLocalDateFailure=Insufficient security permissions to set the system date. +SetLocalTimeFailure=Insufficient security permissions to set the system time. +Argument_UnsupportedFieldType2=File I/O of a structure with field '{0}' of type '{1}' is not valid. +Argument_UnsupportedIOType1=File I/O with type '{0}' is not valid. +Argument_InvalidDateValue1=Argument '{0}' cannot be converted to type 'Date'. +UseFilePutObject=Use 'FilePutObject' instead of 'FilePut' when using argument of type 'Object'. +ArgumentNotNumeric1=Argument '{0}' cannot be converted to a numeric value. +FileIO_StringLengthExceeded=String length exceeds maximum length of 32767 characters for 'FileSystem' APIs. +Argument_IndexLELength2=Argument '{0}' must be less than or equal to the length of argument '{1}'. +MissingMember_NoDefaultMemberFound1=No default member found for type '{0}'. +MissingMember_MemberNotFoundOnType2=Public member '{0}' on type '{1}' not found. +MissingMember_MemberSetNotFoundOnType2=Public Set '{0}' on type '{1}' not found. Use 'CallByName' function with 'CallType.Let'. +MissingMember_MemberLetNotFoundOnType2=Public Let '{0}' on type '{1}' not found. Use 'CallByName' function with 'CallType.Set'. +IntermediateLateBoundNothingResult1=Invocation of '{0}' on type '{1}' returned Nothing. + +YesNoFormatStyle=Yes;Yes;No +OnOffFormatStyle=On;On;Off +TrueFalseFormatStyle=True;True;False +Argument_CollectionIndex=Collection index must be in the range 1 to the size of the collection. +Argument_InvalidNamedArg2=Method '{1}' has no parameter named '{0}'. +NoMethodTakingXArguments2=Method '{0}' cannot be called with {1} argument(s). +NamedArgumentAlreadyUsed1=Named argument '{0}' specified multiple times. +NamedArgumentOnParamArray=Named arguments cannot match ParamArray parameters. +LinguisticRequirements='StrConv.LinguisticCasing' requires 'StrConv.Lowercase' or 'StrConv.Uppercase'. +Argument_ArrayNotInitialized=Cannot determine array type because it is Nothing. +RValueBaseForValueType=Late-bound assignment to a field of value type '{0}' is not valid when '{1}' is the result of a late-bound expression. +InvalidCast_FromToArg4=Argument {1} to method '{0}' has type '{2}' and cannot be converted to '{3}'. +Argument_ArrayDimensionsDontMatch=Array dimensions do not match those specified by the 'VBFixedArray' attribute. +ExpressionNotProcedure=Expression '{0}' is not a procedure, but occurs as the target of a procedure call. +AmbiguousCall_ExactMatch2=No accessible overloaded '{0}' is most specific for these arguments: {1} +AmbiguousCall2=No accessible overloaded '{0}' can be called with these arguments without a narrowing conversion: {1} +AmbiguousCall_WideningConversion2=No accessible overloaded '{0}' can be called with these arguments without a widening conversion: {1} +AmbiguousMatch_NarrowingConversion1=No accessible overloaded '{0}' can be called without a narrowing conversion. +LateboundCallToInheritedComClass=Managed classes derived from a COM class cannot be called late bound. +MissingMember_ReadOnlyField2=Field '{0}' of type '{1}' is 'ReadOnly'. +Invalid_VBFixedArray=Arguments to 'VBFixedArrayAttribute' are not valid. +Invalid_VBFixedString=Arguments to 'VBFixedStringAttribute' are not valid. +Argument_UnsupportedArrayDimensions=Array argument cannot have more than 2 dimensions. +Argument_InvalidFixedLengthString=Length of fixed length string cannot be zero. +Argument_InvalidNamedArgs=Named arguments are not valid as array subscripts. +Argument_IllegalNestedType2='{0}' is a type in '{1}' and cannot be used as an expression. +Argument_PutObjectOfValueType1='FilePutObject' of structure '{0}' is not valid. +SyncLockRequiresReferenceType1='SyncLock' operand cannot be of type '{0}' because '{0}' is not a reference type. +FileOpenedNoRead=File is not opened for read access. +FileOpenedNoWrite=File is not opened for write access. +NullReference_InstanceReqToAccessMember1=Reference to non-shared member '{0}' requires an object reference. +Security_LateBoundCallsNotPermitted=Late bound calls to file system methods in the Visual Basic runtime are not permitted. +Serialization_MissingCultureInfo=Deserialization data is corrupt. The CultureInfo for this Collection is missing. +Serialization_MissingKeys=Deserialization data is corrupt. The keys for this Collection are missing. +Serialization_MissingValues=Deserialization data is corrupt. The values for this Collection are missing. +Serialization_KeyValueDifferentSizes=Deserialization data is corrupt. The keys and values arrays have different sizes. + +MatchArgumentFailure2=Method invocation failed because '{0}' cannot be called with these arguments:{1} +NoGetProperty1=Property '{0}' is WriteOnly. +NoSetProperty1=Property '{0}' is ReadOnly. +MethodAssignment1=Method '{0}' cannot be the target of an assignment. + +NoViableOverloadCandidates1=Overload resolution failed because no '{0}' is Public. +NoArgumentCountOverloadCandidates1=Overload resolution failed because no accessible '{0}' accepts this number of arguments. +NoTypeArgumentCountOverloadCandidates1=Overload resolution failed because no accessible '{0}' accepts this number of type arguments. +NoCallableOverloadCandidates2=Overload resolution failed because no Public '{0}' can be called with these arguments:{1} +NoNonNarrowingOverloadCandidates2=Overload resolution failed because no Public '{0}' can be called without a narrowing conversion:{1} +NoMostSpecificOverload2=Overload resolution failed because no Public '{0}' is most specific for these arguments:{1} +AmbiguousCast2=Conversion from type '{0}' to type '{1}' is ambiguous. + +NotMostSpecificOverload=Not most specific. + +NamedParamNotFound2=Named argument '{0}' matches no parameter of '{1}'. +NamedParamArrayArgument1=Named argument '{0}' cannot match a ParamArray parameter. +NamedArgUsedTwice2=Parameter '{0}' of '{1}' already has a matching argument. +OmittedArgument1=Argument not specified for parameter '{0}'. +OmittedParamArrayArgument=Omitted argument cannot match a ParamArray parameter. + +ArgumentMismatch3=Argument matching parameter '{0}' cannot convert from '{1}' to '{2}'. +ArgumentMismatchAmbiguous3=Argument matching parameter '{0}' cannot convert from '{1}' to '{2}' because the conversion is ambiguous. +ArgumentNarrowing3=Argument matching parameter '{0}' narrows from '{1}' to '{2}'. +ArgumentMismatchCopyBack3=ByRef parameter '{0}' cannot convert from '{1}' to '{2}' when assigning back to the matching argument. +ArgumentMismatchAmbiguousCopyBack3=ByRef parameter '{0}' cannot convert from '{1}' to '{2}' when assigning back to the matching argument because the conversion is ambiguous. +ArgumentNarrowingCopyBack3=ByRef parameter '{0}' narrows from '{1}' to '{2}' when assigning back to the matching argument. + +UnboundTypeParam1=Type parameter '{0}' cannot be determined. +TypeInferenceFails1=Type argument inference fails for argument matching parameter '{0}'. +FailedTypeArgumentBinding=Substitution of type arguments failed. + +NoValidOperator_OneOperand=Operator is not defined for type '{0}'. +NoValidOperator_TwoOperands=Operator is not defined for {0} and {1}. +UnaryOperand2=Operator '{0}' is not defined for type '{1}'. +BinaryOperands3=Operator '{0}' is not defined for {1} and {2}. +NoValidOperator_StringType1=string "{0}" +NoValidOperator_NonStringType1=type '{0}' + +PropertySetMissingArgument1=Call to set property '{0}' requires at least one argument. +EmptyPlaceHolderMessage=Empty placeholder to adjust for 1-based array. + +############################### ;My.Net Strings ############################### + +## +## Mouse exceptions. +## +Mouse_NoMouseIsPresent=No mouse is present. +Mouse_NoWheelIsPresent=No mouse wheel is present. + +## +## VB IO Exceptions +## + +##### {0} will be the localized name of the non-existing special directory. +IO_SpecialDirectoryNotExist=Could not find special directory '{0}'. +##### The name of the special directories that will be put into the argument above. Hence no colon at the end. +IO_SpecialDirectory_MyDocuments=My Documents +IO_SpecialDirectory_MyMusic=My Music +IO_SpecialDirectory_MyPictures=My Pictures +IO_SpecialDirectory_Desktop=Desktop +IO_SpecialDirectory_Programs=Programs +IO_SpecialDirectory_ProgramFiles=Program Files +IO_SpecialDirectory_Temp=Temporary directory +IO_SpecialDirectory_AllUserAppData=All users' application data +IO_SpecialDirectory_UserAppData=Current user's application data + +##### {0} will be the path specified by user. +IO_FileExists_Path=Could not complete operation since a file already exists in this path '{0}'. +IO_FileNotFound_Path=Could not find file '{0}'. +IO_DirectoryExists_Path=Could not complete operation since a directory already exists in this path '{0}'. +IO_DirectoryIsRoot_Path=Could not complete operation since directory is a root directory: '{0}'. +IO_DirectoryNotFound_Path=Could not find directory '{0}'. +IO_GetParentPathIsRoot_Path=Could not get parent path since the given path is a root directory: '{0}'. + +##### {0} will be the argument name. {1} will be the specified argument, if any. +IO_ArgumentIsPath_Name_Path=Argument '{0}' must be a name, and not a relative or absolute path: '{1}'. + +##### Exceptions without place holders. +IO_CopyMoveRecursive=Could not complete operation on some files and directories. See the Data property of the exception for more details. +IO_CyclicOperation=Could not complete operation since target directory is under source directory. +IO_SourceEqualsTargetDirectory=Could not complete operation since source directory and target directory are the same. +IO_GetFiles_NullPattern=One of the wildcards is Nothing or empty string. +IO_DevicePath=The given path is a Win32 device path. Don't use paths starting with '\\\\.\\'. +IO_FilePathException=The given file path ends with a directory separator character. + +## +## General errors +## +General_ArgumentNullException=Argument cannot be Nothing. +General_ArgumentEmptyOrNothing_Name=Argument '{0}' cannot be an empty string or Nothing. +#General_PropertyEmptyOrNothing=Property {0} cannot be set to an empty string or Nothing. +General_PropertyNothing=Property {0} cannot be set to Nothing. + +## +## Application Log errors +## +ApplicationLog_FreeSpaceError=Cannot determine the amount of available disk space. +ApplicationLog_FileExceedsMaximumSize=Unable to write to log file because writing to it would cause it to exceed the MaxFileSize value. +ApplicationLog_ReservedSpaceEncroached=Unable to write to log file because writing to it would reduce free disk space below ReservedSpace value. +ApplicationLog_NegativeNumber=The value of {0} must be a positive number. +ApplicationLogBaseNameNull=BaseFileName cannot be Nothing or an empty String. +ApplicationLogNumberTooSmall=The value of {0} must be greater than or equal to 1000. +ApplicationLog_ExhaustedPossibleStreamNames=Unable to obtain a stream for the log. Potential file names based on {0} are already in use. + +## +## Network Strings +## +Network_InvalidUriString='{0}' is not a valid remote file address. A valid address should include a protocol, a path and a file name. +Network_BadConnectionTimeout=The ConnectionTimeout must be greater than 0. +Network_NetworkNotAvailable=Unable to ping because a network connection is not available. +Network_UploadAddressNeedsFilename=The address for UploadFile needs to include a file name. +Network_DownloadNeedsFilename=destinationFileName needs to include a file name. + +## +## ProgressDialog Strings +## +ProgressDialogDownloadingTitle=Downloading {0} +ProgressDialogUploadingTitle=Uploading {0} +ProgressDialogDownloadingLabel=Downloading {0} to {1} +ProgressDialogUploadingLabel=Uploading {0} to {1} + + +## +## Diagnostic Information exceptions. +## +DiagnosticInfo_Memory=Could not obtain memory information due to internal error. +DiagnosticInfo_FullOSName=Could not obtain full operation system name due to internal error. This might be caused by WMI not existing on the current machine. + +## +## Application Model errors +## +AppModel_CantGetMemoryMappedFile=An unexpected error has occurred because an operating system resource required for single instance startup cannot be acquired. +AppModel_NoStartupForm=A startup form has not been specified. +AppModel_SingleInstanceCantConnect=This single-instance application could not connect to the original instance. +AppModel_SplashAndMainFormTheSame=Splash screen and main form cannot be the same form. + +## +## TextFieldParser exceptions +## +TextFieldParser_NumberOfCharsMustBePositive=NumberOfChars must be greater than zero. +TextFieldParser_StreamNotReadable=The stream passed to TextFieldParser cannot be read. +TextFieldParser_BufferExceededMaxSize=TextFieldParser is unable to complete the read operation because maximum buffer size has been exceeded. +TextFieldParser_MalFormedDelimitedLine=Line {0} cannot be parsed using the current Delimiters. +TextFieldParser_MalFormedFixedWidthLine=Line {0} cannot be parsed using the current FieldWidths. +TextFieldParser_MaxLineSizeExceeded=Line {0} cannot be read because it exceeds the maximum line size. +TextFieldParser_FieldWidthsNothing=Unable to read fixed width fields because FieldWidths is Nothing or empty. +TextFieldParser_DelimitersNothing=Unable to read delimited fields because Delimiters is Nothing or empty. +TextFieldParser_FieldWidthsMustPositive=All field widths, except the last element, must be greater than zero. A field width less than or equal to zero in the last element indicates the last field is of variable length. +TextFieldParser_IllegalDelimiter=Unable to read delimited fields because a double quote is not a legal delimiter when HasFieldsEnclosedInQuotes is set to True. +TextFieldParser_DelimiterNothing=A delimiter cannot be Nothing or an empty String. +TextFieldParser_InvalidComment=A double quote is not a valid comment token for delimited fields where HasFieldsEnclosedInQuotes is set to True. +TextFieldParser_MalformedExtraData=Line Number:{0} +TextFieldParser_WhitespaceInToken=TextFieldParser does not support comment tokens that contain white space. +TextFieldParser_EndCharsInDelimiter=TextFieldParser does not support delimiters that contain end-of-line characters. + + +## Other exceptions. +## +#### {0} is the environment variable name from user. +EnvVarNotFound_Name=Environment variable is not defined: '{0}'. +WinForms_RecursiveFormCreate=The form referred to itself during construction from a default instance, which led to infinite recursion. Within the Form's constructor refer to the form using 'Me.' +WinForms_SeeInnerException=An error occurred creating the form. See Exception.InnerException for details. The error is: {0} +WebNotSupportedOnThisSKU=Current target framework does not support web operations. \ No newline at end of file diff --git a/Microsoft.VisualBasic/runtime/msvbalib/MyServices/ClipboardProxy.vb b/Microsoft.VisualBasic/runtime/msvbalib/MyServices/ClipboardProxy.vb new file mode 100644 index 000000000..425615828 --- /dev/null +++ b/Microsoft.VisualBasic/runtime/msvbalib/MyServices/ClipboardProxy.vb @@ -0,0 +1,285 @@ +' Copyright (c) Microsoft Corporation. All rights reserved. +Option Explicit On +Option Strict On + +Imports System.Collections.Specialized +Imports System.ComponentModel +Imports System.Drawing +Imports System.IO +Imports System.Security.Permissions +Imports System.Windows.Forms + +Namespace Microsoft.VisualBasic.MyServices + + '''***************************************************************************** + ''';ClipboardProxy + ''' + ''' A class that wraps System.Windows.Forms.Clipboard so that + ''' a clipboard can be instanced. + ''' + ''' + _ + Public Class ClipboardProxy + + '==PUBLIC******************************************************************* + + '''************************************************************************* + ''';New + ''' + ''' Only Allows instantiation of the class + ''' + ''' + Friend Sub New() + End Sub + + '''************************************************************************* + ''';GetText + ''' + ''' Gets text from the clipbaord + ''' + ''' The text as a String + ''' + Public Function GetText() As String + Return Clipboard.GetText() + End Function + + '''************************************************************************* + ''';GetText + ''' + ''' Gets text from the clipboard saved in the passed in format + ''' + ''' The type of text to get + ''' The text as a String + ''' + Public Function GetText(ByVal format As TextDataFormat) As String + Return Clipboard.GetText(format) + End Function + + '''************************************************************************* + ''';ContainsText + ''' + ''' Indicates whether or not text is available on the clipboard + ''' + ''' True if text is available, otherwise False + ''' + Public Function ContainsText() As Boolean + Return Clipboard.ContainsText + End Function + + '''************************************************************************* + ''';ContainsText + ''' + ''' Indicates whether or not text is available on the clipboard in + ''' the passed in format + ''' + ''' The type of text being checked for + ''' True if text is available, otherwise False + ''' + Public Function ContainsText(ByVal format As TextDataFormat) As Boolean + Return Clipboard.ContainsText(format) + End Function + + '''************************************************************************* + ''';SetText + ''' + ''' Saves the passed in String to the clipboard + ''' + ''' The String to save + ''' + Public Sub SetText(ByVal text As String) + Clipboard.SetText(text) + End Sub + + '''************************************************************************* + ''';SetText + ''' + ''' Saves the passed in String to the clipboard in the passed in format + ''' + ''' The String to save + ''' The format in which to save the String + ''' + Public Sub SetText(ByVal text As String, ByVal format As TextDataFormat) + Clipboard.SetText(text, format) + End Sub + + '''************************************************************************* + ''';GetImage + ''' + ''' Gets an Image from the clipboard + ''' + ''' The image + ''' + Public Function GetImage() As Image + Return Clipboard.GetImage() + End Function + + '''************************************************************************* + ''';ContainsImage + ''' + ''' Indicate whether or not an image has been saved to the clipboard + ''' + ''' True if an image is available, otherwise False + ''' + Public Function ContainsImage() As Boolean + Return Clipboard.ContainsImage() + End Function + + '''************************************************************************ + ''';SetImage + ''' + ''' Saves the passed in image to the clipboard + ''' + ''' The image to be saved + ''' + Public Sub SetImage(ByVal image As Image) + Clipboard.SetImage(image) + End Sub + + '''************************************************************************ + ''';GetAudioStream + ''' + ''' Gets an audio stream from the clipboard + ''' + ''' The audio stream as a Stream + ''' + Public Function GetAudioStream() As Stream + Return Clipboard.GetAudioStream() + End Function + + '''************************************************************************ + ''';ContainsAudio + ''' + ''' Indicates whether or not there's an audio stream saved to the clipboard + ''' + ''' True if an audio stream is available, otherwise False + ''' + Public Function ContainsAudio() As Boolean + Return Clipboard.ContainsAudio() + End Function + + '''*********************************************************************** + ''';SetAudio + ''' + ''' Saves the passed in audio byte array to the clipboard + ''' + ''' The byte array to be saved + ''' + Public Sub SetAudio(ByVal audioBytes As Byte()) + Clipboard.SetAudio(audioBytes) + End Sub + + '''*********************************************************************** + ''';SetAudio + ''' + ''' Saves the passed in audio stream to the clipboard + ''' + ''' The stream to be saved + ''' + Public Sub SetAudio(ByVal audioStream As Stream) + Clipboard.SetAudio(audioStream) + End Sub + + '''*********************************************************************** + ''';GetFileDropList + ''' + ''' Gets a file drop list from the clipboard + ''' + ''' The list of file paths as a StringCollection + ''' + Public Function GetFileDropList() As StringCollection + Return Clipboard.GetFileDropList() + End Function + + '''*********************************************************************** + ''';ContainsFileDropList + ''' + ''' Indicates whether or not a file drop list has been saved to the clipboard + ''' + ''' True if a file drop list is available, otherwise False + ''' + Public Function ContainsFileDropList() As Boolean + Return Clipboard.ContainsFileDropList() + End Function + + '''*********************************************************************** + ''';SetFileDropList + ''' + ''' Saves the passed in file drop list to the clipboard + ''' + ''' The file drop list as a StringCollection + ''' + Public Sub SetFileDropList(ByVal filePaths As StringCollection) + Clipboard.SetFileDropList(filePaths) + End Sub + + '''*********************************************************************** + ''';GetData + ''' + ''' Gets data from the clipboard that's been saved in the passed in format. + ''' + ''' The type of data being sought + ''' The data + ''' + Public Function GetData(ByVal format As String) As Object + Return Clipboard.GetData(format) + End Function + + '''*********************************************************************** + ''';ContainsData + ''' + ''' Indicates whether or not there is data on the clipboard in the passed in format + ''' + ''' + ''' True if there's data in the passed in format, otherwise False + ''' + Public Function ContainsData(ByVal format As String) As Boolean + Return Clipboard.ContainsData(format) + End Function + + '''*********************************************************************** + ''';SetData + ''' + ''' Saves the passed in data to the clipboard in the passed in format + ''' + ''' The format in which to save the data + ''' The data to be saved + ''' + Public Sub SetData(ByVal format As String, ByVal data As Object) + Clipboard.SetData(format, data) + End Sub + + '''*********************************************************************** + ''';Clear + ''' + ''' Removes everything from the clipboard + ''' + ''' + Public Sub Clear() + Clipboard.Clear() + End Sub + + '''*********************************************************************** + ''';GetDataObject + ''' + ''' Gets a Data Object from the clipboard. + ''' + ''' The data object + ''' This gives the ability to save an object in multiple formats + _ + Public Function GetDataObject() As IDataObject + Return Clipboard.GetDataObject() + End Function + + '''*********************************************************************** + ''';SetDataObject + ''' + ''' Saves a DataObject to the clipboard + ''' + ''' The data object to be saved + ''' This gives the ability to save an object in multiple formats + _ + Public Sub SetDataObject(ByVal data As DataObject) + Clipboard.SetDataObject(data) + End Sub + End Class +End Namespace diff --git a/Microsoft.VisualBasic/runtime/msvbalib/MyServices/FileSystemProxy.vb b/Microsoft.VisualBasic/runtime/msvbalib/MyServices/FileSystemProxy.vb new file mode 100644 index 000000000..f5f1f3e7c --- /dev/null +++ b/Microsoft.VisualBasic/runtime/msvbalib/MyServices/FileSystemProxy.vb @@ -0,0 +1,344 @@ +' Copyright (c) Microsoft Corporation. All rights reserved. + +Option Strict On +Option Explicit On + +Imports System +Imports System.Collections +Imports System.Collections.ObjectModel +Imports System.ComponentModel +Imports microsoft.VisualBasic.FileIO +Imports System.Security.Permissions +Imports System.Text +Imports System.Runtime.Versioning + +Namespace Microsoft.VisualBasic.MyServices + + '''****************************************************************************** + ''' ;FileSystemProxy + ''' + ''' An extremely thin wrapper around Microsoft.VisualBasic.FileIO.FileSystem to expose the type through My. + ''' More details: http://ddwww/spectool/Documents/Whidbey/VB/RAD%20Framework/My%20Proxy.doc + ''' + _ + _ + Public Class FileSystemProxy + + + '= PUBLIC ============================================================= + + Public ReadOnly Property Drives() As ReadOnlyCollection(Of IO.DriveInfo) + Get + Return Microsoft.VisualBasic.FileIO.FileSystem.Drives + End Get + End Property + + Public ReadOnly Property SpecialDirectories() As MyServices.SpecialDirectoriesProxy + Get + If m_SpecialDirectoriesProxy Is Nothing Then + m_SpecialDirectoriesProxy = New SpecialDirectoriesProxy + End If + Return m_SpecialDirectoriesProxy + End Get + End Property + + Public Property CurrentDirectory() As String + Get + Return Microsoft.VisualBasic.FileIO.FileSystem.CurrentDirectory + End Get + Set(ByVal value As String) + Microsoft.VisualBasic.FileIO.FileSystem.CurrentDirectory = value + End Set + End Property + + Public Function DirectoryExists(ByVal directory As String) As Boolean + Return Microsoft.VisualBasic.FileIO.FileSystem.DirectoryExists(directory) + End Function + + Public Function FileExists(ByVal file As String) As Boolean + Return Microsoft.VisualBasic.FileIO.FileSystem.FileExists(file) + End Function + + Public Sub CreateDirectory(ByVal directory As String) + Microsoft.VisualBasic.FileIO.FileSystem.CreateDirectory(directory) + End Sub + + Public Function GetDirectoryInfo(ByVal directory As String) As System.IO.DirectoryInfo + Return Microsoft.VisualBasic.FileIO.FileSystem.GetDirectoryInfo(directory) + End Function + + Public Function GetFileInfo(ByVal file As String) As System.IO.FileInfo + Return Microsoft.VisualBasic.FileIO.FileSystem.GetFileInfo(file) + End Function + + Public Function GetDriveInfo(ByVal drive As String) As System.IO.DriveInfo + Return Microsoft.VisualBasic.FileIO.FileSystem.GetDriveInfo(drive) + End Function + + Public Function GetFiles(ByVal directory As String) As ReadOnlyCollection(Of String) + Return Microsoft.VisualBasic.FileIO.FileSystem.GetFiles(directory) + End Function + + Public Function GetFiles(ByVal directory As String, ByVal searchType As SearchOption, _ + ByVal ParamArray wildcards() As String) As ReadOnlyCollection(Of String) + + Return Microsoft.VisualBasic.FileIO.FileSystem.GetFiles(directory, searchType, wildcards) + End Function + + Public Function GetDirectories(ByVal directory As String) As ReadOnlyCollection(Of String) + Return Microsoft.VisualBasic.FileIO.FileSystem.GetDirectories(directory) + End Function + + Public Function GetDirectories(ByVal directory As String, ByVal searchType As SearchOption, _ + ByVal ParamArray wildcards() As String) As ReadOnlyCollection(Of String) + + Return Microsoft.VisualBasic.FileIO.FileSystem.GetDirectories(directory, searchType, wildcards) + End Function + + Public Function FindInFiles(ByVal directory As String, _ + ByVal containsText As String, ByVal ignoreCase As Boolean, ByVal searchType As SearchOption) As ReadOnlyCollection(Of String) + + Return Microsoft.VisualBasic.FileIO.FileSystem.FindInFiles(directory, containsText, ignoreCase, searchType) + End Function + + Public Function FindInFiles(ByVal directory As String, ByVal containsText As String, ByVal ignoreCase As Boolean, _ + ByVal searchType As SearchOption, ByVal ParamArray fileWildcards() As String) As ReadOnlyCollection(Of String) + + Return Microsoft.VisualBasic.FileIO.FileSystem.FindInFiles(directory, containsText, ignoreCase, searchType, fileWildcards) + End Function + + Public Function GetParentPath(ByVal path As String) As String + Return Microsoft.VisualBasic.FileIO.FileSystem.GetParentPath(path) + End Function + + Public Function CombinePath(ByVal baseDirectory As String, ByVal relativePath As String) As String + Return Microsoft.VisualBasic.FileIO.FileSystem.CombinePath(baseDirectory, relativePath) + End Function + + Public Function GetName(ByVal path As String) As String + Return Microsoft.VisualBasic.FileIO.FileSystem.GetName(path) + End Function + + Public Function GetTempFileName() As String + Return Microsoft.VisualBasic.FileIO.FileSystem.GetTempFileName() + End Function + + Public Function ReadAllText(ByVal file As String) As String + Return Microsoft.VisualBasic.FileIO.FileSystem.ReadAllText(file) + End Function + + Public Function ReadAllText(ByVal file As String, ByVal encoding As Encoding) As String + Return Microsoft.VisualBasic.FileIO.FileSystem.ReadAllText(file, encoding) + End Function + + Public Function ReadAllBytes(ByVal file As String) As Byte() + Return Microsoft.VisualBasic.FileIO.FileSystem.ReadAllBytes(file) + End Function + + Public Sub WriteAllText(ByVal file As String, ByVal text As String, ByVal append As Boolean) + Microsoft.VisualBasic.FileIO.FileSystem.WriteAllText(file, text, append) + End Sub + + Public Sub WriteAllText(ByVal file As String, ByVal text As String, ByVal append As Boolean, _ + ByVal encoding As Encoding) + + Microsoft.VisualBasic.FileIO.FileSystem.WriteAllText(file, text, append, encoding) + End Sub + + Public Sub WriteAllBytes(ByVal file As String, ByVal data() As Byte, ByVal append As Boolean) + Microsoft.VisualBasic.FileIO.FileSystem.WriteAllBytes(file, data, append) + End Sub + + _ + _ + Public Sub CopyFile(ByVal sourceFileName As String, ByVal destinationFileName As String) + Microsoft.VisualBasic.FileIO.FileSystem.CopyFile(sourceFileName, destinationFileName) + End Sub + + _ + _ + Public Sub CopyFile(ByVal sourceFileName As String, ByVal destinationFileName As String, ByVal overwrite As Boolean) + Microsoft.VisualBasic.FileIO.FileSystem.CopyFile(sourceFileName, destinationFileName, overwrite) + End Sub + + _ + _ + Public Sub CopyFile(ByVal sourceFileName As String, ByVal destinationFileName As String, ByVal showUI As UIOption) + Microsoft.VisualBasic.FileIO.FileSystem.CopyFile(sourceFileName, destinationFileName, showUI) + End Sub + + _ + _ + Public Sub CopyFile(ByVal sourceFileName As String, ByVal destinationFileName As String, ByVal showUI As UIOption, ByVal onUserCancel As UICancelOption) + Microsoft.VisualBasic.FileIO.FileSystem.CopyFile(sourceFileName, destinationFileName, showUI, onUserCancel) + End Sub + + _ + _ + Public Sub MoveFile(ByVal sourceFileName As String, ByVal destinationFileName As String) + Microsoft.VisualBasic.FileIO.FileSystem.MoveFile(sourceFileName, destinationFileName) + End Sub + + _ + _ + Public Sub MoveFile(ByVal sourceFileName As String, ByVal destinationFileName As String, ByVal overwrite As Boolean) + Microsoft.VisualBasic.FileIO.FileSystem.MoveFile(sourceFileName, destinationFileName, overwrite) + End Sub + + _ + _ + Public Sub MoveFile(ByVal sourceFileName As String, ByVal destinationFileName As String, ByVal showUI As UIOption) + Microsoft.VisualBasic.FileIO.FileSystem.MoveFile(sourceFileName, destinationFileName, showUI) + End Sub + + _ + _ + Public Sub MoveFile(ByVal sourceFileName As String, ByVal destinationFileName As String, ByVal showUI As UIOption, ByVal onUserCancel As UICancelOption) + Microsoft.VisualBasic.FileIO.FileSystem.MoveFile(sourceFileName, destinationFileName, showUI, onUserCancel) + End Sub + + _ + _ + Public Sub CopyDirectory(ByVal sourceDirectoryName As String, ByVal destinationDirectoryName As String) + Microsoft.VisualBasic.FileIO.FileSystem.CopyDirectory(sourceDirectoryName, destinationDirectoryName) + End Sub + + _ + _ + Public Sub CopyDirectory(ByVal sourceDirectoryName As String, ByVal destinationDirectoryName As String, ByVal overwrite As Boolean) + Microsoft.VisualBasic.FileIO.FileSystem.CopyDirectory(sourceDirectoryName, destinationDirectoryName, overwrite) + End Sub + + _ + _ + Public Sub CopyDirectory(ByVal sourceDirectoryName As String, ByVal destinationDirectoryName As String, ByVal showUI As UIOption) + Microsoft.VisualBasic.FileIO.FileSystem.CopyDirectory(sourceDirectoryName, destinationDirectoryName, showUI) + End Sub + + _ + _ + Public Sub CopyDirectory(ByVal sourceDirectoryName As String, ByVal destinationDirectoryName As String, ByVal showUI As UIOption, ByVal onUserCancel As UICancelOption) + Microsoft.VisualBasic.FileIO.FileSystem.CopyDirectory(sourceDirectoryName, destinationDirectoryName, showUI, onUserCancel) + End Sub + + _ + _ + Public Sub MoveDirectory(ByVal sourceDirectoryName As String, ByVal destinationDirectoryName As String) + Microsoft.VisualBasic.FileIO.FileSystem.MoveDirectory(sourceDirectoryName, destinationDirectoryName) + End Sub + + _ + _ + Public Sub MoveDirectory(ByVal sourceDirectoryName As String, ByVal destinationDirectoryName As String, ByVal overwrite As Boolean) + Microsoft.VisualBasic.FileIO.FileSystem.MoveDirectory(sourceDirectoryName, destinationDirectoryName, overwrite) + End Sub + + _ + _ + Public Sub MoveDirectory(ByVal sourceDirectoryName As String, ByVal destinationDirectoryName As String, ByVal showUI As UIOption) + Microsoft.VisualBasic.FileIO.FileSystem.MoveDirectory(sourceDirectoryName, destinationDirectoryName, showUI) + End Sub + + _ + _ + Public Sub MoveDirectory(ByVal sourceDirectoryName As String, ByVal destinationDirectoryName As String, ByVal showUI As UIOption, ByVal onUserCancel As UICancelOption) + Microsoft.VisualBasic.FileIO.FileSystem.MoveDirectory(sourceDirectoryName, destinationDirectoryName, showUI, onUserCancel) + End Sub + + _ + _ + Public Sub DeleteFile(ByVal file As String) + Microsoft.VisualBasic.FileIO.FileSystem.DeleteFile(file) + End Sub + + _ + _ + Public Sub DeleteFile(ByVal file As String, ByVal showUI As UIOption, ByVal recycle As RecycleOption) + Microsoft.VisualBasic.FileIO.FileSystem.DeleteFile(file, showUI, recycle) + End Sub + + _ + _ + Public Sub DeleteFile(ByVal file As String, ByVal showUI As UIOption, ByVal recycle As RecycleOption, _ + ByVal onUserCancel As UICancelOption) + + Microsoft.VisualBasic.FileIO.FileSystem.DeleteFile(file, showUI, recycle, onUserCancel) + End Sub + + _ + _ + Public Sub DeleteDirectory(ByVal directory As String, ByVal onDirectoryNotEmpty As DeleteDirectoryOption) + Microsoft.VisualBasic.FileIO.FileSystem.DeleteDirectory(directory, onDirectoryNotEmpty) + End Sub + + _ + _ + Public Sub DeleteDirectory(ByVal directory As String, ByVal showUI As UIOption, ByVal recycle As RecycleOption) + + Microsoft.VisualBasic.FileIO.FileSystem.DeleteDirectory(directory, showUI, recycle) + End Sub + + _ + _ + Public Sub DeleteDirectory(ByVal directory As String, _ + ByVal showUI As UIOption, ByVal recycle As RecycleOption, ByVal onUserCancel As UICancelOption) + + Microsoft.VisualBasic.FileIO.FileSystem.DeleteDirectory(directory, showUI, recycle, onUserCancel) + End Sub + + _ + _ + Public Sub RenameFile(ByVal file As String, ByVal newName As String) + Microsoft.VisualBasic.FileIO.FileSystem.RenameFile(file, newName) + End Sub + + _ + _ + Public Sub RenameDirectory(ByVal directory As String, ByVal newName As String) + Microsoft.VisualBasic.FileIO.FileSystem.RenameDirectory(directory, newName) + End Sub + + Public Function OpenTextFieldParser(ByVal file As String) As TextFieldParser + Return Microsoft.VisualBasic.FileIO.FileSystem.OpenTextFieldParser(file) + End Function + + Public Function OpenTextFieldParser(ByVal file As String, ByVal ParamArray delimiters As String()) As TextFieldParser + Return Microsoft.VisualBasic.FileIO.FileSystem.OpenTextFieldParser(file, delimiters) + End Function + + Public Function OpenTextFieldParser(ByVal file As String, ByVal ParamArray fieldWidths As Integer()) As TextFieldParser + Return Microsoft.VisualBasic.FileIO.FileSystem.OpenTextFieldParser(file, fieldWidths) + End Function + + Public Function OpenTextFileReader(ByVal file As String) As IO.StreamReader + Return Microsoft.VisualBasic.FileIO.FileSystem.OpenTextFileReader(file) + End Function + + Public Function OpenTextFileReader(ByVal file As String, ByVal encoding As Encoding) As IO.StreamReader + Return Microsoft.VisualBasic.FileIO.FileSystem.OpenTextFileReader(file, encoding) + End Function + + Public Function OpenTextFileWriter(ByVal file As String, ByVal append As Boolean) As IO.StreamWriter + Return Microsoft.VisualBasic.FileIO.FileSystem.OpenTextFileWriter(file, append) + End Function + + Public Function OpenTextFileWriter(ByVal file As String, ByVal append As Boolean, _ + ByVal encoding As Encoding) As IO.StreamWriter + + Return Microsoft.VisualBasic.FileIO.FileSystem.OpenTextFileWriter(file, append, encoding) + End Function + + '= FRIEND ============================================================= + + '''****************************************************************************** + ''' ;New + ''' + ''' Proxy class can only created by internal classes. + ''' + Friend Sub New() + End Sub + + + Private m_SpecialDirectoriesProxy As SpecialDirectoriesProxy = Nothing + End Class +End Namespace diff --git a/Microsoft.VisualBasic/runtime/msvbalib/MyServices/Internal/ContextValue.vb b/Microsoft.VisualBasic/runtime/msvbalib/MyServices/Internal/ContextValue.vb new file mode 100644 index 000000000..970fefe5e --- /dev/null +++ b/Microsoft.VisualBasic/runtime/msvbalib/MyServices/Internal/ContextValue.vb @@ -0,0 +1,115 @@ +' Copyright (c) Microsoft Corporation. All rights reserved. +Option Explicit On +Option Strict Off + +Imports System.Security + +Namespace Microsoft.VisualBasic.MyServices.Internal + + '''************************************************************************** + ''' ;SkuSafeHttpContext + ''' + ''' Returns the current HTTPContext or nothing if we are not running in a + ''' web context. + ''' + ''' + ''' With the FX dividing into Client and Full skus, we may not always have + ''' access to the System.Web types. So we have to test for the presence + ''' of System.Web.Httpcontext before trying to access it. + ''' + _ + Friend Class SkuSafeHttpContext + Public Shared ReadOnly Property Current() As Object + Get + 'Return the equivalent of System.Web.HttpContext.Current + If m_HttpContextCurrent IsNot Nothing Then + Return m_HttpContextCurrent.GetValue(Nothing, Nothing) + Else + Return Nothing + End If + End Get + End Property + + '''************************************************************************** + ''' ;InitContext + ''' + ''' Initialize the field that holds the type that allows us to access + ''' System.Web.HttpContext + ''' + ''' + ''' The [COR_*] things are substituted by a Perl script launched from + ''' Microsoft.VisualBasic.Build.vbproj + ''' + Private Shared Function InitContext() As System.Reflection.PropertyInfo + Dim HttpContextType As System.Type + HttpContextType = System.Type.GetType( +"System.Web.HttpContext,System.Web,Version=[COR_BUILD_MAJOR].[COR_BUILD_MINOR].[CLR_OFFICIAL_ASSEMBLY_NUMBER].0,Culture=neutral,PublicKeyToken=B03F5F7F11D50A3A") + + If HttpContextType IsNot Nothing Then + Return HttpContextType.GetProperty("Current") + Else + Return Nothing + End If + End Function + + 'This class isn't meant to be constructed. + 'Shut FXCOP up by providing a private ctor so the compiler doesn't synth a public one. + Private Sub New() + End Sub + + Private Shared m_HttpContextCurrent As System.Reflection.PropertyInfo = InitContext() + End Class + + '''************************************************************************** + ''' ;ContextValue + ''' + ''' Stores an object in a context appropriate for the environment we are + ''' running in (web/windows) + ''' + ''' + ''' + ''' "Thread appropriate" means that if we are running on ASP.Net the object will be stored in the + ''' context of the current request (meaning the object is stored per request on the web). Otherwise, + ''' the object is stored per CallContext. Note that an instance of this class can only be associated + ''' with the one item to be stored/retrieved at a time. + ''' + _ + Public Class ContextValue(Of T) + Public Sub New() + m_ContextKey = System.Guid.NewGuid.ToString + End Sub + + '''************************************************************************** + ''' ;Value + ''' + ''' Get the object from the correct thread-appropriate location + ''' + ''' + Public Property Value() As T 'No Synclocks required because we are operating upon instance data and the object is not shared across threads + _ + Get + Dim Context As Object = SkuSafeHttpContext.Current() + If Context IsNot Nothing Then 'we are running on the web + Return DirectCast(Context.Items(m_ContextKey), T) 'Note, Context.Items() can return Nothing and that's ok + Else 'we are running in a DLL + Return DirectCast(System.Runtime.Remoting.Messaging.CallContext.GetData(m_ContextKey), T) 'Note, CallContext.GetData() can return Nothing and that's ok + End If + End Get + _ + Set(ByVal value As T) + Dim Context As Object = SkuSafeHttpContext.Current() + If Context IsNot Nothing Then 'we are running on the web + Context.Items(m_ContextKey) = value + Else 'we are running in a DLL + System.Runtime.Remoting.Messaging.CallContext.SetData(m_ContextKey, value) + End If + End Set + End Property + + '= PRIVATE ============================================================ + + Private ReadOnly m_ContextKey As String 'An item is stored in the dictionary by a guid which this string maintains + + End Class 'ContextValue + +End Namespace diff --git a/Microsoft.VisualBasic/runtime/msvbalib/MyServices/Internal/ProgressDialog.vb b/Microsoft.VisualBasic/runtime/msvbalib/MyServices/Internal/ProgressDialog.vb new file mode 100644 index 000000000..b532d9ece --- /dev/null +++ b/Microsoft.VisualBasic/runtime/msvbalib/MyServices/Internal/ProgressDialog.vb @@ -0,0 +1,325 @@ +' Copyright (c) Microsoft Corporation. All rights reserved. +Option Explicit On +Option Strict On + +Imports System +Imports System.Drawing +Imports System.Globalization +Imports System.Security +Imports System.Threading +Imports System.Windows.Forms + +Namespace Microsoft.VisualBasic.MyServices.Internal + + '''************************************************************************* + ''';ProgressDialog + ''' + ''' A dialog that shows progress used for Network.Download and Network.Upload + ''' + ''' + Friend Class ProgressDialog + Inherits System.Windows.Forms.Form + + '==PUBLIC************************************************************** + + '********************************************************************** + ''';UserCancelledEvent + ''' + ''' Event raised when user cancels the dialog or closes it before the operation is completed + ''' + ''' + Public Event UserHitCancel() + + '''********************************************************************* + ''';New + ''' + ''' Constructor + ''' + ''' + Friend Sub New() + MyBase.New() + InitializeComponent() + End Sub + + '''********************************************************************* + ''';Increment + ''' + ''' Increments the progress bar by the passed in amount + ''' + ''' The amount to increment the bar + ''' + ''' This method should never be called directly. It should be called with + ''' an InvokeBegin by a secondary thread. + ''' + Public Sub Increment(ByVal incrementAmount As Integer) + Me.ProgressBarWork.Increment(incrementAmount) + End Sub + + '''********************************************************************* + ''';CloseDialog + ''' + ''' Closes the Progress Dialog + ''' + ''' + ''' This method should never be called directly. It should be called with + ''' an InvokeBegin by a secondary thread. + ''' + Public Sub CloseDialog() + m_CloseDialogInvoked = True + Me.Close() + End Sub + + '''********************************************************************* + ''';ShowProgressDialog + ''' + ''' Displays the progress dialog modally + ''' + ''' This method should be called on the main thread after the worker thread has been started + Public Sub ShowProgressDialog() + Try + If Not m_Closing Then + Me.ShowDialog() + End If + Finally + FormClosableSemaphore.Set() + End Try + End Sub + + '''********************************************************************** + ''';LabelText + ''' + ''' Sets the text of the label (Usually something like Copying x to y) + ''' + ''' The value to set the label to + ''' This should only be called on the main thread before showing the dialog + Public Property LabelText() As String + Get + Return Me.LabelInfo.Text + End Get + Set(ByVal Value As String) + Me.LabelInfo.Text = Value + End Set + End Property + + '''********************************************************************** + ''';FormClosableSemaphore + ''' + ''' Used to set or get the semaphore which signals when the dialog + ''' is in a closable state. + ''' + ''' The ManualResetEvent + ''' + Public ReadOnly Property FormClosableSemaphore() As ManualResetEvent + Get + Return m_FormClosableSemaphore + End Get + End Property + + '''********************************************************************** + ''';IndicateClosing + ''' + ''' Inform the diaog that CloseDialog will soon be called + ''' + ''' + ''' This method should be called directly from the secondary thread. We want + ''' to indicate we're closing as soon as we can so w don't show the dialog when we + ''' don't need to (when the work is finished before we can show the dialog) + ''' + Public Sub IndicateClosing() + m_Closing = True + End Sub + + '''********************************************************************** + ''';UserCancelled + ''' + ''' Indicated if the user has clicked the cancel button + ''' + ''' True if the user has cancelled, otherwise False + ''' + ''' The secondary thread checks this property directly. If it's True, the thread + ''' breaks out of its loop. + ''' + Public ReadOnly Property UserCanceledTheDialog() As Boolean + Get + Return m_Canceled + End Get + End Property + + '==PROTECTED************************************************************ + + '''********************************************************************* + ''';CreateParams + ''' + ''' This enables a dialog with a close button, sizable borders, and no icon + ''' + ''' + ''' + Protected Overrides ReadOnly Property CreateParams() As CreateParams + _ + Get + Dim cp As CreateParams = MyBase.CreateParams + cp.Style = cp.Style Or WS_THICKFRAME + Return cp + End Get + End Property + + '==PRIVATE************************************************************** + + '''********************************************************************* + ''';ButtonCloseDialog_Click + ''' + ''' Handles user clicking Cancel. Sets a flag read by secondary thread. + ''' + ''' The cancel button + ''' Arguments + ''' + Private Sub ButtonCloseDialog_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles ButtonCloseDialog.Click + Me.ButtonCloseDialog.Enabled = False + m_Canceled = True + RaiseEvent UserHitCancel() + End Sub + + '''********************************************************************* + ''';ProgressDialog_FormClosing + ''' + ''' Indicates the form is closing + ''' + ''' + ''' + ''' + ''' We listen for this event since we want to make closing the dialog before it's + ''' finished behave the same as a cancel + ''' + Private Sub ProgressDialog_FormClosing(ByVal sender As Object, ByVal e As System.Windows.Forms.FormClosingEventArgs) Handles Me.FormClosing + If e.CloseReason = CloseReason.UserClosing And Not m_CloseDialogInvoked Then + ' If the progress bar isn't finished and the user hasn't already cancelled + If Me.ProgressBarWork.Value < 100 And Not m_Canceled Then + ' Cancel the Close since we want the dialog to be closed from a call from the + ' secondary thread + e.Cancel = True + + ' Indicate the user has cancelled. We'll actually close the dialog from WebClientCopy + m_Canceled = True + RaiseEvent UserHitCancel() + End If + End If + End Sub + + '''********************************************************************* + ''';ProgressDialog_Resize + ''' + ''' Ensure the label resizes with the dialog + ''' + ''' + ''' + ''' + ''' Since the label has AutoSize set to True we have to set the maximum size so the label + ''' will grow down rather than off the dialog. As the size of the dialog changes, the maximum + ''' size needs to be adjusted. + ''' + Private Sub ProgressDialog_Resize(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Resize + Me.LabelInfo.MaximumSize = New Size(Me.ClientSize.Width - BORDER_SIZE, 0) + End Sub + + '''********************************************************************* + ''';ProgressDialog_Activated + ''' + ''' Exits the monitor when we're activated + ''' + ''' Dialog + ''' Arguments + ''' + Private Sub ProgressDialog_Activated(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Shown + m_FormClosableSemaphore.Set() + End Sub + + ' Indicates whether or not the dialog is closing + Private m_Closing As Boolean + + ' Indicates whether or not the user has cancelled the copy + Private m_Canceled As Boolean = False + + ' Used to signal when the dialog is in a closable state. The dialog is in a closable + ' state when it has been activated or when it has been flagged to be closed before + ' ShowDialog has been called + Private m_FormClosableSemaphore As ManualResetEvent = New ManualResetEvent(False) + + ' Indicates CloseDialog has been called + Private m_CloseDialogInvoked As Boolean + + ' Constant used to get resizable dialog with border style set to fixed dialog. + Private Const WS_THICKFRAME As Integer = &H40000 + + ' Border area for label (10 pixels on each side) + Private Const BORDER_SIZE As Integer = 20 + + +#Region " Windows Form Designer generated code " + + 'Form overrides dispose to clean up the component list. + Protected Overloads Overrides Sub Dispose(ByVal disposing As Boolean) + If disposing Then + If Not (components Is Nothing) Then + components.Dispose() + End If + If m_FormClosableSemaphore isNot Nothing then + m_FormClosableSemaphore.Dispose() + m_FormClosableSemaphore = Nothing + End If + End If + MyBase.Dispose(disposing) + End Sub + Friend WithEvents LabelInfo As System.Windows.Forms.Label + Friend WithEvents ProgressBarWork As System.Windows.Forms.ProgressBar + Friend WithEvents ButtonCloseDialog As System.Windows.Forms.Button + + 'Required by the Windows Form Designer + Private components As System.ComponentModel.IContainer + + 'NOTE: The following procedure is required by the Windows Form Designer + 'It can be modified using the Windows Form Designer. + 'Do not modify it using the code editor. + _ + Private Sub InitializeComponent() + Dim resources As System.ComponentModel.ComponentResourceManager = New System.ComponentModel.ComponentResourceManager(GetType(ProgressDialog)) + Me.LabelInfo = New System.Windows.Forms.Label + Me.ProgressBarWork = New System.Windows.Forms.ProgressBar + Me.ButtonCloseDialog = New System.Windows.Forms.Button + Me.SuspendLayout() + ' + 'LabelInfo + ' + resources.ApplyResources(Me.LabelInfo, "LabelInfo", CultureInfo.CurrentUICulture) + Me.LabelInfo.MaximumSize = New System.Drawing.Size(300, 0) + Me.LabelInfo.Name = "LabelInfo" + ' + 'ProgressBarWork + ' + resources.ApplyResources(Me.ProgressBarWork, "ProgressBarWork", CultureInfo.CurrentUICulture) + Me.ProgressBarWork.Name = "ProgressBarWork" + ' + 'ButtonCloseDialog + ' + resources.ApplyResources(Me.ButtonCloseDialog, "ButtonCloseDialog", CultureInfo.CurrentUICulture) + Me.ButtonCloseDialog.Name = "ButtonCloseDialog" + ' + 'ProgressDialog + ' + resources.ApplyResources(Me, "$this", CultureInfo.CurrentUICulture) + Me.Controls.Add(Me.ButtonCloseDialog) + Me.Controls.Add(Me.ProgressBarWork) + Me.Controls.Add(Me.LabelInfo) + Me.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedDialog + Me.MaximizeBox = False + Me.MinimizeBox = False + Me.Name = "ProgressDialog" + Me.ShowInTaskbar = False + Me.ResumeLayout(False) + Me.PerformLayout() + + End Sub + +#End Region + + End Class +End Namespace diff --git a/Microsoft.VisualBasic/runtime/msvbalib/MyServices/Internal/WebClientCopy.vb b/Microsoft.VisualBasic/runtime/msvbalib/MyServices/Internal/WebClientCopy.vb new file mode 100644 index 000000000..a4f1c7026 --- /dev/null +++ b/Microsoft.VisualBasic/runtime/msvbalib/MyServices/Internal/WebClientCopy.vb @@ -0,0 +1,252 @@ +' Copyright (c) Microsoft Corporation. All rights reserved. +Option Explicit On +Option Strict On + +Imports System +Imports System.Diagnostics +Imports System.IO +Imports System.Net +Imports System.Threading +Imports System.Windows.Forms +Imports Microsoft.VisualBasic +Imports Microsoft.VisualBasic.CompilerServices + +Namespace Microsoft.VisualBasic.MyServices.Internal + + '''************************************************************************** + ''';WebClientCopy + ''' + ''' Class that controls the thread that does the actual work of downloading or uploading. + ''' + ''' + Friend Class WebClientCopy + + '==PUBLIC*************************************************************** + + '''********************************************************************* + ''';New + ''' + ''' Creates an instance of a WebClientCopy, used to download or upload a file + ''' + ''' The WebClient used to do the downloading or uploading + ''' UI for indicating progress + ''' + Public Sub New(ByVal client As WebClient, ByVal dialog As ProgressDialog) + + Debug.Assert(client IsNot Nothing, "No Webclient") + + m_WebClient = client + m_ProgressDialog = dialog + + End Sub + + '''********************************************************************** + ''';DownloadFile + ''' + ''' Downloads a file + ''' + ''' The source for the file + ''' The path and name where the file is saved + ''' + Public Sub DownloadFile(ByVal address As Uri, ByVal destinationFileName As String) + Debug.Assert(m_WebClient IsNot Nothing, "No WebClient") + Debug.Assert(address IsNot Nothing, "No address") + Debug.Assert(destinationFileName <> "" AndAlso Directory.Exists(Path.GetDirectoryName(Path.GetFullPath(destinationFileName))), "Invalid path") + + + ' If we have a dialog we need to set up an async download + If m_ProgressDialog IsNot Nothing Then + m_WebClient.DownloadFileAsync(address, destinationFileName) + m_ProgressDialog.ShowProgressDialog() 'returns when the download sequence is over, whether due to success, error, or being cancelled + Else + m_WebClient.DownloadFile(address, destinationFileName) + End If + + 'Now that we are back on the main thread, throw the exception we encountered if the user didn't cancel. + If m_ExceptionEncounteredDuringFileTransfer IsNot Nothing Then + If m_ProgressDialog Is Nothing OrElse Not m_ProgressDialog.UserCanceledTheDialog Then + Throw m_ExceptionEncounteredDuringFileTransfer + End If + End If + + End Sub + + '''********************************************************************** + ''';UploadFile + ''' + ''' Uploads a file + ''' + ''' The name and path of the source file + ''' The address to which the file is uploaded + ''' + Public Sub UploadFile(ByVal sourceFileName As String, ByVal address As Uri) + Debug.Assert(m_WebClient IsNot Nothing, "No WebClient") + Debug.Assert(address IsNot Nothing, "No address") + Debug.Assert(sourceFileName <> "" AndAlso File.Exists(sourceFileName), "Invalid file") + + ' If we have a dialog we need to set up an async download + If m_ProgressDialog IsNot Nothing Then + m_WebClient.UploadFileAsync(address, sourceFileName) + m_ProgressDialog.ShowProgressDialog() 'returns when the download sequence is over, whether due to success, error, or being cancelled + Else + m_WebClient.UploadFile(address, sourceFileName) + End If + + 'Now that we are back on the main thread, throw the exception we encountered if the user didn't cancel. + If m_ExceptionEncounteredDuringFileTransfer IsNot Nothing Then + If m_ProgressDialog Is Nothing OrElse Not m_ProgressDialog.UserCanceledTheDialog Then + Throw m_ExceptionEncounteredDuringFileTransfer + End If + End If + End Sub + + '==PRIVATE*************************************************************** + + '''********************************************************************** + ''';InvokeIncrement + ''' + ''' Notifies the progress dialog to increment the progress bar + ''' + ''' The percentage of bytes read + ''' + Private Sub InvokeIncrement(ByVal progressPercentage As Integer) + ' Don't invoke unless dialog is up and running + If m_ProgressDialog IsNot Nothing Then + If m_ProgressDialog.IsHandleCreated Then + + ' For performance, don't invoke if increment is 0 + Dim increment As Integer = progressPercentage - m_Percentage + m_Percentage = progressPercentage + If increment > 0 Then + m_ProgressDialog.BeginInvoke(New DoIncrement(AddressOf m_ProgressDialog.Increment), increment) + End If + + End If + End If + End Sub + + '''******************************************************************** + ''';InvokeCloseDialog + ''' + ''' Posts a message to close the progress dialog + ''' + ''' + Private Sub CloseProgressDialog() + ' Don't invoke unless dialog is up and running + If m_ProgressDialog IsNot Nothing Then + m_ProgressDialog.IndicateClosing() + + If m_ProgressDialog.IsHandleCreated Then + m_ProgressDialog.BeginInvoke(New MethodInvoker(AddressOf m_ProgressDialog.CloseDialog)) + Else + ' Ensure dialog is closed. If we get here it means the file was copied before the handle for + ' the progress dialog was created. + m_ProgressDialog.Close() + End If + End If + End Sub + + '''******************************************************************** + ''';m_WebClient_DownloadFileCompleted + ''' + ''' Handles the WebClient's DownloadFileCompleted event + ''' + ''' + ''' + ''' + Private Sub m_WebClient_DownloadFileCompleted(ByVal sender As Object, ByVal e As System.ComponentModel.AsyncCompletedEventArgs) Handles m_WebClient.DownloadFileCompleted + Try + ' If the download was interupted by an exception, keep track of the exception, which we'll throw from the main thread + If e.Error IsNot Nothing Then + m_ExceptionEncounteredDuringFileTransfer = e.Error + End If + + If Not e.Cancelled AndAlso e.Error Is Nothing Then + InvokeIncrement(100) + End If + Finally + 'We don't close the dialog until we recieve the WebClient.DownloadFileCompleted event + CloseProgressDialog() + End Try + End Sub + + '''******************************************************************** + ''';m_WebClient_DownloadProgressChanged + ''' + ''' Handles event WebClient fires whenever progress of download changes + ''' + ''' + ''' + ''' + Private Sub m_WebClient_DownloadProgressChanged(ByVal sender As Object, ByVal e As System.Net.DownloadProgressChangedEventArgs) Handles m_WebClient.DownloadProgressChanged + InvokeIncrement(e.ProgressPercentage) + End Sub + + '''******************************************************************** + ''';m_WebClient_UploadFileCompleted + ''' + ''' Handles the WebClient's UploadFileCompleted event + ''' + ''' + ''' + ''' + Private Sub m_WebClient_UploadFileCompleted(ByVal sender As Object, ByVal e As System.Net.UploadFileCompletedEventArgs) Handles m_WebClient.UploadFileCompleted + + ' If the upload was interupted by an exception, keep track of the exception, which we'll throw from the main thread + Try + If e.Error IsNot Nothing Then + m_ExceptionEncounteredDuringFileTransfer = e.Error + End If + If Not e.Cancelled AndAlso e.Error Is Nothing Then + InvokeIncrement(100) + End If + Finally + 'We don't close the dialog until we recieve the WebClient.DownloadFileCompleted event + CloseProgressDialog() + End Try + End Sub + + '********************************************************************** + ''';m_WebClient_UploadProgressChanged + ''' + ''' Handles event WebClient fires whenever progress of upload changes + ''' + ''' + ''' + ''' + Private Sub m_WebClient_UploadProgressChanged(ByVal sender As Object, ByVal e As System.Net.UploadProgressChangedEventArgs) Handles m_WebClient.UploadProgressChanged + Dim increment As Long = (e.BytesSent * 100) \ e.TotalBytesToSend + InvokeIncrement(CInt(increment)) + End Sub + + '''********************************************************************* + ''';m_ProgressDialog_UserCancelledEvent + ''' + ''' If the user clicks cancel on the Progress dialog, we need to cancel + ''' the current async file transfer operation + ''' + ''' + ''' Note that we don't want to close the progress dialog here. Wait until + ''' the actual file transfer cancel event comes through and do it there. + ''' + Private Sub m_ProgressDialog_UserCancelledEvent() Handles m_ProgressDialog.UserHitCancel + m_WebClient.CancelAsync() 'cancel the upload/download transfer. We'll close the ProgressDialog as soon as the WebClient cancels the xfer. + End Sub + + ' The WebClient performs the downloading or uploading operations for us + Private WithEvents m_WebClient As WebClient + + ' Dialog shown if user wants to see progress UI. Allows the user to cancel the file transfer. + Private WithEvents m_ProgressDialog As ProgressDialog + + 'Keeps track of the error that happend during upload/download so we can throw it once we can guarantee we are back on the main thread + Private m_ExceptionEncounteredDuringFileTransfer As Exception + + ' Used for invoking ProgressDialog.Increment + Private Delegate Sub DoIncrement(ByVal Increment As Integer) + + ' The percentage of the operation completed + Private m_Percentage As Integer = 0 + + End Class +End Namespace diff --git a/Microsoft.VisualBasic/runtime/msvbalib/MyServices/Internal/my.vb b/Microsoft.VisualBasic/runtime/msvbalib/MyServices/Internal/my.vb new file mode 100644 index 000000000..0d98724b2 --- /dev/null +++ b/Microsoft.VisualBasic/runtime/msvbalib/MyServices/Internal/my.vb @@ -0,0 +1,362 @@ +' Copyright (c) Microsoft Corporation. All rights reserved. +Option Strict On +Option Explicit On +Option Compare Binary + + +#If TARGET = "module" AndAlso _MYTYPE = "" Then +#Const _MYTYPE="Empty" +#End If + +#If _MYTYPE = "WindowsForms" Then + +#Const _MYFORMS = True +#Const _MYWEBSERVICES = True +#Const _MYUSERTYPE = "Windows" +#Const _MYCOMPUTERTYPE = "Windows" +#Const _MYAPPLICATIONTYPE = "WindowsForms" + +#ElseIf _MYTYPE = "WindowsFormsWithCustomSubMain" Then + +#Const _MYFORMS = True +#Const _MYWEBSERVICES = True +#Const _MYUSERTYPE = "Windows" +#Const _MYCOMPUTERTYPE = "Windows" +#Const _MYAPPLICATIONTYPE = "Console" + +#ElseIf _MYTYPE = "Windows" OrElse _MYTYPE = "" Then + +#Const _MYWEBSERVICES = True +#Const _MYUSERTYPE = "Windows" +#Const _MYCOMPUTERTYPE = "Windows" +#Const _MYAPPLICATIONTYPE = "Windows" + +#ElseIf _MYTYPE = "Console" Then + +#Const _MYWEBSERVICES = True +#Const _MYUSERTYPE = "Windows" +#Const _MYCOMPUTERTYPE = "Windows" +#Const _MYAPPLICATIONTYPE = "Console" + +#ElseIf _MYTYPE = "Web" Then + +#Const _MYFORMS = False +#Const _MYWEBSERVICES = False +#Const _MYUSERTYPE = "Web" +#Const _MYCOMPUTERTYPE = "Web" + +#ElseIf _MYTYPE = "WebControl" Then + +#Const _MYFORMS = False +#Const _MYWEBSERVICES = True +#Const _MYUSERTYPE = "Web" +#Const _MYCOMPUTERTYPE = "Web" + +#ElseIf _MYTYPE = "Custom" Then + +#ElseIf _MYTYPE <> "Empty" Then + +#Const _MYTYPE = "Empty" + +#End If + +#If _MYTYPE <> "Empty" Then + +Namespace My + +#If _MYAPPLICATIONTYPE = "WindowsForms" OrElse _MYAPPLICATIONTYPE = "Windows" OrElse _MYAPPLICATIONTYPE = "Console" Then + + _ + Partial Friend Class MyApplication + +#If _MYAPPLICATIONTYPE = "WindowsForms" Then + Inherits Global.Microsoft.VisualBasic.ApplicationServices.WindowsFormsApplicationBase +#If TARGET = "winexe" Then + _ + Friend Shared Sub Main(ByVal Args As String()) + Try + Global.System.Windows.Forms.Application.SetCompatibleTextRenderingDefault(MyApplication.UseCompatibleTextRendering()) + Finally + End Try + My.Application.Run(Args) + End Sub +#End If + +#ElseIf _MYAPPLICATIONTYPE = "Windows" Then + Inherits Global.Microsoft.VisualBasic.ApplicationServices.ApplicationBase +#ElseIf _MYAPPLICATIONTYPE = "Console" Then + Inherits Global.Microsoft.VisualBasic.ApplicationServices.ConsoleApplicationBase +#End If '_MYAPPLICATIONTYPE = "WindowsForms" + + End Class + +#End If '#If _MYAPPLICATIONTYPE = "WindowsForms" Or _MYAPPLICATIONTYPE = "Windows" or _MYAPPLICATIONTYPE = "Console" + +#If _MYCOMPUTERTYPE <> "" Then + + _ + Partial Friend Class MyComputer + +#If _MYCOMPUTERTYPE = "Windows" Then + Inherits Global.Microsoft.VisualBasic.Devices.Computer +#ElseIf _MYCOMPUTERTYPE = "Web" Then + Inherits Global.Microsoft.VisualBasic.Devices.ServerComputer +#End If + _ + _ + Public Sub New() + MyBase.New() + End Sub + End Class +#End If + + _ + _ + Friend Module MyProject + +#If _MYCOMPUTERTYPE <> "" Then + _ + Friend ReadOnly Property Computer() As MyComputer + _ + Get + Return m_ComputerObjectProvider.GetInstance() + End Get + End Property + + Private ReadOnly m_ComputerObjectProvider As New ThreadSafeObjectProvider(Of MyComputer) +#End If + +#If _MYAPPLICATIONTYPE = "Windows" Or _MYAPPLICATIONTYPE = "WindowsForms" Or _MYAPPLICATIONTYPE = "Console" Then + _ + Friend ReadOnly Property Application() As MyApplication + _ + Get + Return m_AppObjectProvider.GetInstance() + End Get + End Property + Private ReadOnly m_AppObjectProvider As New ThreadSafeObjectProvider(Of MyApplication) +#End If + +#If _MYUSERTYPE = "Windows" Then + _ + Friend ReadOnly Property User() As Global.Microsoft.VisualBasic.ApplicationServices.User + _ + Get + Return m_UserObjectProvider.GetInstance() + End Get + End Property + Private ReadOnly m_UserObjectProvider As New ThreadSafeObjectProvider(Of Global.Microsoft.VisualBasic.ApplicationServices.User) +#ElseIf _MYUSERTYPE = "Web" Then + _ + Friend ReadOnly Property User() As Global.Microsoft.VisualBasic.ApplicationServices.WebUser + _ + Get + Return m_UserObjectProvider.GetInstance() + End Get + End Property + Private ReadOnly m_UserObjectProvider As New ThreadSafeObjectProvider(Of Global.Microsoft.VisualBasic.ApplicationServices.WebUser) +#End If + +#If _MYFORMS = True Then + +#Const STARTUP_MY_FORM_FACTORY = "My.MyProject.Forms" + + _ + Friend ReadOnly Property Forms() As MyForms + _ + Get + Return m_MyFormsObjectProvider.GetInstance() + End Get + End Property + + _ + _ + Friend NotInheritable Class MyForms + _ + Private Shared Function Create__Instance__(Of T As {New, Global.System.Windows.Forms.Form})(ByVal Instance As T) As T + If Instance Is Nothing OrElse Instance.IsDisposed Then + If m_FormBeingCreated IsNot Nothing Then + If m_FormBeingCreated.ContainsKey(GetType(T)) = True Then + Throw New Global.System.InvalidOperationException(Global.Microsoft.VisualBasic.CompilerServices.Utils.GetResourceString("WinForms_RecursiveFormCreate")) + End If + Else + m_FormBeingCreated = New Global.System.Collections.Hashtable() + End If + m_FormBeingCreated.Add(GetType(T), Nothing) + Try + Return New T() + Catch ex As Global.System.Reflection.TargetInvocationException When ex.InnerException IsNot Nothing + Dim BetterMessage As String = Global.Microsoft.VisualBasic.CompilerServices.Utils.GetResourceString("WinForms_SeeInnerException", ex.InnerException.Message) + Throw New Global.System.InvalidOperationException(BetterMessage, ex.InnerException) + Finally + m_FormBeingCreated.Remove(GetType(T)) + End Try + Else + Return Instance + End If + End Function + + _ + Private Sub Dispose__Instance__(Of T As Global.System.Windows.Forms.Form)(ByRef instance As T) + instance.Dispose() + instance = Nothing + End Sub + + _ + _ + Public Sub New() + MyBase.New() + End Sub + + Private Shared m_FormBeingCreated As Global.System.Collections.Hashtable + + Public Overrides Function Equals(ByVal o As Object) As Boolean + Return MyBase.Equals(o) + End Function + Public Overrides Function GetHashCode() As Integer + Return MyBase.GetHashCode + End Function + _ + Friend Overloads Function [GetType]() As Global.System.Type + Return GetType(MyForms) + End Function + Public Overrides Function ToString() As String + Return MyBase.ToString + End Function + End Class + + Private m_MyFormsObjectProvider As New ThreadSafeObjectProvider(Of MyForms) + +#End If + +#If _MYWEBSERVICES = True Then + + _ + Friend ReadOnly Property WebServices() As MyWebServices + _ + Get + Return m_MyWebServicesObjectProvider.GetInstance() + End Get + End Property + + _ + _ + Friend NotInheritable Class MyWebServices + + _ + Public Overrides Function Equals(ByVal o As Object) As Boolean + Return MyBase.Equals(o) + End Function + _ + Public Overrides Function GetHashCode() As Integer + Return MyBase.GetHashCode + End Function + _ + Friend Overloads Function [GetType]() As Global.System.Type + Return GetType(MyWebServices) + End Function + _ + Public Overrides Function ToString() As String + Return MyBase.ToString + End Function + + _ + Private Shared Function Create__Instance__(Of T As {New})(ByVal instance As T) As T + If instance Is Nothing Then + Return New T() + Else + Return instance + End If + End Function + + _ + Private Sub Dispose__Instance__(Of T)(ByRef instance As T) + instance = Nothing + End Sub + + _ + _ + Public Sub New() + MyBase.New() + End Sub + End Class + + Private ReadOnly m_MyWebServicesObjectProvider As New ThreadSafeObjectProvider(Of MyWebServices) +#End If + +#If _MYTYPE = "Web" Then + + _ + Friend ReadOnly Property Request() As Global.System.Web.HttpRequest + _ + Get + Dim CurrentContext As Global.System.Web.HttpContext = Global.System.Web.HttpContext.Current + If CurrentContext IsNot Nothing Then + Return CurrentContext.Request + End If + Return Nothing + End Get + End Property + + _ + Friend ReadOnly Property Response() As Global.System.Web.HttpResponse + _ + Get + Dim CurrentContext As Global.System.Web.HttpContext = Global.System.Web.HttpContext.Current + If CurrentContext IsNot Nothing Then + Return CurrentContext.Response + End If + Return Nothing + End Get + End Property + + _ + Friend ReadOnly Property Log() As Global.Microsoft.VisualBasic.Logging.AspLog + _ + Get + Return m_LogObjectProvider.GetInstance() + End Get + End Property + + Private ReadOnly m_LogObjectProvider As New ThreadSafeObjectProvider(Of Global.Microsoft.VisualBasic.Logging.AspLog) + +#End If '_MYTYPE="Web" + + _ + _ + Friend NotInheritable Class ThreadSafeObjectProvider(Of T As New) + Friend ReadOnly Property GetInstance() As T +#If TARGET = "library" Then + _ + Get + Dim Value As T = m_Context.Value + If Value Is Nothing Then + Value = New T + m_Context.Value() = Value + End If + Return Value + End Get +#Else + _ + Get + If m_ThreadStaticValue Is Nothing Then m_ThreadStaticValue = New T + Return m_ThreadStaticValue + End Get +#End If + End Property + + _ + _ + Public Sub New() + MyBase.New() + End Sub + +#If TARGET = "library" Then + Private ReadOnly m_Context As New Global.Microsoft.VisualBasic.MyServices.Internal.ContextValue(Of T) +#Else + Private Shared m_ThreadStaticValue As T +#End If + End Class + End Module +End Namespace +#End If diff --git a/Microsoft.VisualBasic/runtime/msvbalib/MyServices/RegistryProxy.vb b/Microsoft.VisualBasic/runtime/msvbalib/MyServices/RegistryProxy.vb new file mode 100644 index 000000000..063fb409d --- /dev/null +++ b/Microsoft.VisualBasic/runtime/msvbalib/MyServices/RegistryProxy.vb @@ -0,0 +1,94 @@ +' Copyright (c) Microsoft Corporation. All rights reserved. + +Imports Microsoft.Win32 +Imports System.ComponentModel +Imports System.Security.Permissions + +Namespace Microsoft.VisualBasic.MyServices + + '''************************************************************************* + ''' ;RegistryProxy + ''' + ''' An extremely thin wrapper around Microsoft.Win32.Registry to expose the type through My. + ''' More details: http://ddwww/spectool/Documents/Whidbey/VB/RAD%20Framework/My%20Proxy.doc + ''' + _ + _ + Public Class RegistryProxy + + + '= PUBLIC ============================================================= + + Public ReadOnly Property CurrentUser() As RegistryKey + Get + Return Registry.CurrentUser + End Get + End Property + + Public ReadOnly Property LocalMachine() As RegistryKey + Get + Return Registry.LocalMachine + End Get + End Property + + Public ReadOnly Property ClassesRoot() As RegistryKey + Get + Return Registry.ClassesRoot + End Get + End Property + + Public ReadOnly Property Users() As RegistryKey + Get + Return Registry.Users + End Get + End Property + + Public ReadOnly Property PerformanceData() As RegistryKey + Get + Return Registry.PerformanceData + End Get + End Property + + Public ReadOnly Property CurrentConfig() As RegistryKey + Get + Return Registry.CurrentConfig + End Get + End Property + + _ + Public ReadOnly Property DynData() As RegistryKey + Get + Return Nothing + End Get + End Property + + Public Function GetValue(ByVal keyName As String, ByVal valueName As String, _ + ByVal defaultValue As Object) As Object + + Return Registry.GetValue(keyName, valueName, defaultValue) + End Function + + Public Sub SetValue(ByVal keyName As String, ByVal valueName As String, ByVal value As Object) + Registry.SetValue(keyName, valueName, value) + End Sub + + Public Sub SetValue(ByVal keyName As String, ByVal valueName As String, ByVal value As Object, _ + ByVal valueKind As Microsoft.Win32.RegistryValueKind) + + Registry.SetValue(keyName, valueName, value, valueKind) + End Sub + + '= FRIEND ============================================================= + + '''************************************************************************* + ''' ;New + ''' + ''' Proxy class can only created by internal classes. + ''' + Friend Sub New() + End Sub + + + End Class +End Namespace + diff --git a/Microsoft.VisualBasic/runtime/msvbalib/MyServices/SpecialDirectoriesProxy.vb b/Microsoft.VisualBasic/runtime/msvbalib/MyServices/SpecialDirectoriesProxy.vb new file mode 100644 index 000000000..30079a637 --- /dev/null +++ b/Microsoft.VisualBasic/runtime/msvbalib/MyServices/SpecialDirectoriesProxy.vb @@ -0,0 +1,89 @@ +' Copyright (c) Microsoft Corporation. All rights reserved. + +Imports System.ComponentModel +Imports System.Security.Permissions +Imports microsoft.VisualBasic.FileIO + +Namespace Microsoft.VisualBasic.MyServices + + '''************************************************************************* + ''' ;SpecialDirectoriesProxy + ''' + ''' An extremely thin wrapper around Microsoft.VisualBasic.FileIO.SpecialDirectories to expose the type through My. + ''' More details: http://ddwww/spectool/Documents/Whidbey/VB/RAD%20Framework/My%20Proxy.doc + ''' + ''' + _ + _ + Public Class SpecialDirectoriesProxy + + + '= PUBLIC ============================================================= + + Public ReadOnly Property MyDocuments() As String + Get + Return SpecialDirectories.MyDocuments + End Get + End Property + + Public ReadOnly Property MyMusic() As String + Get + Return SpecialDirectories.MyMusic + End Get + End Property + + Public ReadOnly Property MyPictures() As String + Get + Return SpecialDirectories.MyPictures + End Get + End Property + + Public ReadOnly Property Desktop() As String + Get + Return SpecialDirectories.Desktop + End Get + End Property + + Public ReadOnly Property Programs() As String + Get + Return SpecialDirectories.Programs + End Get + End Property + + Public ReadOnly Property ProgramFiles() As String + Get + Return SpecialDirectories.ProgramFiles + End Get + End Property + + Public ReadOnly Property Temp() As String + Get + Return SpecialDirectories.Temp + End Get + End Property + + Public ReadOnly Property CurrentUserApplicationData() As String + Get + Return SpecialDirectories.CurrentUserApplicationData + End Get + End Property + + Public ReadOnly Property AllUsersApplicationData() As String + Get + Return SpecialDirectories.AllUsersApplicationData + End Get + End Property + + '= FRIEND ============================================================= + + '''************************************************************************* + ''' ;New + ''' + ''' Proxy class can only created by internal classes. + ''' + Friend Sub New() + End Sub + + End Class + +End Namespace diff --git a/Microsoft.VisualBasic/runtime/msvbalib/Resources/My/Net/ProgressDialog.resx b/Microsoft.VisualBasic/runtime/msvbalib/Resources/My/Net/ProgressDialog.resx new file mode 100644 index 000000000..b921d332a --- /dev/null +++ b/Microsoft.VisualBasic/runtime/msvbalib/Resources/My/Net/ProgressDialog.resx @@ -0,0 +1,219 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + + Top, Left, Right + + + + True + + + + 10, 23 + + + 350, 39 + + + 0 + + + Progress Dialog + + + LabelInfo + + + System.Windows.Forms.Label, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + $this + + + 2 + + + Top, Left, Right + + + 10, 78 + + + 268, 12 + + + 1 + + + ProgressBarWork + + + System.Windows.Forms.ProgressBar, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + $this + + + 1 + + + Top, Right + + + True + + + 286, 78 + + + 2 + + + &Cancel + + + ButtonCloseDialog + + + System.Windows.Forms.Button, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + $this + + + 0 + + + True + + + 5, 13 + + + 373, 109 + + + ProgressDialog + + + ProgressDialog + + + System.Windows.Forms.Form, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/Microsoft.VisualBasic/runtime/msvbalib/Strings.vb b/Microsoft.VisualBasic/runtime/msvbalib/Strings.vb new file mode 100644 index 000000000..64def5411 --- /dev/null +++ b/Microsoft.VisualBasic/runtime/msvbalib/Strings.vb @@ -0,0 +1,2358 @@ +' Copyright (c) Microsoft Corporation. All rights reserved. + +Imports System +Imports System.Security +Imports System.Security.Permissions +Imports System.Text +Imports System.Globalization +Imports System.Runtime.InteropServices +Imports System.Runtime.Versioning + +Imports Microsoft.VisualBasic.CompilerServices +Imports Microsoft.VisualBasic.CompilerServices.ExceptionUtils +Imports Microsoft.VisualBasic.CompilerServices.Utils + +Namespace Microsoft.VisualBasic + +#If Not LATEBINDING Then + Friend NotInheritable Class FormatInfoHolder + Implements IFormatProvider + + Friend Sub New(ByVal nfi As NumberFormatInfo) + MyBase.New() + Me.nfi = nfi + End Sub + + Private nfi As NumberFormatInfo + + Private Function GetFormat(ByVal service As Type) As Object Implements IFormatProvider.GetFormat + If service Is GetType(NumberFormatInfo) Then + Return nfi + End If + Throw New ArgumentException(GetResourceString(ResID.InternalError)) + End Function + + End Class +#End If + Public Module Strings +#If Not LATEBINDING Then + 'Positive format strings + '0 $n + '1 n$ + '2 $ n + '3 n $ + Private ReadOnly CurrencyPositiveFormatStrings() As String = {"'$'n", "n'$'", "'$' n", "n '$'"} 'Note, we wrap the $ in the literal symbol to avoid misinterpretation in case sombody is using the escape character \ as a currency mark (see VSWHIDBEY 497942 where this happens on a Japanese system) + + 'The negative currency pattern needs to be selected based + ' on the criteria provided for parens + 'nfi.CurrencyPositivePattern + 'Negative format strings + '0 ($n) + '1 -$n + '2 $-n + '3 $n- + '4 (n$) + '5 -n$ + '6 n-$ + '7 n$- + '8 -n $ + '9 -$ n + '10 n $- + '11 $ n- + '12 $- n + '13 n- $ + '14 ($ n) + '15 (n $) + Private ReadOnly CurrencyNegativeFormatStrings() As String = _ + {"('$'n)", "-'$'n", "'$'-n", "'$'n-", "(n'$')", "-n'$'", "n-'$'", "n'$'-", _ + "-n '$'", "-'$' n", "n '$'-", "'$' n-", "'$'- n", "n- '$'", "('$' n)", "(n '$')"} 'Note, we wrap the $ in the literal symbol to avoid misinterpretation in case sombody is using the escape character \ as a currency mark (see VSWHIDBEY 497942 where this happens on a Japanese system) + + 'Value Associated Pattern + '0 (n) + '1 -n + '2 - n + '3 n- + '4 n - + Private ReadOnly NumberNegativeFormatStrings() As String = _ + {"(n)", "-n", "- n", "n-", "n -"} + + Private Enum NamedFormats + UNKNOWN + GENERAL_NUMBER + LONG_TIME + MEDIUM_TIME + SHORT_TIME + GENERAL_DATE + LONG_DATE + MEDIUM_DATE + SHORT_DATE + FIXED + STANDARD + PERCENT + SCIENTIFIC + CURRENCY + TRUE_FALSE + YES_NO + ON_OFF + End Enum + + Friend Enum FormatType + Number = 0 + Percent = 1 + [Currency] = 2 + End Enum + + Private Const CODEPAGE_SIMPLIFIED_CHINESE As Integer = 936 + Private Const CODEPAGE_TRADITIONAL_CHINESE As Integer = 950 + Private Const STANDARD_COMPARE_FLAGS As CompareOptions = _ + CompareOptions.IgnoreCase Or CompareOptions.IgnoreWidth Or CompareOptions.IgnoreKanaType + Private Const InvariantCultureID As Integer = &H7F + + Private Const NAMEDFORMAT_FIXED As String = "fixed" + Private Const NAMEDFORMAT_YES_NO As String = "yes/no" + Private Const NAMEDFORMAT_ON_OFF As String = "on/off" + Private Const NAMEDFORMAT_PERCENT As String = "percent" + Private Const NAMEDFORMAT_STANDARD As String = "standard" + Private Const NAMEDFORMAT_CURRENCY As String = "currency" + Private Const NAMEDFORMAT_LONG_TIME As String = "long time" + Private Const NAMEDFORMAT_LONG_DATE As String = "long date" + Private Const NAMEDFORMAT_SCIENTIFIC As String = "scientific" + Private Const NAMEDFORMAT_TRUE_FALSE As String = "true/false" + Private Const NAMEDFORMAT_SHORT_TIME As String = "short time" + Private Const NAMEDFORMAT_SHORT_DATE As String = "short date" + Private Const NAMEDFORMAT_MEDIUM_DATE As String = "medium date" + Private Const NAMEDFORMAT_MEDIUM_TIME As String = "medium time" + Private Const NAMEDFORMAT_GENERAL_DATE As String = "general date" + Private Const NAMEDFORMAT_GENERAL_NUMBER As String = "general number" + + Friend ReadOnly m_InvariantCompareInfo As CompareInfo = CultureInfo.InvariantCulture.CompareInfo + + 'This is shared across Cached + Private m_SyncObject As Object = New Object + Private m_LastUsedYesNoCulture As CultureInfo + Private m_CachedYesNoFormatStyle As String + + Private ReadOnly Property CachedYesNoFormatStyle() As String + Get + Dim ci As CultureInfo = GetCultureInfo() + SyncLock m_SyncObject + If Not m_LastUsedYesNoCulture Is ci Then + m_LastUsedYesNoCulture = ci + m_CachedYesNoFormatStyle = GetResourceString(ResID.YesNoFormatStyle) + End If + Return m_CachedYesNoFormatStyle + End SyncLock + End Get + End Property + + Private m_LastUsedOnOffCulture As CultureInfo + Private m_CachedOnOffFormatStyle As String + Private ReadOnly Property CachedOnOffFormatStyle() As String + Get + Dim ci As CultureInfo = GetCultureInfo() + SyncLock m_SyncObject + If Not m_LastUsedOnOffCulture Is ci Then + m_LastUsedOnOffCulture = ci + m_CachedOnOffFormatStyle = GetResourceString(ResID.OnOffFormatStyle) + End If + Return m_CachedOnOffFormatStyle + End SyncLock + End Get + End Property + + Private m_LastUsedTrueFalseCulture As CultureInfo + Private m_CachedTrueFalseFormatStyle As String + Private ReadOnly Property CachedTrueFalseFormatStyle() As String + Get + Dim ci As CultureInfo = GetCultureInfo() + SyncLock m_SyncObject + If Not m_LastUsedTrueFalseCulture Is ci Then + m_LastUsedTrueFalseCulture = ci + m_CachedTrueFalseFormatStyle = GetResourceString(ResID.TrueFalseFormatStyle) + End If + Return m_CachedTrueFalseFormatStyle + End SyncLock + End Get + End Property + +#If Not TELESTO Then 'TELESTO doesn't have the concept of single vs double byte encoding. + Private Function PRIMARYLANGID(ByVal lcid As Integer) As Integer + Return (lcid And &H3FF) + End Function + + '============================================================================ + ' Character manipulation functions. + '============================================================================ + Public Function Asc(ByVal [String] As Char) As Integer + + 'The IConvertible.ToInt32 implementation on Char + ' just calls Convert.ToInt32() + Dim CharValue As Integer = Convert.ToInt32([String]) + + '*** BEGIN PERFOPT *** + If CharValue < 128 Then + Return CharValue + End If + '*** END PERFOPT *** + + Try + Dim enc As Encoding + Dim b() As Byte + Dim c() As Char + Dim iByteCount As Integer + + enc = GetFileIOEncoding() + + c = New Char() {[String]} + + If enc.IsSingleByte Then + 'SBCS + b = New Byte(0) {} + iByteCount = enc.GetBytes(c, 0, 1, b, 0) + Return b(0) + End If + + 'DBCS char + b = New Byte(1) {} + iByteCount = enc.GetBytes(c, 0, 1, b, 0) + If iByteCount = 1 Then + Return b(0) + End If + If BitConverter.IsLittleEndian Then + 'Swap the bytes since storage is big-endian + Dim byt As Byte + byt = b(0) + b(0) = b(1) + b(1) = byt + End If + Return BitConverter.ToInt16(b, 0) + + Catch ex As Exception + Throw ex + + End Try + + End Function + + Public Function Asc(ByVal [String] As String) As Integer + + If ([String] Is Nothing) OrElse ([String].Length = 0) Then + Throw New ArgumentException(GetResourceString(ResID.Argument_LengthGTZero1, "String")) + End If + + Dim ch As Char = [String].Chars(0) + Return Asc(ch) + + End Function +#End If +#End If + Public Function AscW(ByVal [String] As String) As Integer + If ([String] Is Nothing) OrElse ([String].Length = 0) Then + Throw New ArgumentException(GetResourceString(ResID.Argument_LengthGTZero1, "String")) + End If + + Return AscW([String].Chars(0)) + End Function + + Public Function AscW(ByVal [String] As Char) As Integer + Return AscW([String]) 'yes it's strange. deal with it. the compiler optimizes away the call to AscW, so this isn't recursive. + End Function + +#If Not TELESTO Then 'TELESTO doesn't have the concept of single vs double byte encoding. + Public Function Chr(ByVal CharCode As Integer) As Char + + If CharCode < -32768 OrElse CharCode > 65535 Then + Throw New ArgumentException(GetResourceString(ResID.Argument_RangeTwoBytes1, "CharCode")) + End If + + '*** BEGIN PERFOPT + If CharCode >= 0 AndAlso CharCode <= 127 Then + Return Convert.ToChar(CharCode) + End If + '*** END PERFOPT + + Try + Dim enc As Encoding + + enc = Encoding.GetEncoding(GetLocaleCodePage()) + + If enc.IsSingleByte Then + If CharCode < 0 OrElse CharCode > 255 Then + 'CONSIDER: Make a more descriptive message + Throw VbMakeException(vbErrors.IllegalFuncCall) + End If + End If + + Dim dec As Decoder + Dim CharCount As Integer + Dim c(1) As Char 'Use 2 char array, but only return first Char if two returned + Dim b(1) As Byte + + dec = enc.GetDecoder() + If CharCode >= 0 AndAlso CharCode <= 255 Then + b(0) = CByte(CharCode And &HFFS) + CharCount = dec.GetChars(b, 0, 1, c, 0) + + Else + 'Bytes must be swapped in memory to HI/LO + b(0) = CByte((CharCode And &HFF00I) >> 8) + b(1) = CByte(CharCode And &HFFI) + CharCount = dec.GetChars(b, 0, 2, c, 0) + + End If + + 'VB6 ignored the lobyte if it hibyte was not a valid lead character + 'CharCount will be zero if the hibyte was not a lead character + + Return c(0) + + Catch ex As Exception + Throw ex + End Try + End Function +#End If + + Public Function ChrW(ByVal CharCode As Integer) As Char + If CharCode < -32768 OrElse CharCode > 65535 Then + Throw New ArgumentException(GetResourceString(ResID.Argument_RangeTwoBytes1, "CharCode")) + End If + Return Convert.ToChar(CharCode And &HFFFFI) + End Function +#If Not LATEBINDING Then + '============================================================================ + ' String manipulation functions. + '============================================================================ + Public Function Filter(ByVal Source() As Object, ByVal Match As String, Optional ByVal Include As Boolean = True, Optional ByVal [Compare] As CompareMethod = CompareMethod.Binary) As String() + + Dim Size As Integer = UBound(Source) + Dim StringSource(Size) As String + + Try + For i As Integer = 0 To Size + StringSource(i) = CStr(Source(i)) + Next i + Catch ex As StackOverflowException + Throw ex + Catch ex As OutOfMemoryException + Throw ex + Catch ex As System.Threading.ThreadAbortException + Throw ex + Catch + Throw New ArgumentException(GetResourceString(ResID.Argument_InvalidValueType2, "Source", "String")) + End Try + + Return Filter(StringSource, Match, Include, [Compare]) + End Function + + Public Function Filter(ByVal Source() As String, ByVal Match As String, Optional ByVal Include As Boolean = True, Optional ByVal [Compare] As CompareMethod = CompareMethod.Binary) As String() + Try + Dim TmpResult() As String + Dim lNumElements As Integer + Dim lSourceIndex As Integer + Dim lResultIndex As Integer + Dim sStringElement As String + Dim iFlags As CompareOptions + Dim CompInfo As CompareInfo + Dim Loc As CultureInfo + + 'Do error checking + If Source.Rank <> 1 Then + Throw New ArgumentException(GetResourceString(ResID.Argument_RankEQOne1)) + End If + + If Match Is Nothing OrElse Match.Length = 0 Then + Return Nothing + End If + + lNumElements = Source.Length + + 'up the globilization info + Loc = GetCultureInfo() + CompInfo = Loc.CompareInfo + + If [Compare] = CompareMethod.Text Then + iFlags = CompareOptions.IgnoreCase + End If + + 'Compare each element and build the result array + ReDim TmpResult(lNumElements - 1) + + For lSourceIndex = 0 To lNumElements - 1 + sStringElement = Source(lSourceIndex) + + If (sStringElement Is Nothing) Then + 'Skip + ElseIf (CompInfo.IndexOf(sStringElement, Match, iFlags) >= 0) = Include Then + TmpResult(lResultIndex) = sStringElement + lResultIndex = lResultIndex + 1 + End If + Next lSourceIndex + + If lResultIndex = 0 Then + ReDim TmpResult(-1) + Return TmpResult + End If + + If lResultIndex = TmpResult.Length Then + 'No redim required + Return TmpResult + End If + + ReDim Preserve TmpResult(lResultIndex - 1) + Return TmpResult + Catch ex As Exception + Throw ex + End Try + End Function + + Public Function InStr(ByVal String1 As String, ByVal String2 As String, Optional ByVal [Compare] As CompareMethod = CompareMethod.Binary) As Integer + If Compare = CompareMethod.Binary Then + Return (InternalInStrBinary(0, String1, String2) + 1) + Else + Return (InternalInStrText(0, String1, String2) + 1) + End If + End Function + + Public Function InStr(ByVal Start As Integer, ByVal String1 As String, ByVal String2 As String, Optional ByVal [Compare] As CompareMethod = CompareMethod.Binary) As Integer + If Start < 1 Then + Throw New ArgumentException(GetResourceString(ResID.Argument_GTZero1, "Start")) + End If + + If Compare = CompareMethod.Binary Then + Return (InternalInStrBinary(Start - 1, String1, String2) + 1) + Else + Return (InternalInStrText(Start - 1, String1, String2) + 1) + End If + End Function + + 'THIS FUNCTION IS ZERO BASED + Private Function InternalInStrBinary(ByVal StartPos As Integer, ByVal sSrc As String, ByVal sFind As String) As Integer + Dim SrcLength As Integer + + If sSrc IsNot Nothing Then + SrcLength = sSrc.Length + Else + SrcLength = 0 + End If + + If StartPos > SrcLength OrElse SrcLength = 0 Then + Return -1 + End If + + If (sFind Is Nothing) OrElse (sFind.Length = 0) Then + Return StartPos + End If + + Return m_InvariantCompareInfo.IndexOf(sSrc, sFind, StartPos, CompareOptions.Ordinal) + End Function + + Private Function InternalInStrText(ByVal lStartPos As Integer, ByVal sSrc As String, ByVal sFind As String) As Integer + Dim lSrcLen As Integer + + If Not sSrc Is Nothing Then + lSrcLen = sSrc.Length + Else + lSrcLen = 0 + End If + + If lStartPos > lSrcLen OrElse lSrcLen = 0 Then + Return -1 + End If + + If (sFind Is Nothing) OrElse (sFind.Length = 0) Then + Return lStartPos + End If + + Return GetCultureInfo().CompareInfo.IndexOf(sSrc, sFind, lStartPos, STANDARD_COMPARE_FLAGS) + End Function + + Public Function InStrRev(ByVal StringCheck As String, ByVal StringMatch As String, Optional ByVal Start As Integer = -1, Optional ByVal [Compare] As CompareMethod = CompareMethod.Binary) As Integer + Try + Dim lStrLen As Integer + + If Start = 0 OrElse Start < -1 Then + Throw New ArgumentException(GetResourceString(ResID.Argument_MinusOneOrGTZero1, "Start")) + End If + + If StringCheck Is Nothing Then + lStrLen = 0 + Else + lStrLen = StringCheck.Length + End If + + If Start = -1 Then + Start = lStrLen + End If + + If (Start > lStrLen) OrElse (lStrLen = 0) Then + Return 0 + End If + + If StringMatch Is Nothing Then + GoTo EmptyMatchString + End If + + If StringMatch.Length = 0 Then +EmptyMatchString: + Return Start + End If + + If [Compare] = CompareMethod.Binary Then + Return (m_InvariantCompareInfo.LastIndexOf(StringCheck, StringMatch, Start - 1, Start, CompareOptions.Ordinal) + 1) + Else + Return (GetCultureInfo().CompareInfo.LastIndexOf(StringCheck, StringMatch, Start - 1, Start, STANDARD_COMPARE_FLAGS) + 1) + End If + Catch ex As Exception + Throw ex + End Try + End Function + + Public Function Join(ByVal SourceArray() As Object, Optional ByVal Delimiter As String = " ") As String + Dim Size As Integer = UBound(SourceArray) + Dim StringSource(Size) As String + Dim i As Integer + + Try + For i = 0 To Size + StringSource(i) = CStr(SourceArray(i)) + Next i + Catch ex As StackOverflowException + Throw ex + Catch ex As OutOfMemoryException + Throw ex + Catch ex As System.Threading.ThreadAbortException + Throw ex + Catch + Throw New ArgumentException(GetResourceString(ResID.Argument_InvalidValueType2, "SourceArray", "String")) + End Try + + Return Join(StringSource, Delimiter) + End Function + + Public Function Join(ByVal SourceArray() As String, Optional ByVal Delimiter As String = " ") As String + Try + If IsArrayEmpty(SourceArray) Then + 'EmptyArray returns empty string + Return Nothing + End If + + If SourceArray.Rank <> 1 Then + Throw New ArgumentException(GetResourceString(ResID.Argument_RankEQOne1)) + End If + + Return System.String.Join(Delimiter, SourceArray) + Catch ex As Exception + Throw ex + End Try + End Function + + Public Function LCase(ByVal Value As String) As String + Try + If Value Is Nothing Then + Return Nothing + Else + Return Threading.Thread.CurrentThread.CurrentCulture.TextInfo.ToLower(Value) + End If + Catch ex As Exception + Throw ex + End Try + End Function + + Public Function LCase(ByVal Value As Char) As Char + Try + Return Threading.Thread.CurrentThread.CurrentCulture.TextInfo.ToLower(Value) + Catch ex As Exception + Throw ex + End Try + End Function + +#If Not TELESTO Then ' DevDiv Bugs 111924 - remove other Len functions in Silverlight. + Public Function Len(ByVal Expression As Boolean) As Integer + Return 2 + End Function + + _ + Public Function Len(ByVal Expression As SByte) As Integer + Return 1 + End Function + + Public Function Len(ByVal Expression As Byte) As Integer + Return 1 + End Function + + Public Function Len(ByVal Expression As Int16) As Integer + Return 2 + End Function + + _ + Public Function Len(ByVal Expression As UInt16) As Integer + Return 2 + End Function + + Public Function Len(ByVal Expression As Int32) As Integer + Return 4 + End Function + + _ + Public Function Len(ByVal Expression As UInt32) As Integer + Return 4 + End Function + + Public Function Len(ByVal Expression As Int64) As Integer + Return 8 + End Function + + _ + Public Function Len(ByVal Expression As UInt64) As Integer + Return 8 + End Function + + Public Function Len(ByVal Expression As Decimal) As Integer + 'This must return the length for VB6 Currency + Return 8 + End Function + + Public Function Len(ByVal Expression As Single) As Integer + Return 4 + End Function + + Public Function Len(ByVal Expression As Double) As Integer + Return 8 + End Function + + Public Function Len(ByVal Expression As DateTime) As Integer + Return 8 + End Function + + Public Function Len(ByVal Expression As Char) As Integer + Return 2 + End Function +#End If ' Not TELESTO + + Public Function Len(ByVal Expression As String) As Integer + If Expression Is Nothing Then + Return 0 + End If + + Return Expression.Length + End Function +#If NOT TELESTO + Public Function Len(ByVal Expression As Object) As Integer + If Expression Is Nothing Then + Return 0 + End If + + Dim ValueInterface As IConvertible = TryCast(Expression, IConvertible) + If Not ValueInterface Is Nothing Then + Select Case ValueInterface.GetTypeCode() + Case TypeCode.Boolean + Return 2 + Case TypeCode.SByte + Return 1 + Case TypeCode.Byte + Return 1 + Case TypeCode.Int16 + Return 2 + Case TypeCode.UInt16 + Return 2 + Case TypeCode.Int32 + Return 4 + Case TypeCode.UInt32 + Return 4 + Case TypeCode.Int64 + Return 8 + Case TypeCode.UInt64 + Return 8 + Case TypeCode.Decimal + Return 16 + Case TypeCode.Single + Return 4 + Case TypeCode.Double + Return 8 + Case TypeCode.DateTime + Return 8 + Case TypeCode.Char + Return 2 + Case TypeCode.String + Return Expression.ToString().Length + Case TypeCode.Object + 'Fallthrough to below + End Select + + Else + Dim CharArray As Char() = TryCast(Expression, Char()) + + If CharArray IsNot Nothing Then + 'REVIEW: Should this be char length or byte length? + Return CharArray.Length + End If + End If + + If TypeOf Expression Is ValueType Then + Call New ReflectionPermission(ReflectionPermissionFlag.MemberAccess).Assert() 'need this permission for System.Reflection.FieldInfo.GetValue() + Dim Length As Integer = StructUtils.GetRecordLength(Expression, 1) + System.Security.PermissionSet.RevertAssert() 'reverts all previous asserts for the current frame. No need to finally block this - CLR removes asserts if an exception is thrown. + Return Length + End If + + Throw VbMakeException(vbErrors.TypeMismatch) + End Function +#End If 'Not TELESTO + + Public Function Replace(ByVal Expression As String, ByVal Find As String, ByVal Replacement As String, Optional ByVal Start As Integer = 1, Optional ByVal Count As Integer = -1, Optional ByVal [Compare] As CompareMethod = CompareMethod.Binary) As String + Try + 'Validate Parameters + If Count < -1 Then + Throw New ArgumentException(GetResourceString(ResID.Argument_GEMinusOne1, "Count")) + End If + + If Start <= 0 Then + Throw New ArgumentException(GetResourceString("Argument_GTZero1", "Start")) + End If + + If (Expression Is Nothing) OrElse (Start > Expression.Length) Then + Return Nothing + End If + + If Start <> 1 Then + Expression = Expression.Substring(Start - 1) + End If + + If Find Is Nothing Then + GoTo EmptyFindString + End If + + If Find.Length = 0 OrElse Count = 0 Then +EmptyFindString: + Return Expression + End If + + If Count = -1 Then + Count = Expression.Length + End If + + Return ReplaceInternal(Expression, Find, Replacement, Count, [Compare]) + + Catch ex As Exception + Throw ex + End Try + End Function + + Private Function ReplaceInternal(ByVal Expression As String, ByVal Find As String, ByVal Replacement As String, ByVal Count As Integer, ByVal [Compare] As CompareMethod) As String + + System.Diagnostics.Debug.Assert(Expression <> "", "Expression is empty") + System.Diagnostics.Debug.Assert(Find <> "", "Find is empty") + System.Diagnostics.Debug.Assert(Count > 0, "Number of replacements is 0 or less") + System.Diagnostics.Debug.Assert([Compare] = CompareMethod.Text Or [Compare] = CompareMethod.Binary, "Unknown compare.") + + Dim ExpressionLength As Integer = Expression.Length + Dim FindLength As Integer = Find.Length + + Dim Start As Integer + Dim FindLocation As Integer + Dim Replacements As Integer + + Dim Comparer As CompareInfo + Dim CompareFlags As CompareOptions + + Dim Builder As StringBuilder = New StringBuilder(ExpressionLength) + + If [Compare] = CompareMethod.Text Then + Comparer = GetCultureInfo().CompareInfo + CompareFlags = STANDARD_COMPARE_FLAGS + Else + Comparer = m_InvariantCompareInfo + CompareFlags = CompareOptions.Ordinal + End If + + 'We build the new string (with the replacements) by walking through Expression, searching for the + 'Find, and appending sections of Expression or Replacement as we go. For example, if + 'Expression = "This is a test.", Find = "is" and Replacement = "YYY" then we would append + '"Th" to the new string, then "YYY" then " " then "YYY" and finally " a test." + While Start < ExpressionLength + If Replacements = Count Then + 'We've made all the replacements the caller wanted so append the remaining string + Builder.Append(Expression.Substring(Start)) + Exit While + End If + + FindLocation = Comparer.IndexOf(Expression, Find, Start, CompareFlags) + If FindLocation < 0 Then + 'We didn't find the Find string append the rest of the string + Builder.Append(Expression.Substring(Start)) + Exit While + Else + 'Append to our string builder everything up to the found string, then + 'append the replacement + Builder.Append(Expression.Substring(Start, FindLocation - Start)) + Builder.Append(Replacement) + Replacements += 1 + + 'Move the start of our search past the string we just replaced + Start = FindLocation + FindLength + End If + End While + + Return Builder.ToString() + + End Function + + Public Function Space(ByVal Number As Integer) As String + + If Number >= 0 Then + Return New String(ChrW(32), Number) + End If + + Throw New ArgumentException(GetResourceString(ResID.Argument_GEZero1, "Number")) + + End Function + + Public Function Split(ByVal Expression As String, Optional ByVal Delimiter As String = " ", Optional ByVal Limit As Integer = -1, Optional ByVal [Compare] As CompareMethod = CompareMethod.Binary) As String() + Try + 'Use String.Split + Dim aList() As String + Dim iDelLen As Integer + + If Expression Is Nothing Then + GoTo EmptyExpression + End If + + If Expression.Length = 0 Then +EmptyExpression: + ReDim aList(0) + aList(0) = "" + Return aList + End If + + If Limit = -1 Then + Limit = Expression.Length + 1 + End If + + If Delimiter Is Nothing Then + iDelLen = 0 + Else + iDelLen = Delimiter.Length + End If + + If iDelLen = 0 Then +EmptyDelimiterString: + ReDim aList(0) + aList(0) = Expression + Return aList + End If + + 'UNDONE: LIGATURE expansion!!!!! + Return SplitHelper(Expression, Delimiter, Limit, [Compare]) + Catch ex As Exception + Throw ex + End Try + End Function + + Private Function SplitHelper(ByVal sSrc As String, ByVal sFind As String, ByVal cMaxSubStrings As Integer, ByVal [Compare] As Integer) As String() + Dim cSubStrings As Integer + Dim iIndex As Integer + Dim iFindLen As Integer + Dim iSrcLen As Integer + Dim asSubstrings() As String + Dim sSubString As String + Dim iLastIndex As Integer + Dim cDelimPosMax As Integer + Dim cmpInfo As CompareInfo + Dim flags As CompareOptions + + If sFind Is Nothing Then + iFindLen = 0 + Else + iFindLen = sFind.Length + End If + + If sSrc Is Nothing Then + iSrcLen = 0 + Else + iSrcLen = sSrc.Length + End If + + If iFindLen = 0 Then + ReDim asSubstrings(0) + asSubstrings(0) = sSrc + Return asSubstrings + End If + + If iSrcLen = 0 Then + ReDim asSubstrings(0) + asSubstrings(0) = sSrc + Return asSubstrings + End If + + cDelimPosMax = 20 + + If cDelimPosMax > cMaxSubStrings Then + cDelimPosMax = cMaxSubStrings + End If + + ReDim asSubstrings(cDelimPosMax) + + If [Compare] = CompareMethod.Binary Then + flags = CompareOptions.Ordinal + cmpInfo = m_InvariantCompareInfo + Else + cmpInfo = GetCultureInfo().CompareInfo + flags = STANDARD_COMPARE_FLAGS + End If + + Do While (iLastIndex < iSrcLen) + iIndex = cmpInfo.IndexOf(sSrc, sFind, iLastIndex, iSrcLen - iLastIndex, flags) + + If (iIndex = -1) OrElse (cSubStrings + 1 = cMaxSubStrings) Then + 'Just put the remainder of the string in the next element + sSubString = sSrc.Substring(iLastIndex) + If sSubString Is Nothing Then + sSubString = "" + End If + asSubstrings(cSubStrings) = sSubString + Exit Do + Else + 'Put the characters between iLastIndex and iIndex into the next element + sSubString = sSrc.Substring(iLastIndex, iIndex - iLastIndex) + If sSubString Is Nothing Then + sSubString = "" + End If + asSubstrings(cSubStrings) = sSubString + iLastIndex = iIndex + iFindLen + End If + + cSubStrings += 1 + + If (cSubStrings > cDelimPosMax) Then + cDelimPosMax += 20 + If cDelimPosMax > cMaxSubStrings Then + cDelimPosMax = cMaxSubStrings + 1 + End If + ReDim Preserve asSubstrings(cDelimPosMax) + End If + + 'Must Initialize to empty string, otherwise it looks like an object + asSubstrings(cSubStrings) = "" + + If cSubStrings = cMaxSubStrings Then + sSubString = sSrc.Substring(iLastIndex) + If sSubString Is Nothing Then + sSubString = "" + End If + asSubstrings(cSubStrings) = sSubString + Exit Do + End If + Loop + +RedimAndExit: + If cSubStrings + 1 = asSubstrings.Length Then + Return asSubstrings + End If + + ReDim Preserve asSubstrings(cSubStrings) + Return asSubstrings + End Function + + '============================================================================ + ' Fixed-length string functions. + '============================================================================ + Public Function LSet(ByVal Source As String, ByVal Length As Integer) As String + If (Length = 0) Then + Return "" + ElseIf (Source Is Nothing) Then + Return New String(" "c, Length) + End If + + If Length > Source.Length Then + Return Source.PadRight(Length) + Else + Return Source.Substring(0, Length) + End If + End Function + + Public Function RSet(ByVal Source As String, ByVal Length As Integer) As String + If (Length = 0) Then + Return "" + ElseIf Source Is Nothing Then + Return New String(" "c, Length) + End If + + If Length > Source.Length Then + Return Source.PadLeft(Length) + Else + Return Source.Substring(0, Length) + End If + End Function + + Public Function StrDup(ByVal Number As Integer, ByVal Character As Object) As Object + Dim s As String + Dim SingleChar As Char + + If Number < 0 Then + Throw New ArgumentException(GetResourceString(ResID.Argument_InvalidValue1, "Number")) + End If + + If Character Is Nothing Then + Throw New ArgumentNullException(GetResourceString(ResID.Argument_InvalidNullValue1, "Character")) + End If + + s = TryCast(Character, String) + + If s IsNot Nothing Then + If s.Length = 0 Then + Throw New ArgumentException(GetResourceString(ResID.Argument_LengthGTZero1, "Character")) + End If + SingleChar = s.Chars(0) + Else + Try + SingleChar = CChar(Character) + Catch ex As StackOverflowException + Throw ex + Catch ex As OutOfMemoryException + Throw ex + Catch ex As System.Threading.ThreadAbortException + Throw ex + Catch + Throw New ArgumentException(GetResourceString(ResID.Argument_InvalidValue1, "Character")) + End Try + End If + + Return New String(SingleChar, Number) + End Function + + Public Function StrDup(ByVal Number As Integer, ByVal Character As Char) As String + If Number < 0 Then + Throw New ArgumentException(GetResourceString(ResID.Argument_GEZero1, "Number")) + End If + + Return New String(Character, Number) + End Function + + Public Function StrDup(ByVal Number As Integer, ByVal Character As String) As String + If Number < 0 Then + Throw New ArgumentException(GetResourceString(ResID.Argument_GEZero1, "Number")) + End If + + If Character Is Nothing OrElse Character.Length = 0 Then + Throw New ArgumentException(GetResourceString(ResID.Argument_LengthGTZero1, "Character")) + End If + + Return New String(Character.Chars(0), Number) + End Function + + Public Function StrReverse(ByVal Expression As String) As String + + If (Expression Is Nothing) Then + Return "" + End If + + Dim chars As Char() + Dim uc As UnicodeCategory + Dim ch As Char + Dim SrcIndex, Length As Integer + + Length = Expression.Length + If Length = 0 Then + Return "" + End If + + 'CONSIDER: Get System.String to add a surrogate aware Reverse method + + 'Detect if there are any graphemes that need special handling + For SrcIndex = 0 To Length - 1 + ch = Expression.Chars(SrcIndex) + uc = Char.GetUnicodeCategory(ch) + If uc = UnicodeCategory.Surrogate OrElse _ + uc = UnicodeCategory.NonSpacingMark OrElse _ + uc = UnicodeCategory.SpacingCombiningMark OrElse _ + uc = UnicodeCategory.EnclosingMark Then + 'Need to use special handling + Return InternalStrReverse(Expression, SrcIndex, Length) + End If + Next SrcIndex + + chars = Expression.ToCharArray() + System.Array.Reverse(chars) + Return New String(chars) + + End Function + + 'This routine handles reversing Strings containing graphemes + ' GRAPHEME: a text element that is displayed as a single character + ' + Private Function InternalStrReverse(ByVal Expression As String, ByVal SrcIndex As Integer, ByVal Length As Integer) As String + + Dim TextEnum As TextElementEnumerator + Dim DestIndex, LastSrcIndex, NextSrcIndex As Integer + Dim sb As StringBuilder + + 'This code can only be hit one time + sb = New StringBuilder(Length) + sb.Length = Length + + TextEnum = StringInfo.GetTextElementEnumerator(Expression, SrcIndex) + + 'Init enumerator position + If Not TextEnum.MoveNext() Then + Return "" + End If + + LastSrcIndex = 0 + DestIndex = Length - 1 + + 'Copy up the first surrogate found + Do While LastSrcIndex < SrcIndex + sb.Chars(DestIndex) = Expression.Chars(LastSrcIndex) + DestIndex -= 1 + LastSrcIndex += 1 + Loop + + 'Now iterate through the text elements and copy them to the reversed string + NextSrcIndex = TextEnum.ElementIndex + + Do While DestIndex >= 0 + SrcIndex = NextSrcIndex + + 'Move to next element + If (TextEnum.MoveNext()) Then + NextSrcIndex = TextEnum.ElementIndex + Else + 'Point NextSrcIndex to end of string + NextSrcIndex = Length + End If + LastSrcIndex = NextSrcIndex - 1 + + Do While LastSrcIndex >= SrcIndex + sb.Chars(DestIndex) = Expression.Chars(LastSrcIndex) + DestIndex -= 1 + LastSrcIndex -= 1 + Loop + Loop + + Return sb.ToString() + + End Function + + Public Function UCase(ByVal [Value] As String) As String + Try + If Value Is Nothing Then + Return "" + Else + Return Threading.Thread.CurrentThread.CurrentCulture.TextInfo.ToUpper(Value) + End If + Catch ex As Exception + Throw ex + End Try + End Function + + Public Function UCase(ByVal Value As Char) As Char + Try + Return Threading.Thread.CurrentThread.CurrentCulture.TextInfo.ToUpper(Value) + Catch ex As Exception + Throw ex + End Try + End Function + + '************************************************************* + '** PERF NOTE: + '** All Format calls must go through FormatNamed + '** But we don't want to put a bunch of overhead on the more + '** common cases that are not named formats + '** The expensive CompareInfo.Compare calls have been limited + '** just one call + '************************************************************** + + Private Function FormatNamed(ByVal Expression As Object, ByVal Style As String, ByRef ReturnValue As String) As Boolean + Dim StyleLength As Integer = Style.Length + + ReturnValue = Nothing + + Select Case StyleLength + + Case 5 + Select Case Style.Chars(0) + '(F)ixed + Case "f"c, "F"c + If String.Compare(Style, NAMEDFORMAT_FIXED, StringComparison.OrdinalIgnoreCase) = 0 Then + ReturnValue = CDbl(Expression).ToString("0.00", Nothing) + Return True + End If + End Select + + Case 6 + 'switch off 1st char (index 0) to reduce number of string compares + '(Y)es/no + '(O)n/off + Select Case Style.Chars(0) + Case "y"c, "Y"c + If String.Compare(Style, NAMEDFORMAT_YES_NO, StringComparison.OrdinalIgnoreCase) = 0 Then + ReturnValue = CInt(CBool(Expression)).ToString(CachedYesNoFormatStyle, Nothing) + Return True + End If + + Case "o"c, "O"c + If String.Compare(Style, NAMEDFORMAT_ON_OFF, StringComparison.OrdinalIgnoreCase) = 0 Then + ReturnValue = CInt(CBool(Expression)).ToString(CachedOnOffFormatStyle, Nothing) + Return True + End If + End Select + + Case 7 + 'switch off 1st char (index 0) to reduce number of string compares + '(P)ercent + Select Case Style.Chars(0) + Case "p"c, "P"c + If String.Compare(Style, NAMEDFORMAT_PERCENT, StringComparison.OrdinalIgnoreCase) = 0 Then + ReturnValue = CDbl(Expression).ToString("0.00%", Nothing) + Return True + End If + End Select + + Case 8 + 'switch off 6th char (index 5) to reduce number of string compares + '(S)tandard + '(C)urrency + + Select Case Style.Chars(0) + Case "s"c, "S"c + If String.Compare(Style, NAMEDFORMAT_STANDARD, StringComparison.OrdinalIgnoreCase) = 0 Then + ReturnValue = CDbl(Expression).ToString("N2", Nothing) + Return True + End If + Case "c"c, "C"c + If String.Compare(Style, NAMEDFORMAT_CURRENCY, StringComparison.OrdinalIgnoreCase) = 0 Then + ReturnValue = CDbl(Expression).ToString("C", Nothing) + Return True + End If + End Select + + Case 9 + 'switch off 6th char (index 5) to reduce number of string compares + 'Long (T)ime + 'Long (D)ate + + Select Case Style.Chars(5) + Case "t"c, "T"c + If String.Compare(Style, NAMEDFORMAT_LONG_TIME, StringComparison.OrdinalIgnoreCase) = 0 Then + ReturnValue = CDate(Expression).ToString("T", Nothing) + Return True + End If + + Case "d"c, "D"c + If String.Compare(Style, NAMEDFORMAT_LONG_DATE, StringComparison.OrdinalIgnoreCase) = 0 Then + ReturnValue = CDate(Expression).ToString("D", Nothing) + Return True + End If + End Select + + Case 10 + 'switch off 7th char (index 6) to reduce number of string compares + 'true/f(A)lse + 'short (T)ime + 'short (D)ate + 'scient(I)fic + + Select Case Style.Chars(6) + Case "a"c, "A"c + If String.Compare(Style, NAMEDFORMAT_TRUE_FALSE, StringComparison.OrdinalIgnoreCase) = 0 Then + ReturnValue = CInt(CBool(Expression)).ToString(CachedTrueFalseFormatStyle, Nothing) + Return True + End If + + Case "t"c, "T"c + If String.Compare(Style, NAMEDFORMAT_SHORT_TIME, StringComparison.OrdinalIgnoreCase) = 0 Then + ReturnValue = CDate(Expression).ToString("t", Nothing) + Return True + End If + + Case "d"c, "D"c + If String.Compare(Style, NAMEDFORMAT_SHORT_DATE, StringComparison.OrdinalIgnoreCase) = 0 Then + ReturnValue = CDate(Expression).ToString("d", Nothing) + Return True + End If + + Case "i"c, "I"c + If String.Compare(Style, NAMEDFORMAT_SCIENTIFIC, StringComparison.OrdinalIgnoreCase) = 0 Then + Dim dbl As Double + dbl = CDbl(Expression) + If System.Double.IsNaN(dbl) OrElse System.Double.IsInfinity(dbl) Then + ReturnValue = dbl.ToString("G", Nothing) + Else + ReturnValue = dbl.ToString("0.00E+00", Nothing) + End If + Return True + End If + + End Select + + Case 11 + 'switch off 8th char (index 7) to reduce number of string compares + 'medium (T)ime + 'medium (D)ate + + Select Case Style.Chars(7) + Case "t"c, "T"c + If String.Compare(Style, NAMEDFORMAT_MEDIUM_TIME, StringComparison.OrdinalIgnoreCase) = 0 Then + ReturnValue = CDate(Expression).ToString("T", Nothing) + Return True + End If + + Case "d"c, "D"c + If String.Compare(Style, NAMEDFORMAT_MEDIUM_DATE, StringComparison.OrdinalIgnoreCase) = 0 Then + ReturnValue = CDate(Expression).ToString("D", Nothing) + Return True + End If + End Select + + Case 12 + Select Case Style.Chars(0) + Case "g"c, "G"c + If String.Compare(Style, NAMEDFORMAT_GENERAL_DATE, StringComparison.OrdinalIgnoreCase) = 0 Then + ReturnValue = CDate(Expression).ToString("G", Nothing) + Return True + End If + End Select + + Case 14 + Select Case Style.Chars(0) + Case "g"c, "G"c + If String.Compare(Style, NAMEDFORMAT_GENERAL_NUMBER, StringComparison.OrdinalIgnoreCase) = 0 Then + ReturnValue = CDbl(Expression).ToString("G", Nothing) + Return True + End If + End Select + + End Select + + Return False + + End Function + + '============================================================================ + ' Format functions. + '============================================================================ + Public Function Format(ByVal Expression As Object, Optional ByVal Style As String = "") As String + Try + Dim cp As IFormatProvider = Nothing 'GetCultureInfo() + Dim tc As TypeCode + Dim iformat As IFormattable = Nothing + + If (Expression Is Nothing) OrElse (Expression.GetType() Is Nothing) Then + Return "" + End If + + If Style Is Nothing OrElse Style.Length = 0 Then + Return CStr(Expression) + End If + + Dim ConvertibleExpression As IConvertible = CType(Expression, IConvertible) + tc = ConvertibleExpression.GetTypeCode() + + If Style.Length > 0 Then + Try + Dim ReturnValue As String = Nothing + + If FormatNamed(Expression, Style, ReturnValue) Then + Return ReturnValue + End If + Catch ex As StackOverflowException + Throw ex + Catch ex As OutOfMemoryException + Throw ex + Catch ex As System.Threading.ThreadAbortException + Throw ex + Catch + 'Object could not be converted to required type + 'so just return the string + Return CStr(Expression) + End Try + End If + + iformat = TryCast(Expression, IFormattable) + + If iformat Is Nothing Then + tc = System.Convert.GetTypeCode(Expression) + If tc <> TypeCode.String AndAlso tc <> TypeCode.Boolean Then + Throw New ArgumentException(GetResourceString(ResID.Argument_InvalidValue1, "Expression")) + End If + End If + + Select Case tc + Case TypeCode.Boolean + Return System.String.Format(cp, Style, CStr(ConvertibleExpression.ToBoolean(Nothing))) + Case TypeCode.SByte, _ + TypeCode.Byte, _ + TypeCode.Int16, _ + TypeCode.UInt16, _ + TypeCode.Int32, _ + TypeCode.UInt32, _ + TypeCode.Int64, _ + TypeCode.UInt64, _ + TypeCode.Decimal, _ + TypeCode.DateTime, _ + TypeCode.Char, _ + TypeCode.Object + Return iformat.ToString(Style, cp) + Case TypeCode.DBNull + Return "" + Case TypeCode.Double + Dim dbl As Double + + dbl = ConvertibleExpression.ToDouble(Nothing) + + If Style Is Nothing OrElse Style.Length = 0 Then + Return CStr(dbl) + End If + + If dbl = 0 Then + 'Used to get rid of possible negative zero, + 'which will format as -0 + dbl = 0 + End If + Return dbl.ToString(Style, cp) + Case TypeCode.Empty + Return "" + Case TypeCode.Single + Dim sng As Single + + sng = ConvertibleExpression.ToSingle(Nothing) + + If Style Is Nothing OrElse Style.Length = 0 Then + Return CStr(sng) + End If + + If sng = 0 Then + 'Used to get rid of possible negative zero + sng = 0 + End If + + Return sng.ToString(Style, cp) + Case TypeCode.String + Return System.String.Format(cp, Style, Expression) + Case Else + Return iformat.ToString(Style, cp) + End Select + Catch ex As Exception + Throw ex + End Try + End Function + + Public Function FormatCurrency(ByVal Expression As Object, _ + Optional ByVal NumDigitsAfterDecimal As Integer = -1, _ + Optional ByVal IncludeLeadingDigit As TriState = TriState.UseDefault, _ + Optional ByVal UseParensForNegativeNumbers As TriState = TriState.UseDefault, _ + Optional ByVal GroupDigits As TriState = TriState.UseDefault) As String + + Dim ifmt As IFormattable + Dim typ As Type + Dim fp As IFormatProvider = Nothing + Dim dbl As Double + + Try + ValidateTriState(IncludeLeadingDigit) + ValidateTriState(UseParensForNegativeNumbers) + ValidateTriState(GroupDigits) + + If NumDigitsAfterDecimal > 99 Then 'Was 255 in VB6, but System.Globalization.NumberFormatInfo.CurrencyDecimalDigits limits this to 99. + Throw New ArgumentException(GetResourceString(ResID.Argument_Range0to99_1, "NumDigitsAfterDecimal")) + End If + + If Expression Is Nothing Then + Return "" + End If + + typ = Expression.GetType() + + If typ Is GetType(System.String) Then + Expression = CDbl(Expression) + ElseIf Not Symbols.IsNumericType(typ) Then + Throw New InvalidCastException(GetResourceString(ResID.InvalidCast_FromTo, VBFriendlyName(typ), "Currency")) + End If + + ifmt = CType(Expression, IFormattable) + + Dim FormatStyle As String + If IncludeLeadingDigit = TriState.False Then + dbl = CDbl(Expression) + If dbl >= 1 OrElse dbl <= -1 Then + 'HACKHACK - if leading digit doesn't matter, this avoids + ' going through the overhead of creating a format string + IncludeLeadingDigit = TriState.True + End If + End If + FormatStyle = GetCurrencyFormatString(IncludeLeadingDigit, NumDigitsAfterDecimal, UseParensForNegativeNumbers, GroupDigits, fp) + + Return ifmt.ToString(FormatStyle, fp) + + Catch ex As Exception + Throw ex + End Try + End Function + + Public Function FormatDateTime(ByVal Expression As DateTime, Optional ByVal NamedFormat As DateFormat = DateFormat.GeneralDate) As String + Dim sFormat As String + + Try + Select Case NamedFormat + Case DateFormat.LongDate + sFormat = "D" + Case DateFormat.ShortDate + sFormat = "d" + Case DateFormat.LongTime + sFormat = "T" + Case DateFormat.ShortTime + sFormat = "HH:mm" + Case DateFormat.GeneralDate + If Expression.TimeOfDay.Ticks = Expression.Ticks Then + 'Date is 1/1/0001 - don't print date part + 'Same as LongTime + sFormat = "T" + ElseIf Expression.TimeOfDay.Ticks = 0 Then + '12AM - don't print time part + 'Same as ShortDate + sFormat = "d" + Else + 'Short date + Long Time + sFormat = "G" + End If + Case Else + Throw VbMakeException(vbErrors.IllegalFuncCall) + End Select + + Return Expression.ToString(sFormat, Nothing) + Catch ex As Exception + Throw ex + End Try + End Function + + Public Function FormatNumber(ByVal Expression As Object, Optional ByVal NumDigitsAfterDecimal As Integer = -1, Optional ByVal IncludeLeadingDigit As TriState = TriState.UseDefault, Optional ByVal UseParensForNegativeNumbers As TriState = TriState.UseDefault, Optional ByVal GroupDigits As TriState = TriState.UseDefault) As String + Dim ifmt As IFormattable + Dim typ As Type + + Try + ValidateTriState(IncludeLeadingDigit) + ValidateTriState(UseParensForNegativeNumbers) + ValidateTriState(GroupDigits) + + If Expression Is Nothing Then + Return "" + End If + + typ = Expression.GetType() + + If typ Is GetType(System.String) Then + Expression = CDbl(Expression) + ElseIf typ Is GetType(System.Boolean) Then + If CBool(Expression) Then + Expression = -1.0 + Else + Expression = 0.0 + End If + ElseIf Not Symbols.IsNumericType(typ) Then + Throw New InvalidCastException(GetResourceString(ResID.InvalidCast_FromTo, VBFriendlyName(typ), "Currency")) + End If + + ifmt = CType(Expression, IFormattable) + + Return ifmt.ToString(GetNumberFormatString(NumDigitsAfterDecimal, IncludeLeadingDigit, _ + UseParensForNegativeNumbers, GroupDigits), Nothing) + Catch ex As Exception + Throw ex + End Try + End Function + + Friend Function GetFormatString(ByVal NumDigitsAfterDecimal As Integer, _ + ByVal IncludeLeadingDigit As TriState, ByVal UseParensForNegativeNumbers As TriState, _ + ByVal GroupDigits As TriState, ByVal FormatTypeValue As FormatType) As String + + Dim nfi As NumberFormatInfo + Dim sb As StringBuilder + Dim sGroup As String + Dim sLeadDigit As String + Dim sDigitsAfterDecimal As String + Dim ci As CultureInfo + + sb = New StringBuilder(30) + + ci = GetCultureInfo() + nfi = CType(ci.GetFormat(GetType(System.Globalization.NumberFormatInfo)), NumberFormatInfo) + + If NumDigitsAfterDecimal < -1 Then + Throw VbMakeException(vbErrors.IllegalFuncCall) + ElseIf NumDigitsAfterDecimal = -1 Then + If FormatTypeValue = FormatType.Percent Then + 'NOTE: We use NumberDecimalDigits, which is set in the + ' control panel for VB6 compatibility + ' The urt does not use this setting, but makes a default + ' of their own. + NumDigitsAfterDecimal = nfi.NumberDecimalDigits + ElseIf FormatTypeValue = FormatType.Number Then + NumDigitsAfterDecimal = nfi.NumberDecimalDigits + ElseIf FormatTypeValue = FormatType.Currency Then + NumDigitsAfterDecimal = nfi.CurrencyDecimalDigits + End If + End If + + If GroupDigits = TriState.UseDefault Then + GroupDigits = TriState.True + If FormatTypeValue = FormatType.Percent Then + If IsArrayEmpty(nfi.PercentGroupSizes) Then + GroupDigits = TriState.False + End If + ElseIf FormatTypeValue = FormatType.Number Then + If IsArrayEmpty(nfi.NumberGroupSizes) Then + GroupDigits = TriState.False + End If + ElseIf FormatTypeValue = FormatType.Currency Then + If IsArrayEmpty(nfi.CurrencyGroupSizes) Then + GroupDigits = TriState.False + End If + End If + End If + + If UseParensForNegativeNumbers = TriState.UseDefault Then + UseParensForNegativeNumbers = TriState.False + 'If FormatTypeValue = FormatType.Percent Then + ' If nfi.PercentNegativePattern = 0 Then + ' UseParensForNegativeNumbers = TriState.True + ' End If + 'Else + + If FormatTypeValue = FormatType.Number Then + If nfi.NumberNegativePattern = 0 Then + UseParensForNegativeNumbers = TriState.True + End If + ElseIf FormatTypeValue = FormatType.Currency Then + If nfi.CurrencyNegativePattern = 0 Then + UseParensForNegativeNumbers = TriState.True + End If + End If + End If + + If GroupDigits = TriState.True Then + sGroup = "#,##" + Else + sGroup = "" + End If + + If IncludeLeadingDigit <> TriState.False Then + sLeadDigit = "0" + Else + sLeadDigit = "#" + End If + + If NumDigitsAfterDecimal > 0 Then + sDigitsAfterDecimal = "." & (New System.String("0"c, NumDigitsAfterDecimal)) + Else + sDigitsAfterDecimal = "" + End If + + 'Now put together the string + If FormatTypeValue = FormatType.Currency Then + sb.Append(nfi.CurrencySymbol) + End If + + sb.Append(sGroup) + sb.Append(sLeadDigit) + sb.Append(sDigitsAfterDecimal) + + If FormatTypeValue = FormatType.Percent Then + sb.Append(nfi.PercentSymbol) + End If + + If UseParensForNegativeNumbers = TriState.True Then + Dim sTmp As String + sTmp = sb.ToString() + sb.Append(";(") + sb.Append(sTmp) + sb.Append(")") + End If + + Return sb.ToString() + End Function + + Friend Function GetCurrencyFormatString( _ + ByVal IncludeLeadingDigit As TriState, _ + ByVal NumDigitsAfterDecimal As Integer, _ + ByVal UseParensForNegativeNumbers As TriState, _ + ByVal GroupDigits As TriState, _ + ByRef formatProvider As IFormatProvider) As String + + Dim nfi As NumberFormatInfo + Dim ci As CultureInfo + Dim CurrencyNegativePattern, CurrencyPositivePattern As Integer + Dim FormatString, NumberFormat As String + + GetCurrencyFormatString = "C" + + ci = GetCultureInfo() + nfi = CType(ci.GetFormat(GetType(System.Globalization.NumberFormatInfo)), NumberFormatInfo) + nfi = CType(nfi.Clone(), NumberFormatInfo) + + If GroupDigits = TriState.False Then + nfi.CurrencyGroupSizes = New Int32() {0} + End If + + CurrencyPositivePattern = nfi.CurrencyPositivePattern + CurrencyNegativePattern = nfi.CurrencyNegativePattern + + If UseParensForNegativeNumbers = TriState.UseDefault Then + + Select Case CurrencyNegativePattern + Case 0, 4, 14, 15 + UseParensForNegativeNumbers = TriState.True + Case Else + UseParensForNegativeNumbers = TriState.False + End Select + + ElseIf UseParensForNegativeNumbers = TriState.False Then + + Select Case CurrencyNegativePattern + Case 0 + CurrencyNegativePattern = 1 + Case 4 + CurrencyNegativePattern = 5 + Case 14 + CurrencyNegativePattern = 9 + Case 15 + CurrencyNegativePattern = 10 + End Select + + Else + + UseParensForNegativeNumbers = TriState.True + + Select Case CurrencyNegativePattern + Case 1, 2, 3 'leading $ w/o space + CurrencyNegativePattern = 0 + Case 5, 6, 7 'trailing $ w/o space + CurrencyNegativePattern = 4 + Case 8, 10, 13 'Trailing $ / leading with space + CurrencyNegativePattern = 15 + Case 9, 11, 12 + CurrencyNegativePattern = 14 + End Select + + End If + + nfi.CurrencyNegativePattern = CurrencyNegativePattern + + If NumDigitsAfterDecimal = -1 Then + NumDigitsAfterDecimal = nfi.CurrencyDecimalDigits + End If + nfi.CurrencyDecimalDigits = NumDigitsAfterDecimal + + formatProvider = New FormatInfoHolder(nfi) + + If IncludeLeadingDigit = TriState.False Then + 'We need to build our own string in this case, since the NDP does not + ' make this accessible + + nfi.NumberGroupSizes = nfi.CurrencyGroupSizes + + FormatString = CurrencyPositiveFormatStrings(CurrencyPositivePattern) & ";" & _ + CurrencyNegativeFormatStrings(CurrencyNegativePattern) + + If GroupDigits = TriState.False Then + If IncludeLeadingDigit = TriState.False Then + NumberFormat = "#" + Else + NumberFormat = "0" + End If + Else + If IncludeLeadingDigit = TriState.False Then + NumberFormat = "#,###" + Else + NumberFormat = "#,##0" + End If + End If + + If NumDigitsAfterDecimal > 0 Then + NumberFormat = NumberFormat & "." & New String("0"c, NumDigitsAfterDecimal) + End If + + If System.String.CompareOrdinal("$", nfi.CurrencySymbol) <> 0 Then + 'Replace the '$' sign with the locale specific symbol + 'Note, the currency symbol in the FormatString is surrounded by the literal symbol ', e.g. '$' + 'We do this to guard against the case where the currency symbol is the literal symbol \ This was causing problems on Japanese + 'systems because that meant our format string ended up as "\#,###.00" when we wanted "'\'#,###.00" But because the currency symbol + 'we are replacing with could concievably be the literal symbol ' as well, we need to make sure we don't end up with a horked string like "'''#,###.00" + 'So if the currency symbol is a ' we replace it with '' so that our format string will be balanced like "''''#,###.00" which will result in the format + 'succeeding. You won't see the ' as the currency symbol in this case but this was never supported anyway. + FormatString = FormatString.Replace("$", nfi.CurrencySymbol.Replace("'", "''")) + End If + + Return FormatString.Replace("n", NumberFormat) + End If + + End Function + + Friend Function GetNumberFormatString( _ + ByVal NumDigitsAfterDecimal As Integer, _ + ByVal IncludeLeadingDigit As TriState, _ + ByVal UseParensForNegativeNumbers As TriState, _ + ByVal GroupDigits As TriState) As String + + Dim nfi As NumberFormatInfo + Dim ci As CultureInfo + Dim NumberNegativePattern As Integer + Dim FormatString, NumberFormat As String + + ci = GetCultureInfo() + nfi = CType(ci.GetFormat(GetType(System.Globalization.NumberFormatInfo)), NumberFormatInfo) + + If NumDigitsAfterDecimal = -1 Then + NumDigitsAfterDecimal = nfi.NumberDecimalDigits + ElseIf (NumDigitsAfterDecimal > 99) OrElse (NumDigitsAfterDecimal < -1) Then + Throw New ArgumentException(GetResourceString(ResID.Argument_Range0to99_1, "NumDigitsAfterDecimal")) + End If + + If GroupDigits = TriState.UseDefault Then + If nfi.NumberGroupSizes Is Nothing OrElse nfi.NumberGroupSizes.Length = 0 Then + GroupDigits = TriState.False + Else + GroupDigits = TriState.True + End If + End If + + NumberNegativePattern = nfi.NumberNegativePattern + + 'Value Associated Pattern + '0 (n) + '1 - n + '2 - n + '3 n - + '4 n - + If UseParensForNegativeNumbers = TriState.UseDefault Then + Select Case NumberNegativePattern + Case 0 + UseParensForNegativeNumbers = TriState.True + Case Else + UseParensForNegativeNumbers = TriState.False + End Select + ElseIf UseParensForNegativeNumbers = TriState.False Then + If NumberNegativePattern = 0 Then + NumberNegativePattern = 1 + End If + Else + UseParensForNegativeNumbers = TriState.True + + Select Case NumberNegativePattern + Case 1, 2, 3, 4 + NumberNegativePattern = 0 + End Select + End If + + If UseParensForNegativeNumbers = TriState.UseDefault Then + UseParensForNegativeNumbers = TriState.True + End If + + FormatString = "n;" & NumberNegativeFormatStrings(NumberNegativePattern) + If System.String.CompareOrdinal("-", nfi.NegativeSign) <> 0 Then + 'Replace the "-" sign with the actual locale-specific symbol (escaped with quotes). + ' Note: there appears to be no performance benefit in using a StringBuilder over simple concats. + FormatString = FormatString.Replace("-", """" & nfi.NegativeSign & """") + End If + + If IncludeLeadingDigit <> TriState.False Then + NumberFormat = "0" + Else + NumberFormat = "#" + End If + + If GroupDigits = TriState.False OrElse nfi.NumberGroupSizes.Length = 0 Then + 'Just use setting done above '#' or '0' + Else + If nfi.NumberGroupSizes.Length = 1 Then + NumberFormat = "#," & New String("#"c, nfi.NumberGroupSizes(0)) & NumberFormat + Else + Dim i As Integer + + NumberFormat = New String("#"c, nfi.NumberGroupSizes(0) - 1) & NumberFormat + For i = 1 To nfi.NumberGroupSizes.GetUpperBound(0) + NumberFormat = "," & New String("#"c, nfi.NumberGroupSizes(i)) & "," & NumberFormat + Next i + End If + End If + + If NumDigitsAfterDecimal > 0 Then + NumberFormat = NumberFormat & "." & New String("0"c, NumDigitsAfterDecimal) + End If + + Return Replace(FormatString, "n", NumberFormat) + End Function + + Public Function FormatPercent(ByVal Expression As Object, _ + Optional ByVal NumDigitsAfterDecimal As Integer = -1, _ + Optional ByVal IncludeLeadingDigit As TriState = TriState.UseDefault, _ + Optional ByVal UseParensForNegativeNumbers As TriState = TriState.UseDefault, _ + Optional ByVal GroupDigits As TriState = TriState.UseDefault) As String + + Dim ifmt As IFormattable + Dim typ As Type + Dim sFormat As String + + ValidateTriState(IncludeLeadingDigit) + ValidateTriState(UseParensForNegativeNumbers) + ValidateTriState(GroupDigits) + + If Expression Is Nothing Then + Return "" + End If + + typ = Expression.GetType() + + If typ Is GetType(System.String) Then + Expression = CDbl(Expression) + ElseIf Not Symbols.IsNumericType(typ) Then + Throw New InvalidCastException(GetResourceString(ResID.InvalidCast_FromTo, VBFriendlyName(typ), "numeric")) + End If + + ifmt = CType(Expression, IFormattable) + sFormat = GetFormatString(NumDigitsAfterDecimal, IncludeLeadingDigit, UseParensForNegativeNumbers, _ + GroupDigits, FormatType.Percent) + Return ifmt.ToString(sFormat, Nothing) + End Function + + '============================================================================ + ' GetChar function (new for VB7) + '============================================================================ + Public Function GetChar(ByVal [str] As String, ByVal Index As Integer) As Char + If [str] Is Nothing Then + Throw New ArgumentException(GetResourceString(ResID.Argument_LengthGTZero1, "String")) + ElseIf (Index < 1) Then + Throw New ArgumentException(GetResourceString(ResID.Argument_GEOne1, "Index")) + ElseIf (Index > [str].Length) Then + Throw New ArgumentException(GetResourceString(ResID.Argument_IndexLELength2, "Index", "String")) + Else + Return [str].Chars(Index - 1) + End If + End Function +#End If + + '============================================================================ + ' Left/Right/Mid/Trim functions. + '============================================================================ + Public Function Left(ByVal [str] As String, ByVal Length As Integer) As String + '------------------------------------------------------------- + ' lLen < 0 throws InvalidArgument exception + ' lLen > Len([str]) let lLen = Len([str]) + ' returned computed string + '------------------------------------------------------------- + If Length < 0 Then + Throw New ArgumentException(GetResourceString(ResID.Argument_GEZero1, "Length")) + ElseIf Length = 0 OrElse [str] Is Nothing Then + Return "" + Else + If Length >= [str].Length Then + Return [str] + Else + Return [str].Substring(0, Length) + End If + End If + End Function +#If Not LATEBINDING Then + Public Function LTrim(ByVal str As String) As String + If str Is Nothing OrElse str.Length = 0 Then + Return "" + Else + Dim ch As Char + ch = str.Chars(0) + + If ch = chSpace OrElse ch = chIntlSpace Then + Return str.TrimStart(m_achIntlSpace) + End If + Return str + End If + End Function + + Public Function Mid(ByVal [str] As String, ByVal Start As Integer) As String + Try + If [str] Is Nothing Then + Return Nothing + Else + Return Mid([str], Start, [str].Length) + End If + Catch ex As Exception + Throw ex + End Try + End Function + + Public Function Mid(ByVal [str] As String, ByVal Start As Integer, ByVal Length As Integer) As String + '------------------------------------------------------------- + ' Notes: + ' VB6 order of execution + ' verify Start > 0 ==> vbIllegalFuncCall + ' verify lLen > 0 ==> vbIllegalFuncCall + ' return computed string + '------------------------------------------------------------- + If Start <= 0 Then + Throw New ArgumentException(GetResourceString(ResID.Argument_GTZero1, "Start")) + ElseIf Length < 0 Then + Throw New ArgumentException(GetResourceString(ResID.Argument_GEZero1, "Length")) + ElseIf Length = 0 OrElse [str] Is Nothing Then + Return "" + End If + + Dim lStrLen As Integer + + lStrLen = [str].Length + + If Start > lStrLen Then + Return "" + ElseIf (Start + Length) > lStrLen Then + Return [str].Substring(Start - 1) + Else + Return [str].Substring(Start - 1, Length) + End If + End Function + + Public Function Right(ByVal [str] As String, ByVal Length As Integer) As String + If Length < 0 Then + Throw New ArgumentException(GetResourceString(ResID.Argument_GEZero1, "Length")) + End If + + If Length = 0 OrElse [str] Is Nothing Then + Return "" + End If + + Dim lStrLen As Integer + + lStrLen = [str].Length + + If Length >= lStrLen Then + Return [str] + End If + + Return [str].Substring(lStrLen - Length, Length) + End Function + + Public Function RTrim(ByVal [str] As String) As String + Try + If [str] Is Nothing OrElse str.Length = 0 Then + Return "" + End If + + Dim ch As Char + ch = str.Chars(str.Length - 1) + + If ch = chSpace OrElse ch = chIntlSpace Then + Return [str].TrimEnd(m_achIntlSpace) + End If + Return str + Catch ex As Exception + Throw ex + End Try + End Function + + Public Function Trim(ByVal str As String) As String + Try + If str Is Nothing OrElse str.Length = 0 Then + Return "" + End If + + Dim ch As Char = str.Chars(0) + If ch = chSpace OrElse ch = chIntlSpace Then + Return str.Trim(m_achIntlSpace) + Else + ch = str.Chars(str.Length - 1) + If ch = chSpace OrElse ch = chIntlSpace Then + Return str.Trim(m_achIntlSpace) + End If + End If + Return str + + Catch ex As Exception + Throw ex + End Try + End Function + + '============================================================================ + ' String comparison/conversion functions. + '============================================================================ + Public Function StrComp(ByVal String1 As String, ByVal String2 As String, Optional ByVal [Compare] As CompareMethod = CompareMethod.Binary) As Integer + Try + If ([Compare] = CompareMethod.Binary) Then + Return Operators.CompareString(String1, String2, False) + ElseIf ([Compare] = CompareMethod.Text) Then + Return Operators.CompareString(String1, String2, True) + Else + Throw New ArgumentException(GetResourceString(ResID.Argument_InvalidValue1, "Compare")) + End If + Catch ex As Exception + Throw ex + End Try + End Function + +#If Not TELESTO Then + Friend Function IsValidCodePage(ByVal codepage As Integer) As Boolean + IsValidCodePage = False + + Try + If Encoding.GetEncoding(codepage) IsNot Nothing Then + IsValidCodePage = True + End If + Catch ex As StackOverflowException + Throw ex + Catch ex As OutOfMemoryException + Throw ex + Catch ex As System.Threading.ThreadAbortException + Throw ex + Catch + End Try + End Function + + Public Function StrConv(ByVal [str] As String, ByVal Conversion As VbStrConv, Optional ByVal LocaleID As Integer = 0) As String + Try + Const LANG_CHINESE As Integer = &H4I + Const LANG_JAPANESE As Integer = &H11I + Const LANG_KOREAN As Integer = &H12I + Dim dwMapFlags As Integer + Dim loc As CultureInfo + Dim langid As Integer + + If (LocaleID = 0 OrElse LocaleID = 1) Then + loc = GetCultureInfo() + LocaleID = loc.LCID() + Else + Try + loc = New CultureInfo(LocaleID And &HFFFFI) + Catch ex As StackOverflowException + Throw ex + Catch ex As OutOfMemoryException + Throw ex + Catch ex As System.Threading.ThreadAbortException + Throw ex + Catch + Throw New ArgumentException(GetResourceString(ResID.Argument_LCIDNotSupported1, CStr(LocaleID))) + End Try + End If + + langid = PRIMARYLANGID(LocaleID) + + 'Ensure only valid bits for Conversion are passed in. + If (Conversion And Not (VbStrConv.UpperCase Or VbStrConv.LowerCase Or VbStrConv.Wide Or VbStrConv.Narrow _ + Or VbStrConv.Katakana Or VbStrConv.Hiragana Or VbStrConv.SimplifiedChinese Or VbStrConv.TraditionalChinese _ + Or VbStrConv.LinguisticCasing)) <> 0 Then + Throw New ArgumentException(GetResourceString(ResID.Argument_InvalidVbStrConv)) + End If + + '*** VbStrConv.SimplifiedChinese/VbStrConv.TraditionalChinese handling + Select Case (Conversion And (VbStrConv.SimplifiedChinese + VbStrConv.TraditionalChinese)) + + Case 0 + 'Flags not used + Case (VbStrConv.SimplifiedChinese + VbStrConv.TraditionalChinese) + Throw New ArgumentException(GetResourceString(ResID.Argument_StrConvSCandTC)) + Case VbStrConv.SimplifiedChinese + 'UNDONE: Verify locale is supported + If IsValidCodePage(CODEPAGE_SIMPLIFIED_CHINESE) AndAlso IsValidCodePage(CODEPAGE_TRADITIONAL_CHINESE) Then + dwMapFlags = dwMapFlags Or NativeTypes.LCMAP_SIMPLIFIED_CHINESE + Else + Throw New ArgumentException(GetResourceString(ResID.Argument_SCNotSupported)) + End If + Case VbStrConv.TraditionalChinese + If IsValidCodePage(CODEPAGE_SIMPLIFIED_CHINESE) AndAlso IsValidCodePage(CODEPAGE_TRADITIONAL_CHINESE) Then + dwMapFlags = dwMapFlags Or NativeTypes.LCMAP_TRADITIONAL_CHINESE + Else + Throw New ArgumentException(GetResourceString(ResID.Argument_TCNotSupported)) + End If + End Select + + '*** Upper/Lowercase handling + Select Case (Conversion And (VbStrConv.UpperCase Or VbStrConv.LowerCase)) + Case VbStrConv.None + 'No conversion + If (Conversion And VbStrConv.LinguisticCasing) <> 0 Then + Throw New ArgumentException(GetResourceString(ResID.LinguisticRequirements)) + End If + + Case (VbStrConv.UpperCase Or VbStrConv.LowerCase) ' VbStrConv.ProperCase is special: see below + 'Proper casing gets done below + dwMapFlags = 0 + Case VbStrConv.UpperCase + If Conversion = VbStrConv.UpperCase Then + Return loc.TextInfo.ToUpper(str) + Else + dwMapFlags = dwMapFlags Or NativeTypes.LCMAP_UPPERCASE + End If + Case VbStrConv.LowerCase + If Conversion = VbStrConv.LowerCase Then + Return loc.TextInfo.ToLower(str) + Else + dwMapFlags = dwMapFlags Or NativeTypes.LCMAP_LOWERCASE + End If + End Select + + If ((Conversion And (VbStrConv.Katakana + VbStrConv.Hiragana)) <> 0) Then + If (langid <> LANG_JAPANESE) OrElse (Not ValidLCID(LocaleID)) Then + Throw New ArgumentException(GetResourceString(ResID.Argument_JPNNotSupported)) + Else + 'Locale is ok + End If + End If + + If (Conversion And (VbStrConv.Wide Or VbStrConv.Narrow)) <> 0 Then + If (langid = LANG_JAPANESE) OrElse _ + (langid = LANG_KOREAN) OrElse _ + (langid = LANG_CHINESE) Then + If Not ValidLCID(LocaleID) Then + Throw New ArgumentException(GetResourceString(ResID.Argument_LocalNotSupported)) + End If + Else + Throw New ArgumentException(GetResourceString(ResID.Argument_WideNarrowNotApplicable)) + End If + End If + + '*** Width handling + Select Case (Conversion And (VbStrConv.Wide Or VbStrConv.Narrow)) + Case VbStrConv.None + Case VbStrConv.Wide Or VbStrConv.Narrow ' VbStrConv.Wide+VbStrConv.Narrow is reserved + Throw New ArgumentException(GetResourceString(ResID.Argument_IllegalWideNarrow)) + Case VbStrConv.Wide ' VbStrConv.Wide + dwMapFlags = dwMapFlags Or NativeTypes.LCMAP_FULLWIDTH + Case VbStrConv.Narrow ' VbStrConv.Narrow + dwMapFlags = dwMapFlags Or NativeTypes.LCMAP_HALFWIDTH + End Select + + '*** Kana handling + Select Case (Conversion And (VbStrConv.Katakana Or VbStrConv.Hiragana)) + Case VbStrConv.None + Case (VbStrConv.Katakana Or VbStrConv.Hiragana) ' VbStrConv.Katakana+VbStrConv.Hiragana is reserved + Throw New ArgumentException(GetResourceString(ResID.Argument_IllegalKataHira)) + Case VbStrConv.Katakana ' VbStrConv.Katakana + dwMapFlags = dwMapFlags Or NativeTypes.LCMAP_KATAKANA + Case VbStrConv.Hiragana ' VbStrConv.Hiragana + dwMapFlags = dwMapFlags Or NativeTypes.LCMAP_HIRAGANA + End Select + + ' accents field (Conversion And 192) in Conversion is reserved + If ((Conversion And VbStrConv.ProperCase) = VbStrConv.ProperCase) Then + Return ProperCaseString(loc, dwMapFlags, [str]) + ElseIf dwMapFlags <> 0 Then + Return vbLCMapString(loc, dwMapFlags, [str]) + Else + Return [str] + End If + Catch ex As Exception + Throw ex + End Try + End Function + + Friend Function ValidLCID(ByVal LocaleID As Integer) As Boolean + 'UNDONE: Is there a less expensive way of determining if the LCID is supported? + Try + Dim loc As CultureInfo = New CultureInfo(LocaleID) + ValidLCID = True + Catch ex As StackOverflowException + Throw ex + Catch ex As OutOfMemoryException + Throw ex + Catch ex As System.Threading.ThreadAbortException + Throw ex + Catch + ValidLCID = False + End Try + End Function + + Private Function ProperCaseString(ByVal loc As CultureInfo, ByVal dwMapFlags As Integer, ByVal sSrc As String) As String + Dim iSrcLen As Integer + Dim sb As StringBuilder + + If sSrc Is Nothing Then + iSrcLen = 0 + Else + iSrcLen = sSrc.Length + End If + + If iSrcLen = 0 Then + Return "" + End If + + ' do the mapping specified by dwMapFlags, and at the same time, lowercase + ' the whole string + sb = New StringBuilder(vbLCMapString(loc, dwMapFlags Or NativeTypes.LCMAP_LOWERCASE, sSrc)) + + 'ToTitleCase is a more linguistically correct casing for the current locale + Return loc.TextInfo.ToTitleCase(sb.ToString()) + + End Function +#End If 'Not TELESTO + +#If Not TELESTO Then + 'REVIEW : Are there any issues with converting dbcs characters on Win9x + _ + _ + _ + Friend Function vbLCMapString(ByVal loc As CultureInfo, ByVal dwMapFlags As Integer, ByVal sSrc As String) As String + Dim length As Integer + + If sSrc Is Nothing Then + length = 0 + Else + length = sSrc.Length + End If + + If length = 0 Then + Return "" + End If + + Dim sDest As String + Dim lenDest As Integer + Dim lcid As Integer = loc.LCID + Dim enc As Text.Encoding = Text.Encoding.GetEncoding(loc.TextInfo.ANSICodePage) + + If Not enc.IsSingleByte Then + + 'VB6 syntax note: ByVal String in Declare statements is really a ByRef String syntax + 'So sTemp will always be updated here on copyback + Dim sTemp As String = sSrc + Dim bytesSrc, bytesDest As Byte() + + 'Forced to use ANSI here + 'Char count can actual increase or decrease + + 'Get byte array + bytesSrc = enc.GetBytes(sTemp) + + 'Get required byte length for new destination + lenDest = UnsafeNativeMethods.LCMapStringA(lcid, dwMapFlags, bytesSrc, bytesSrc.Length, Nothing, 0) + + 'Create destination byte array of required length + bytesDest = New Byte(lenDest - 1) {} + + 'Call again to do the actual translation + lenDest = UnsafeNativeMethods.LCMapStringA(lcid, dwMapFlags, bytesSrc, bytesSrc.Length, bytesDest, lenDest) + + 'Now convert back to a string + sDest = enc.GetString(bytesDest) + + Return sDest + + Else + 'We do not use StringBuilder here because embedded NULLs cause an early termination of the string + sDest = New String(" "c, length) + lenDest = UnsafeNativeMethods.LCMapString(lcid, dwMapFlags, sSrc, length, sDest, length) + Return sDest + End If + + End Function +#End If + + Private Sub ValidateTriState(ByVal Param As TriState) + If (Param <> vbTrue) AndAlso (Param <> vbFalse) AndAlso (Param <> vbUseDefault) Then + Throw VbMakeException(vbErrors.IllegalFuncCall) + End If + End Sub + + Private Function IsArrayEmpty(ByVal array As System.Array) As Boolean + If array Is Nothing Then + Return True + End If + Return (array.Length = 0) + End Function +#End If + End Module + +End Namespace + diff --git a/README.md b/README.md index b4c446132..bde36a281 100644 --- a/README.md +++ b/README.md @@ -1,14 +1,19 @@ # .NET Reference Source -The referencesource repository contains sources from [Microsoft .NET Reference Source](http://referencesource.microsoft.com/) +If you landed here via redirect from https://referencesource.microsoft.com/ and this repo with .NET Framework reference sources does not meet your need please [file an issue](https://github.com/microsoft/referencesource/issues/new/choose). + +The referencesource repository contains sources from [Microsoft .NET Reference Source](https://referencesource.microsoft.com/) that represent a subset of the .NET Framework. This subset contains similar functionality to the class libraries that are being -developed in [.NET Core](https://github.com/dotnet/corefx). We intend to consult the referencesource repository as we develop +developed in [.NET Core](https://github.com/dotnet/runtime). We intend to consult the referencesource repository as we develop .NET Core. It is also for the community to leverage to enable more scenarios for .NET developers. -**Please note that the referencesource repository is read-only**. [See this blog post](http://blogs.msdn.com/b/dotnet/archive/2014/11/12/net-core-is-open-source.aspx) for the rationale. +**Please note that the referencesource repository is read-only**. [See this blog post](https://devblogs.microsoft.com/dotnet/net-core-is-open-source/) for the rationale. + +This repository does not accept feature requests or bug reports. To submit those, you need to go elsewhere: -Questions, bugs, and pull requests should be done through [.NET Core](https://github.com/dotnet/corefx). +* [.NET Framework](https://developercommunity.visualstudio.com/dotnet) +* [.NET Core](https://github.com/dotnet/core) ## License -The files in this repository are licensed with the [MIT](LICENSE.txt) license unless otherwise specified in the file header. +The files in this repository are licensed under the [MIT](LICENSE.txt) license unless otherwise specified in the file header. If the file header only contains a copyright header (e.g., "Copyright (c) Microsoft Corporation. All rights reserved.", you can assume the associated file to be MIT-licensed. diff --git a/SECURITY.md b/SECURITY.md new file mode 100644 index 000000000..869fdfe2b --- /dev/null +++ b/SECURITY.md @@ -0,0 +1,41 @@ + + +## Security + +Microsoft takes the security of our software products and services seriously, which includes all source code repositories managed through our GitHub organizations, which include [Microsoft](https://github.com/Microsoft), [Azure](https://github.com/Azure), [DotNet](https://github.com/dotnet), [AspNet](https://github.com/aspnet), [Xamarin](https://github.com/xamarin), and [our GitHub organizations](https://opensource.microsoft.com/). + +If you believe you have found a security vulnerability in any Microsoft-owned repository that meets [Microsoft's definition of a security vulnerability](https://aka.ms/opensource/security/definition), please report it to us as described below. + +## Reporting Security Issues + +**Please do not report security vulnerabilities through public GitHub issues.** + +Instead, please report them to the Microsoft Security Response Center (MSRC) at [https://msrc.microsoft.com/create-report](https://aka.ms/opensource/security/create-report). + +If you prefer to submit without logging in, send email to [secure@microsoft.com](mailto:secure@microsoft.com). If possible, encrypt your message with our PGP key; please download it from the [Microsoft Security Response Center PGP Key page](https://aka.ms/opensource/security/pgpkey). + +You should receive a response within 24 hours. If for some reason you do not, please follow up via email to ensure we received your original message. Additional information can be found at [microsoft.com/msrc](https://aka.ms/opensource/security/msrc). + +Please include the requested information listed below (as much as you can provide) to help us better understand the nature and scope of the possible issue: + + * Type of issue (e.g. buffer overflow, SQL injection, cross-site scripting, etc.) + * Full paths of source file(s) related to the manifestation of the issue + * The location of the affected source code (tag/branch/commit or direct URL) + * Any special configuration required to reproduce the issue + * Step-by-step instructions to reproduce the issue + * Proof-of-concept or exploit code (if possible) + * Impact of the issue, including how an attacker might exploit the issue + +This information will help us triage your report more quickly. + +If you are reporting for a bug bounty, more complete reports can contribute to a higher bounty award. Please visit our [Microsoft Bug Bounty Program](https://aka.ms/opensource/security/bounty) page for more details about our active programs. + +## Preferred Languages + +We prefer all communications to be in English. + +## Policy + +Microsoft follows the principle of [Coordinated Vulnerability Disclosure](https://aka.ms/opensource/security/cvd). + + diff --git a/SMDiagnostics/System/ServiceModel/Diagnostics/LegacyDiagnosticTrace.cs b/SMDiagnostics/System/ServiceModel/Diagnostics/LegacyDiagnosticTrace.cs index 86ad22c03..2e8be3d60 100644 --- a/SMDiagnostics/System/ServiceModel/Diagnostics/LegacyDiagnosticTrace.cs +++ b/SMDiagnostics/System/ServiceModel/Diagnostics/LegacyDiagnosticTrace.cs @@ -156,9 +156,8 @@ internal void TraceEvent(TraceEventType type, int code, string msdnTraceCode, st static internal string GenerateMsdnTraceCode(string traceSource, string traceCodeString) { return string.Format(CultureInfo.InvariantCulture, - "http://msdn.microsoft.com/{0}/library/{1}.{2}.aspx", - CultureInfo.CurrentCulture.Name, - traceSource, traceCodeString); + "https://docs.microsoft.com/dotnet/framework/wcf/diagnostics/tracing/{0}-{1}", + traceSource.Replace('.', '-'), traceCodeString); } #pragma warning disable 56500 diff --git a/SMDiagnostics/System/ServiceModel/Diagnostics/TraceXPathNavigator.cs b/SMDiagnostics/System/ServiceModel/Diagnostics/TraceXPathNavigator.cs index 9ebdcfe05..f377f8107 100644 --- a/SMDiagnostics/System/ServiceModel/Diagnostics/TraceXPathNavigator.cs +++ b/SMDiagnostics/System/ServiceModel/Diagnostics/TraceXPathNavigator.cs @@ -493,7 +493,7 @@ string LookupPrefix(string ns, ElementNode node) } } - if (string.IsNullOrEmpty(retval) && node.parent != null) + if (retval == null && node.parent != null) { retval = LookupPrefix(ns, node.parent); } diff --git a/System.Activities.Core.Presentation/SR.resx b/System.Activities.Core.Presentation/SR.resx new file mode 100644 index 000000000..c82555bd4 --- /dev/null +++ b/System.Activities.Core.Presentation/SR.resx @@ -0,0 +1,389 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Cannot construct line segment between point {0} and point {1}. + + + Cannot use a query correlation initializer when a message is XmlSerializable. + + + Add parameter + + + The collection already contains the key '{0}'. Please choose a different key. + + + Edit Parameters + + + <Enter an expression> + + + Create Link(s) + + + Delete Link(s) + + + Delete Flowchart node + + + False branch already exists. + + + Connection can only be from True or False branch. + + + The True and False branches have already been defined. + + + False + + + True + + + Cannot connect to the connection point. + + + Move Flowchart Link + + + Cannot create more than one outgoing connection for this activity. + + + True branch already exists. + + + FlowSwitch case rename + + + FlowSwitch DefaultCaseDisplayName change + + + FlowSwitch cases must be unique. + + + <Input case here> + + + The entered key is invalid + + + A switch case with the key '{0}' already exists. Choose a different key. + + + Change type collection + + + Request in ReceiveReply '{0}' does not belong to the workflow. + + + Request in SendReply '{0}' does not belong to the workflow. + + + Case{0} + + + {0} activity is copied to the clipboard. Please paste it in your workflow as appropriate. + + + Resize Flowchart + + + Add new case + + + Case key value cannot be converted from/to type string. + + + Default case already exists. + + + Equals() and/or GetHashCode() on type '{0}' were not overridden correctly. + + + Define... + + + View message... + + + View parameter... + + + Change Case Value + + + Expected argument named '{0}' was not found. + + + + The directionality of activity delegate argument '{0}' in DelegateArguments dictionary is '{1}'. The expected directionality is '{2}'. + + + + The argument provided for the delegate input named '{0}' had an invalid type. The delegate input is of type '{1}' and the argument is of type '{2}'. The argument's type must be coercible to the delegate input's type. + + + + The argument provided for the delegate output named '{0}' had an invalid type. The delegate output is of type '{1}' and the argument is of type '{2}'. The delegate output's type must be coercible to the argument's type. + + + + '{0}' cannot be resolved. + + + '{0}' is not an ActivityDelegate. + + + ActivityDelegate arguments don't match. Check your activity configuration to make sure it is in sync with the declared ActivityDelegate. + + + Property Reference Change + + + Cannot create link. + + + Cannot create transition from a state to its descendant. + + + Cannot create transition to a composite state. + + + Cannot set a composite state as the initial state. + + + Cannot set a final state as the initial state. + + + Clear initial state + + + Drag a line to create a transition + + + Create transition + + + Double-click to edit transition details + + + Workflow Designer + + + Item Move + + + Move link + + + Resize StateMachine + + + Set initial state + + + Drag a line to create a new transition that shares the same trigger + + + The Encoding with code page '{0}' is not supported. + + + ConnectionConfigurationName '{0}' does not exist in configuration file. + + + (Custom) + + + Editing Command + + + Editing Connection Settings + + + Editing Parameters + + + Parameters + + + Resize State + + + Connect items automatically + + + Fill Delegate Arguments + + + Split connector automatically + + + Reorder items + + + Go to {0} + + + (empty) + + + (null) + + + Drag a line to connect to the initial state + + + '{0}' is not a concrete type of ActivityDelegate. + + + Transition + + + Double-click to edit state details. + + + Select and press Ctrl-V to paste the transition. + + + Select and press Ctrl-C to copy the transition. + + + Add an activity + + + Cannot paste the transition if one of the selected source states is a Final state. + + + Cannot paste the transition if the selection contains any item that is not a State. + + + Cannot paste the transition because '{0}' has all connection points occupied. + + + Cannot paste the transition if the destination state of the copied transition is removed or not in the StateMachine. + + + Parallel Separator + + \ No newline at end of file diff --git a/System.Activities.Core.Presentation/Settings.StyleCop b/System.Activities.Core.Presentation/Settings.StyleCop new file mode 100644 index 000000000..e6d3c4bb7 --- /dev/null +++ b/System.Activities.Core.Presentation/Settings.StyleCop @@ -0,0 +1,226 @@ + + + + + + False + Linked + %_NTDRIVE%%_NTROOT%\ndp\cdf\src\Legacy.StyleCop + + + NoPersistScopeDesigner.xaml.cs + trackdesigner.xaml.cs + IsSequenceExpandedValueConverter.cs + FlowchartDesigner.Container.cs + FlowchartDesigner.Helpers.cs + FlowchartDesigner.ModelChangeReactions.cs + FlowchartDesigner.ModelChanges.cs + FlowchartDesigner.xaml.cs + ParallelSeparator.xaml.cs + WhileDesigner.xaml.cs + DoWhileDesigner.xaml.cs + ForEachDesigner.xaml.cs + IfElseDesigner.xaml.cs + FlowDecisionDesigner.xaml.cs + FlowSwitchDesigner.xaml.cs + InteropDesigner.xaml.cs + FlowchartResizeGrip.cs + WFItemsToSpacerVisibility.cs + AssemblyInfo.cs + AtrributeColumnTemplateSelector.cs + WorkflowShape.cs + PropertyNames.cs + CaseLabelVisibilityConverter.cs + FlowchartExpressionAutomationPeer.cs + FlowchartSizeFeature.cs + SR.Designer.cs + ArrowControl.xaml.cs + AssignDesigner.xaml.cs + ConnectionPointType.cs + LocationChangedEventArgs.cs + ParallelDesigner.xaml.cs + PickDesigner.xaml.cs + PickBranchDesigner.xaml.cs + TryCatchDesigner.xaml.cs + CatchDesigner.xaml.cs + DesignerMetadata.cs + SequenceDesigner.xaml.cs + VerticalConnector.xaml.cs + BindingEditor.xaml.cs + ServiceDesigner.xaml.cs + TypeToTreeConverter.cs + ContentButtonTitleConverter.cs + DesignerStyleDictionary.xaml.cs + EditorCategoryTemplateDictionary.xaml.cs + StringResourceDictionary.xaml.cs + DynamicArgumentDesignerOptions.cs + DynamicArgumentDialog.cs + ActivityXRefPropertyEditor.cs + TypeCollectionDesigner.xaml.cs + DynamicArgumentDesigner.xaml.cs + TypeCollectionPropertyEditor.cs + ActivityDesignerHelper.cs + ActivityXRefConverter.cs + BindingPropertyValueEditor.cs + ContentCorrelationTypeExpander.xaml.cs + EndpointDesigner.cs + ReceiveDesigner.xaml.cs + ReceiveReplyDesigner.xaml.cs + SendDesigner.xaml.cs + SendReplyDesigner.xaml.cs + CaseDesigner.xaml.cs + SwitchDesigner.xaml.cs + CancellationScopeDesigner.xaml.cs + CompensableActivityDesigner.xaml.cs + FlowchartStart.xaml.cs + InvokeMethodDesigner.xaml.cs + TransactionScopeDesigner.xaml.cs + ArgumentCollectionPropertyEditor.cs + CaseKeyBox.xaml.cs + CaseKeyBox.ViewModel.cs + CaseKeyBox.ViewInterface.cs + ComboBoxHelper.cs + ExpressionToExpressionTextConverter.cs + ReceiveAndSendReplyFactory.cs + SendAndReceiveReplyFactory.cs + CorrelatesOnValueEditor.cs + MessageQueryEditor.xaml.cs + MessageQuerySetDesigner.xaml.cs + CorrelationDataDesigner.xaml.cs + CorrelationInitializerDesigner.xaml.cs + CorrelationInitializerValueEditor.cs + InitializeCorrelationDesigner.xaml.cs + FakeRoot.cs + FlowchartDesignerCommands.cs + FlowSwitchCaseEditorDialog.cs + FlowSwitchLinkCasePropertyEditor.cs + FlowSwitchLink.cs + FlowSwitchLinkMultiValueConverter.cs + GenericFlowSwitchHelper.cs + GenericForEachWithBodyFactory.cs + GenericParallelForEachWithBodyFactory.cs + FlowchartExpressionAdorner.cs + FlowDecisionLabelFeature.cs + FlowStart.cs + IFlowSwitch.cs + GenericTypeArgumentConverter.cs + MaxValueConverter.cs + MorphHelpers.cs + PickWithTwoBranchesFactory.cs + NotConverter.cs + CaseKeyBoxIsEnabledConverter.cs + SwitchTryCatchDesignerHelper.cs + HintTextConverter.cs + HintTextMaxWidthConverter.cs + WriteLineDesigner.xaml.cs + ContentDialogViewModel.cs + MessagingContentPropertyEditorResources.xaml.cs + ReceiveContentDialog.xaml.cs + ReceiveContentPropertyEditor.cs + ReceiveReplyValidationFeature.cs + SendFrameworkVersionDetectorFeature.cs + SendContentDialog.xaml.cs + SendContentPropertyEditor.cs + SendReplyValidationFeature.cs + TransactedReceiveScope.xaml.cs + CorrelationScopeDesigner.xaml.cs + FlowchartConnectionPointsAdorner.cs + FlowchartFreeFormPanel.cs + StateContainerResizeGrip.cs + FinalState.cs + StateMachineWithInitialStateFactory.cs + InitialNode.xaml.cs + StateContainerEditor.CompositeView.cs + StateContainerEditor.Utilities.cs + StateContainerEditor.ModelChangeReactions.cs + StateContainerEditor.ModelChanges.cs + StateContainerEditor.xaml.cs + StateDesigner.xaml.cs + StateMachineDesigner.xaml.cs + TransitionDesigner.xaml.cs + StateMachineConnetionPointsAdorner.cs + StateMachineFreeFormPanel.cs + HttpUriBuilderDialog.xaml.cs + HttpUriBuilderDialogViewModel.cs + SendMessageContentSearchableStringConverter.cs + SendParametersContentSearchableStringConverter.cs + ReceiveMessageContentSearchableStringConverter.cs + ReceiveParametersContentSearchableStringConverter.cs + XPathMessageQuerySearchableStringConverter.cs + InitializerFactory.cs + TrackWithInitializerFactory.cs + EditMailDialog.xaml.cs + EncodingComboBox.cs + EncodingPropertyEditor.cs + EncodingToIndexConverter.cs + InitializerControl.xaml.cs + InitializerControl.Utilities.cs + InitializerDesigner.xaml.cs + SendMailCollectionPropertyEditor.cs + SendMailDesigner.xaml.cs + TrackDesigner.xaml.cs + CommandDialog.ViewModel.cs + CommandDialog.xaml.cs + CommandSettings.cs + ConnectionDialogForRehost.ViewModel.cs + ConnectionDialogForRehost.xaml.cs + ConnectionDialogForVS.ViewModel.cs + ConnectionDialogForVS.xaml.cs + ConnectionSettingsHelper.cs + ExecuteSqlDesigner.cs + ExecuteSqlDesignersFeature.cs + ExecuteSqlDesignersHelper.cs + ExecuteSqlDesignerViewModel.cs + ExecuteSqlNonQueryDesigner.ViewModel.cs + ExecuteSqlNonQueryDesigner.xaml.cs + ExecuteSqlQueryDesigner.ViewModel.cs + ExecuteSqlQueryDesigner.xaml.cs + ExecuteSqlQuery.cs + ExecuteSqlNonQuery.cs + GenericExecuteSqlQueryDesigner.ViewModel.cs + GenericExecuteSqlQueryDesigner.xaml.cs + IDataServiceAwareObject.cs + IExecuteSqlDesigner.cs + ParametersOwner.cs + ArgumentDictionaryEditor.cs + ArgumentDictionaryEditorViewModel.cs + CommandTextPropertyEditor.cs + CommandTextPropertyEditorControl.ViewModel.cs + CommandTextPropertyEditorControl.xaml.cs + ConnectionStringPropertyEditor.cs + ConnectionStringPropertyEditorControl.ViewModel.cs + ConnectionStringPropertyEditorControl.xaml.cs + IArgumentDictionaryEditor.cs + PropertyEditorResources.xaml.cs + IWorker.cs + PropertyBinding.cs + PropertyValueExtensions.cs + WaitCursorWorker.cs + FxTrace.cs + ConnectionPoint.cs + ConnectionPointConverter.cs + Connector.xaml.cs + ConnectorEditor.cs + ConnectorIdentityConverter.cs + ConnectorLabelMarginConverter.cs + ConnectorLabelVisibilityConverter.cs + ConnectorMovedEventArgs.cs + ConnectorMovedEventHandler.cs + ConnectorPointsToArrowMarginConverter.cs + ConnectorPointsToArrowTransformConverter.cs + ConnectorPointsToSegmentsConverter.cs + ConnectorRouter.cs + DesignerGeometryHelper.cs + FreeFormPanel.cs + LocationChangedEventHandler.cs + RequiredSizeChangedEventArgs.cs + RequiredSizeChangedEventHandler.cs + ResizeGrip.cs + StartSymbol.xaml.cs + StartNode.cs + InitializerHelper.cs + InitializerModelTreeRootNode.cs + InitializerModelTreeNode.cs + WorkflowServiceFrameworkVersionDetectorFeature.cs + + diff --git a/System.Activities.Core.Presentation/System.Activities.Core.Presentation.csproj b/System.Activities.Core.Presentation/System.Activities.Core.Presentation.csproj new file mode 100644 index 000000000..0ba96c219 --- /dev/null +++ b/System.Activities.Core.Presentation/System.Activities.Core.Presentation.csproj @@ -0,0 +1,629 @@ + + + + + $([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildProjectDirectory), 'Microsoft.CDF.Settings.targets')) + + + + 10.0.10621 + 2.0 + true + {1B5D5CFE-8DD6-44BF-8331-7498B96D0FFD} + Library + Properties + System.Activities.Core.Presentation + System.Activities.Core.Presentation + 512 + $(AssemblyName) + true + + $(PowershellRefPath) + $(ADPDocumentationPath)\$(AssemblyName).xml + $(DefineConstants);NONAPTCA + + + true + full + false + DEBUG;TRACE + prompt + + + pdbonly + true + TRACE + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + System\Activities\Presentation + System\Activities\Core\Presentation + System\Activities\Core\Presentation\Themes + System\ServiceModel\Presentation + System\ServiceModel\Activities\Presentation + + + + + + + FlowchartDesigner.xaml + + + FlowchartDesigner.xaml + + + FlowchartDesigner.xaml + + + FlowchartDesigner.xaml + + + FlowchartDesigner.xaml + + + ParallelSeparator.xaml + + + WhileDesigner.xaml + + + DoWhileDesigner.xaml + + + ForEachDesigner.xaml + + + IfElseDesigner.xaml + + + FlowDecisionDesigner.xaml + + + FlowSwitchDesigner.xaml + + + InteropDesigner.xaml + + + + + + + + + True + True + SR.resx + + + AssignDesigner.xaml + + + + + ParallelDesigner.xaml + Code + + + PickDesigner.xaml + Code + + + PickBranchDesigner.xaml + Code + + + + + TryCatchDesigner.xaml + Code + + + CatchDesigner.xaml + Code + + + + + SequenceDesigner.xaml + + + VerticalConnector.xaml + + + BindingEditor.xaml + + + ServiceDesigner.xaml + + + + + DesignerStyleDictionary.xaml + + + EditorCategoryTemplateDictionary.xaml + + + StringResourceDictionary.xaml + + + + + + TypeCollectionDesigner.xaml + + + DynamicArgumentDesigner.xaml + + + + + + + ContentCorrelationTypeExpander.xaml + + + + + ReceiveDesigner.xaml + + + ReceiveReplyDesigner.xaml + + + SendDesigner.xaml + + + SendReplyDesigner.xaml + + + CaseDesigner.xaml + + + SwitchDesigner.xaml + + + CancellationScopeDesigner.xaml + + + CompensableActivityDesigner.xaml + + + StartSymbol.xaml + + + InvokeMethodDesigner.xaml + + + TransactionScopeDesigner.xaml + + + + CaseKeyBox.xaml + + + CaseKeyBox.xaml + + + CaseKeyBox.xaml + + + + + + + + + + + + + MessageQueryEditor.xaml + + + MessageQuerySetDesigner.xaml + + + CorrelationDataDesigner.xaml + + + CorrelationInitializerDesigner.xaml + + + + InitializeCorrelationDesigner.xaml + + + + + + + + + + + + + + + + + + + + + + + + + + WriteLineDesigner.xaml + + + + MessagingContentPropertyEditorResources.xaml + + + ReceiveContentDialog.xaml + + + + + SendContentDialog.xaml + + + + + TransactedReceiveScope.xaml + + + CorrelationScopeDesigner.xaml + + + + DynamicActivityPropertyChooser.xaml + + + + + + + InvokeDelegateDesigner.xaml + + + + + + + NoPersistScopeDesigner.xaml + + + + ReorderableListEditor.xaml + + + + + + + + + + StateConnectionPointToolTip.xaml + + + + + + MSBuild:Compile + Designer + + + MSBuild:Compile + Designer + + + Designer + MSBuild:Compile + + + MSBuild:Compile + Designer + + + MSBuild:Compile + Designer + + + Designer + MSBuild:Compile + + + + + MSBuild:Compile + Designer + + + MSBuild:Compile + Designer + + + MSBuild:Compile + Designer + + + MSBuild:Compile + Designer + + + MSBuild:Compile + Designer + + + MSBuild:Compile + Designer + + + MSBuild:Compile + Designer + + + MSBuild:Compile + Designer + + + MSBuild:Compile + Designer + + + Designer + MSBuild:Compile + + + MSBuild:Compile + Designer + + + MSBuild:Compile + Designer + + + MSBuild:Compile + Designer + + + MSBuild:Compile + Designer + + + MSBuild:Compile + Designer + + + Designer + MSBuild:Compile + + + Designer + MSBuild:Compile + + + MSBuild:Compile + Designer + + + Designer + MSBuild:Compile + + + MSBuild:Compile + Designer + + + MSBuild:Compile + Designer + + + MSBuild:Compile + Designer + + + Designer + MSBuild:Compile + + + MSBuild:Compile + Designer + + + MSBuild:Compile + Designer + + + MSBuild:Compile + Designer + + + MSBuild:Compile + Designer + + + MSBuild:Compile + Designer + + + MSBuild:Compile + Designer + + + MSBuild:Compile + Designer + + + Designer + MSBuild:Compile + + + MSBuild:Compile + Designer + + + Designer + MSBuild:Compile + + + MSBuild:Compile + Designer + + + MSBuild:Compile + Designer + + + MSBuild:Compile + Designer + + + MSBuild:Compile + Designer + + + MSBuild:Compile + Designer + + + MSBuild:Compile + Designer + + + MSBuild:Compile + Designer + + + Designer + MSBuild:Compile + + + Designer + MSBuild:Compile + + + MSBuild:Compile + Designer + + + MSBuild:Compile + Designer + + + Designer + MSBuild:Compile + + + MSBuild:Compile + Designer + + + + + + StateContainerEditor.xaml + + + StateContainerEditor.xaml + + + StateContainerEditor.xaml + + + StateContainerEditor.xaml + + + StateContainerEditor.xaml + + + StateDesigner.xaml + + + StateMachineDesigner.xaml + + + TransitionDesigner.xaml + + + + + + + + + MSBuild:Compile + Designer + + + MSBuild:Compile + Designer + + + MSBuild:Compile + Designer + + + MSBuild:Compile + Designer + + + MSBuild:Compile + Designer + + + + + ResXFileCodeGenerator + SR.Designer.cs + Designer + + + + + + + msbuild /t:checkuid $(ProjectPath) + + \ No newline at end of file diff --git a/System.Activities.Core.Presentation/System/Activities/Core/Presentation/AssignDesigner.xaml b/System.Activities.Core.Presentation/System/Activities/Core/Presentation/AssignDesigner.xaml new file mode 100644 index 000000000..e425ce34f --- /dev/null +++ b/System.Activities.Core.Presentation/System/Activities/Core/Presentation/AssignDesigner.xaml @@ -0,0 +1,68 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/System.Activities.Core.Presentation/System/Activities/Core/Presentation/CancellationScopeDesigner.xaml b/System.Activities.Core.Presentation/System/Activities/Core/Presentation/CancellationScopeDesigner.xaml new file mode 100644 index 000000000..87cf45137 --- /dev/null +++ b/System.Activities.Core.Presentation/System/Activities/Core/Presentation/CancellationScopeDesigner.xaml @@ -0,0 +1,68 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/System.Activities.Core.Presentation/System/Activities/Core/Presentation/CaseDesigner.xaml b/System.Activities.Core.Presentation/System/Activities/Core/Presentation/CaseDesigner.xaml new file mode 100644 index 000000000..ef74e6368 --- /dev/null +++ b/System.Activities.Core.Presentation/System/Activities/Core/Presentation/CaseDesigner.xaml @@ -0,0 +1,127 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Case + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/System.Activities.Core.Presentation/System/Activities/Core/Presentation/CaseKeyBox.xaml b/System.Activities.Core.Presentation/System/Activities/Core/Presentation/CaseKeyBox.xaml new file mode 100644 index 000000000..164aabdca --- /dev/null +++ b/System.Activities.Core.Presentation/System/Activities/Core/Presentation/CaseKeyBox.xaml @@ -0,0 +1,54 @@ + + + + + + + + + + + + + + + + + diff --git a/System.Activities.Core.Presentation/System/Activities/Core/Presentation/CatchDesigner.xaml b/System.Activities.Core.Presentation/System/Activities/Core/Presentation/CatchDesigner.xaml new file mode 100644 index 000000000..b4b142634 --- /dev/null +++ b/System.Activities.Core.Presentation/System/Activities/Core/Presentation/CatchDesigner.xaml @@ -0,0 +1,124 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/System.Activities.Core.Presentation/System/Activities/Core/Presentation/CompensableActivityDesigner.xaml b/System.Activities.Core.Presentation/System/Activities/Core/Presentation/CompensableActivityDesigner.xaml new file mode 100644 index 000000000..243ff0133 --- /dev/null +++ b/System.Activities.Core.Presentation/System/Activities/Core/Presentation/CompensableActivityDesigner.xaml @@ -0,0 +1,100 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/System.Activities.Core.Presentation/System/Activities/Core/Presentation/DoWhileDesigner.xaml b/System.Activities.Core.Presentation/System/Activities/Core/Presentation/DoWhileDesigner.xaml new file mode 100644 index 000000000..3353756a1 --- /dev/null +++ b/System.Activities.Core.Presentation/System/Activities/Core/Presentation/DoWhileDesigner.xaml @@ -0,0 +1,70 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/System.Activities.Core.Presentation/System/Activities/Core/Presentation/DynamicActivityPropertyChooser.xaml b/System.Activities.Core.Presentation/System/Activities/Core/Presentation/DynamicActivityPropertyChooser.xaml new file mode 100644 index 000000000..69fcb8bea --- /dev/null +++ b/System.Activities.Core.Presentation/System/Activities/Core/Presentation/DynamicActivityPropertyChooser.xaml @@ -0,0 +1,30 @@ + + + + + Choose Dynamic Activity Property + + + + + + + + + + + + + + \ No newline at end of file diff --git a/System.Activities.Core.Presentation/System/Activities/Core/Presentation/FlowDecisionDesigner.xaml b/System.Activities.Core.Presentation/System/Activities/Core/Presentation/FlowDecisionDesigner.xaml new file mode 100644 index 000000000..04ac71b89 --- /dev/null +++ b/System.Activities.Core.Presentation/System/Activities/Core/Presentation/FlowDecisionDesigner.xaml @@ -0,0 +1,91 @@ + + + + + + + + + + + Expression Button + Annotation Button + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/System.Activities.Core.Presentation/System/Activities/Core/Presentation/FlowSwitchDesigner.xaml b/System.Activities.Core.Presentation/System/Activities/Core/Presentation/FlowSwitchDesigner.xaml new file mode 100644 index 000000000..d142146ed --- /dev/null +++ b/System.Activities.Core.Presentation/System/Activities/Core/Presentation/FlowSwitchDesigner.xaml @@ -0,0 +1,75 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/System.Activities.Core.Presentation/System/Activities/Core/Presentation/FlowchartDesigner.xaml b/System.Activities.Core.Presentation/System/Activities/Core/Presentation/FlowchartDesigner.xaml new file mode 100644 index 000000000..5e1ad4957 --- /dev/null +++ b/System.Activities.Core.Presentation/System/Activities/Core/Presentation/FlowchartDesigner.xaml @@ -0,0 +1,68 @@ + + + + + + + + + + + + + + + + + + Set _as Start Node + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/System.Activities.Core.Presentation/System/Activities/Core/Presentation/ForEachDesigner.xaml b/System.Activities.Core.Presentation/System/Activities/Core/Presentation/ForEachDesigner.xaml new file mode 100644 index 000000000..dbe402427 --- /dev/null +++ b/System.Activities.Core.Presentation/System/Activities/Core/Presentation/ForEachDesigner.xaml @@ -0,0 +1,67 @@ + + + + + + + + + + + + + + + + + + + + + Foreach + + in + + + + + + + + + + + + + diff --git a/System.Activities.Core.Presentation/System/Activities/Core/Presentation/IfElseDesigner.xaml b/System.Activities.Core.Presentation/System/Activities/Core/Presentation/IfElseDesigner.xaml new file mode 100644 index 000000000..f1f0dc771 --- /dev/null +++ b/System.Activities.Core.Presentation/System/Activities/Core/Presentation/IfElseDesigner.xaml @@ -0,0 +1,78 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/System.Activities.Core.Presentation/System/Activities/Core/Presentation/InteropDesigner.xaml b/System.Activities.Core.Presentation/System/Activities/Core/Presentation/InteropDesigner.xaml new file mode 100644 index 000000000..d8c03c4cd --- /dev/null +++ b/System.Activities.Core.Presentation/System/Activities/Core/Presentation/InteropDesigner.xaml @@ -0,0 +1,36 @@ + + + + + + + + + + + + + + + + + + diff --git a/System.Activities.Core.Presentation/System/Activities/Core/Presentation/InvokeDelegateDesigner.xaml b/System.Activities.Core.Presentation/System/Activities/Core/Presentation/InvokeDelegateDesigner.xaml new file mode 100644 index 000000000..d7431f229 --- /dev/null +++ b/System.Activities.Core.Presentation/System/Activities/Core/Presentation/InvokeDelegateDesigner.xaml @@ -0,0 +1,28 @@ + + + + + + + + + + + + + + + + + + + + diff --git a/System.Activities.Core.Presentation/System/Activities/Core/Presentation/InvokeMethodDesigner.xaml b/System.Activities.Core.Presentation/System/Activities/Core/Presentation/InvokeMethodDesigner.xaml new file mode 100644 index 000000000..a19142c93 --- /dev/null +++ b/System.Activities.Core.Presentation/System/Activities/Core/Presentation/InvokeMethodDesigner.xaml @@ -0,0 +1,58 @@ + + + + + + + + + + + + + + + + + + + + + + + TargetType + TargetObject + MethodName + + + + + + + + + + diff --git a/System.Activities.Core.Presentation/System/Activities/Core/Presentation/LocalAppContextSwitches.cs b/System.Activities.Core.Presentation/System/Activities/Core/Presentation/LocalAppContextSwitches.cs new file mode 100644 index 000000000..a1a946312 --- /dev/null +++ b/System.Activities.Core.Presentation/System/Activities/Core/Presentation/LocalAppContextSwitches.cs @@ -0,0 +1,27 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ + +namespace System.Activities.Core.Presentation +{ + internal static class LocalAppContextSwitches + { + public static bool UseLegacyAccessibilityFeatures + { + get + { + return System.Activities.Presentation.LocalAppContextSwitches.UseLegacyAccessibilityFeatures; + } + } + + public static bool UseLegacyAccessibilityFeatures2 + { + get + { + return System.Activities.Presentation.LocalAppContextSwitches.UseLegacyAccessibilityFeatures2; + } + } + } +} diff --git a/System.Activities.Core.Presentation/System/Activities/Core/Presentation/NoPersistScopeDesigner.xaml b/System.Activities.Core.Presentation/System/Activities/Core/Presentation/NoPersistScopeDesigner.xaml new file mode 100644 index 000000000..eaa5dbd04 --- /dev/null +++ b/System.Activities.Core.Presentation/System/Activities/Core/Presentation/NoPersistScopeDesigner.xaml @@ -0,0 +1,49 @@ + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/System.Activities.Core.Presentation/System/Activities/Core/Presentation/ParallelDesigner.xaml b/System.Activities.Core.Presentation/System/Activities/Core/Presentation/ParallelDesigner.xaml new file mode 100644 index 000000000..376c59b1f --- /dev/null +++ b/System.Activities.Core.Presentation/System/Activities/Core/Presentation/ParallelDesigner.xaml @@ -0,0 +1,49 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/System.Activities.Core.Presentation/System/Activities/Core/Presentation/ParallelSeparator.xaml b/System.Activities.Core.Presentation/System/Activities/Core/Presentation/ParallelSeparator.xaml new file mode 100644 index 000000000..5330edef7 --- /dev/null +++ b/System.Activities.Core.Presentation/System/Activities/Core/Presentation/ParallelSeparator.xaml @@ -0,0 +1,37 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/System.Activities.Core.Presentation/System/Activities/Core/Presentation/PickBranchDesigner.xaml b/System.Activities.Core.Presentation/System/Activities/Core/Presentation/PickBranchDesigner.xaml new file mode 100644 index 000000000..6e5dcfe8e --- /dev/null +++ b/System.Activities.Core.Presentation/System/Activities/Core/Presentation/PickBranchDesigner.xaml @@ -0,0 +1,61 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/System.Activities.Core.Presentation/System/Activities/Core/Presentation/PickDesigner.xaml b/System.Activities.Core.Presentation/System/Activities/Core/Presentation/PickDesigner.xaml new file mode 100644 index 000000000..4e139078e --- /dev/null +++ b/System.Activities.Core.Presentation/System/Activities/Core/Presentation/PickDesigner.xaml @@ -0,0 +1,70 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/System.Activities.Core.Presentation/System/Activities/Core/Presentation/ReorderableListEditor.xaml b/System.Activities.Core.Presentation/System/Activities/Core/Presentation/ReorderableListEditor.xaml new file mode 100644 index 000000000..84a3bb84e --- /dev/null +++ b/System.Activities.Core.Presentation/System/Activities/Core/Presentation/ReorderableListEditor.xaml @@ -0,0 +1,130 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/System.Activities.Core.Presentation/System/Activities/Core/Presentation/SequenceDesigner.xaml b/System.Activities.Core.Presentation/System/Activities/Core/Presentation/SequenceDesigner.xaml new file mode 100644 index 000000000..147735bf1 --- /dev/null +++ b/System.Activities.Core.Presentation/System/Activities/Core/Presentation/SequenceDesigner.xaml @@ -0,0 +1,19 @@ + + + + + + + + + + + + + diff --git a/System.Activities.Core.Presentation/System/Activities/Core/Presentation/StartSymbol.xaml b/System.Activities.Core.Presentation/System/Activities/Core/Presentation/StartSymbol.xaml new file mode 100644 index 000000000..0b985e2fa --- /dev/null +++ b/System.Activities.Core.Presentation/System/Activities/Core/Presentation/StartSymbol.xaml @@ -0,0 +1,46 @@ + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/System.Activities.Core.Presentation/System/Activities/Core/Presentation/StateConnectionPointToolTip.xaml b/System.Activities.Core.Presentation/System/Activities/Core/Presentation/StateConnectionPointToolTip.xaml new file mode 100644 index 000000000..27572f04f --- /dev/null +++ b/System.Activities.Core.Presentation/System/Activities/Core/Presentation/StateConnectionPointToolTip.xaml @@ -0,0 +1,34 @@ + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/System.Activities.Core.Presentation/System/Activities/Core/Presentation/StateContainerEditor.xaml b/System.Activities.Core.Presentation/System/Activities/Core/Presentation/StateContainerEditor.xaml new file mode 100644 index 000000000..6b82fb50e --- /dev/null +++ b/System.Activities.Core.Presentation/System/Activities/Core/Presentation/StateContainerEditor.xaml @@ -0,0 +1,49 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/System.Activities.Core.Presentation/System/Activities/Core/Presentation/StateDesigner.xaml b/System.Activities.Core.Presentation/System/Activities/Core/Presentation/StateDesigner.xaml new file mode 100644 index 000000000..d0222a66a --- /dev/null +++ b/System.Activities.Core.Presentation/System/Activities/Core/Presentation/StateDesigner.xaml @@ -0,0 +1,206 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/System.Activities.Core.Presentation/System/Activities/Presentation/TypeCollectionDesigner.xaml b/System.Activities.Core.Presentation/System/Activities/Presentation/TypeCollectionDesigner.xaml new file mode 100644 index 000000000..16970dfac --- /dev/null +++ b/System.Activities.Core.Presentation/System/Activities/Presentation/TypeCollectionDesigner.xaml @@ -0,0 +1,102 @@ + + + + + + + + + + + + Type Collection Editor + Cannot save type collection. Types must be unique. + Add new type + Type + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/System.Activities.Core.Presentation/System/ServiceModel/Activities/Presentation/BindingEditor.xaml b/System.Activities.Core.Presentation/System/ServiceModel/Activities/Presentation/BindingEditor.xaml new file mode 100644 index 000000000..4a76921e8 --- /dev/null +++ b/System.Activities.Core.Presentation/System/ServiceModel/Activities/Presentation/BindingEditor.xaml @@ -0,0 +1,27 @@ + + + + + + + + + + + + + diff --git a/System.Activities.Core.Presentation/System/ServiceModel/Activities/Presentation/ContentCorrelationTypeExpander.xaml b/System.Activities.Core.Presentation/System/ServiceModel/Activities/Presentation/ContentCorrelationTypeExpander.xaml new file mode 100644 index 000000000..dde9e8778 --- /dev/null +++ b/System.Activities.Core.Presentation/System/ServiceModel/Activities/Presentation/ContentCorrelationTypeExpander.xaml @@ -0,0 +1,89 @@ + + + + + + + + + + + + This entry is not serializable and cannot be used for correlation + No types to expand + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/System.Activities.Core.Presentation/System/ServiceModel/Activities/Presentation/CorrelationDataDesigner.xaml b/System.Activities.Core.Presentation/System/ServiceModel/Activities/Presentation/CorrelationDataDesigner.xaml new file mode 100644 index 000000000..75c2dbdd7 --- /dev/null +++ b/System.Activities.Core.Presentation/System/ServiceModel/Activities/Presentation/CorrelationDataDesigner.xaml @@ -0,0 +1,133 @@ + + + + + + + + + + Initialize Correlation + Correlation + Initialize On + Key + Value + Add new item + Edit InitalizeCorrelation CorrelationData + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/System.Activities.Core.Presentation/System/ServiceModel/Activities/Presentation/CorrelationInitializerDesigner.xaml b/System.Activities.Core.Presentation/System/ServiceModel/Activities/Presentation/CorrelationInitializerDesigner.xaml new file mode 100644 index 000000000..7aa53df67 --- /dev/null +++ b/System.Activities.Core.Presentation/System/ServiceModel/Activities/Presentation/CorrelationInitializerDesigner.xaml @@ -0,0 +1,176 @@ + + + + + + + + + + Add Correlation Initializers + Correlation Initializers + Add initializer + Edit initializer + Correlation type + Grid Splitter + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/System.Activities.Core.Presentation/System/ServiceModel/Activities/Presentation/CorrelationScopeDesigner.xaml b/System.Activities.Core.Presentation/System/ServiceModel/Activities/Presentation/CorrelationScopeDesigner.xaml new file mode 100644 index 000000000..cb68bd55b --- /dev/null +++ b/System.Activities.Core.Presentation/System/ServiceModel/Activities/Presentation/CorrelationScopeDesigner.xaml @@ -0,0 +1,81 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/System.Activities.Core.Presentation/System/ServiceModel/Activities/Presentation/InitializeCorrelationDesigner.xaml b/System.Activities.Core.Presentation/System/ServiceModel/Activities/Presentation/InitializeCorrelationDesigner.xaml new file mode 100644 index 000000000..e7622d222 --- /dev/null +++ b/System.Activities.Core.Presentation/System/ServiceModel/Activities/Presentation/InitializeCorrelationDesigner.xaml @@ -0,0 +1,65 @@ + + + + + + + + + + + + Define... + View... + + + + + + + + + + + + + + + + + + + + +