mirror of
https://github.com/PowerShell/PowerShell
synced 2026-06-08 12:12:50 +00:00
This change moves powershell to .NET Core 2.0. Major changes are: 1. PowerShell assemblies are now targeting `netcoreapp2.0`. We are using `microsoft.netcore.app-2.0.0-preview1-001913-00`, which is from dotnet-core build 4/4/17. We cannot target `netstandard2.0` because the packages `System.Reflection.Emit` and `System.Reflection.Emit.Lightweight`, which are needed for powershell class, cannot be referenced when targeting `netstandard2.0`. 2. Refactor code to remove most CLR stub types and extension types. 3. Update build scripts to enable CI builds. The `-cache` section is specified to depend on `appveyor.yml`, so the cache will be invalidated if `appveyor.yml` is changed. 4. Ship `netcoreapp` reference assemblies with powershell to fix the issues in `Add-Type` (#2764). By default `Add-Type` will reference all those reference assemblies when compiling C# code. If `-ReferenceAssembly` is specified, then we search reference assemblies first, then the framework runtime assemblies, and lastly the loaded assemblies (possibly a third-party one that was already loaded). 5. `dotnet publish` generates executable on Unix platforms, but doesn't set "x" permission and thus it cannot execute. Currently, the "x" permission is set in the build script, `dotnet/cli` issue [#6286](https://github.com/dotnet/cli/issues/6286) is tracking this. 6. Replace the use of some APIs with the ones that take `SecureString`. 7. osx.10.12 is required to update to `netcoreapp2.0` because `dotnet-cli` 2.0.0-preview only works on osx.10.12. 8. Add dependency to `System.ValueTuple` to work around a ambiguous type identity issue in coreclr. The issue is tracked by `dotnet/corefx` [#17797](https://github.com/dotnet/corefx/issues/17797). When moving to newer version of `netcoreapp2.0`, we need to verify if this dependency is still needed.
324 lines
11 KiB
C#
324 lines
11 KiB
C#
/********************************************************************++
|
|
Copyright (c) Microsoft Corporation. All rights reserved.
|
|
--********************************************************************/
|
|
|
|
using System.Diagnostics;
|
|
using System.Diagnostics.CodeAnalysis;
|
|
|
|
namespace System.Management.Automation
|
|
{
|
|
[Flags]
|
|
internal enum VariablePathFlags
|
|
{
|
|
None = 0x00,
|
|
Local = 0x01,
|
|
Script = 0x02,
|
|
Global = 0x04,
|
|
Private = 0x08,
|
|
Variable = 0x10,
|
|
Function = 0x20,
|
|
DriveQualified = 0x40,
|
|
Unqualified = 0x80,
|
|
|
|
// If any of these bits are set, the path does not represent an unscoped variable.
|
|
UnscopedVariableMask = Local | Script | Global | Private | Function | DriveQualified,
|
|
}
|
|
|
|
/// <summary>
|
|
/// A variable path that you can query the scope and drive of the variable reference.
|
|
/// </summary>
|
|
public class VariablePath
|
|
{
|
|
#region private data
|
|
|
|
/// <summary>
|
|
/// Stores the path that was passed to the constructor.
|
|
/// </summary>
|
|
private string _userPath;
|
|
|
|
/// <summary>
|
|
/// The name of the variable without any scope or drive.
|
|
/// </summary>
|
|
private string _unqualifiedPath;
|
|
|
|
/// <summary>
|
|
/// Store flags about the path, such as private/global/local/etc.
|
|
/// </summary>
|
|
private VariablePathFlags _flags = VariablePathFlags.None;
|
|
|
|
#endregion private data
|
|
|
|
#region Constructor
|
|
|
|
/// <summary>
|
|
/// Private constructor for CloneAndSetLocal().
|
|
/// </summary>
|
|
private VariablePath()
|
|
{
|
|
}
|
|
|
|
/// <summary>
|
|
/// Constructs a variable path.
|
|
/// </summary>
|
|
/// <param name="path">The path to parse.</param>
|
|
/// <exception cref="ArgumentNullException">
|
|
/// If <paramref name="path"/> is null.
|
|
/// </exception>
|
|
public VariablePath(string path)
|
|
: this(path, VariablePathFlags.None)
|
|
{
|
|
}
|
|
|
|
/// <summary>
|
|
/// Constructs a scoped item lookup path.
|
|
/// </summary>
|
|
///
|
|
/// <param name="path">The path to parse.</param>
|
|
/// <param name="knownFlags">
|
|
/// These flags for anything known about the path (such as, is it a function) before
|
|
/// being scanned.
|
|
/// </param>
|
|
///
|
|
/// <exception cref="ArgumentNullException">
|
|
/// If <paramref name="path"/> is null.
|
|
/// </exception>
|
|
internal VariablePath(string path, VariablePathFlags knownFlags)
|
|
{
|
|
if (string.IsNullOrEmpty(path))
|
|
{
|
|
throw PSTraceSource.NewArgumentException("path");
|
|
}
|
|
|
|
_userPath = path;
|
|
_flags = knownFlags;
|
|
|
|
string candidateScope = null;
|
|
string candidateScopeUpper = null;
|
|
VariablePathFlags candidateFlags = VariablePathFlags.Unqualified;
|
|
|
|
int currentCharIndex = 0;
|
|
int lastScannedColon = -1;
|
|
|
|
scanScope:
|
|
switch (path[0])
|
|
{
|
|
case 'g':
|
|
case 'G':
|
|
candidateScope = "lobal";
|
|
candidateScopeUpper = "LOBAL";
|
|
candidateFlags = VariablePathFlags.Global;
|
|
break;
|
|
case 'l':
|
|
case 'L':
|
|
candidateScope = "ocal";
|
|
candidateScopeUpper = "OCAL";
|
|
candidateFlags = VariablePathFlags.Local;
|
|
break;
|
|
case 'p':
|
|
case 'P':
|
|
candidateScope = "rivate";
|
|
candidateScopeUpper = "RIVATE";
|
|
candidateFlags = VariablePathFlags.Private;
|
|
break;
|
|
case 's':
|
|
case 'S':
|
|
candidateScope = "cript";
|
|
candidateScopeUpper = "CRIPT";
|
|
candidateFlags = VariablePathFlags.Script;
|
|
break;
|
|
case 'v':
|
|
case 'V':
|
|
if (knownFlags == VariablePathFlags.None)
|
|
{
|
|
// If we see 'variable:', our namespaceId will be empty, and
|
|
// we'll also need to scan for the scope again.
|
|
candidateScope = "ariable";
|
|
candidateScopeUpper = "ARIABLE";
|
|
candidateFlags = VariablePathFlags.Variable;
|
|
}
|
|
break;
|
|
}
|
|
|
|
if (candidateScope != null)
|
|
{
|
|
currentCharIndex += 1; // First character already matched.
|
|
int j;
|
|
for (j = 0; currentCharIndex < path.Length && j < candidateScope.Length; ++j, ++currentCharIndex)
|
|
{
|
|
if (path[currentCharIndex] != candidateScope[j] && path[currentCharIndex] != candidateScopeUpper[j])
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (j == candidateScope.Length &&
|
|
currentCharIndex < path.Length &&
|
|
path[currentCharIndex] == ':')
|
|
{
|
|
if (_flags == VariablePathFlags.None)
|
|
{
|
|
_flags = VariablePathFlags.Variable;
|
|
}
|
|
_flags |= candidateFlags;
|
|
lastScannedColon = currentCharIndex;
|
|
currentCharIndex += 1;
|
|
|
|
// If saw 'variable:', we need to look for a scope after 'variable:'.
|
|
if (candidateFlags == VariablePathFlags.Variable)
|
|
{
|
|
knownFlags = VariablePathFlags.Variable;
|
|
candidateScope = candidateScopeUpper = null;
|
|
candidateFlags = VariablePathFlags.None;
|
|
goto scanScope;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (_flags == VariablePathFlags.None)
|
|
{
|
|
lastScannedColon = path.IndexOf(':', currentCharIndex);
|
|
// No colon, or a colon as the first character means we have
|
|
// a simple variable, otherwise it's a drive.
|
|
if (lastScannedColon > 0)
|
|
{
|
|
_flags = VariablePathFlags.DriveQualified;
|
|
}
|
|
}
|
|
|
|
if (lastScannedColon == -1)
|
|
{
|
|
_unqualifiedPath = _userPath;
|
|
}
|
|
else
|
|
{
|
|
_unqualifiedPath = _userPath.Substring(lastScannedColon + 1);
|
|
}
|
|
|
|
if (_flags == VariablePathFlags.None)
|
|
{
|
|
_flags = VariablePathFlags.Unqualified | VariablePathFlags.Variable;
|
|
}
|
|
}
|
|
|
|
internal VariablePath CloneAndSetLocal()
|
|
{
|
|
Debug.Assert(IsUnscopedVariable, "Special method to clone, input must be unqualified");
|
|
|
|
VariablePath result = new VariablePath();
|
|
result._userPath = _userPath;
|
|
result._unqualifiedPath = _unqualifiedPath;
|
|
result._flags = VariablePathFlags.Local | VariablePathFlags.Variable;
|
|
return result;
|
|
}
|
|
|
|
#endregion Constructor
|
|
|
|
#region data accessors
|
|
|
|
/// <summary>
|
|
/// Gets the full path including any possibly specified scope and/or drive name.
|
|
/// </summary>
|
|
public string UserPath { get { return _userPath; } }
|
|
|
|
/// <summary>
|
|
/// Returns true if the path explicitly specifies 'global:'.
|
|
/// </summary>
|
|
public bool IsGlobal { get { return 0 != (_flags & VariablePathFlags.Global); } }
|
|
|
|
/// <summary>
|
|
/// Returns true if the path explicitly specifies 'local:'.
|
|
/// </summary>
|
|
public bool IsLocal { get { return 0 != (_flags & VariablePathFlags.Local); } }
|
|
|
|
/// <summary>
|
|
/// Returns true if the path explicitly specifies 'private:'.
|
|
/// </summary>
|
|
public bool IsPrivate { get { return 0 != (_flags & VariablePathFlags.Private); } }
|
|
|
|
/// <summary>
|
|
/// Returns true if the path explicitly specifies 'script:'.
|
|
/// </summary>
|
|
public bool IsScript { get { return 0 != (_flags & VariablePathFlags.Script); } }
|
|
|
|
/// <summary>
|
|
/// Returns true if the path specifies no drive or scope qualifiers.
|
|
/// </summary>
|
|
public bool IsUnqualified { get { return 0 != (_flags & VariablePathFlags.Unqualified); } }
|
|
|
|
/// <summary>
|
|
/// Returns true if the path specifies a variable path with no scope qualifiers.
|
|
/// </summary>
|
|
[SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Unscoped")]
|
|
public bool IsUnscopedVariable { get { return (0 == (_flags & VariablePathFlags.UnscopedVariableMask)); } }
|
|
|
|
/// <summary>
|
|
/// Returns true if the path defines a variable.
|
|
/// </summary>
|
|
public bool IsVariable { get { return 0 != (_flags & VariablePathFlags.Variable); } }
|
|
|
|
/// <summary>
|
|
/// Returns true if the path defines a function.
|
|
/// </summary>
|
|
internal bool IsFunction { get { return 0 != (_flags & VariablePathFlags.Function); } }
|
|
|
|
/// <summary>
|
|
/// Returns true if the path specifies a drive other than the variable drive.
|
|
/// </summary>
|
|
public bool IsDriveQualified { get { return 0 != (_flags & VariablePathFlags.DriveQualified); } }
|
|
|
|
/// <summary>
|
|
/// The drive name, or null if the path is for a variable.
|
|
/// It may also be null for some functions (specifically if this is a FunctionScopedItemLookupPath.)
|
|
/// </summary>
|
|
public string DriveName
|
|
{
|
|
get
|
|
{
|
|
if (!IsDriveQualified)
|
|
{
|
|
return null;
|
|
}
|
|
|
|
// The drive name is asked for infrequently. Lots of VariablePath
|
|
// objects are created, so rather than allocate an extra string that will
|
|
// always be null, just compute the drive name on demand.
|
|
return _userPath.Substring(0, _userPath.IndexOf(':'));
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets the namespace specific string
|
|
/// </summary>
|
|
internal string UnqualifiedPath
|
|
{
|
|
get { return _unqualifiedPath; }
|
|
}
|
|
|
|
/// <summary>
|
|
/// Return the drive qualified name, if any drive specified, otherwise the simple variable name.
|
|
/// </summary>
|
|
internal string QualifiedName
|
|
{
|
|
get { return IsDriveQualified ? _userPath : _unqualifiedPath; }
|
|
}
|
|
|
|
#endregion data accessors
|
|
|
|
/// <summary>
|
|
/// Helpful for debugging.
|
|
/// </summary>
|
|
public override string ToString()
|
|
{
|
|
return _userPath;
|
|
}
|
|
}
|
|
|
|
internal class FunctionLookupPath : VariablePath
|
|
{
|
|
internal FunctionLookupPath(string path)
|
|
: base(path, VariablePathFlags.Function | VariablePathFlags.Unqualified)
|
|
{
|
|
}
|
|
}
|
|
} // namespace System.Management.Automation
|