diff --git a/src/System.Management.Automation/engine/CommandInfo.cs b/src/System.Management.Automation/engine/CommandInfo.cs
index 2c4cd08eb75..9f1168de64e 100644
--- a/src/System.Management.Automation/engine/CommandInfo.cs
+++ b/src/System.Management.Automation/engine/CommandInfo.cs
@@ -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
@@ -793,6 +797,17 @@ public PSTypeName(string name)
_type = null;
}
+ ///
+ /// This constructor is used when the creating a PSObject with a custom typename.
+ ///
+ /// The name of the type.
+ /// The real type.
+ public PSTypeName(string name, Type type)
+ {
+ Name = name;
+ _type = type;
+ }
+
///
/// This constructor is used when the type is defined in PowerShell.
///
@@ -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;
+ }
+ }
+
+ ///
+ /// Represents dynamic types such as ,
+ /// 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.
+ ///
+ internal class PSSyntheticTypeName : PSTypeName
+ {
+ internal static PSSyntheticTypeName Create(string typename, IList membersTypes) => Create(new PSTypeName(typename), membersTypes);
+
+ internal static PSSyntheticTypeName Create(Type type, IList membersTypes) => Create(new PSTypeName(type), membersTypes);
+
+ internal static PSSyntheticTypeName Create(PSTypeName typename, IList membersTypes)
+ {
+ var typeName = GetMemberTypeProjection(typename.Name, membersTypes);
+ var members = new List();
+ 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 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 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 Members { get; }
+ }
+
internal interface IScriptCommandInfo
{
ScriptBlock ScriptBlock { get; }
diff --git a/src/System.Management.Automation/engine/MshMemberInfo.cs b/src/System.Management.Automation/engine/MshMemberInfo.cs
index a49351846fe..6f50677a8f1 100644
--- a/src/System.Management.Automation/engine/MshMemberInfo.cs
+++ b/src/System.Management.Automation/engine/MshMemberInfo.cs
@@ -83,9 +83,13 @@ public enum PSMemberTypes
///
Dynamic = 4096,
///
+ /// Members that are inferred by type inference for PSObject and hashtable.
+ ///
+ InferredProperty = 8192,
+ ///
/// All property member types
///
- Properties = AliasProperty | CodeProperty | Property | NoteProperty | ScriptProperty,
+ Properties = AliasProperty | CodeProperty | Property | NoteProperty | ScriptProperty | InferredProperty,
///
/// All method member types
///
@@ -954,6 +958,34 @@ public override string TypeNameOfValue
}
+ ///
+ /// Type used to capture the properties inferred from Hashtable and PSObject.
+ ///
+ 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}";
+ }
+
///
/// Used to access the adapted or base properties from the BaseObject
///
diff --git a/src/System.Management.Automation/engine/parser/TypeInferenceVisitor.cs b/src/System.Management.Automation/engine/parser/TypeInferenceVisitor.cs
index 8a2ea1a68c3..482d212e1b4 100644
--- a/src/System.Management.Automation/engine/parser/TypeInferenceVisitor.cs
+++ b/src/System.Management.Automation/engine/parser/TypeInferenceVisitor.cs
@@ -8,6 +8,7 @@
using System.Management.Automation.Language;
using System.Management.Automation.Runspaces;
using System.Reflection;
+using System.Text;
using System.Text.RegularExpressions;
using Microsoft.PowerShell.Commands;
@@ -174,6 +175,14 @@ internal IList