/Languages/IronPython/IronPython/Runtime/Types/NewTypeMaker.cs
C# | 1845 lines | 1334 code | 305 blank | 206 comment | 285 complexity | c9beb103b9e51a1da237a8512b974654 MD5 | raw file
Possible License(s): CPL-1.0, BSD-3-Clause, ISC, GPL-2.0, MPL-2.0-no-copyleft-exception
Large files files are truncated, but you can click here to view the full file
- /* ****************************************************************************
- *
- * Copyright (c) Microsoft Corporation.
- *
- * This source code is subject to terms and conditions of the Apache License, Version 2.0. A
- * copy of the license can be found in the License.html file at the root of this distribution. If
- * you cannot locate the Apache License, Version 2.0, please send an email to
- * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound
- * by the terms of the Apache License, Version 2.0.
- *
- * You must not remove this notice, or any other, from this software.
- *
- *
- * ***************************************************************************/
- using System;
- using System.Collections.Generic;
- using System.ComponentModel;
- using System.Diagnostics;
- using System.Linq;
- using System.Reflection;
- using System.Reflection.Emit;
- using System.Runtime.CompilerServices;
- using System.Dynamic;
- using System.Text;
- using Microsoft.Scripting;
- using Microsoft.Scripting.Actions;
- using Microsoft.Scripting.Generation;
- using Microsoft.Scripting.Runtime;
- using Microsoft.Scripting.Utils;
- using IronPython.Runtime.Binding;
- using IronPython.Runtime.Operations;
- namespace IronPython.Runtime.Types {
- /// <summary>
- /// Python class hierarchy is represented using the __class__ field in the object. It does not
- /// use the CLI type system for pure Python types. However, Python types which inherit from a
- /// CLI type, or from a builtin Python type which is implemented in the engine by a CLI type,
- /// do have to use the CLI type system to interoperate with the CLI world. This means that
- /// objects of different Python types, but with the same CLI base type, can use the same CLI type -
- /// they will just have different values for the __class__ field.
- ///
- /// The easiest way to inspect the functionality implemented by NewTypeMaker is to persist the
- /// generated IL using "ipy.exe -X:SaveAssemblies", and then inspect the
- /// persisted IL using ildasm.
- /// </summary>
- sealed class NewTypeMaker {
- private Type _baseType;
- private IList<Type> _interfaceTypes;
- #if FEATURE_REFEMIT
- private TypeBuilder _tg;
- private FieldInfo _typeField;
- private FieldInfo _dictField;
- private FieldInfo _slotsField;
- private FieldInfo _explicitMO;
-
- private ILGen _cctor;
- private int _site;
- [MultiRuntimeAware]
- private static int _typeCount;
- #endif
- public const string VtableNamesField = "#VTableNames#";
- public const string TypePrefix = "IronPython.NewTypes.";
- public const string BaseMethodPrefix = "#base#";
- public const string FieldGetterPrefix = "#field_get#", FieldSetterPrefix = "#field_set#";
- #if FEATURE_REFEMIT
- public const string ClassFieldName = ".class", DictFieldName = ".dict", SlotsAndWeakRefFieldName = ".slots_and_weakref";
- #else
- public const string ClassFieldName = "_class", DictFieldName = "_dict", SlotsAndWeakRefFieldName = "_slots_and_weakref";
- #endif
- private const string _constructorTypeName = "PythonCachedTypeConstructor";
- private const string _constructorMethodName = "GetTypeInfo";
- [MultiRuntimeAware]
- internal static readonly Publisher<NewTypeInfo, Type> _newTypes = new Publisher<NewTypeInfo, Type>();
- [MultiRuntimeAware]
- private static readonly Dictionary<Type, Dictionary<string, List<MethodInfo>>> _overriddenMethods = new Dictionary<Type, Dictionary<string, List<MethodInfo>>>();
- [MultiRuntimeAware]
- private static readonly Dictionary<Type, Dictionary<string, List<ExtensionPropertyTracker>>> _overriddenProperties = new Dictionary<Type, Dictionary<string, List<ExtensionPropertyTracker>>>();
-
- private NewTypeMaker(NewTypeInfo typeInfo) {
- _baseType = typeInfo.BaseType;
- _interfaceTypes = typeInfo.InterfaceTypes;
- }
- #region Public API
- #if FEATURE_REFEMIT
- public static Type/*!*/ GetNewType(string/*!*/ typeName, PythonTuple/*!*/ bases) {
- Assert.NotNull(typeName, bases);
- NewTypeInfo typeInfo = NewTypeInfo.GetTypeInfo(typeName, bases);
- if (typeInfo.BaseType.IsValueType()) {
- throw PythonOps.TypeError("cannot derive from {0} because it is a value type", typeInfo.BaseType.FullName);
- } else if (typeInfo.BaseType.IsSealed()) {
- throw PythonOps.TypeError("cannot derive from {0} because it is sealed", typeInfo.BaseType.FullName);
- }
- Type ret = _newTypes.GetOrCreateValue(typeInfo,
- () => {
- if (typeInfo.InterfaceTypes.Count == 0) {
- // types that the have DynamicBaseType attribute can be used as NewType's directly, no
- // need to create a new type unless we're adding interfaces
- var attrs = typeInfo.BaseType.GetTypeInfo().GetCustomAttributes(typeof(DynamicBaseTypeAttribute), false);
- if (attrs.Any()) {
- return typeInfo.BaseType;
- }
- }
- // creation code
- return new NewTypeMaker(typeInfo).CreateNewType();
- });
-
- return ret;
- }
- public static void SaveNewTypes(string assemblyName, IList<PythonTuple> types) {
- Assert.NotNull(assemblyName, types);
- AssemblyGen ag = new AssemblyGen(new AssemblyName(assemblyName), ".", ".dll", false);
- TypeBuilder tb = ag.DefinePublicType(_constructorTypeName, typeof(object), true);
- tb.SetCustomAttribute(new CustomAttributeBuilder(typeof(PythonCachedTypeInfoAttribute).GetConstructor(ReflectionUtils.EmptyTypes), new object[0]));
- MethodBuilder mb = tb.DefineMethod(_constructorMethodName, MethodAttributes.Public | MethodAttributes.Static, typeof(CachedNewTypeInfo[]), ReflectionUtils.EmptyTypes);
- ILGenerator ilg = mb.GetILGenerator();
-
- // new CachedTypeInfo[types.Count]
- // we leave this on the stack (duping it) and storing into it.
- EmitInt(ilg, types.Count);
- ilg.Emit(OpCodes.Newarr, typeof(CachedNewTypeInfo));
- int curType = 0;
- foreach (var v in types) {
- NewTypeInfo nti = NewTypeInfo.GetTypeInfo(String.Empty, v);
-
- var typeInfos = new NewTypeMaker(nti).SaveType(ag, "Python" + _typeCount++ + "$" + nti.BaseType.Name);
- // prepare for storing the element into our final array
- ilg.Emit(OpCodes.Dup);
- EmitInt(ilg, curType++);
- // new CachedNewTypeInfo(type, specialNames, interfaceTypes):
- // load the type
- ilg.Emit(OpCodes.Ldtoken, typeInfos.Key);
- ilg.Emit(OpCodes.Call, typeof(Type).GetMethod("GetTypeFromHandle"));
- // create the dictionary<str, str[]> of special names
- ilg.Emit(OpCodes.Newobj, typeof(Dictionary<string, string[]>).GetConstructor(new Type[0]));
- foreach (var specialName in typeInfos.Value) {
- // dup dict
- ilg.Emit(OpCodes.Dup);
-
- // emit key
- ilg.Emit(OpCodes.Ldstr, specialName.Key);
- // emit value
- int iVal = specialName.Value.Length;
- EmitInt(ilg, iVal);
- ilg.Emit(OpCodes.Newarr, typeof(string));
- for (int i = 0; i < specialName.Value.Length; i++) {
- ilg.Emit(OpCodes.Dup);
- EmitInt(ilg, i);
- ilg.Emit(OpCodes.Ldstr, specialName.Value[0]);
- ilg.Emit(OpCodes.Stelem_Ref);
- }
- // assign to dict
- ilg.Emit(OpCodes.Call, typeof(Dictionary<string, string[]>).GetMethod("set_Item"));
- }
- // emit the interface types (if any)
- if (nti.InterfaceTypes.Count != 0) {
- EmitInt(ilg, nti.InterfaceTypes.Count);
- ilg.Emit(OpCodes.Newarr, typeof(Type));
- for (int i = 0; i < nti.InterfaceTypes.Count; i++) {
- ilg.Emit(OpCodes.Dup);
- EmitInt(ilg, i);
- ilg.Emit(OpCodes.Ldtoken, nti.InterfaceTypes[i]);
- ilg.Emit(OpCodes.Call, typeof(Type).GetMethod("GetTypeFromHandle"));
- ilg.Emit(OpCodes.Stelem_Ref);
- }
- } else {
- ilg.Emit(OpCodes.Ldnull);
- }
- // crated the CachedNewTypeInfo and store it in the array
- ilg.Emit(OpCodes.Newobj, typeof(CachedNewTypeInfo).GetConstructors()[0]);
- ilg.Emit(OpCodes.Stelem_Ref);
- }
- ilg.Emit(OpCodes.Ret);
- tb.CreateType();
- ag.SaveAssembly();
- }
- #endif
- /// <summary>
- /// Loads any available new types from the provided assembly and makes them
- /// available via the GetNewType API.
- /// </summary>
- public static void LoadNewTypes(Assembly/*!*/ asm) {
- Assert.NotNull(asm);
- Type t = asm.GetType(_constructorTypeName);
- if (t == null || !t.GetTypeInfo().IsDefined(typeof(PythonCachedTypeInfoAttribute), false)) {
- return;
- }
- MethodInfo mi = t.GetMethod(_constructorMethodName);
- var typeInfo = (CachedNewTypeInfo[])mi.Invoke(null, new object[0]);
- foreach (var v in typeInfo) {
- _newTypes.GetOrCreateValue(
- new NewTypeInfo(v.Type.GetBaseType(), v.InterfaceTypes),
- () => {
- // type wasn't already created, go ahead and publish
- // the info and return the type.
- AddBaseMethods(v.Type, v.SpecialNames);
- return v.Type;
- }
- );
- }
- }
- /// <summary>
- /// Is this a type used for instances Python types (and not for the types themselves)?
- /// </summary>
- public static bool IsInstanceType(Type type) {
- return type.FullName.IndexOf(NewTypeMaker.TypePrefix) == 0 ||
- // Users can create sub-types of instance-types using __clrtype__ without using
- // NewTypeMaker.TypePrefix
- ((type.GetBaseType() != null) && IsInstanceType(type.GetBaseType()));
- }
- #endregion
- #region Type Generation
- #if FEATURE_REFEMIT
- private Type CreateNewType() {
- string name = GetName();
- _tg = Snippets.Shared.DefinePublicType(TypePrefix + name, _baseType);
- Dictionary<string, string[]> specialNames = ImplementType();
- Type ret = FinishType();
- AddBaseMethods(ret, specialNames);
- return ret;
- }
- // Build a name which is unique to this TypeInfo.
- private string GetName() {
- StringBuilder name = new StringBuilder(_baseType.Namespace);
- name.Append('.');
- name.Append(_baseType.Name);
- foreach (Type interfaceType in _interfaceTypes) {
- name.Append("#");
- name.Append(interfaceType.Name);
- }
- name.Append("_");
- name.Append(System.Threading.Interlocked.Increment(ref _typeCount));
- return name.ToString();
- }
- private Dictionary<string, string[]> ImplementType() {
- DefineInterfaces();
- ImplementPythonObject();
- ImplementConstructors();
- Dictionary<string, string[]> specialNames = new Dictionary<string, string[]>();
- OverrideMethods(_baseType, specialNames);
- ImplementProtectedFieldAccessors(specialNames);
- Dictionary<Type, bool> doneTypes = new Dictionary<Type, bool>();
- foreach (Type interfaceType in _interfaceTypes) {
- DoInterfaceType(interfaceType, doneTypes, specialNames);
- }
- return specialNames;
- }
- private void DefineInterfaces() {
- foreach (Type interfaceType in _interfaceTypes) {
- ImplementInterface(interfaceType);
- }
- }
- private void ImplementInterface(Type interfaceType) {
- _tg.AddInterfaceImplementation(interfaceType);
- }
- private void ImplementPythonObject() {
- ImplementIPythonObject();
- ImplementDynamicObject();
- #if FEATURE_CUSTOM_TYPE_DESCRIPTOR
- ImplementCustomTypeDescriptor();
- #endif
- #if CLR2
- ImplementPythonEquals();
- #endif
- ImplementWeakReference();
- AddDebugView();
- }
- private void AddDebugView() {
- _tg.SetCustomAttribute(
- new CustomAttributeBuilder(
- typeof(DebuggerTypeProxyAttribute).GetConstructor(new[] { typeof(Type) }),
- new object[] { typeof(UserTypeDebugView) }
- )
- );
- _tg.SetCustomAttribute(
- new CustomAttributeBuilder(
- typeof(DebuggerDisplayAttribute).GetConstructor(new[] { typeof(string) }),
- new object[] { "{get_PythonType().GetTypeDebuggerDisplay()}" }
- )
- );
- }
- private void EmitGetDict(ILGen gen) {
- gen.EmitFieldGet(_dictField);
- }
- private void EmitSetDict(ILGen gen) {
- gen.EmitFieldSet(_dictField);
- }
- private ParameterInfoWrapper[] GetOverrideCtorSignature(ParameterInfo[] originalParameterInfo) {
- ParameterInfoWrapper[] original = originalParameterInfo.Select(o => new ParameterInfoWrapper(o)).ToArray();
- if (typeof(IPythonObject).IsAssignableFrom(_baseType)) {
- return original;
- }
- ParameterInfoWrapper[] argTypes = new ParameterInfoWrapper[original.Length + 1];
- if (original.Length == 0 || original[0].ParameterType != typeof(CodeContext)) {
- argTypes[0] = new ParameterInfoWrapper(typeof(PythonType), "cls");
- Array.Copy(original, 0, argTypes, 1, argTypes.Length - 1);
- } else {
- argTypes[0] = original[0];
- argTypes[1] = new ParameterInfoWrapper(typeof(PythonType), "cls");
- Array.Copy(original, 1, argTypes, 2, argTypes.Length - 2);
- }
- return argTypes;
- }
- private void ImplementConstructors() {
- ConstructorInfo[] constructors;
- constructors = _baseType.GetConstructors(BindingFlags.Public |
- BindingFlags.NonPublic |
- BindingFlags.Instance
- );
- foreach (ConstructorInfo ci in constructors) {
- if (ci.IsPublic || ci.IsProtected()) {
- OverrideConstructor(ci);
- }
- }
- }
- private static bool CanOverrideMethod(MethodInfo mi) {
- #if !SILVERLIGHT
- return true;
- #else
- // can only override the method if it is not SecurityCritical
- return mi.GetCustomAttributes(typeof(System.Security.SecurityCriticalAttribute), false).Length == 0;
- #endif
- }
- private void DoInterfaceType(Type interfaceType, Dictionary<Type, bool> doneTypes, Dictionary<string, string[]> specialNames) {
- if (interfaceType == typeof(IDynamicMetaObjectProvider)) {
- // very tricky, we'll handle it when we're creating
- // our own IDynamicMetaObjectProvider interface
- return;
- }
- if (doneTypes.ContainsKey(interfaceType)) return;
- doneTypes.Add(interfaceType, true);
- OverrideMethods(interfaceType, specialNames);
- foreach (Type t in interfaceType.GetInterfaces()) {
- DoInterfaceType(t, doneTypes, specialNames);
- }
- }
- private void OverrideConstructor(ConstructorInfo parentConstructor) {
- ParameterInfo[] pis = parentConstructor.GetParameters();
- if (pis.Length == 0 && typeof(IPythonObject).IsAssignableFrom(_baseType)) {
- // default ctor on a base type, don't override this one, it assumes
- // the PythonType is some default value and we'll always be unique.
- return;
- }
- ParameterInfoWrapper[] overrideParams = GetOverrideCtorSignature(pis);
- Type[] argTypes = new Type[overrideParams.Length];
- string[] paramNames = new string[overrideParams.Length];
- for (int i = 0; i < overrideParams.Length; i++) {
- argTypes[i] = overrideParams[i].ParameterType;
- paramNames[i] = overrideParams[i].Name;
- }
- ConstructorBuilder cb = _tg.DefineConstructor(MethodAttributes.Public, CallingConventions.Standard, argTypes);
- for (int i = 0; i < overrideParams.Length; i++) {
- ParameterBuilder pb = cb.DefineParameter(i + 1,
- overrideParams[i].Attributes,
- overrideParams[i].Name);
- int origIndex = GetOriginalIndex(pis, overrideParams, i);
- if (origIndex >= 0) {
- ParameterInfo pi = pis[origIndex];
- // Defines attributes that might be used in .net methods for overload detection and other
- // parameter based attribute logic. E.g. [BytesConversionAttribute] to make
- // enable automatic cast between .net IList<byte> and string
- if (pi.IsDefined(typeof(ParamArrayAttribute), false)) {
- pb.SetCustomAttribute(new CustomAttributeBuilder(
- typeof(ParamArrayAttribute).GetConstructor(ReflectionUtils.EmptyTypes), ArrayUtils.EmptyObjects));
- } else if (pi.IsDefined(typeof(ParamDictionaryAttribute), false)) {
- pb.SetCustomAttribute(new CustomAttributeBuilder(
- typeof(ParamDictionaryAttribute).GetConstructor(ReflectionUtils.EmptyTypes), ArrayUtils.EmptyObjects));
- } else if (pi.IsDefined(typeof(BytesConversionAttribute), false)) {
- pb.SetCustomAttribute(new CustomAttributeBuilder(
- typeof(BytesConversionAttribute).GetConstructor(ReflectionUtils.EmptyTypes), ArrayUtils.EmptyObjects));
- } else if (pi.IsDefined(typeof(BytesConversionNoStringAttribute), false)) {
- pb.SetCustomAttribute(new CustomAttributeBuilder(
- typeof(BytesConversionNoStringAttribute).GetConstructor(ReflectionUtils.EmptyTypes), ArrayUtils.EmptyObjects));
- }
- if ((pi.Attributes & ParameterAttributes.HasDefault) != 0) {
- if (pi.DefaultValue == null || pi.ParameterType.IsAssignableFrom(pi.DefaultValue.GetType())) {
- pb.SetConstant(pi.DefaultValue);
- } else {
- pb.SetConstant(Convert.ChangeType(
- pi.DefaultValue, pi.ParameterType,
- System.Globalization.CultureInfo.CurrentCulture
- ));
- }
- }
- }
- }
- ILGen il = new ILGen(cb.GetILGenerator());
- int typeArg;
- if (pis.Length == 0 || pis[0].ParameterType != typeof(CodeContext)) {
- typeArg = 1;
- } else {
- typeArg = 2;
- }
- // this.__class__ = <arg?>
- // can occur 2 ways:
- // 1. If we have our own _typeField then we set it
- // 2. If we're a subclass of IPythonObject (e.g. one of our exception classes) then we'll flow it to the
- // base type constructor which will set it.
- if (!typeof(IPythonObject).IsAssignableFrom(_baseType)) {
- il.EmitLoadArg(0);
- // base class could have CodeContext parameter in which case our type is the 2nd parameter.
- il.EmitLoadArg(typeArg);
- il.EmitFieldSet(_typeField);
- }
- if (_explicitMO != null) {
- il.Emit(OpCodes.Ldarg_0);
- il.EmitNew(_explicitMO.FieldType.GetConstructor(ReflectionUtils.EmptyTypes));
- il.Emit(OpCodes.Stfld, _explicitMO);
- }
- // initialize all slots to Uninitialized.instance
- MethodInfo init = typeof(PythonOps).GetMethod("InitializeUserTypeSlots");
- il.EmitLoadArg(0);
-
- il.EmitLoadArg(typeArg);
- il.EmitCall(init);
- Debug.Assert(_slotsField != null);
- il.EmitFieldSet(_slotsField);
- CallBaseConstructor(parentConstructor, pis, overrideParams, il);
- }
- /// <summary>
- /// Gets the position for the parameter which we are overriding.
- /// </summary>
- /// <param name="pis"></param>
- /// <param name="overrideParams"></param>
- /// <param name="i"></param>
- /// <returns></returns>
- private static int GetOriginalIndex(ParameterInfo[] pis, ParameterInfoWrapper[] overrideParams, int i) {
- if (pis.Length == 0 || pis[0].ParameterType != typeof(CodeContext)) {
- return i - (overrideParams.Length - pis.Length);
- }
- // context & cls are swapped, context comes first.
- if (i == 1) return -1;
- if (i == 0) return 0;
- return i - (overrideParams.Length - pis.Length);
- }
- private static void CallBaseConstructor(ConstructorInfo parentConstructor, ParameterInfo[] pis, ParameterInfoWrapper[] overrideParams, ILGen il) {
- il.EmitLoadArg(0);
- #if DEBUG
- int lastIndex = -1;
- #endif
- for (int i = 0; i < overrideParams.Length; i++) {
- int index = GetOriginalIndex(pis, overrideParams, i);
- #if DEBUG
- // we insert a new parameter (the class) but the parametrers should
- // still remain in the same order after the extra parameter is removed.
- if (index >= 0) {
- Debug.Assert(index > lastIndex);
- lastIndex = index;
- }
- #endif
- if (index >= 0) {
- il.EmitLoadArg(i + 1);
- }
- }
- il.Emit(OpCodes.Call, parentConstructor);
- il.Emit(OpCodes.Ret);
- }
- ILGen GetCCtor() {
- if (_cctor == null) {
- ConstructorBuilder cctor = _tg.DefineTypeInitializer();
- _cctor = new ILGen(cctor.GetILGenerator());
- }
- return _cctor;
- }
- #if FEATURE_CUSTOM_TYPE_DESCRIPTOR
- private void ImplementCustomTypeDescriptor() {
- ImplementInterface(typeof(ICustomTypeDescriptor));
- foreach (MethodInfo m in typeof(ICustomTypeDescriptor).GetMethods()) {
- ImplementCTDOverride(m);
- }
- }
- private void ImplementCTDOverride(MethodInfo m) {
- MethodBuilder builder;
- ILGen il = DefineExplicitInterfaceImplementation(m, out builder);
- il.EmitLoadArg(0);
- ParameterInfo[] pis = m.GetParameters();
- Type[] paramTypes = new Type[pis.Length + 1];
- paramTypes[0] = typeof(object);
- for (int i = 0; i < pis.Length; i++) {
- il.EmitLoadArg(i + 1);
- paramTypes[i + 1] = pis[i].ParameterType;
- }
- il.EmitCall(typeof(CustomTypeDescHelpers), m.Name, paramTypes);
- il.EmitBoxing(m.ReturnType);
- il.Emit(OpCodes.Ret);
- _tg.DefineMethodOverride(builder, m);
- }
- #endif
- private bool NeedsPythonObject {
- get {
- return !typeof(IPythonObject).IsAssignableFrom(_baseType);
- }
- }
- private void ImplementDynamicObject() {
- // true if the user has explicitly included IDynamicMetaObjectProvider in the list of interfaces
- bool explicitDynamicObject = false;
- foreach (Type t in _interfaceTypes) {
- if (t == typeof(IDynamicMetaObjectProvider)) {
- explicitDynamicObject = true;
- break;
- }
- }
- // true if our base type implements IDMOP already
- bool baseIdo = typeof(IDynamicMetaObjectProvider).IsAssignableFrom(_baseType);
- if (baseIdo) {
- InterfaceMapping mapping = _baseType.GetTypeInfo().GetRuntimeInterfaceMap(typeof(IDynamicMetaObjectProvider));
- if (mapping.TargetMethods[0].IsPrivate) {
- // explicitly implemented IDynamicMetaObjectProvider, we cannot override it.
- if (_baseType.GetTypeInfo().IsDefined(typeof(DynamicBaseTypeAttribute), true)) {
- // but it's been implemented by IronPython so it's going to return a MetaUserObject
- return;
- }
- // we can't dispatch to the subclasses IDMOP implementation, completely
- // replace it with our own.
- baseIdo = false;
- }
- }
- ImplementInterface(typeof(IDynamicMetaObjectProvider));
- MethodInfo decl;
- MethodBuilder impl;
- ILGen il = DefineMethodOverride(MethodAttributes.Private, typeof(IDynamicMetaObjectProvider), "GetMetaObject", out decl, out impl);
- MethodInfo mi = typeof(UserTypeOps).GetMethod("GetMetaObjectHelper");
- LocalBuilder retVal = il.DeclareLocal(typeof(DynamicMetaObject));
- Label retLabel = il.DefineLabel();
- if (explicitDynamicObject) {
- _explicitMO = _tg.DefineField("__gettingMO", typeof(ThreadLocal<bool>), FieldAttributes.InitOnly | FieldAttributes.Private);
- Label ipyImpl = il.DefineLabel();
- Label noOverride = il.DefineLabel();
- Label retNull = il.DefineLabel();
- var valueProperty = typeof(ThreadLocal<bool>).GetDeclaredProperty("Value");
- // check if the we're recursing (this enables the user to refer to self
- // during GetMetaObject calls)
- il.Emit(OpCodes.Ldarg_0);
- il.Emit(OpCodes.Ldfld, _explicitMO);
- il.EmitPropertyGet(valueProperty);
- il.Emit(OpCodes.Brtrue, ipyImpl);
- // we're not recursing, set the flag...
- il.Emit(OpCodes.Ldarg_0);
- il.Emit(OpCodes.Ldfld, _explicitMO);
- il.Emit(OpCodes.Ldc_I4_1);
- il.EmitPropertySet(valueProperty);
- il.BeginExceptionBlock();
- LocalBuilder callTarget = EmitNonInheritedMethodLookup("GetMetaObject", il);
- il.Emit(OpCodes.Brfalse, noOverride);
- // call the user GetMetaObject function
- EmitClrCallStub(il, typeof(IDynamicMetaObjectProvider).GetMethod("GetMetaObject"), callTarget);
- // check for null return
- il.Emit(OpCodes.Dup);
- il.Emit(OpCodes.Ldnull);
- il.Emit(OpCodes.Beq, retNull);
-
- // store the local value
- il.Emit(OpCodes.Stloc_S, retVal.LocalIndex);
- // returned a value, that's our result
- il.Emit(OpCodes.Leave, retLabel);
- // user returned null, fallback to base impl
- il.MarkLabel(retNull);
- il.Emit(OpCodes.Pop);
-
- // no override exists
- il.MarkLabel(noOverride);
- // will emit leave to end of exception block
- il.BeginFinallyBlock();
- // restore the flag now that we're done
- il.Emit(OpCodes.Ldarg_0);
- il.Emit(OpCodes.Ldfld, _explicitMO);
- il.Emit(OpCodes.Ldc_I4_0);
- il.EmitPropertySet(typeof(ThreadLocal<bool>).GetDeclaredProperty("Value"));
- il.EndExceptionBlock();
- // no user defined function or no result
- il.MarkLabel(ipyImpl);
- }
- il.EmitLoadArg(0); // this
- il.EmitLoadArg(1); // parameter
-
- // baseMetaObject
- if (baseIdo) {
- InterfaceMapping imap = _baseType.GetTypeInfo().GetRuntimeInterfaceMap(typeof(IDynamicMetaObjectProvider));
- il.EmitLoadArg(0); // this
- il.EmitLoadArg(1); // parameter
- il.EmitCall(imap.TargetMethods[0]);
- } else {
- il.EmitNull();
- }
- il.EmitCall(mi);
- il.Emit(OpCodes.Stloc, retVal.LocalIndex);
- il.MarkLabel(retLabel);
- il.Emit(OpCodes.Ldloc, retVal.LocalIndex);
- il.Emit(OpCodes.Ret);
- _tg.DefineMethodOverride(impl, decl);
- }
- private void ImplementIPythonObject() {
- ILGen il;
- MethodInfo decl;
- MethodBuilder impl;
- if (NeedsPythonObject) {
- _typeField = _tg.DefineField(ClassFieldName, typeof(PythonType), FieldAttributes.Public);
- _dictField = _tg.DefineField(DictFieldName, typeof(PythonDictionary), FieldAttributes.Public);
- ImplementInterface(typeof(IPythonObject));
- MethodAttributes attrs = MethodAttributes.Private;
- il = DefineMethodOverride(attrs, typeof(IPythonObject), "get_Dict", out decl, out impl);
- il.EmitLoadArg(0);
- EmitGetDict(il);
- il.Emit(OpCodes.Ret);
- _tg.DefineMethodOverride(impl, decl);
- il = DefineMethodOverride(attrs, typeof(IPythonObject), "ReplaceDict", out decl, out impl);
- il.EmitLoadArg(0);
- il.EmitLoadArg(1);
- EmitSetDict(il);
- il.EmitBoolean(true);
- il.Emit(OpCodes.Ret);
- _tg.DefineMethodOverride(impl, decl);
- il = DefineMethodOverride(attrs, typeof(IPythonObject), "SetDict", out decl, out impl);
- il.EmitLoadArg(0);
- il.EmitFieldAddress(_dictField);
- il.EmitLoadArg(1);
- il.EmitCall(typeof(UserTypeOps), "SetDictHelper");
- il.Emit(OpCodes.Ret);
- _tg.DefineMethodOverride(impl, decl);
- il = DefineMethodOverride(attrs, typeof(IPythonObject), "get_PythonType", out decl, out impl);
- il.EmitLoadArg(0);
- il.EmitFieldGet(_typeField);
- il.Emit(OpCodes.Ret);
- _tg.DefineMethodOverride(impl, decl);
- il = DefineMethodOverride(attrs, typeof(IPythonObject), "SetPythonType", out decl, out impl);
- il.EmitLoadArg(0);
- il.EmitLoadArg(1);
- il.EmitFieldSet(_typeField);
- il.Emit(OpCodes.Ret);
- _tg.DefineMethodOverride(impl, decl);
- }
-
- // Types w/ DynamicBaseType attribute still need new slots implementation
- _slotsField = _tg.DefineField(SlotsAndWeakRefFieldName, typeof(object[]), FieldAttributes.Public);
- il = DefineMethodOverride(MethodAttributes.Private, typeof(IPythonObject), "GetSlots", out decl, out impl);
- il.EmitLoadArg(0);
- il.EmitFieldGet(_slotsField);
- il.Emit(OpCodes.Ret);
- _tg.DefineMethodOverride(impl, decl);
- il = DefineMethodOverride(MethodAttributes.Private, typeof(IPythonObject), "GetSlotsCreate", out decl, out impl);
- il.EmitLoadArg(0);
- il.EmitLoadArg(0);
- il.EmitFieldAddress(_slotsField);
- il.EmitCall(typeof(UserTypeOps).GetMethod("GetSlotsCreate"));
- il.Emit(OpCodes.Ret);
- _tg.DefineMethodOverride(impl, decl);
- }
- /// <summary>
- /// Defines an interface on the type that forwards all calls
- /// to a helper method in UserType. The method names all will
- /// have Helper appended to them to get the name for UserType. The
- /// UserType version should take 1 extra parameter (self).
- /// </summary>
- private void DefineHelperInterface(Type intf) {
- ImplementInterface(intf);
- MethodInfo[] mis = intf.GetMethods();
- foreach (MethodInfo mi in mis) {
- MethodBuilder impl;
- ILGen il = DefineExplicitInterfaceImplementation(mi, out impl);
- ParameterInfo[] pis = mi.GetParameters();
- MethodInfo helperMethod = typeof(UserTypeOps).GetMethod(mi.Name + "Helper");
- int offset = 0;
- if (pis.Length > 0 && pis[0].ParameterType == typeof(CodeContext)) {
- // if the interface takes CodeContext then the helper method better take
- // it as well.
- Debug.Assert(helperMethod.GetParameters()[0].ParameterType == typeof(CodeContext));
- offset = 1;
- il.EmitLoadArg(1);
- }
- il.EmitLoadArg(0);
- for (int i = offset; i < pis.Length; i++) {
- il.EmitLoadArg(i + 1);
- }
- il.EmitCall(helperMethod);
- il.Emit(OpCodes.Ret);
- _tg.DefineMethodOverride(impl, mi);
- }
- }
- #if CLR2
- private void ImplementPythonEquals() {
- if (this._baseType.GetInterface("IValueEquality", false) == null) {
- DefineHelperInterface(typeof(IValueEquality));
- }
- }
- #endif
- private void ImplementWeakReference() {
- if (typeof(IWeakReferenceable).IsAssignableFrom(_baseType)) {
- return;
- }
- DefineHelperInterface(typeof(IWeakReferenceable));
- }
- private void ImplementProtectedFieldAccessors(Dictionary<string, string[]> specialNames) {
- // For protected fields to be accessible from the derived type in Silverlight,
- // we need to create public helper methods that expose them. These methods are
- // used by the IDynamicMetaObjectProvider implementation (in MetaUserObject)
- foreach (FieldInfo fi in _baseType.GetInheritedFields(flattenHierarchy: true)) {
- if (!fi.IsProtected()) {
- continue;
- }
- List<string> fieldAccessorNames = new List<string>();
- PropertyBuilder pb = _tg.DefineProperty(fi.Name, PropertyAttributes.None, fi.FieldType, ReflectionUtils.EmptyTypes);
- MethodAttributes methodAttrs = MethodAttributes.Public | MethodAttributes.HideBySig | MethodAttributes.SpecialName;
- if (fi.IsStatic) {
- methodAttrs |= MethodAttributes.Static;
- }
- MethodBuilder method;
- method = _tg.DefineMethod(FieldGetterPrefix + fi.Name, methodAttrs, fi.FieldType, ReflectionUtils.EmptyTypes);
- ILGen il = new ILGen(method.GetILGenerator());
- if (!fi.IsStatic) {
- il.EmitLoadArg(0);
- }
- if (fi.IsLiteral) {
- // literal fields need to be inlined directly in here... We use GetRawConstant
- // which will work even in partial trust if the constant is protected.
- object value = fi.GetRawConstantValue();
- switch (fi.FieldType.GetTypeCode()) {
- case TypeCode.Boolean:
- if ((bool)value) {
- il.Emit(OpCodes.Ldc_I4_1);
- } else {
- il.Emit(OpCodes.Ldc_I4_0);
- }
- break;
- case TypeCode.Byte: il.Emit(OpCodes.Ldc_I4, (byte)value); break;
- case TypeCode.Char: il.Emit(OpCodes.Ldc_I4, (char)value); break;
- case TypeCode.Double: il.Emit(OpCodes.Ldc_R8, (double)value); break;
- case TypeCode.Int16: il.Emit(OpCodes.Ldc_I4, (short)value); break;
- case TypeCode.Int32: il.Emit(OpCodes.Ldc_I4, (int)value); break;
- case TypeCode.Int64: il.Emit(OpCodes.Ldc_I8, (long)value); break;
- case TypeCode.SByte: il.Emit(OpCodes.Ldc_I4, (sbyte)value); break;
- case TypeCode.Single: il.Emit(OpCodes.Ldc_R4, (float)value); break;
- case TypeCode.String: il.Emit(OpCodes.Ldstr, (string)value); break;
- case TypeCode.UInt16: il.Emit(OpCodes.Ldc_I4, (ushort)value); break;
- case TypeCode.UInt32: il.Emit(OpCodes.Ldc_I4, (uint)value); break;
- case TypeCode.UInt64: il.Emit(OpCodes.Ldc_I8, (ulong)value); break;
- }
- } else {
- il.EmitFieldGet(fi);
- }
- il.Emit(OpCodes.Ret);
- pb.SetGetMethod(method);
- fieldAccessorNames.Add(method.Name);
- if (!fi.IsLiteral && !fi.IsInitOnly) {
- method = _tg.DefineMethod(FieldSetterPrefix + fi.Name, methodAttrs,
- null, new Type[] { fi.FieldType });
- method.DefineParameter(1, ParameterAttributes.None, "value");
- il = new ILGen(method.GetILGenerator());
- il.EmitLoadArg(0);
- if (!fi.IsStatic) {
- il.EmitLoadArg(1);
- }
- il.EmitFieldSet(fi);
- il.Emit(OpCodes.Ret);
- pb.SetSetMethod(method);
- fieldAccessorNames.Add(method.Name);
- }
- specialNames[fi.Name] = fieldAccessorNames.ToArray();
- }
- }
- /// <summary>
- /// Overrides methods - this includes all accessible virtual methods as well as protected non-virtual members
- /// including statics and non-statics.
- /// </summary>
- private void OverrideMethods(Type type, Dictionary<string, string[]> specialNames) {
- // if we have conflicting virtual's do to new slots only override the methods on the
- // most derived class.
- Dictionary<KeyValuePair<string, MethodSignatureInfo>, MethodInfo> added = new Dictionary<KeyValuePair<string, MethodSignatureInfo>, MethodInfo>();
- MethodInfo overridden;
- MethodInfo[] methods = type.GetMethods(BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static | BindingFlags.FlattenHierarchy);
- foreach (MethodInfo mi in methods) {
- KeyValuePair<string, MethodSignatureInfo> key = new KeyValuePair<string, MethodSignatureInfo>(mi.Name, new MethodSignatureInfo(mi));
- if (!added.TryGetValue(key, out overridden)) {
- added[key] = mi;
- continue;
- }
- if (overridden.DeclaringType.IsAssignableFrom(mi.DeclaringType)) {
- added[key] = mi;
- }
- }
-
- if (type.IsAbstract() && !type.IsInterface()) {
- // abstract types can define interfaces w/o implementations
- foreach (Type iface in type.GetInterfaces()) {
- InterfaceMapping mapping = type.GetTypeInfo().GetRuntimeInterfaceMap(iface);
- for (int i = 0; i < mapping.TargetMethods.Length; i++) {
-
- if (mapping.TargetMethods[i] == null) {
- MethodInfo mi = mapping.InterfaceMethods[i];
- KeyValuePair<string, MethodSignatureInfo> key = new KeyValuePair<string, MethodSignatureInfo>(mi.Name, new MethodSignatureInfo(mi));
- added[key] = mi;
- }
- }
- }
- }
- Dictionary<PropertyInfo, PropertyBuilder> overriddenProperties = new Dictionary<PropertyInfo, PropertyBuilder>();
- foreach (MethodInfo mi in added.Values) {
- if (!CanOverrideMethod(mi)) continue;
- if (mi.IsPublic || mi.IsProtected()) {
- if (mi.IsSpecialName) {
- OverrideSpecialName(mi, specialNames, overriddenProperties);
- } else {
- OverrideBaseMethod(mi, specialNames);
- }
- }
- }
- }
- private void OverrideSpecialName(MethodInfo mi, Dictionary<string, string[]> specialNames, Dictionary<PropertyInfo, PropertyBuilder> overridden) {
- if (!mi.IsVirtual || mi.IsFinal) {
- // TODO: A better check here would be mi.IsFamily && mi.IsSpecialName. But we need to also check
- // the other property method (getter if we're a setter, setter if we're a getter) because if one is protected
- // and the other isn't we need to still override both (so our newslot property is also both a getter and a setter).
- if ((mi.IsProtected() || mi.IsSpecialName) && (mi.Name.StartsWith("get_") || mi.Name.StartsWith("set_"))) {
- // need to be able to call into protected getter/setter methods from derived types,
- // even if these methods aren't virtual and we are in partial trust.
- specialNames[mi.Name] = new[] { mi.Name };
- MethodBuilder mb = CreateSuperCallHelper(mi);
- foreach (PropertyInfo pi in mi.DeclaringType.GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)) {
- if (pi.GetGetMethod(true).MemberEquals(mi) || pi.GetSetMethod(true).MemberEquals(mi)) {
- AddPublicProperty(mi, overridden, mb, pi);
- break;
- }
- }
- }
- } else if (!TryOverrideProperty(mi, specialNames, overridden)) {
- string name;
- EventInfo[] eis = mi.DeclaringType.GetEvents(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
- foreach (EventInfo ei in eis) {
- if (ei.GetAddMethod().MemberEquals(mi)) {
- if (NameConverter.TryGetName(DynamicHelpers.GetPythonTypeFromType(mi.DeclaringType), ei, mi, out name) == NameType.None) return;
- CreateVTableEventOverride(mi, mi.Name);
- return;
- } else if (ei.GetRemoveMethod().MemberEquals(mi)) {
- if (NameConverter.TryGetName(DynamicHelpers.GetPythonTypeFromType(mi.DeclaringType), ei, mi, out name) == NameType.None) return;
- CreateVTableEventOverride(mi, mi.Name);
- return;
- }
- }
- OverrideBaseMethod(mi, specialNames);
- }
- }
- private bool TryOverrideProperty(MethodInfo mi, Dictionary<string, string[]> specialNames, Dictionary<PropertyInfo, PropertyBuilder> overridden) {
- string name;
- PropertyInfo[] pis = mi.DeclaringType.GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
- specialNames[mi.Name] = new[] { mi.Name };
- MethodBuilder mb = null;
- PropertyInfo foundProperty = null;
- foreach (PropertyInfo pi in pis) {
- if (pi.GetIndexParameters().Length > 0) {
- if (mi.MemberEquals(pi.GetGetMethod(true))) {
- mb = CreateVTableMethodOverride(mi, "__getitem__");
- if (!mi.IsAbstract) {
- CreateSuperCallHelper(mi);
- }
- foundProperty = pi;
- break;
- } else if (mi.MemberEquals(pi.GetSetMethod(true))) {
- mb = CreateVTableMethodOverride(mi, "__setitem__");
- if (!mi.IsAbstract) {
- CreateSuperCallHelper(mi);
- }
- foundProperty = pi;
- break;
- }
- } else if (mi.MemberEquals(pi.GetGetMethod(true))) {
- if (mi.Name != "get_PythonType") {
- if (NameConverter.TryGetName(GetBaseTypeForMethod(mi), pi, mi, out name) == NameType.None) {
- return true;
- }
- mb = CreateVTableGetterOverride(mi, name);
- if (!mi.IsAbstract) {
- CreateSuperCallHelper(mi);
- }
- }
- foundProperty = pi;
- break;
- } else if (mi.MemberEquals(pi.GetSetMethod(true))) {
- if (NameConverter.TryGetName(GetBaseTypeForMethod(mi), pi, mi, out name) == NameType.None) {
- return true;
- }
- mb = CreateVTableSetterOverride(mi, name);
- if (!mi.IsAbstract) {
- CreateSuperCallHelper(mi);
- }
- foundProperty = pi;
- break;
- }
- }
- if (foundProperty != null) {
- AddPublicProperty(mi, overridden, mb, foundProperty);
- return true;
- }
- return false;
- }
- private void AddPublicProperty(MethodInfo mi, Dictionary<PropertyInfo, PropertyBuilder> overridden, MethodBuilder mb, PropertyInfo foundProperty) {
- MethodInfo getter = foundProperty.GetGetMethod(true);
- MethodInfo setter = foundProperty.GetSetMethod(true);
- if (getter != null && getter.IsProtected() || setter != null && setter.IsProtected()) {
- PropertyBuilder builder;
- if (!overridden.TryGetValue(foundProperty, out builder)) {
- ParameterInfo[] indexArgs = foundProperty.GetIndexParameters();
- Type[] paramTypes = new Type[indexArgs.Length];
- for (int i = 0; i < paramTypes.Length; i++) {
- paramTypes[i] = indexArgs[i].ParameterType;
- }
- overridden[foundProperty] = builder = _tg.DefineProperty(foundProperty.Name, foundProperty.Attributes, foundProperty.PropertyType, paramTypes);
- }
- if (foundProperty.GetGetMethod(true).MemberEquals(mi)) {
- builder.SetGetMethod(mb);
- } else if (foundProperty.GetSetMethod(true).MemberEquals(mi)) {
- builder.SetSetMethod(mb);
- }
- }
- }
- /// <summary>
- /// Loads all the incoming arguments and forwards them to mi which
- /// has the same signature and then returns the result
- /// </summary>
- private static void EmitBaseMethodDispatch(MethodInfo mi, ILGen il) {
- if (!mi.IsAbstract) {
- int offset = 0;
- if (!mi.IsStatic) {
- il.EmitLoadArg(0);
- offset = 1;
- }
- ParameterInfo[] parameters = mi.GetParameters();
- for (int i = 0; i < parameters.Length; i++) {
- il.EmitLoadArg(i + offset);
- }
- il.EmitCall(OpCodes.Call, mi, null); // base call must be non-virtual
- il.Emit(OpCodes.Ret);
- } else {
- il.EmitLoadArg(0);
- il.EmitString(mi.Name);
- il.EmitCall(typeof(PythonOps), "MissingInvokeMethodException");
- il.Emit(OpCodes.Throw);
- }
- }
- private void OverrideBaseMethod(MethodInfo mi, Dictionary<string, string[]> specialNames) {
- if ((!mi.IsVirtual || mi.IsFinal) && !mi.IsProtected()) {
- return;
- }
- PythonType basePythonType = GetBaseTypeForMethod(mi);
- string name = null;
- if (NameConverter.TryGetName(basePythonType, mi, out name) == NameType.None)
- return;
- if (mi.DeclaringType == typeof(object) && mi.Name == "Finalize") return;
- specialNames[mi.Name] = new[] { mi.Name };
- if (!mi.IsStatic) {
- CreateVTableMethodOverride(mi, name);
- }
- if (!mi.IsAbstract) {
- CreateSuperCallHelper(mi);
- }
- }
- private PythonType GetBaseTypeForMethod(MethodInfo mi) {
- PythonType basePythonType;
- if (_baseType == …
Large files files are truncated, but you can click here to view the full file