Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
202 changes: 38 additions & 164 deletions src/Microsoft.PowerShell.Commands.Utility/commands/utility/AddType.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,29 +2,27 @@
Copyright (c) Microsoft Corporation. All rights reserved.
--********************************************************************/

#region Using directives

using System;

using System.Collections.Generic;
using System.Collections.Immutable;
using System.Collections.ObjectModel;
using System.Diagnostics.CodeAnalysis;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Management.Automation;
using System.Management.Automation.Internal;
using System.Reflection;
using System.Security;
using System.Text;

using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using System.IO;
using Microsoft.CodeAnalysis.Emit;
using System.Collections.Immutable;
using System.Security;

using PathType = System.IO.Path;

#endregion

namespace Microsoft.PowerShell.Commands
{
Expand Down Expand Up @@ -143,14 +141,15 @@ public class AddTypeCompilerError
}

/// <summary>
/// Base class that contains logic for Add-Type cmdlet based on
/// - CodeDomProvider
/// - CodeAnalysis(Roslyn)
/// Adds a new type to the Application Domain.
/// This version is based on CodeAnalysis (Roslyn).
/// </summary>
public abstract class AddTypeCommandBase : PSCmdlet
[Cmdlet(VerbsCommon.Add, "Type", DefaultParameterSetName = "FromSource", HelpUri = "https://go.microsoft.com/fwlink/?LinkID=135195")]
[OutputType(typeof(Type))]
public sealed class AddTypeCommand : PSCmdlet
{
/// <summary>
/// The source code of this type
/// The source code of this type.
/// </summary>
[Parameter(Mandatory = true, Position = 0, ParameterSetName = "FromSource")]
public String TypeDefinition
Expand All @@ -166,13 +165,13 @@ public String TypeDefinition
}

/// <summary>
/// The name of the type used for auto-generated types
/// The name of the type used for auto-generated types.
/// </summary>
[Parameter(Mandatory = true, Position = 0, ParameterSetName = "FromMember")]
public String Name { get; set; }

/// <summary>
/// The source code of this method / member
/// The source code of this method / member.
/// </summary>
[Parameter(Mandatory = true, Position = 1, ParameterSetName = "FromMember")]
[SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays")]
Expand All @@ -188,19 +187,15 @@ public String[] MemberDefinition

if (value != null)
{
for (int counter = 0; counter < value.Length; counter++)
{
sourceCode += value[counter] + "\n";
}
sourceCode = String.Join("\n", value);
}
}
}

internal String sourceCode;


/// <summary>
/// The namespaced used for the auto-generated type
/// The namespaced used for the auto-generated type.
/// </summary>
[Parameter(ParameterSetName = "FromMember")]
[Alias("NS")]
Expand All @@ -213,26 +208,22 @@ public String Namespace
}
set
{
typeNamespace = value;
if (typeNamespace != null)
{
typeNamespace = typeNamespace.Trim();
}
typeNamespace = value?.Trim();
}
}

internal string typeNamespace = "Microsoft.PowerShell.Commands.AddType.AutoGeneratedTypes";

/// <summary>
/// Any using statements required by the auto-generated type
/// Any using statements required by the auto-generated type.
/// </summary>
[Parameter(ParameterSetName = "FromMember")]
[Alias("Using")]
[SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays")]
public String[] UsingNamespace { get; set; } = Utils.EmptyArray<string>();


/// <summary>
/// The path to the source code or DLL to load
/// The path to the source code or DLL to load.
/// </summary>
[Parameter(Mandatory = true, Position = 0, ParameterSetName = "FromPath")]
[SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays")]
Expand Down Expand Up @@ -278,7 +269,7 @@ public string[] Path
}

/// <summary>
/// The literal path to the source code or DLL to load
/// The literal path to the source code or DLL to load.
/// </summary>
[Parameter(Mandatory = true, ParameterSetName = "FromLiteralPath")]
[Alias("PSPath")]
Expand Down Expand Up @@ -377,7 +368,7 @@ private void ProcessPaths(List<string> resolvedPaths)
internal string[] paths;

/// <summary>
/// The name of the assembly to load
/// The name of the assembly to load.
/// </summary>
[Parameter(Mandatory = true, ParameterSetName = "FromAssemblyName")]
[Alias("AN")]
Expand All @@ -396,45 +387,17 @@ public String[] AssemblyName
}

