Skip to content
Merged
Show file tree
Hide file tree
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
104 changes: 104 additions & 0 deletions src/System.Management.Automation/engine/CommandInfo.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,15 @@

using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Management.Automation.Language;
using System.Management.Automation.Runspaces;
using System.Reflection;
using System.Runtime.ExceptionServices;
using System.Text;

using Microsoft.PowerShell.Commands;

namespace System.Management.Automation
Expand Down Expand Up @@ -793,6 +797,17 @@ public PSTypeName(string name)
_type = null;
}

/// <summary>
/// This constructor is used when the creating a PSObject with a custom typename.
/// </summary>
/// <param name="name">The name of the type.</param>
/// <param name="type">The real type.</param>
public PSTypeName(string name, Type type)
{
Name = name;
_type = type;
}

/// <summary>
/// This constructor is used when the type is defined in PowerShell.
/// </summary>
Expand Down Expand Up @@ -901,6 +916,95 @@ public override string ToString()
}
}

[DebuggerDisplay("{PSTypeName} {Name}")]
internal struct PSMemberNameAndType
{
public readonly string Name;

public readonly PSTypeName PSTypeName;

public readonly object Value;

public PSMemberNameAndType(string name, PSTypeName typeName, object value = null)
{
Name = name;
PSTypeName = typeName;
Value = value;
}
}

/// <summary>
/// Represents dynamic types such as <see cref="System.Management.Automation.PSObject"/>,
/// but can be used where a real type might not be available, in which case the name of the type can be used.
/// The type encodes the members of dynamic objects in the type name.
/// </summary>
internal class PSSyntheticTypeName : PSTypeName
{
internal static PSSyntheticTypeName Create(string typename, IList<PSMemberNameAndType> membersTypes) => Create(new PSTypeName(typename), membersTypes);

internal static PSSyntheticTypeName Create(Type type, IList<PSMemberNameAndType> membersTypes) => Create(new PSTypeName(type), membersTypes);

internal static PSSyntheticTypeName Create(PSTypeName typename, IList<PSMemberNameAndType> membersTypes)
{
var typeName = GetMemberTypeProjection(typename.Name, membersTypes);
var members = new List<PSMemberNameAndType>();
members.AddRange(membersTypes);
members.Sort((c1,c2) =>string.Compare(c1.Name, c2.Name, StringComparison.CurrentCultureIgnoreCase));
return new PSSyntheticTypeName(typeName, typename.Type, members);
}

private PSSyntheticTypeName(string typeName, Type type, IList<PSMemberNameAndType> membersTypes)
: base(typeName, type)
{
Members = membersTypes;
if (type != typeof(PSObject))
{
return;
}

for (int i = 0; i < Members.Count; i++)
{
var psMemberNameAndType = Members[i];
if (IsPSTypeName(psMemberNameAndType))
{
Members.RemoveAt(i);
break;
}
}
}

private static bool IsPSTypeName(PSMemberNameAndType member) => member.Name.Equals(nameof(PSTypeName), StringComparison.CurrentCultureIgnoreCase);

private static string GetMemberTypeProjection(string typename, IList<PSMemberNameAndType> members)
{
if (typename == typeof(PSObject).FullName)
{
foreach (var mem in members)
{
if (IsPSTypeName(mem))
{
typename = mem.Value.ToString();
}
}
}

var builder = new StringBuilder(typename, members.Count * 7);
builder.Append('#');
foreach (var m in members.OrderBy(m => m.Name))
{
if (!IsPSTypeName(m))
{
builder.Append(m.Name).Append(":");
}
}

builder.Length--;
return builder.ToString();
}

public IList<PSMemberNameAndType> Members { get; }
}

internal interface IScriptCommandInfo
{
ScriptBlock ScriptBlock { get; }
Expand Down
34 changes: 33 additions & 1 deletion src/System.Management.Automation/engine/MshMemberInfo.cs
Original file line number Diff line number Diff line change
Expand Up @@ -83,9 +83,13 @@ public enum PSMemberTypes
/// </summary>
Dynamic = 4096,
/// <summary>
/// Members that are inferred by type inference for PSObject and hashtable.
/// </summary>
InferredProperty = 8192,
/// <summary>
/// All property member types
/// </summary>
Properties = AliasProperty | CodeProperty | Property | NoteProperty | ScriptProperty,
Properties = AliasProperty | CodeProperty | Property | NoteProperty | ScriptProperty | InferredProperty,
/// <summary>
/// All method member types
/// </summary>
Expand Down Expand Up @@ -954,6 +958,34 @@ public override string TypeNameOfValue

}

/// <summary>
/// Type used to capture the properties inferred from Hashtable and PSObject.
/// </summary>
internal class PSInferredProperty : PSPropertyInfo
{
public PSInferredProperty(string name, PSTypeName typeName)
{
this.name = name;
TypeName = typeName;
}

internal PSTypeName TypeName { get; }

public override PSMemberTypes MemberType => PSMemberTypes.InferredProperty;

public override object Value { get; set; }

public override string TypeNameOfValue => TypeName.Name;

public override PSMemberInfo Copy() => new PSInferredProperty(Name, TypeName);

public override bool IsSettable => false;

public override bool IsGettable => false;

public override string ToString() => $"{ToStringCodeMethods.Type(TypeName.Type)} {Name}";
}

/// <summary>
/// Used to access the adapted or base properties from the BaseObject
/// </summary>
Expand Down
Loading