PowerShell/src/System.Management.Automation/engine/parser/PSType.cs
xtqqczze 883ca98dd7
Seal private classes (#15725)
* Seal private classes

* Fix CS0509

* Fix CS0628
2021-07-19 14:09:12 +05:00

1476 lines
68 KiB
C#

// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Globalization;
using System.Linq;
using System.Management.Automation.Internal;
using System.Reflection;
using System.Reflection.Emit;
using System.Threading;
using Microsoft.PowerShell;
namespace System.Management.Automation.Language
{
internal static class TypeDefiner
{
internal const string DynamicClassAssemblyName = "PowerShell Class Assembly";
internal const string DynamicClassAssemblyFullNamePrefix = "PowerShell Class Assembly,";
private static int s_globalCounter = 0;
private static readonly CustomAttributeBuilder s_hiddenCustomAttributeBuilder =
new CustomAttributeBuilder(typeof(HiddenAttribute).GetConstructor(Type.EmptyTypes), Array.Empty<object>());
private static readonly string s_sessionStateKeeperFieldName = "__sessionStateKeeper";
internal static readonly string SessionStateFieldName = "__sessionState";
private static readonly MethodInfo s_sessionStateKeeper_GetSessionState =
typeof(SessionStateKeeper).GetMethod("GetSessionState", BindingFlags.Instance | BindingFlags.Public);
private static bool TryConvertArg(object arg, Type type, out object result, Parser parser, IScriptExtent errorExtent)
{
// This code could be added to LanguagePrimitives.ConvertTo
if (arg != null && arg.GetType() == type)
{
result = arg;
return true;
}
if (!LanguagePrimitives.TryConvertTo(arg, type, out result))
{
parser.ReportError(errorExtent,
nameof(ParserStrings.CannotConvertValue),
ParserStrings.CannotConvertValue,
ToStringCodeMethods.Type(type));
return false;
}
return true;
}
private static CustomAttributeBuilder GetAttributeBuilder(Parser parser, AttributeAst attributeAst, AttributeTargets attributeTargets)
{
var attributeType = attributeAst.TypeName.GetReflectionAttributeType();
Diagnostics.Assert(attributeType != null, "Semantic checks should have verified attribute type exists");
Diagnostics.Assert(
attributeType.GetCustomAttribute<AttributeUsageAttribute>(true) == null ||
(attributeType.GetCustomAttribute<AttributeUsageAttribute>(true).ValidOn & attributeTargets) != 0, "Semantic checks should have verified attribute usage");
var positionalArgs = new object[attributeAst.PositionalArguments.Count];
var cvv = new ConstantValueVisitor { AttributeArgument = false };
for (var i = 0; i < attributeAst.PositionalArguments.Count; i++)
{
var posArg = attributeAst.PositionalArguments[i];
positionalArgs[i] = posArg.Accept(cvv);
}
var ctorInfos = attributeType.GetConstructors();
var newConstructors = DotNetAdapter.GetMethodInformationArray(ctorInfos);
string errorId = null;
string errorMsg = null;
bool expandParamsOnBest;
bool callNonVirtually;
var positionalArgCount = positionalArgs.Length;
var bestMethod = Adapter.FindBestMethod(
newConstructors,
invocationConstraints: null,
allowCastingToByRefLikeType: false,
positionalArgs,
ref errorId,
ref errorMsg,
out expandParamsOnBest,
out callNonVirtually);
if (bestMethod == null)
{
parser.ReportError(new ParseError(attributeAst.Extent, errorId,
string.Format(CultureInfo.InvariantCulture, errorMsg, attributeType.Name, attributeAst.PositionalArguments.Count)));
return null;
}
var constructorInfo = (ConstructorInfo)bestMethod.method;
var parameterInfo = constructorInfo.GetParameters();
var ctorArgs = new object[parameterInfo.Length];
object arg;
for (var argIndex = 0; argIndex < parameterInfo.Length; ++argIndex)
{
var resultType = parameterInfo[argIndex].ParameterType;
// The extension method 'CustomAttributeExtensions.GetCustomAttributes(ParameterInfo, Type, Boolean)' has inconsistent
// behavior on its return value in both FullCLR and CoreCLR. According to MSDN, if the attribute cannot be found, it
// should return an empty collection. However, it returns null in some rare cases [when the parameter isn't backed by
// actual metadata].
// This inconsistent behavior affects OneCore powershell because we are using the extension method here when compiling
// against CoreCLR. So we need to add a null check until this is fixed in CLR.
var paramArrayAttrs = parameterInfo[argIndex].GetCustomAttributes(typeof(ParamArrayAttribute), true);
if (paramArrayAttrs != null && paramArrayAttrs.Length > 0 && expandParamsOnBest)
{
var elementType = parameterInfo[argIndex].ParameterType.GetElementType();
var paramsArray = Array.CreateInstance(elementType, positionalArgCount - argIndex);
ctorArgs[argIndex] = paramsArray;
for (var i = 0; i < paramsArray.Length; ++i, ++argIndex)
{
if (!TryConvertArg(positionalArgs[argIndex], elementType, out arg,
parser, attributeAst.PositionalArguments[argIndex].Extent))
{
return null;
}
paramsArray.SetValue(arg, i);
}
break;
}
if (!TryConvertArg(positionalArgs[argIndex], resultType, out arg,
parser, attributeAst.PositionalArguments[argIndex].Extent))
{
return null;
}
ctorArgs[argIndex] = arg;
}
if (attributeAst.NamedArguments.Count == 0)
{
return new CustomAttributeBuilder(constructorInfo, ctorArgs);
}
var propertyInfoList = new List<PropertyInfo>();
var propertyArgs = new List<object>();
var fieldInfoList = new List<FieldInfo>();
var fieldArgs = new List<object>();
foreach (var namedArg in attributeAst.NamedArguments)
{
var name = namedArg.ArgumentName;
var members = attributeType.GetMember(name, MemberTypes.Field | MemberTypes.Property,
BindingFlags.IgnoreCase | BindingFlags.Public | BindingFlags.Instance | BindingFlags.FlattenHierarchy);
Diagnostics.Assert(members.Length == 1 && (members[0] is PropertyInfo || members[0] is FieldInfo),
"Semantic checks should have ensured names attribute argument exists");
arg = namedArg.Argument.Accept(cvv);
var propertyInfo = members[0] as PropertyInfo;
if (propertyInfo != null)
{
Diagnostics.Assert(propertyInfo.GetSetMethod() != null, "Semantic checks ensures property is settable");
if (!TryConvertArg(arg, propertyInfo.PropertyType, out arg, parser, namedArg.Argument.Extent))
{
return null;
}
propertyInfoList.Add(propertyInfo);
propertyArgs.Add(arg);
continue;
}
var fieldInfo = (FieldInfo)members[0];
Diagnostics.Assert(!fieldInfo.IsInitOnly && !fieldInfo.IsLiteral, "Semantic checks ensures field is settable");
if (!TryConvertArg(arg, fieldInfo.FieldType, out arg, parser, namedArg.Argument.Extent))
{
return null;
}
fieldInfoList.Add(fieldInfo);
fieldArgs.Add(arg);
}
return new CustomAttributeBuilder(constructorInfo, ctorArgs,
propertyInfoList.ToArray(), propertyArgs.ToArray(),
fieldInfoList.ToArray(), fieldArgs.ToArray());
}
internal static void DefineCustomAttributes(TypeBuilder member, ReadOnlyCollection<AttributeAst> attributes, Parser parser, AttributeTargets attributeTargets)
{
if (attributes != null)
{
foreach (var attr in attributes)
{
var cabuilder = GetAttributeBuilder(parser, attr, attributeTargets);
if (cabuilder != null)
{
member.SetCustomAttribute(cabuilder);
}
}
}
}
internal static void DefineCustomAttributes(PropertyBuilder member, ReadOnlyCollection<AttributeAst> attributes, Parser parser, AttributeTargets attributeTargets)
{
if (attributes != null)
{
foreach (var attr in attributes)
{
var cabuilder = GetAttributeBuilder(parser, attr, attributeTargets);
if (cabuilder != null)
{
member.SetCustomAttribute(cabuilder);
}
}
}
}
internal static void DefineCustomAttributes(ConstructorBuilder member, ReadOnlyCollection<AttributeAst> attributes, Parser parser, AttributeTargets attributeTargets)
{
if (attributes != null)
{
foreach (var attr in attributes)
{
var cabuilder = GetAttributeBuilder(parser, attr, attributeTargets);
if (cabuilder != null)
{
member.SetCustomAttribute(cabuilder);
}
}
}
}
internal static void DefineCustomAttributes(MethodBuilder member, ReadOnlyCollection<AttributeAst> attributes, Parser parser, AttributeTargets attributeTargets)
{
if (attributes != null)
{
foreach (var attr in attributes)
{
var cabuilder = GetAttributeBuilder(parser, attr, attributeTargets);
if (cabuilder != null)
{
member.SetCustomAttribute(cabuilder);
}
}
}
}
internal static void DefineCustomAttributes(EnumBuilder member, ReadOnlyCollection<AttributeAst> attributes, Parser parser, AttributeTargets attributeTargets)
{
if (attributes != null)
{
foreach (var attr in attributes)
{
var cabuilder = GetAttributeBuilder(parser, attr, attributeTargets);
if (cabuilder != null)
{
member.SetCustomAttribute(cabuilder);
}
}
}
}
private sealed class DefineTypeHelper
{
private readonly Parser _parser;
internal readonly TypeDefinitionAst _typeDefinitionAst;
internal readonly TypeBuilder _typeBuilder;
internal readonly FieldBuilder _sessionStateField;
internal readonly FieldBuilder _sessionStateKeeperField;
internal readonly ModuleBuilder _moduleBuilder;
internal readonly TypeBuilder _staticHelpersTypeBuilder;
private readonly Dictionary<string, PropertyMemberAst> _definedProperties;
private readonly Dictionary<string, List<Tuple<FunctionMemberAst, Type[]>>> _definedMethods;
private HashSet<Tuple<string, Type>> _interfaceProperties;
internal readonly List<(string fieldName, IParameterMetadataProvider bodyAst, bool isStatic)> _fieldsToInitForMemberFunctions;
private bool _baseClassHasDefaultCtor;
/// <summary>
/// If type has fatal errors we cannot construct .NET type from it.
/// TypeBuilder.CreateTypeInfo() would throw exception.
/// </summary>
public bool HasFatalErrors { get; private set; }
public DefineTypeHelper(Parser parser, ModuleBuilder module, TypeDefinitionAst typeDefinitionAst, string typeName)
{
_moduleBuilder = module;
_parser = parser;
_typeDefinitionAst = typeDefinitionAst;
List<Type> interfaces;
var baseClass = this.GetBaseTypes(parser, typeDefinitionAst, out interfaces);
_typeBuilder = module.DefineType(typeName, Reflection.TypeAttributes.Class | Reflection.TypeAttributes.Public, baseClass, interfaces.ToArray());
_staticHelpersTypeBuilder = module.DefineType(string.Format(CultureInfo.InvariantCulture, "{0}_<staticHelpers>", typeName), Reflection.TypeAttributes.Class);
DefineCustomAttributes(_typeBuilder, typeDefinitionAst.Attributes, _parser, AttributeTargets.Class);
_typeDefinitionAst.Type = _typeBuilder;
_fieldsToInitForMemberFunctions = new List<(string, IParameterMetadataProvider, bool)>();
_definedMethods = new Dictionary<string, List<Tuple<FunctionMemberAst, Type[]>>>(StringComparer.OrdinalIgnoreCase);
_definedProperties = new Dictionary<string, PropertyMemberAst>(StringComparer.OrdinalIgnoreCase);
_sessionStateField = _typeBuilder.DefineField(SessionStateFieldName, typeof(SessionStateInternal), FieldAttributes.Private);
_sessionStateKeeperField = _staticHelpersTypeBuilder.DefineField(s_sessionStateKeeperFieldName, typeof(SessionStateKeeper), FieldAttributes.Assembly | FieldAttributes.Static);
}
/// <summary>
/// Return base class type, never return null.
/// </summary>
/// <param name="parser"></param>
/// <param name="typeDefinitionAst"></param>
/// <param name="interfaces">Return declared interfaces.</param>
/// <returns></returns>
private Type GetBaseTypes(Parser parser, TypeDefinitionAst typeDefinitionAst, out List<Type> interfaces)
{
// Define base types and report errors.
Type baseClass = null;
interfaces = new List<Type>();
// Default base class is System.Object and it has a default ctor.
_baseClassHasDefaultCtor = true;
if (typeDefinitionAst.BaseTypes.Count > 0)
{
// base class
var baseTypeAsts = typeDefinitionAst.BaseTypes;
var firstBaseTypeAst = baseTypeAsts[0];
if (firstBaseTypeAst.TypeName.IsArray)
{
parser.ReportError(firstBaseTypeAst.Extent,
nameof(ParserStrings.SubtypeArray),
ParserStrings.SubtypeArray,
firstBaseTypeAst.TypeName.FullName);
// fall to the default base type
}
else
{
baseClass = firstBaseTypeAst.TypeName.GetReflectionType();
if (baseClass == null)
{
parser.ReportError(firstBaseTypeAst.Extent,
nameof(ParserStrings.TypeNotFound),
ParserStrings.TypeNotFound,
firstBaseTypeAst.TypeName.FullName);
// fall to the default base type
}
else
{
if (baseClass.IsSealed)
{
parser.ReportError(firstBaseTypeAst.Extent,
nameof(ParserStrings.SealedBaseClass),
ParserStrings.SealedBaseClass,
baseClass.Name);
// ignore base type if it's sealed.
baseClass = null;
}
else if (baseClass.IsGenericType && !baseClass.IsConstructedGenericType)
{
parser.ReportError(firstBaseTypeAst.Extent,
nameof(ParserStrings.SubtypeUnclosedGeneric),
ParserStrings.SubtypeUnclosedGeneric,
baseClass.Name);
// ignore base type, we cannot inherit from unclosed generic.
baseClass = null;
}
else if (baseClass.IsInterface)
{
// First Ast can represent interface as well as BaseClass.
interfaces.Add(baseClass);
baseClass = null;
}
}
}
if (baseClass != null)
{
// All PS classes are TypeName instances.
// For PS classes we cannot use reflection API, because type is not created yet.
var baseTypeName = firstBaseTypeAst.TypeName as TypeName;
if (baseTypeName != null)
{
_baseClassHasDefaultCtor = baseTypeName.HasDefaultCtor();
}
else
{
_baseClassHasDefaultCtor = baseClass.HasDefaultCtor();
}
}
for (int i = 0; i < baseTypeAsts.Count; i++)
{
if (baseTypeAsts[i].TypeName.IsArray)
{
parser.ReportError(baseTypeAsts[i].Extent,
nameof(ParserStrings.SubtypeArray),
ParserStrings.SubtypeArray,
baseTypeAsts[i].TypeName.FullName);
this.HasFatalErrors = true;
}
}
for (int i = 1; i < baseTypeAsts.Count; i++)
{
if (baseTypeAsts[i].TypeName.IsArray)
{
parser.ReportError(baseTypeAsts[i].Extent,
nameof(ParserStrings.SubtypeArray),
ParserStrings.SubtypeArray,
baseTypeAsts[i].TypeName.FullName);
}
else
{
Type interfaceType = baseTypeAsts[i].TypeName.GetReflectionType();
if (interfaceType == null)
{
parser.ReportError(baseTypeAsts[i].Extent,
nameof(ParserStrings.TypeNotFound),
ParserStrings.TypeNotFound,
baseTypeAsts[i].TypeName.FullName);
}
else
{
if (interfaceType.IsInterface)
{
interfaces.Add(interfaceType);
}
else
{
parser.ReportError(baseTypeAsts[i].Extent,
nameof(ParserStrings.InterfaceNameExpected),
ParserStrings.InterfaceNameExpected,
interfaceType.Name);
}
}
}
}
}
return baseClass ?? typeof(object);
}
private bool ShouldImplementProperty(string name, Type type)
{
if (_interfaceProperties == null)
{
_interfaceProperties = new HashSet<Tuple<string, Type>>();
var allInterfaces = new HashSet<Type>();
// TypeBuilder.GetInterfaces() returns only the interfaces that was explicitly passed to its constructor.
// During compilation the interface hierarchy is flattened, so we only need to resolve one level of ancestral interfaces.
foreach (var interfaceType in _typeBuilder.GetInterfaces())
{
foreach (var parentInterface in interfaceType.GetInterfaces())
{
allInterfaces.Add(parentInterface);
}
allInterfaces.Add(interfaceType);
}
foreach (var interfaceType in allInterfaces)
{
foreach (var property in interfaceType.GetProperties())
{
_interfaceProperties.Add(Tuple.Create(property.Name, property.PropertyType));
}
}
}
return _interfaceProperties.Contains(Tuple.Create(name, type));
}
public void DefineMembers()
{
// If user didn't provide any instance ctors or static ctor we will generate default ctor or static ctor respectively.
// We can avoid explicit default ctor and static ctor, if we don't have any properties to initialize.
bool needStaticCtor = false;
bool needDefaultCtor = false;
bool hasAnyMethods = false;
List<FunctionMemberAst> staticCtors = new List<FunctionMemberAst>();
List<FunctionMemberAst> instanceCtors = new List<FunctionMemberAst>();
foreach (var member in _typeDefinitionAst.Members)
{
var propertyMemberAst = member as PropertyMemberAst;
if (propertyMemberAst != null)
{
DefineProperty(propertyMemberAst);
if (propertyMemberAst.InitialValue != null)
{
if (propertyMemberAst.IsStatic)
{
needStaticCtor = true;
}
else
{
needDefaultCtor = true;
}
}
}
else
{
FunctionMemberAst method = member as FunctionMemberAst;
Diagnostics.Assert(method != null, StringUtil.Format("Unexpected subtype of MemberAst '{0}'. Expect `{1}`",
member.GetType().Name, typeof(FunctionMemberAst).GetType().Name));
if (method.IsConstructor)
{
if (method.IsStatic)
{
staticCtors.Add(method);
}
else
{
instanceCtors.Add(method);
}
}
hasAnyMethods = true;
DefineMethod(method);
}
}
// inside ctor we put logic to capture session state from execution context,
// we cannot delegate default ctor creation to _typeBuilder, if we have any methods.
// If there are only static methods, we still want to capture context to allow static method calls on instances in the right context.
if (hasAnyMethods)
{
needDefaultCtor = true;
}
if (needStaticCtor)
{
foreach (var ctor in staticCtors)
{
var parameters = ((IParameterMetadataProvider)ctor.Body).Parameters;
// We report error for static ctors with parameters, even with default values.
// We don't take them into account.
if (parameters == null || parameters.Count == 0)
{
needStaticCtor = false;
}
}
}
if (needDefaultCtor)
{
needDefaultCtor = instanceCtors.Count == 0;
}
//// Now we can decide to create explicit default ctors or report error.
if (needStaticCtor)
{
var staticCtorAst = new CompilerGeneratedMemberFunctionAst(PositionUtilities.EmptyExtent, _typeDefinitionAst, SpecialMemberFunctionType.StaticConstructor);
DefineConstructor(staticCtorAst, null, true, Reflection.MethodAttributes.Private | Reflection.MethodAttributes.Static, Type.EmptyTypes);
}
if (_baseClassHasDefaultCtor)
{
if (needDefaultCtor)
{
var defaultCtorAst = new CompilerGeneratedMemberFunctionAst(PositionUtilities.EmptyExtent, _typeDefinitionAst, SpecialMemberFunctionType.DefaultConstructor);
DefineConstructor(defaultCtorAst, null, true, Reflection.MethodAttributes.Public, Type.EmptyTypes);
}
}
else
{
if (instanceCtors.Count == 0)
{
_parser.ReportError(_typeDefinitionAst.Extent,
nameof(ParserStrings.BaseClassNoDefaultCtor),
ParserStrings.BaseClassNoDefaultCtor,
_typeBuilder.BaseType.Name);
this.HasFatalErrors = true;
}
}
}
private void DefineProperty(PropertyMemberAst propertyMemberAst)
{
if (_definedProperties.ContainsKey(propertyMemberAst.Name))
{
_parser.ReportError(propertyMemberAst.Extent,
nameof(ParserStrings.MemberAlreadyDefined),
ParserStrings.MemberAlreadyDefined,
propertyMemberAst.Name);
return;
}
_definedProperties.Add(propertyMemberAst.Name, propertyMemberAst);
Type type;
if (propertyMemberAst.PropertyType == null)
{
type = typeof(object);
}
else
{
type = propertyMemberAst.PropertyType.TypeName.GetReflectionType();
Diagnostics.Assert(type != null, "Semantic checks should have ensure type can't be null");
}
PropertyBuilder property = this.EmitPropertyIl(propertyMemberAst, type);
// Define custom attributes on the property, not on the backingField
DefineCustomAttributes(property, propertyMemberAst.Attributes, _parser, AttributeTargets.Field | AttributeTargets.Property);
}
private PropertyBuilder EmitPropertyIl(PropertyMemberAst propertyMemberAst, Type type)
{
// backing field is always private.
var backingFieldAttributes = FieldAttributes.Private;
// The property set and property get methods require a special set of attributes.
var getSetAttributes = Reflection.MethodAttributes.SpecialName | Reflection.MethodAttributes.HideBySig;
getSetAttributes |= propertyMemberAst.IsPublic ? Reflection.MethodAttributes.Public : Reflection.MethodAttributes.Private;
if (ShouldImplementProperty(propertyMemberAst.Name, type))
{
getSetAttributes |= Reflection.MethodAttributes.Virtual;
}
if (propertyMemberAst.IsStatic)
{
backingFieldAttributes |= FieldAttributes.Static;
getSetAttributes |= Reflection.MethodAttributes.Static;
}
// C# naming convention for backing fields.
string backingFieldName = string.Format(CultureInfo.InvariantCulture, "<{0}>k__BackingField", propertyMemberAst.Name);
var backingField = _typeBuilder.DefineField(backingFieldName, type, backingFieldAttributes);
bool hasValidateAttributes = false;
if (propertyMemberAst.Attributes != null)
{
for (int i = 0; i < propertyMemberAst.Attributes.Count; i++)
{
Type attributeType = propertyMemberAst.Attributes[i].TypeName.GetReflectionAttributeType();
if (attributeType != null && attributeType.IsSubclassOf(typeof(ValidateArgumentsAttribute)))
{
hasValidateAttributes = true;
break;
}
}
}
// The last argument of DefineProperty is null, because the property has no parameters.
PropertyBuilder property = _typeBuilder.DefineProperty(propertyMemberAst.Name, Reflection.PropertyAttributes.None, type, null);
// Define the "get" accessor method.
MethodBuilder getMethod = _typeBuilder.DefineMethod(string.Concat("get_", propertyMemberAst.Name), getSetAttributes, type, Type.EmptyTypes);
ILGenerator getIlGen = getMethod.GetILGenerator();
if (propertyMemberAst.IsStatic)
{
// static
getIlGen.Emit(OpCodes.Ldsfld, backingField);
getIlGen.Emit(OpCodes.Ret);
}
else
{
// instance
getIlGen.Emit(OpCodes.Ldarg_0);
getIlGen.Emit(OpCodes.Ldfld, backingField);
getIlGen.Emit(OpCodes.Ret);
}
// Define the "set" accessor method.
MethodBuilder setMethod = _typeBuilder.DefineMethod(string.Concat("set_", propertyMemberAst.Name), getSetAttributes, null, new Type[] { type });
ILGenerator setIlGen = setMethod.GetILGenerator();
if (hasValidateAttributes)
{
Type typeToLoad = _typeBuilder;
setIlGen.Emit(OpCodes.Ldtoken, typeToLoad);
setIlGen.Emit(OpCodes.Call, typeof(Type).GetMethod("GetTypeFromHandle")); // load current Type on stack
setIlGen.Emit(OpCodes.Ldstr, propertyMemberAst.Name); // load name of Property
setIlGen.Emit(propertyMemberAst.IsStatic ? OpCodes.Ldarg_0 : OpCodes.Ldarg_1); // load set value
if (type.IsValueType)
{
setIlGen.Emit(OpCodes.Box, type);
}
setIlGen.Emit(OpCodes.Call, CachedReflectionInfo.ClassOps_ValidateSetProperty);
}
if (propertyMemberAst.IsStatic)
{
setIlGen.Emit(OpCodes.Ldarg_0);
setIlGen.Emit(OpCodes.Stsfld, backingField);
}
else
{
setIlGen.Emit(OpCodes.Ldarg_0);
setIlGen.Emit(OpCodes.Ldarg_1);
setIlGen.Emit(OpCodes.Stfld, backingField);
}
setIlGen.Emit(OpCodes.Ret);
// Map the two methods created above to our PropertyBuilder to
// their corresponding behaviors, "get" and "set" respectively.
property.SetGetMethod(getMethod);
property.SetSetMethod(setMethod);
if (propertyMemberAst.IsHidden)
{
property.SetCustomAttribute(s_hiddenCustomAttributeBuilder);
}
return property;
}
private bool CheckForDuplicateOverload(FunctionMemberAst functionMemberAst, Type[] newParameters)
{
List<Tuple<FunctionMemberAst, Type[]>> overloads;
if (!_definedMethods.TryGetValue(functionMemberAst.Name, out overloads))
{
overloads = new List<Tuple<FunctionMemberAst, Type[]>>();
_definedMethods.Add(functionMemberAst.Name, overloads);
}
else
{
foreach (var overload in overloads)
{
var overloadParameters = overload.Item2;
// This test won't be correct when defaults are supported
if (newParameters.Length != overloadParameters.Length)
{
continue;
}
var sameSignature = true;
for (int i = 0; i < newParameters.Length; i++)
{
if (newParameters[i] != overloadParameters[i])
{
sameSignature = false;
break;
}
}
if (sameSignature)
{
// If both are both static/instance, it's an error.
// Otherwise, signatures can match only for the constructor.
if (overload.Item1.IsStatic == functionMemberAst.IsStatic ||
!functionMemberAst.IsConstructor)
{
_parser.ReportError(functionMemberAst.NameExtent ?? functionMemberAst.Extent,
nameof(ParserStrings.MemberAlreadyDefined),
ParserStrings.MemberAlreadyDefined,
functionMemberAst.Name);
return true;
}
}
}
}
overloads.Add(Tuple.Create(functionMemberAst, newParameters));
return false;
}
private Type[] GetParameterTypes(FunctionMemberAst functionMemberAst)
{
var parameters = ((IParameterMetadataProvider)functionMemberAst).Parameters;
if (parameters == null)
{
return Type.EmptyTypes;
}
bool anyErrors = false;
var result = new Type[parameters.Count];
for (var i = 0; i < parameters.Count; i++)
{
var typeConstraint = parameters[i].Attributes.OfType<TypeConstraintAst>().FirstOrDefault();
var paramType = (typeConstraint != null)
? typeConstraint.TypeName.GetReflectionType()
: typeof(object);
if (paramType == null)
{
_parser.ReportError(typeConstraint.Extent,
nameof(ParserStrings.TypeNotFound),
ParserStrings.TypeNotFound,
typeConstraint.TypeName.FullName);
anyErrors = true;
}
else if (paramType == typeof(void) || paramType.IsGenericTypeDefinition)
{
_parser.ReportError(typeConstraint.Extent,
nameof(ParserStrings.TypeNotAllowedForParameter),
ParserStrings.TypeNotAllowedForParameter,
typeConstraint.TypeName.FullName);
anyErrors = true;
}
result[i] = paramType;
}
return anyErrors ? null : result;
}
private bool MethodExistsOnBaseClassAndFinal(string methodName, Type[] parameterTypes)
{
Type baseType = _typeBuilder.BaseType;
// If baseType is PS class, then method will be virtual, once we define it.
if (baseType is TypeBuilder)
{
return false;
}
var mi = baseType.GetMethod(methodName, parameterTypes);
return mi != null && mi.IsFinal;
}
private void DefineMethod(FunctionMemberAst functionMemberAst)
{
var parameterTypes = GetParameterTypes(functionMemberAst);
if (parameterTypes == null)
{
// There must have been an error, just return
return;
}
if (CheckForDuplicateOverload(functionMemberAst, parameterTypes))
{
return;
}
if (functionMemberAst.IsConstructor)
{
var methodAttributes = Reflection.MethodAttributes.Public;
if (functionMemberAst.IsStatic)
{
var parameters = functionMemberAst.Parameters;
if (parameters.Count > 0)
{
IScriptExtent errorExtent = Parser.ExtentOf(parameters[0], parameters.Last());
_parser.ReportError(errorExtent,
nameof(ParserStrings.StaticConstructorCantHaveParameters),
ParserStrings.StaticConstructorCantHaveParameters);
return;
}
methodAttributes |= Reflection.MethodAttributes.Static;
}
DefineConstructor(functionMemberAst, functionMemberAst.Attributes, functionMemberAst.IsHidden, methodAttributes, parameterTypes);
return;
}
var attributes = functionMemberAst.IsPublic
? Reflection.MethodAttributes.Public
: Reflection.MethodAttributes.Private;
if (functionMemberAst.IsStatic)
{
attributes |= Reflection.MethodAttributes.Static;
}
else
{
if (this.MethodExistsOnBaseClassAndFinal(functionMemberAst.Name, parameterTypes))
{
attributes |= Reflection.MethodAttributes.HideBySig;
attributes |= Reflection.MethodAttributes.NewSlot;
}
attributes |= Reflection.MethodAttributes.Virtual;
}
var returnType = functionMemberAst.GetReturnType();
if (returnType == null)
{
_parser.ReportError(functionMemberAst.ReturnType.Extent,
nameof(ParserStrings.TypeNotFound),
ParserStrings.TypeNotFound,
functionMemberAst.ReturnType.TypeName.FullName);
return;
}
var method = _typeBuilder.DefineMethod(functionMemberAst.Name, attributes, returnType, parameterTypes);
DefineCustomAttributes(method, functionMemberAst.Attributes, _parser, AttributeTargets.Method);
if (functionMemberAst.IsHidden)
{
method.SetCustomAttribute(s_hiddenCustomAttributeBuilder);
}
var ilGenerator = method.GetILGenerator();
DefineMethodBody(functionMemberAst, ilGenerator, GetMetaDataName(method.Name, parameterTypes.Length), functionMemberAst.IsStatic, parameterTypes, returnType,
(i, n) => method.DefineParameter(i, ParameterAttributes.None, n));
}
private void DefineConstructor(IParameterMetadataProvider ipmp, ReadOnlyCollection<AttributeAst> attributeAsts, bool isHidden, Reflection.MethodAttributes methodAttributes, Type[] parameterTypes)
{
bool isStatic = (methodAttributes & Reflection.MethodAttributes.Static) != 0;
var ctor = isStatic
? _typeBuilder.DefineTypeInitializer()
: _typeBuilder.DefineConstructor(methodAttributes, CallingConventions.Standard, parameterTypes);
DefineCustomAttributes(ctor, attributeAsts, _parser, AttributeTargets.Constructor);
if (isHidden)
{
ctor.SetCustomAttribute(s_hiddenCustomAttributeBuilder);
}
var ilGenerator = ctor.GetILGenerator();
if (!isStatic)
{
ilGenerator.Emit(OpCodes.Ldarg_0); // load 'this' on stack for Stfld call
ilGenerator.Emit(OpCodes.Ldnull);
ilGenerator.Emit(OpCodes.Ldfld, _sessionStateKeeperField);
ilGenerator.EmitCall(OpCodes.Call, s_sessionStateKeeper_GetSessionState, null); // load 'sessionState' on stack for Stfld call
ilGenerator.Emit(OpCodes.Stfld, _sessionStateField);
}
DefineMethodBody(ipmp, ilGenerator, GetMetaDataName(ctor.Name, parameterTypes.Length), isStatic, parameterTypes, typeof(void),
(i, n) => ctor.DefineParameter(i, ParameterAttributes.None, n));
}
private static string GetMetaDataName(string name, int numberOfParameters)
{
int currentId = Interlocked.Increment(ref s_globalCounter);
string metaDataName = name + "_" + numberOfParameters + "_" + currentId;
return metaDataName;
}
private void DefineMethodBody(
IParameterMetadataProvider ipmp,
ILGenerator ilGenerator,
string metadataToken,
bool isStatic,
Type[] parameterTypes,
Type returnType,
Action<int, string> parameterNameSetter)
{
var wrapperFieldName = string.Format(CultureInfo.InvariantCulture, "<{0}>", metadataToken);
var scriptBlockWrapperField = _staticHelpersTypeBuilder.DefineField(wrapperFieldName,
typeof(ScriptBlockMemberMethodWrapper),
FieldAttributes.Assembly | FieldAttributes.Static);
ilGenerator.Emit(OpCodes.Ldsfld, scriptBlockWrapperField);
if (isStatic)
{
ilGenerator.Emit(OpCodes.Ldnull); // pass null (no this)
ilGenerator.Emit(OpCodes.Ldnull); // pass null (no sessionStateInternal)
}
else
{
EmitLdarg(ilGenerator, 0); // pass this
ilGenerator.Emit(OpCodes.Ldarg_0); // pass 'this' for Ldfld call
ilGenerator.Emit(OpCodes.Ldfld, _sessionStateField); // pass sessionStateInternal
}
int parameterCount = parameterTypes.Length;
if (parameterCount > 0)
{
var parameters = ipmp.Parameters;
var local = ilGenerator.DeclareLocal(typeof(object[]));
EmitLdc(ilGenerator, parameterCount); // Create an array to hold all
ilGenerator.Emit(OpCodes.Newarr, typeof(object)); // of the parameters
ilGenerator.Emit(OpCodes.Stloc, local); // Save array for repeated use
int j = isStatic ? 0 : 1;
for (int i = 0; i < parameterCount; i++, j++)
{
ilGenerator.Emit(OpCodes.Ldloc, local); // load array
EmitLdc(ilGenerator, i); // index to save at
EmitLdarg(ilGenerator, j); // load argument (skipping this)
if (parameterTypes[i].IsValueType) // value types must be boxed
{
ilGenerator.Emit(OpCodes.Box, parameterTypes[i]);
}
ilGenerator.Emit(OpCodes.Stelem_Ref); // save the argument in the array
// Set the parameter name, mostly for Get-Member
// Parameters are indexed beginning with the number 1 for the first parameter
parameterNameSetter(i + 1, parameters[i].Name.VariablePath.UserPath);
}
ilGenerator.Emit(OpCodes.Ldloc, local); // load array
}
else
{
ilGenerator.Emit(OpCodes.Ldsfld, typeof(ScriptBlockMemberMethodWrapper).GetField("_emptyArgumentArray", BindingFlags.Static | BindingFlags.Public));
}
MethodInfo invokeHelper;
if (returnType == typeof(void))
{
invokeHelper = typeof(ScriptBlockMemberMethodWrapper).GetMethod("InvokeHelper", BindingFlags.Instance | BindingFlags.Public);
}
else
{
invokeHelper = typeof(ScriptBlockMemberMethodWrapper).GetMethod("InvokeHelperT", BindingFlags.Instance | BindingFlags.Public).MakeGenericMethod(returnType);
}
ilGenerator.Emit(OpCodes.Tailcall);
ilGenerator.EmitCall(OpCodes.Call, invokeHelper, null);
ilGenerator.Emit(OpCodes.Ret);
_fieldsToInitForMemberFunctions.Add((wrapperFieldName, ipmp, isStatic));
}
}
private sealed class DefineEnumHelper
{
private readonly Parser _parser;
private readonly TypeDefinitionAst _enumDefinitionAst;
private readonly ModuleBuilder _moduleBuilder;
private readonly string _typeName;
internal DefineEnumHelper(Parser parser, ModuleBuilder module, TypeDefinitionAst enumDefinitionAst, string typeName)
{
_parser = parser;
_enumDefinitionAst = enumDefinitionAst;
_moduleBuilder = module;
_typeName = typeName;
}
internal static List<DefineEnumHelper> Sort(List<DefineEnumHelper> defineEnumHelpers, Parser parser)
{
// This function does a topological sort of the enums to be defined. This is needed so we
// can allow one enum member to use the value of another w/o needing to worry about the order
// they are declared in. For example:
//
// enum E1 { e1 = [E2]::e2 }
// enum E2 { e2 = 42 }
//
// We also want to report an error for recursive expressions, e.g.
//
// enum E1 { e1 = [E2]::e2 }
// enum E2 { e2 = [E1]::e1 }
//
// Note that this code is not as permissive as it could be, e.g. we could (but do not) allow:
//
// enum E1 { e1 = [E2]::e2 }
// enum E2 {
// e2 = 42
// e2a = [E1]::e1
// }
//
// In this case, there is no cycle in the constant values despite E1 referencing E2 and vice versa.
//
// The basic algorithm is to create a graph where the edges represent a dependency, using this example:
//
// enum E1 { e1 = [E2]::e2 }
// enum E2 { e2 = 42 }
//
// We have an edge E1->E2. E2 has no dependencies.
if (defineEnumHelpers.Count == 1)
{
return defineEnumHelpers;
}
// There won't be many nodes in our graph, so we just use a dictionary with a list of edges instead
// of something cleaner.
var graph = new Dictionary<TypeDefinitionAst, Tuple<DefineEnumHelper, List<TypeDefinitionAst>>>();
// Add all of our nodes to the graph
foreach (var helper in defineEnumHelpers)
{
graph.Add(helper._enumDefinitionAst, Tuple.Create(helper, new List<TypeDefinitionAst>()));
}
// Now find any edges.
foreach (var helper in defineEnumHelpers)
{
foreach (var enumerator in helper._enumDefinitionAst.Members)
{
var initExpr = ((PropertyMemberAst)enumerator).InitialValue;
if (initExpr == null)
{
// No initializer, so no dependency (this is incorrect assumption if
// we wanted to be more general like C#.)
continue;
}
// The expression may have multiple member expressions, e.g. [E]::e1 + [E]::e2
foreach (var memberExpr in initExpr.FindAll(static ast => ast is MemberExpressionAst, false))
{
var typeExpr = ((MemberExpressionAst)memberExpr).Expression as TypeExpressionAst;
if (typeExpr != null)
{
// We only want to add edges for enums being defined in the current scope.
// We detect this by seeing if the ast is in our graph or not.
var typeName = typeExpr.TypeName as TypeName;
if (typeName != null
&& typeName._typeDefinitionAst != null
&& typeName._typeDefinitionAst != helper._enumDefinitionAst // Don't add self edges
&& graph.ContainsKey(typeName._typeDefinitionAst))
{
var edgeList = graph[helper._enumDefinitionAst].Item2;
if (!edgeList.Contains(typeName._typeDefinitionAst)) // Only add 1 edge per enum
{
edgeList.Add(typeName._typeDefinitionAst);
}
}
}
}
}
}
// Our graph is built. The ready list will hold nodes that don't depend on anything not already
// in the result list. We start with a list of nodes with no edges (no dependencies).
var result = new List<DefineEnumHelper>(defineEnumHelpers.Count);
var readyList = new List<DefineEnumHelper>(defineEnumHelpers.Count);
readyList.AddRange(from value in graph.Values where value.Item2.Count == 0 select value.Item1);
while (readyList.Count > 0)
{
var node = readyList[readyList.Count - 1];
readyList.RemoveAt(readyList.Count - 1);
result.Add(node);
// Remove all edges to this node as it is in our result list now.
foreach (var value in graph.Values)
{
value.Item2.Remove(node._enumDefinitionAst);
// If we removed the last edge, we can put this node on the ready list (assuming it
// wasn't already there or in our result list.)
if (value.Item2.Count == 0 && !result.Contains(value.Item1) && !readyList.Contains(value.Item1))
{
readyList.Add(value.Item1);
}
}
}
if (result.Count < defineEnumHelpers.Count)
{
// There was a cycle, report an error on each enum.
foreach (var helper in defineEnumHelpers)
{
if (!result.Contains(helper))
{
parser.ReportError(helper._enumDefinitionAst.Extent,
nameof(ParserStrings.CycleInEnumInitializers),
ParserStrings.CycleInEnumInitializers);
}
}
}
else
{
Diagnostics.Assert(result.Count == defineEnumHelpers.Count, "Logic error if we have more outgoing results than incoming");
}
return result;
}
internal void DefineEnum()
{
var typeConstraintAst = _enumDefinitionAst.BaseTypes.FirstOrDefault();
var underlyingType = typeConstraintAst == null ? typeof(int) : typeConstraintAst.TypeName.GetReflectionType();
var definedEnumerators = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
var enumBuilder = _moduleBuilder.DefineEnum(_typeName, Reflection.TypeAttributes.Public, underlyingType);
DefineCustomAttributes(enumBuilder, _enumDefinitionAst.Attributes, _parser, AttributeTargets.Enum);
dynamic value = 0;
dynamic maxValue = 0;
switch (Type.GetTypeCode(underlyingType))
{
case TypeCode.Byte:
maxValue = byte.MaxValue;
break;
case TypeCode.Int16:
maxValue = short.MaxValue;
break;
case TypeCode.Int32:
maxValue = int.MaxValue;
break;
case TypeCode.Int64:
maxValue = long.MaxValue;
break;
case TypeCode.SByte:
maxValue = sbyte.MaxValue;
break;
case TypeCode.UInt16:
maxValue = ushort.MaxValue;
break;
case TypeCode.UInt32:
maxValue = uint.MaxValue;
break;
case TypeCode.UInt64:
maxValue = ulong.MaxValue;
break;
default:
_parser.ReportError(
typeConstraintAst.Extent,
nameof(ParserStrings.InvalidUnderlyingType),
ParserStrings.InvalidUnderlyingType,
underlyingType);
break;
}
bool valueTooBig = false;
foreach (var member in _enumDefinitionAst.Members)
{
var enumerator = (PropertyMemberAst)member;
if (enumerator.InitialValue != null)
{
object constValue;
if (IsConstantValueVisitor.IsConstant(enumerator.InitialValue, out constValue, false, false))
{
if (!LanguagePrimitives.TryConvertTo(constValue, underlyingType, out value))
{
if (constValue != null &&
LanguagePrimitives.IsNumeric(LanguagePrimitives.GetTypeCode(constValue.GetType())))
{
_parser.ReportError(
enumerator.InitialValue.Extent,
nameof(ParserStrings.EnumeratorValueOutOfBounds),
ParserStrings.EnumeratorValueOutOfBounds,
ToStringCodeMethods.Type(underlyingType));
}
else
{
_parser.ReportError(
enumerator.InitialValue.Extent,
nameof(ParserStrings.CannotConvertValue),
ParserStrings.CannotConvertValue,
ToStringCodeMethods.Type(underlyingType));
}
}
}
else
{
_parser.ReportError(
enumerator.InitialValue.Extent,
nameof(ParserStrings.EnumeratorValueMustBeConstant),
ParserStrings.EnumeratorValueMustBeConstant);
}
valueTooBig = value > maxValue;
}
if (valueTooBig)
{
_parser.ReportError(
enumerator.Extent,
nameof(ParserStrings.EnumeratorValueOutOfBounds),
ParserStrings.EnumeratorValueOutOfBounds,
ToStringCodeMethods.Type(underlyingType));
}
if (definedEnumerators.Contains(enumerator.Name))
{
_parser.ReportError(
enumerator.Extent,
nameof(ParserStrings.MemberAlreadyDefined),
ParserStrings.MemberAlreadyDefined,
enumerator.Name);
}
else if (value != null)
{
value = Convert.ChangeType(value, underlyingType);
definedEnumerators.Add(enumerator.Name);
enumBuilder.DefineLiteral(enumerator.Name, value);
}
if (value < maxValue)
{
value += 1;
valueTooBig = false;
}
else
{
valueTooBig = true;
}
}
_enumDefinitionAst.Type = enumBuilder.CreateTypeInfo().AsType();
}
}
private static IEnumerable<CustomAttributeBuilder> GetAssemblyAttributeBuilders(string scriptFile)
{
var ctor = typeof(DynamicClassImplementationAssemblyAttribute).GetConstructor(Type.EmptyTypes);
var emptyArgs = Array.Empty<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, Array.Empty<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);
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>();
foreach (var typeDefinitionAst in typeDefinitions)
{
var typeName = GetClassNameInAssembly(typeDefinitionAst);
if (!definedTypes.Contains(typeName))
{
definedTypes.Add(typeName);
if ((typeDefinitionAst.TypeAttributes & TypeAttributes.Class) == TypeAttributes.Class)
{
defineTypeHelpers.Add(new DefineTypeHelper(parser, module, typeDefinitionAst, typeName));
}
else if ((typeDefinitionAst.TypeAttributes & TypeAttributes.Enum) == TypeAttributes.Enum)
{
defineEnumHelpers.Add(new DefineEnumHelper(parser, module, typeDefinitionAst, typeName));
}
}
}
// Define enums before classes so members of classes can use these enum types.
defineEnumHelpers = DefineEnumHelper.Sort(defineEnumHelpers, parser);
foreach (var helper in defineEnumHelpers)
{
helper.DefineEnum();
}
foreach (var helper in defineTypeHelpers)
{
helper.DefineMembers();
}
foreach (var helper in defineTypeHelpers)
{
Diagnostics.Assert(helper._typeDefinitionAst.Type is TypeBuilder, "Type should be the TypeBuilder");
bool runtimeTypeAssigned = false;
if (!helper.HasFatalErrors)
{
try
{
var type = helper._typeBuilder.CreateType();
helper._typeDefinitionAst.Type = type;
runtimeTypeAssigned = true;
var helperType = helper._staticHelpersTypeBuilder.CreateType();
SessionStateKeeper sessionStateKeeper = new SessionStateKeeper();
helperType.GetField(s_sessionStateKeeperFieldName, BindingFlags.NonPublic | BindingFlags.Static).SetValue(null, sessionStateKeeper);
if (helper._fieldsToInitForMemberFunctions != null)
{
foreach (var tuple in helper._fieldsToInitForMemberFunctions)
{
// If the wrapper is for a static method, we need the sessionStateKeeper to determine the right SessionState to run.
// If the wrapper is for an instance method, we use the SessionState that the instance is bound to, and thus don't need sessionStateKeeper.
var methodWrapper = tuple.isStatic
? new ScriptBlockMemberMethodWrapper(tuple.bodyAst, sessionStateKeeper)
: new ScriptBlockMemberMethodWrapper(tuple.bodyAst);
helperType.GetField(tuple.fieldName, BindingFlags.NonPublic | BindingFlags.Static)
.SetValue(null, methodWrapper);
}
}
}
catch (TypeLoadException e)
{
// This is a cheap way to get error messages about non-implemented abstract/interface methods (and maybe some other errors).
// We use .NET API to perform this check during type creation.
//
// Presumably this catch could go away when we will not create Type at parse time.
// Error checking should be moved/added to semantic checks.
parser.ReportError(helper._typeDefinitionAst.Extent,
nameof(ParserStrings.TypeCreationError),
ParserStrings.TypeCreationError,
helper._typeBuilder.Name,
e.Message);
}
}
if (!runtimeTypeAssigned)
{
// Clean up ast
helper._typeDefinitionAst.Type = null;
}
}
return assembly;
}
private static string GetClassNameInAssembly(TypeDefinitionAst typeDefinitionAst)
{
// Only allocate a list if necessary - in the most common case, we don't need it.
List<string> nameParts = null;
var parent = typeDefinitionAst.Parent;
while (parent.Parent != null)
{
if (parent is IParameterMetadataProvider)
{
nameParts ??= new List<string>();
var fnDefn = parent.Parent as FunctionDefinitionAst;
if (fnDefn != null)
{
parent = fnDefn;
nameParts.Add(fnDefn.Name);
}
else
{
nameParts.Add("<" + parent.Extent.Text.GetHashCode().ToString("x", CultureInfo.InvariantCulture) + ">");
}
}
parent = parent.Parent;
}
if (nameParts == null)
{
return typeDefinitionAst.Name;
}
nameParts.Reverse();
nameParts.Add(typeDefinitionAst.Name);
return string.Join(".", nameParts);
}
private static readonly OpCode[] s_ldc =
{
OpCodes.Ldc_I4_0, OpCodes.Ldc_I4_1, OpCodes.Ldc_I4_2, OpCodes.Ldc_I4_3, OpCodes.Ldc_I4_4,
OpCodes.Ldc_I4_5, OpCodes.Ldc_I4_6, OpCodes.Ldc_I4_7, OpCodes.Ldc_I4_8
};
private static void EmitLdc(ILGenerator emitter, int c)
{
if (c < s_ldc.Length)
{
emitter.Emit(s_ldc[c]);
}
else
{
emitter.Emit(OpCodes.Ldc_I4, c);
}
}
private static readonly OpCode[] s_ldarg =
{
OpCodes.Ldarg_0, OpCodes.Ldarg_1, OpCodes.Ldarg_2, OpCodes.Ldarg_3
};
private static void EmitLdarg(ILGenerator emitter, int c)
{
if (c < s_ldarg.Length)
{
emitter.Emit(s_ldarg[c]);
}
else
{
emitter.Emit(OpCodes.Ldarg, c);
}
}
}
}