internal String[] assemblyNames;

internal bool loadAssembly = false;


/// <summary>
/// The language used to generate source code
/// The language used to generate source code.
/// </summary>
[Parameter(ParameterSetName = "FromSource")]
[Parameter(ParameterSetName = "FromMember")]
public Language Language
{
get
{
return language;
}
set
{
language = value;
languageSpecified = true;

PostSetLanguage(language);
}
}

/// <summary>
/// Post-action for Language setter.
/// </summary>
/// <param name="language"></param>
internal virtual void PostSetLanguage(Language language)
{
}


internal bool languageSpecified = false;
internal Language language = Language.CSharp;

public Language Language { get; set; } = Language.CSharp;

/// <summary>
/// Any reference DLLs to use in the compilation
/// Any reference DLLs to use in the compilation.
/// </summary>
[Parameter(ParameterSetName = "FromSource")]
[Parameter(ParameterSetName = "FromMember")]
Expand All @@ -453,7 +416,7 @@ public String[] ReferencedAssemblies
internal string[] referencedAssemblies = Utils.EmptyArray<string>();

/// <summary>
/// The path to the output assembly
/// The path to the output assembly.
/// </summary>
[Parameter(ParameterSetName = "FromSource")]
[Parameter(ParameterSetName = "FromMember")]
Expand Down Expand Up @@ -533,7 +496,7 @@ public string OutputAssembly
internal string outputAssembly = null;

/// <summary>
/// The output type of the assembly
/// The output type of the assembly.
/// </summary>
[Parameter(ParameterSetName = "FromSource")]
[Parameter(ParameterSetName = "FromMember")]
Expand All @@ -557,40 +520,16 @@ public OutputAssemblyType OutputType


/// <summary>
/// Flag to pass the resulting types along
/// Flag to pass the resulting types along.
/// </summary>
[Parameter()]
public SwitchParameter PassThru
{
get
{
return passThru;
}
set
{
passThru = value;
}
}
internal SwitchParameter passThru;
public SwitchParameter PassThru { get; set; }

/// <summary>
/// Flag to ignore warnings during compilation
/// Flag to ignore warnings during compilation.
/// </summary>
[Parameter()]
public SwitchParameter IgnoreWarnings
{
get
{
return ignoreWarnings;
}
set
{
ignoreWarnings = value;
ignoreWarningsSpecified = true;
}
}
internal bool ignoreWarningsSpecified;
internal SwitchParameter ignoreWarnings;
public SwitchParameter IgnoreWarnings { get; set; }

internal string GenerateTypeSource(string typeNamespace, string name, string sourceCode, Language language)
{
Expand Down Expand Up @@ -757,62 +696,6 @@ internal string GetUsingSet(Language language)
return usingNamespaceSet.ToString();
}

/// <summary>
/// Perform common error checks.
/// Populate source code.
/// We only keep the code for backward compatibility.
/// </summary>
protected override void EndProcessing()
{
// Generate an error if they've specified an output
// assembly type without an output assembly
if (String.IsNullOrEmpty(outputAssembly) && outputTypeSpecified)
{
ErrorRecord errorRecord = new ErrorRecord(
new Exception(
String.Format(
CultureInfo.CurrentCulture,
AddTypeStrings.OutputTypeRequiresOutputAssembly)),
"OUTPUTTYPE_REQUIRES_ASSEMBLY",
ErrorCategory.InvalidArgument,
outputType);

ThrowTerminatingError(errorRecord);
return;
}

PopulateSource();
}

