mirror of
https://github.com/PowerShell/PowerShell.git
synced 2024-11-28 04:13:31 +08:00
Fix dynamic class assembly name (#5292)
Using the assembly name to hint at the source of the classes was problematic in multiple ways. This change stores the actual filename in an attribute on the assembly. So for a given type, one can get the assembly this way: [SomeType].Assembly.GetCustomAttributes() | ? { $_ -is [System.Management.Automation.DynamicClassImplementationAssemblyAttribute] } | % { $_.ScriptFile }
This commit is contained in:
parent
237ccbdf6d
commit
71d5439bbe
@ -560,6 +560,10 @@ namespace System.Management.Automation
|
||||
[AttributeUsage(AttributeTargets.Assembly)]
|
||||
public class DynamicClassImplementationAssemblyAttribute : Attribute
|
||||
{
|
||||
/// <summary>
|
||||
/// The (possibly null) path to the file defining this class.
|
||||
/// </summary>
|
||||
public string ScriptFile { get; set; }
|
||||
}
|
||||
|
||||
#endregion Misc Attributes
|
||||
|
@ -16,10 +16,11 @@ namespace System.Management.Automation.Language
|
||||
{
|
||||
internal class TypeDefiner
|
||||
{
|
||||
internal const string DynamicClassAssemblyName = "PowerShell Class Assembly";
|
||||
|
||||
private static int s_globalCounter = 0;
|
||||
private static readonly object[] s_emptyArgArray = Utils.EmptyArray<object>();
|
||||
private static readonly CustomAttributeBuilder s_hiddenCustomAttributeBuilder =
|
||||
new CustomAttributeBuilder(typeof(HiddenAttribute).GetConstructor(Type.EmptyTypes), s_emptyArgArray);
|
||||
new CustomAttributeBuilder(typeof(HiddenAttribute).GetConstructor(Type.EmptyTypes), Utils.EmptyArray<object>());
|
||||
|
||||
private static readonly string s_sessionStateKeeperFieldName = "__sessionStateKeeper";
|
||||
internal static readonly string SessionStateFieldName = "__sessionState";
|
||||
@ -1100,30 +1101,40 @@ namespace System.Management.Automation.Language
|
||||
}
|
||||
}
|
||||
|
||||
private static IEnumerable<CustomAttributeBuilder> GetAssemblyAttributeBuilders()
|
||||
private static IEnumerable<CustomAttributeBuilder> GetAssemblyAttributeBuilders(string scriptFile)
|
||||
{
|
||||
yield return new CustomAttributeBuilder(typeof(DynamicClassImplementationAssemblyAttribute).GetConstructor(Type.EmptyTypes), s_emptyArgArray);
|
||||
var ctor = typeof(DynamicClassImplementationAssemblyAttribute).GetConstructor(Type.EmptyTypes);
|
||||
var emptyArgs = Utils.EmptyArray<object>();
|
||||
|
||||
if (string.IsNullOrEmpty(scriptFile)) {
|
||||
yield return new CustomAttributeBuilder(ctor, emptyArgs);
|
||||
yield break;
|
||||
}
|
||||
|
||||
var propertyInfo = new PropertyInfo[] {
|
||||
typeof(DynamicClassImplementationAssemblyAttribute).GetProperty(nameof(DynamicClassImplementationAssemblyAttribute.ScriptFile)) };
|
||||
var propertyArgs = new object[] { scriptFile };
|
||||
|
||||
yield return new CustomAttributeBuilder(ctor, emptyArgs,
|
||||
propertyInfo, propertyArgs, Utils.EmptyArray<FieldInfo>(), emptyArgs);
|
||||
|
||||
}
|
||||
|
||||
private static int counter = 0;
|
||||
internal static Assembly DefineTypes(Parser parser, Ast rootAst, TypeDefinitionAst[] typeDefinitions)
|
||||
{
|
||||
Diagnostics.Assert(rootAst.Parent == null, "Caller should only define types from the root ast");
|
||||
|
||||
var definedTypes = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
|
||||
|
||||
// First character is a special mark that allows us to cheaply ignore dynamic generated assemblies in ClrFacade.GetAssemblies()
|
||||
// The replaces at the end are for not-allowed characters. They are replaced by similar-looking chars.
|
||||
string assemblyName = ClrFacade.FIRST_CHAR_PSASSEMBLY_MARK + (string.IsNullOrWhiteSpace(rootAst.Extent.File)
|
||||
? "powershell"
|
||||
: rootAst.Extent.File
|
||||
.Replace('\\', (char)0x29f9)
|
||||
.Replace('/', (char)0x29f9)
|
||||
.Replace(',', (char)0x201a)
|
||||
.Replace(':', (char)0x0589));
|
||||
|
||||
var assembly = AssemblyBuilder.DefineDynamicAssembly(new AssemblyName(assemblyName),
|
||||
AssemblyBuilderAccess.RunAndCollect, GetAssemblyAttributeBuilders());
|
||||
var module = assembly.DefineDynamicModule(assemblyName);
|
||||
var assemblyName = new AssemblyName(DynamicClassAssemblyName)
|
||||
{
|
||||
// We could generate a unique name, but a unique version works too.
|
||||
Version = new Version(1, 0, 0, Interlocked.Increment(ref counter))
|
||||
};
|
||||
var assembly = AssemblyBuilder.DefineDynamicAssembly(assemblyName,
|
||||
AssemblyBuilderAccess.RunAndCollect, GetAssemblyAttributeBuilders(rootAst.Extent.File));
|
||||
var module = assembly.DefineDynamicModule(DynamicClassAssemblyName);
|
||||
|
||||
var defineTypeHelpers = new List<DefineTypeHelper>();
|
||||
var defineEnumHelpers = new List<DefineEnumHelper>();
|
||||
|
@ -41,12 +41,6 @@ namespace System.Management.Automation
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// We need it to avoid calling lookups inside dynamic assemblies with PS Types, so we exclude it from GetAssemblies().
|
||||
/// We use this convention for names to archive it.
|
||||
/// </summary>
|
||||
internal static readonly char FIRST_CHAR_PSASSEMBLY_MARK = (char)0x29f9;
|
||||
|
||||
#region Assembly
|
||||
|
||||
internal static IEnumerable<Assembly> GetAssemblies(TypeResolutionState typeResolutionState, TypeName typeName)
|
||||
@ -65,7 +59,8 @@ namespace System.Management.Automation
|
||||
internal static IEnumerable<Assembly> GetAssemblies(string namespaceQualifiedTypeName = null)
|
||||
{
|
||||
return PSAssemblyLoadContext.GetAssembly(namespaceQualifiedTypeName) ??
|
||||
AppDomain.CurrentDomain.GetAssemblies().Where(a => !(a.FullName.Length > 0 && a.FullName[0] == FIRST_CHAR_PSASSEMBLY_MARK));
|
||||
AppDomain.CurrentDomain.GetAssemblies().Where(a =>
|
||||
!TypeDefiner.DynamicClassAssemblyName.Equals(a.GetName().Name, StringComparison.Ordinal));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -688,6 +688,14 @@ Describe 'Type building' -Tags "CI" {
|
||||
++$a::a | Should Be 2
|
||||
}
|
||||
}
|
||||
|
||||
It 'should get the script from a class type' {
|
||||
class C {}
|
||||
|
||||
$a = [C].Assembly.GetCustomAttributes($false).Where{
|
||||
$_ -is [System.Management.Automation.DynamicClassImplementationAssemblyAttribute]}
|
||||
$a.ScriptFile | Should BeExactly $PSCommandPath
|
||||
}
|
||||
}
|
||||
|
||||
Describe 'RuntimeType created for TypeDefinitionAst' -Tags "CI" {
|
||||
|
Loading…
Reference in New Issue
Block a user