// We only keep the code for backward compatibility.
internal void PopulateSource()
{
// Prevent code compilation in ConstrainedLanguage mode
if (SessionState.LanguageMode == PSLanguageMode.ConstrainedLanguage)
{
ThrowTerminatingError(
new ErrorRecord(
new PSNotSupportedException(AddTypeStrings.CannotDefineNewType), "CannotDefineNewType", ErrorCategory.PermissionDenied, null));
}

// Load the source if they want to load from a file
if (String.Equals(ParameterSetName, "FromPath", StringComparison.OrdinalIgnoreCase) ||
String.Equals(ParameterSetName, "FromLiteralPath", StringComparison.OrdinalIgnoreCase)
)
{
sourceCode = "";
foreach (string file in paths)
{
sourceCode += System.IO.File.ReadAllText(file) + "\n";
}
}

if (String.Equals(ParameterSetName, "FromMember", StringComparison.OrdinalIgnoreCase))
{
sourceCode = GenerateTypeSource(typeNamespace, Name, sourceCode, language);
}
}

internal void HandleCompilerErrors(AddTypeCompilerError[] compilerErrors)
{
// Get the source code that corresponds to their type in the case of errors
Expand Down Expand Up @@ -898,20 +781,11 @@ private void OutputError(AddTypeCompilerError error, string[] actualSource)
WriteError(errorRecord);
}
}
}

/// <summary>
/// Adds a new type to the Application Domain.
/// This version is based on CodeAnalysis (Roslyn).
/// </summary>
[Cmdlet(VerbsCommon.Add, "Type", DefaultParameterSetName = "FromSource", HelpUri = "https://go.microsoft.com/fwlink/?LinkID=135195")]
[OutputType(typeof(Type))]
public sealed class AddTypeCommand : AddTypeCommandBase
{
private static Dictionary<string, int> s_sourceCache = new Dictionary<string, int>();

/// <summary>
/// Generate the type(s)
/// Generate the type(s).
/// </summary>
protected override void EndProcessing()
{
Expand Down Expand Up @@ -985,7 +859,7 @@ protected override void EndProcessing()
}
else if (String.Equals(ParameterSetName, "FromMember", StringComparison.OrdinalIgnoreCase))
{
sourceCode = GenerateTypeSource(typeNamespace, Name, sourceCode, language);
sourceCode = GenerateTypeSource(typeNamespace, Name, sourceCode, Language);
}

CompileSourceToAssembly(this.sourceCode);
Expand All @@ -1004,7 +878,7 @@ private void LoadAssemblies(IEnumerable<string> assemblies)
assembly = Assembly.LoadFrom(ResolveAssemblyName(assemblyName, false));
}

if (passThru)
if (PassThru)
{
WriteTypes(assembly);
}
Expand Down Expand Up @@ -1232,9 +1106,9 @@ private void WriteTypes(Assembly assembly)
private void CompileSourceToAssembly(string source)
{
CSharpParseOptions parseOptions;
if (IsCSharp(language))
if (IsCSharp(Language))
{
switch (language)
switch (Language)
{
case Language.CSharpVersion1:
parseOptions = new CSharpParseOptions(LanguageVersion.CSharp1);
Expand Down Expand Up @@ -1268,10 +1142,10 @@ private void CompileSourceToAssembly(string source)
else
{
ErrorRecord errorRecord = new ErrorRecord(
new Exception(String.Format(CultureInfo.CurrentCulture, AddTypeStrings.SpecialNetVersionRequired, language.ToString(), string.Empty)),
new Exception(String.Format(CultureInfo.CurrentCulture, AddTypeStrings.SpecialNetVersionRequired, Language.ToString(), string.Empty)),
"LANGUAGE_NOT_SUPPORTED",
ErrorCategory.InvalidArgument,
language);
Language);

ThrowTerminatingError(errorRecord);
parseOptions = null;
Expand Down Expand Up @@ -1318,7 +1192,7 @@ private void CompileSourceToAssembly(string source)
ms.Seek(0, SeekOrigin.Begin);
Assembly assembly = Assembly.Load(ms.ToArray());
CheckTypesForDuplicates(assembly);
if (passThru)
if (PassThru)
{
WriteTypes(assembly);
}
Expand All @@ -1330,7 +1204,7 @@ private void CompileSourceToAssembly(string source)
emitResult = compilation.Emit(outputAssembly);
if (emitResult.Success)
{
if (passThru)
if (PassThru)
{
Assembly assembly = Assembly.LoadFrom(outputAssembly);
CheckTypesForDuplicates(assembly);
Expand Down