Show NewTypeMaker.cs syntax highlighted
/* **********************************************************************************
*
* Copyright (c) Microsoft Corporation. All rights reserved.
*
* This source code is subject to terms and conditions of the Shared Source License
* for IronPython. A copy of the license can be found in the License.html file
* at the root of this distribution. If you can not locate the Shared Source License
* for IronPython, please send an email to ironpy@microsoft.com.
* By using this source code in any fashion, you are agreeing to be bound by
* the terms of the Shared Source License for IronPython.
*
* You must not remove this notice, or any other, from this software.
*
* **********************************************************************************/
using System;
using System.Collections;
using System.Collections.Generic;
using System.Text;
using System.IO;
using System.ComponentModel;
using System.Threading;
using System.Resources;
using System.Diagnostics;
using System.Diagnostics.SymbolStore;
using System.Reflection;
using System.Reflection.Emit;
using IronPython.Runtime;
using IronPython.Runtime.Types;
using IronPython.Runtime.Calls;
using IronPython.Runtime.Operations;
using ClrModule = IronPython.Modules.ClrModule;
namespace IronPython.Compiler.Generation {
/// <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>
class NewTypeMaker {
public const string VtableNamesField = "#VTableNames#";
public const string TypePrefix = "IronPython.NewTypes.";
private static Publisher<NewTypeInfo, Type> newTypes = new Publisher<NewTypeInfo, Type>();
private static int typeCount = 0;
protected Type baseType;
protected IList<string> slots;
protected TypeGen tg;
protected Slot typeField, dictField, weakrefField;
protected IEnumerable<Type> interfaceTypes;
protected Tuple baseClasses;
private bool hasBaseTypeField = false;
private IList<Slot> slotsSlots;
private Dictionary<string, VTableEntry> vtable = new Dictionary<string, VTableEntry>();
public static Type GetNewType(string typeName, Tuple bases, IDictionary<object, object> dict) {
// we're really only interested in the "correct" base type pulled out of bases
// and any slot information contained in dict
// other info might be used for future optimizations
Debug.Assert(bases != null);
NewTypeInfo typeInfo = GetTypeInfo(typeName, bases, GetSlots(dict));
if (typeInfo.BaseType.IsSealed || typeInfo.BaseType.IsValueType)
throw Ops.TypeError("cannot derive from sealed or value types");
Type ret = newTypes.GetOrCreateValue(typeInfo,
delegate() {
// creation code
return GetTypeMaker(bases, typeName, typeInfo).CreateNewType();
});
if (typeInfo.Slots != null) {
// update dict w/ slots that point at the correct fields.
for (int i = 0; i < typeInfo.Slots.Count; i++) {
PropertyInfo pi = ret.GetProperty(typeInfo.Slots[i]);
string name = typeInfo.Slots[i];
if (name.StartsWith("__") && !name.EndsWith("__")) {
name = "_" + typeName + name;
}
dict[name] = new ReflectedSlotProperty(pi, pi.GetGetMethod(), pi.GetSetMethod(), NameType.PythonProperty);
}
}
return ret;
}
private static NewTypeMaker GetTypeMaker(Tuple bases, string typeName, NewTypeInfo ti) {
if (IsInstanceType(ti.BaseType)) {
return new NewSubtypeMaker(bases, ti);
}
return new NewTypeMaker(bases, ti);
}
private static List<string> GetSlots(IDictionary<object, object> dict) {
List<string> res = null;
object slots;
IAttributesDictionary attrDict = dict as IAttributesDictionary;
if (attrDict != null && attrDict.TryGetValue(SymbolTable.Slots, out slots)) {
res = SlotsToList(slots);
}
return res;
}
internal static List<string> SlotsToList(object slots) {
List<string> res = new List<string>();
ISequence seq = slots as ISequence;
if (seq != null && !(seq is ExtensibleString)) {
res = new List<string>(seq.GetLength());
for (int i = 0; i < seq.GetLength(); i++) {
res.Add(GetSlotName(seq[i]));
}
res.Sort();
} else {
res = new List<string>(1);
res.Add(GetSlotName(slots));
}
return res;
}
private static string GetSlotName(object o) {
string value;
if (!Converter.TryConvertToString(o, out value) || String.IsNullOrEmpty(value))
throw Ops.TypeError("slots must be one string or a list of strings");
for (int i = 0; i < value.Length; i++) {
if ((value[i] >= 'a' && value[i] <= 'z') ||
(value[i] >= 'A' && value[i] <= 'Z') ||
(i != 0 && value[i] >= '0' && value[i] <= '9') ||
value[i] == '_') {
continue;
}
throw Ops.TypeError("__slots__ must be valid identifiers");
}
return value;
}
/// <summary>
/// Is this a type used for instances Python types (and not for the types themselves)?
/// </summary>
internal static bool IsInstanceType(Type type) {
return type.FullName.IndexOf(NewTypeMaker.TypePrefix) == 0;
}
/// <summary>
/// "bases" contains a set of PythonTypes. These can include types defined in Python (say cpy1, cpy2),
/// CLI types (say cCLI1, cCLI2), and CLI interfaces (say iCLI1, iCLI2). Here are some
/// examples of how this works:
///
/// (bases) => baseType, {interfaceTypes}
///
/// (cpy1) => System.Object, {}
/// (cpy1, cpy2) => System.Object, {}
/// (cpy1, cCLI1, iCLI1, iCLI2) => cCLI1, {iCLI1, iCLI2}
/// [some type that satisfies the line above] =>
/// cCLI1, {iCLI1, iCLI2}
/// (cCLI1, cCLI2) => error
/// </summary>
private static NewTypeInfo GetTypeInfo(string typeName, Tuple bases, List<string> slots) {
List<Type> interfaceTypes = new List<Type>();
Type baseCLIType = typeof(object); // Pure Python object instances inherit from System.Object
DynamicType basePythonType = null;
foreach (object curBaseType in bases) {
DynamicType curBasePythonType = curBaseType as DynamicType;
if (curBasePythonType == null) {
if (curBaseType is OldClass)
continue;
throw Ops.TypeError(typeName + ": unsupported base type for new-style class: " + baseCLIType);
}
IList<Type> baseInterfaces;
Type curTypeToExtend = curBasePythonType.GetTypesToExtend(out baseInterfaces);
if (curTypeToExtend == null || curTypeToExtend == typeof(BuiltinFunction))
throw Ops.TypeError(typeName + ": {0} is not an acceptable base type", curBasePythonType);
if (curTypeToExtend.ContainsGenericParameters)
throw Ops.TypeError(typeName + ": cannot inhert from open generic instantiation {0}. Only closed instantiations are supported.", curBasePythonType);
foreach (Type interfaceType in baseInterfaces) {
if (interfaceType.ContainsGenericParameters)
throw Ops.TypeError(typeName + ": cannot inhert from open generic instantiation {0}. Only closed instantiations are supported.", interfaceType);
interfaceTypes.Add(interfaceType);
}
if (curTypeToExtend != typeof(object)) {
if (baseCLIType != typeof(object) && baseCLIType != curTypeToExtend) {
bool isOkConflit = false;
if (IsInstanceType(baseCLIType) && IsInstanceType(curTypeToExtend)) {
List<string> slots1 = SlotsToList(curBasePythonType.dict[SymbolTable.Slots]);
List<string> slots2 = SlotsToList(basePythonType.dict[SymbolTable.Slots]);
if (curBasePythonType.type.BaseType == basePythonType.type.BaseType &&
slots1.Count == 1 && slots2.Count == 1 &&
((slots1[0] == "__dict__" && slots2[0] == "__weakref__") ||
(slots2[0] == "__dict__" && slots1[0] == "__weakref__"))) {
isOkConflit = true;
curTypeToExtend = curBasePythonType.type.BaseType;
if (slots != null) {
if (slots.Contains("__weakref__"))
throw Ops.TypeError("__weakref__ disallowed, base class already defines this");
slots.Add("__weakref__");
if (!slots.Contains("__dict__"))
slots.Add("__dict__");
}
}
}
if (!isOkConflit) throw Ops.TypeError(typeName + ": can only extend one CLI or builtin type, not both {0} (for {1}) and {2} (for {3})",
baseCLIType.FullName, basePythonType, curTypeToExtend.FullName, curBasePythonType);
}
baseCLIType = curTypeToExtend;
basePythonType = curBasePythonType;
}
}
return new NewTypeInfo(baseCLIType, interfaceTypes, slots);
}
protected NewTypeMaker(Tuple baseClasses, NewTypeInfo typeInfo) {
this.baseType = typeInfo.BaseType;
this.baseClasses = baseClasses;
this.interfaceTypes = typeInfo.InterfaceTypes;
this.slots = typeInfo.Slots;
}
private static string GetBaseName(MethodInfo mi, Dictionary<string, bool> specialNames) {
Debug.Assert(mi.Name.StartsWith("#base#"));
string newName = mi.Name.Substring(6);
Debug.Assert(specialNames.ContainsKey(newName));
if (specialNames[newName] == true) {
if (newName == "get_Item") return "__getitem__";
else if (newName == "set_Item") return "__setitem__";
}
return newName;
}
// Coverts a well-known CLI name to its Python equivalent
private static string NormalizeName(string name) {
if (name == "ToString") return "__str__";
return name;
}
// Build a name which is unique to this TypeInfo.
protected virtual string GetName() {
StringBuilder name = new StringBuilder(baseType.FullName);
foreach (Type interfaceType in interfaceTypes) {
name.Append("#");
name.Append(interfaceType.Name);
}
name.Append("_");
name.Append(System.Threading.Interlocked.Increment(ref typeCount));
return name.ToString();
}
protected virtual void ImplementInterfaces() {
foreach (Type interfaceType in interfaceTypes) {
ImplementInterface(interfaceType);
}
}
protected void ImplementInterface(Type interfaceType) {
tg.myType.AddInterfaceImplementation(interfaceType);
}
private Type CreateNewType() {
AssemblyGen ag = OutputGenerator.Snippets;
string name = GetName();
tg = ag.DefinePublicType(TypePrefix + name, baseType);
ImplementInterfaces();
GetOrDefineClass();
GetOrDefineDict();
ImplementSlots();
ImplementPythonObject();
ImplementConstructors();
Dictionary<string, bool> specialNames = new Dictionary<string, bool>();
OverrideVirtualMethods(baseType, specialNames);
Dictionary<Type, bool> doneTypes = new Dictionary<Type, bool>();
foreach (Type interfaceType in interfaceTypes) {
DoInterfaceType(interfaceType, doneTypes, specialNames);
}
InitializeVTableStrings();
// Hashtable slots = collectSlots(dict, tg);
// if (slots != null) tg.createAttrMethods(slots);
Type ret = tg.FinishType();
AddBaseMethods(ret, specialNames);
return ret;
}
protected virtual void ImplementPythonObject() {
ImplementDynamicObject();
ImplementCustomTypeDescriptor();
ImplementPythonEquals();
ImplementPythonComparable();
ImplementWeakReference();
}
private void GetOrDefineDict() {
FieldInfo baseDictField = baseType.GetField("dict");
if (baseDictField == null) {
dictField = tg.AddField(typeof(IAttributesDictionary), "__dict__");
} else {
dictField = new FieldSlot(new ThisSlot(tg.myType), baseDictField);
}
}
private void GetOrDefineClass() {
FieldInfo baseTypeField = baseType.GetField("__class__");
if (baseTypeField == null) {
typeField = tg.AddField(typeof(UserType), "__class__");
} else {
Debug.Assert(baseTypeField.FieldType == typeof(UserType));
typeField = new FieldSlot(new ThisSlot(tg.myType), baseTypeField);
hasBaseTypeField = true;
}
}
protected virtual ParameterInfo[] GetOverrideCtorSignature(ParameterInfo[] original) {
ParameterInfo[] argTypes = new ParameterInfo[original.Length + 1];
argTypes[0] = new TrackingParamInfo(typeField.Type, "cls");
Array.Copy(original, 0, argTypes, 1, argTypes.Length - 1);
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.IsFamily)) continue;
OverrideConstructor(ci);
}
}
protected virtual bool ShouldOverrideVirtual(MethodInfo mi) {
return true;
}
private void AddBaseMethods(Type finishedType, Dictionary<string, bool> specialNames) {
// "Adds" base methods to super type (should really add to the derived type)
// this makes super(...).xyz to work - otherwise we'd return a function that
// did a virtual call resulting in a stack overflow.
ReflectedType rt = Ops.GetDynamicTypeFromType(baseType) as ReflectedType;
rt.Initialize();
foreach (MethodInfo mi in finishedType.GetMethods()) {
if (!ShouldOverrideVirtual(mi)) continue;
string methodName = mi.Name;
if (methodName.StartsWith("#base#")) {
string newName = GetBaseName(mi, specialNames);
// don't store __ names for extensible types. super will dispatch to the
// non-virtual method declard on the Ops type, and adding these will just slow
// down the normal calls in this case. For non-extensible types we need to add this
// so we dispatch w/o stack overflowing on super calls.
if (!mi.DeclaringType.FullName.StartsWith(TypePrefix + "IronPython.Runtime.Operations.Extensible")) {
ParameterInfo[] pis = mi.GetParameters();
Type[] types = new Type[pis.Length];
int i = 0;
foreach (ParameterInfo pi in pis) {
types[i++] = pi.ParameterType;
}
MethodInfo overriding = this.baseType.GetMethod(methodName.Substring(6),
BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance,
null,
types,
null);
Debug.Assert(overriding != null);
string outName;
NameType nt = NameConverter.TryGetName(Ops.GetDynamicTypeFromType(baseType) as ReflectedType, overriding, out outName);
if (nt != NameType.None && outName != newName) {
rt.StoreReflectedBaseMethod(outName, mi, nt);
}
}
rt.StoreReflectedBaseMethod(newName, mi, NameType.Method);
}
}
}
private void DoInterfaceType(Type interfaceType, Dictionary<Type, bool> doneTypes, Dictionary<string, bool> specialNames) {
if (doneTypes.ContainsKey(interfaceType)) return;
doneTypes.Add(interfaceType, true);
OverrideVirtualMethods(interfaceType, specialNames);
foreach (Type t in interfaceType.GetInterfaces()) {
DoInterfaceType(t, doneTypes, specialNames);
}
}
private ConstructorBuilder OverrideConstructor(ConstructorInfo parentConstructor) {
ParameterInfo[] pis = parentConstructor.GetParameters();
ParameterInfo[] 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.myType.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 = i - (overrideParams.Length - pis.Length);
if (origIndex >= 0) {
if (pis[origIndex].IsDefined(typeof(ParamArrayAttribute), false)) {
pb.SetCustomAttribute(new CustomAttributeBuilder(
typeof(ParamArrayAttribute).GetConstructor(Type.EmptyTypes), Ops.EMPTY));
} else if (pis[origIndex].IsDefined(typeof(ParamDictAttribute), false)) {
pb.SetCustomAttribute(new CustomAttributeBuilder(
typeof(ParamDictAttribute).GetConstructor(Type.EmptyTypes), Ops.EMPTY));
}
}
}
CodeGen cg = new CodeGen(tg, cb, cb.GetILGenerator(), argTypes);
// <typeField> = <arg0>
cg.EmitArgGet(0);
typeField.EmitSet(cg);
// initialize all slots to Uninitialized.instance
if (slots != null) {
for (int i = 0; i < slots.Count; i++) {
if (slots[i] != "__weakref__" && slots[i] != "__dict__") {
cg.EmitUninitialized();
} else {
cg.Emit(OpCodes.Ldnull);
}
slotsSlots[i].EmitSet(cg);
}
}
CallBaseConstructor(parentConstructor, overrideParams.Length - pis.Length, overrideParams, cg);
return cb;
}
private static void CallBaseConstructor(ConstructorInfo parentConstructor, int offset, ParameterInfo[] pis, CodeGen cg) {
cg.EmitThis();
for (int i = offset; i < pis.Length; i++) {
cg.EmitArgGet(i);
}
cg.Emit(OpCodes.Call, parentConstructor);
cg.Emit(OpCodes.Ret);
}
private void InitializeVTableStrings() {
string[] names = new string[vtable.Count];
foreach (VTableEntry slot in vtable.Values) {
names[slot.index] = slot.name;
}
Slot namesField = tg.AddStaticField(typeof(string[]), VtableNamesField);
CodeGen cg = tg.GetOrMakeInitializer();
cg.EmitStringArray(names);
namesField.EmitSet(cg);
}
private void ImplementCustomTypeDescriptor() {
tg.myType.AddInterfaceImplementation(typeof(ICustomTypeDescriptor));
foreach (MethodInfo m in typeof(ICustomTypeDescriptor).GetMethods()) {
ImplementCTDOverride(m);
}
}
private void ImplementCTDOverride(MethodInfo m) {
CodeGen cg = tg.DefineExplicitInterfaceImplementation(m);
cg.EmitThis();
ParameterInfo[] pis = m.GetParameters();
Type[] paramTypes = new Type[pis.Length + 1];
paramTypes[0] = typeof(object);
for (int i = 0; i < pis.Length; i++) {
cg.EmitArgGet(i);
paramTypes[i + 1] = pis[i].ParameterType;
}
cg.EmitCall(typeof(CustomTypeDescHelpers), m.Name, paramTypes);
cg.EmitConvertToObject(m.ReturnType);
cg.EmitReturn();
cg.Finish();
}
protected bool NeedsDictionary {
get {
if (slots == null) return true;
if (slots.Contains("__dict__")) return true;
foreach (IPythonType pt in baseClasses) {
if (pt is UserType) return true;
}
return false;
}
}
private void ImplementDynamicObject() {
CodeGen cg;
tg.myType.AddInterfaceImplementation(typeof(ISuperDynamicObject));
MethodAttributes attrs = (MethodAttributes)0;
if (slots != null) attrs = MethodAttributes.Virtual;
cg = tg.DefineMethodOverride(attrs, typeof(ISuperDynamicObject).GetMethod("GetDict"));
if (NeedsDictionary) {
dictField.EmitGet(cg);
cg.EmitReturn();
} else {
cg.Emit(OpCodes.Ldnull);
cg.EmitReturn();
}
cg.Finish();
cg = tg.DefineMethodOverride(attrs, typeof(ISuperDynamicObject).GetMethod("SetDict"));
if (NeedsDictionary) {
cg.EmitArgGet(0);
dictField.EmitSet(cg);
cg.EmitRawConstant(true);
cg.EmitReturn();
} else {
cg.EmitRawConstant(false);
cg.EmitReturn();
}
cg.Finish();
if (hasBaseTypeField) return;
cg = tg.DefineMethodOverride(attrs, typeof(IDynamicObject).GetMethod("GetDynamicType"));
typeField.EmitGet(cg);
cg.EmitReturn();
cg.Finish();
cg = tg.DefineMethodOverride(attrs, typeof(ISuperDynamicObject).GetMethod("SetDynamicType"));
cg.EmitArgGet(0);
typeField.EmitSet(cg);
cg.EmitReturn();
cg.Finish();
}
/// <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>
/// <param name="intf"></param>
private void DefineHelperInterface(Type intf, bool fExplicit) {
tg.myType.AddInterfaceImplementation(intf);
MethodInfo[] mis = intf.GetMethods();
foreach (MethodInfo mi in mis) {
CodeGen cg = fExplicit ? tg.DefineExplicitInterfaceImplementation(mi) : tg.DefineMethodOverride(mi);
cg.EmitThis();
ParameterInfo[] pis = mi.GetParameters();
for (int i = 0; i < pis.Length; i++) {
cg.EmitArgGet(i);
}
cg.EmitCall(typeof(UserType).GetMethod(mi.Name + "Helper"));
cg.EmitReturn();
cg.Finish();
}
}
private void ImplementPythonEquals() {
if (this.baseType.GetInterface("IRichEquality") == null) {
DefineHelperInterface(typeof(IRichEquality), false);
}
}
private void ImplementPythonComparable() {
if (this.baseType.GetInterface("IRichComparable") == null) {
DefineHelperInterface(typeof(IRichComparable), false);
}
}
private void CreateWeakRefField() {
if (weakrefField != null) return;
FieldInfo fi = baseType.GetField("__weakref__");
if (fi != null) {
// base defines it
weakrefField = new FieldSlot(new ThisSlot(baseType), fi);
}
if (weakrefField == null) {
weakrefField = tg.AddField(typeof(WeakRefTracker), "__weakref__");
}
}
internal bool BaseHasWeakRef(IPythonType curType) {
UserType ut = curType as UserType;
if (ut != null && ut.HasSlots && ut.HasWeakRef) {
return true;
}
foreach (IPythonType baseType in curType.BaseClasses) {
if (BaseHasWeakRef(baseType)) return true;
}
return false;
}
protected virtual void ImplementWeakReference() {
CreateWeakRefField();
bool isWeakRefAble = true;
if (slots != null && !slots.Contains("__weakref__")) {
// always define the field, only implement the interface
// if we are slotless or the user defined __weakref__ in slots
bool baseHasWeakRef = false;
foreach (IPythonType pt in baseClasses) {
if (BaseHasWeakRef(pt)) {
baseHasWeakRef = true;
break;
}
}
if (baseHasWeakRef) return;
isWeakRefAble = false;
}
tg.myType.AddInterfaceImplementation(typeof(IWeakReferenceable));
CodeGen cg = tg.DefineMethodOverride(typeof(IWeakReferenceable).GetMethod("SetWeakRef"));
if (!isWeakRefAble) {
cg.EmitRawConstant(false);
cg.EmitReturn();
} else {
cg.EmitArgGet(0);
weakrefField.EmitSet(cg);
cg.EmitRawConstant(true);
cg.EmitReturn();
}
cg.Finish();
cg = tg.DefineMethodOverride(typeof(IWeakReferenceable).GetMethod("SetFinalizer"));
cg.EmitArgGet(0);
weakrefField.EmitSet(cg);
cg.EmitReturn();
cg.Finish();
cg = tg.DefineMethodOverride(typeof(IWeakReferenceable).GetMethod("GetWeakRef"));
weakrefField.EmitGet(cg);
cg.EmitReturn();
cg.Finish();
}
private void ImplementSlots() {
if (slots != null) {
slotsSlots = new List<Slot>();
for (int i = 0; i < slots.Count; i++) {
Slot s;
switch (slots[i]) {
case "__weakref__": CreateWeakRefField(); s = weakrefField; break;
case "__dict__": s = dictField; break;
default:
// mangled w/ a . to never collide w/ any regular names
s = tg.AddField(typeof(object), "." + slots[i]);
break;
}
slotsSlots.Add(s);
PropertyBuilder pb = tg.DefineProperty(slots[i], PropertyAttributes.None, s.Type);
CodeGen getter = tg.DefineMethod(MethodAttributes.Public,
"get_" + slots[i],
s.Type,
new Type[0],
new string[0]);
s.EmitGet(getter);
getter.Emit(OpCodes.Dup);
getter.EmitThis();
getter.EmitString(slots[i]);
getter.EmitCall(typeof(Ops), "CheckInitializedAttribute");
getter.EmitReturn();
getter.Finish();
CodeGen setter = tg.DefineMethod(MethodAttributes.Public,
"set_" + slots[i],
typeof(void),
new Type[] { s.Type },
new string[] { "value" });
setter.EmitArgGet(0);
s.EmitSet(setter);
setter.EmitReturn();
setter.Finish();
pb.SetGetMethod(getter.MethodInfo as MethodBuilder);
pb.SetSetMethod(setter.MethodInfo as MethodBuilder);
}
}
}
private void OverrideVirtualMethods(Type type, Dictionary<string, bool> specialNames) {
foreach (MethodInfo mi in type.GetMethods(BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance)) {
if (!ShouldOverrideVirtual(mi)) continue;
if (mi.IsPublic || mi.IsFamily || mi.IsFamilyOrAssembly) {
if (mi.IsSpecialName) {
OverrideSpecialName(mi, specialNames);
} else {
OverrideBaseMethod(mi, specialNames);
}
}
}
}
private static string[] SkipMethodNames = new string[] { "GetDynamicType", };
private void OverrideSpecialName(MethodInfo mi, Dictionary<string, bool> specialNames) {
if (mi == null || !mi.IsVirtual || mi.IsFinal) return;
string name;
PropertyInfo[] pis = mi.DeclaringType.GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
specialNames[mi.Name] = true;
foreach (PropertyInfo pi in pis) {
if (pi.GetIndexParameters().Length > 0) {
if (mi == pi.GetGetMethod(true)) {
Slot methField = GetExistingField("__getitem__");
CreateVirtualMethodOverride(mi, methField);
if (!mi.IsAbstract) CreateVirtualMethodHelper(tg, mi);
return;
} else if (mi == pi.GetSetMethod(true)) {
Slot methField = GetExistingField("__setitem__");
CreateVirtualMethodOverride(mi, methField);
if (!mi.IsAbstract) CreateVirtualMethodHelper(tg, mi);
return;
}
} else if (mi == pi.GetGetMethod(true)) {
if (NameConverter.TryGetName((ReflectedType)Ops.GetDynamicTypeFromType(mi.DeclaringType), pi, mi, out name) == NameType.None) return;
CreateVTableGetterOverride(mi, GetOrMakeVTableEntry(name));
return;
} else if (mi == pi.GetSetMethod(true)) {
if (NameConverter.TryGetName((ReflectedType)Ops.GetDynamicTypeFromType(mi.DeclaringType), pi, mi, out name) == NameType.None) return;
CreateVTableSetterOverride(mi, GetOrMakeVTableEntry(name));
return;
}
}
EventInfo[] eis = mi.DeclaringType.GetEvents(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
foreach (EventInfo ei in eis) {
if (ei.GetAddMethod() == mi) {
if (NameConverter.TryGetName((ReflectedType)Ops.GetDynamicTypeFromType(mi.DeclaringType), ei, mi, out name) == NameType.None) return;
CreateVTableEventOverride(ei, mi, GetOrMakeVTableEntry(mi.Name));
return;
} else if (ei.GetRemoveMethod() == mi) {
if (NameConverter.TryGetName((ReflectedType)Ops.GetDynamicTypeFromType(mi.DeclaringType), ei, mi, out name) == NameType.None) return;
CreateVTableEventOverride(ei, mi, GetOrMakeVTableEntry(mi.Name));
return;
}
}
}
/// <summary>
/// Loads all the incoming arguments of cg and forwards them to mi which
/// has the same signature and then returns the result
/// </summary>
private static void EmitBaseMethodDispatch(MethodInfo mi, CodeGen cg) {
if (!mi.IsAbstract) {
cg.EmitThis();
foreach (Slot argSlot in cg.argumentSlots) argSlot.EmitGet(cg);
cg.EmitCall(OpCodes.Call, mi, null); // base call must be non-virtual
cg.EmitReturn();
} else {
cg.EmitThis();
cg.EmitString(mi.Name);
cg.EmitCall(typeof(Ops), "MissingInvokeMethodException");
cg.Emit(OpCodes.Throw);
}
}
private void OverrideBaseMethod(MethodInfo mi, Dictionary<string, bool> specialNames) {
if (mi == null ||
!mi.IsVirtual ||
mi.IsFinal ||
Array.IndexOf(SkipMethodNames, mi.Name) != -1) {
return;
}
ReflectedType baseDynamicType;
if (baseType == mi.DeclaringType || baseType.IsSubclassOf(mi.DeclaringType)) {
baseDynamicType = (ReflectedType)Ops.GetDynamicTypeFromType(baseType);
} else {
// We must be inherting from an interface
Debug.Assert(mi.DeclaringType.IsInterface);
baseDynamicType = (ReflectedType)Ops.GetDynamicTypeFromType(mi.DeclaringType);
}
string name = null;
if (NameConverter.TryGetName(baseDynamicType, mi, out name) == NameType.None)
return;
if (mi.DeclaringType == typeof(object) && mi.Name == "Finalize") return;
specialNames[mi.Name] = false;
Slot methField = GetExistingField(name);
if (methField != null) CreateVirtualMethodOverride(mi, methField);
else CreateVTableMethodOverride(mi, GetOrMakeVTableEntry(name));
if (!mi.IsAbstract) CreateVirtualMethodHelper(tg, mi);
}
private VTableEntry GetOrMakeVTableEntry(string name) {
VTableEntry ret;
if (vtable.TryGetValue(name, out ret)) return ret;
ret = new VTableEntry(name, vtable.Count);
vtable[name] = ret;
return ret;
}
public Slot GetExistingField(string name) {
name = NormalizeName(name);
FieldInfo fi = typeof(DynamicType).GetField(name + "F");
if (fi != null) return new FieldSlot(typeField, fi);
else return null;
}
private static void EmitBadCallThrow(CodeGen cg, MethodInfo mi, string reason) {
cg.EmitString("Cannot override method from IronPython {0} because " + reason);
cg.EmitInt(1);
cg.Emit(OpCodes.Newarr, typeof(object));
cg.Emit(OpCodes.Dup);
cg.EmitInt(0);
cg.EmitString(mi.Name);
cg.Emit(OpCodes.Stelem_Ref);
cg.EmitCall(typeof(Ops), "TypeError");
cg.Emit(OpCodes.Throw);
}
/// <summary>
/// Emits code to check if the class has overriden this specific
/// function. For example:
///
/// MyDerivedType.SomeVirtualFunction = ...
/// or
///
/// class MyDerivedType(MyBaseType):
/// def SomeVirtualFunction(self, ...):
///
/// </summary>
internal Slot EmitBaseClassCallCheckForProperties(CodeGen cg, MethodInfo baseMethod, VTableEntry methField) {
Label instanceCall = cg.DefineLabel();
Slot callTarget = cg.GetLocalTmp(typeof(object));
typeField.EmitGet(cg);
cg.EmitInt(methField.index);
callTarget.EmitGetAddr(cg);
cg.EmitCall(typeof(UserType), "TryGetNonInheritedValueHelper");
cg.Emit(OpCodes.Brtrue, instanceCall);
EmitBaseMethodDispatch(baseMethod, cg);
cg.MarkLabel(instanceCall);
return callTarget;
}
private void CreateVTableGetterOverride(MethodInfo mi, VTableEntry methField) {
CodeGen cg = tg.DefineMethodOverride(mi);
Slot callTarget = EmitBaseClassCallCheckForProperties(cg, mi, methField);
callTarget.EmitGet(cg);
cg.EmitThis();
cg.EmitSymbolId(methField.name);
cg.EmitCall(typeof(UserType), "GetPropertyHelper");
cg.EmitReturnFromObject();
cg.Finish();
}
private void CreateVTableSetterOverride(MethodInfo mi, VTableEntry methField) {
CodeGen cg = tg.DefineMethodOverride(mi);
Slot callTarget = EmitBaseClassCallCheckForProperties(cg, mi, methField);
callTarget.EmitGet(cg);
cg.EmitThis();
cg.EmitArgGet(0);
cg.EmitConvertToObject(mi.GetParameters()[0].ParameterType);
cg.EmitSymbolId(methField.name);
cg.EmitCall(typeof(UserType), "SetPropertyHelper");
cg.EmitReturn();
cg.Finish();
}
private void CreateVTableEventOverride(EventInfo ei, MethodInfo mi, VTableEntry methField) {
// override the add/remove method
CodeGen cg = tg.DefineMethodOverride(mi);
Slot callTarget = EmitBaseClassCallCheckForProperties(cg, mi, methField);
callTarget.EmitGet(cg);
cg.EmitThis();
typeField.EmitGet(cg);
cg.EmitArgGet(0);
cg.EmitConvertToObject(mi.GetParameters()[0].ParameterType);
cg.EmitSymbolId(methField.name);
cg.EmitCall(typeof(UserType), "AddRemoveEventHelper");
cg.EmitReturn();
cg.Finish();
}
public static void EmitCallFromClrToPython(CodeGen cg, Slot callTarget, int firstArg) {
EmitCallFromClrToPython(cg, callTarget, firstArg, IronPython.Compiler.Ast.FunctionAttributes.None);
}
public static void EmitCallFromClrToPython(CodeGen cg, Slot callTarget, int firstArg, Ast.FunctionAttributes functionAttributes) {
callTarget.EmitGet(cg);
List<ReturnFixer> fixers = new List<ReturnFixer>(0);
Slot[] args = cg.argumentSlots;
int nargs = args.Length - firstArg;
if (nargs <= Ops.MaximumCallArgs && (functionAttributes & IronPython.Compiler.Ast.FunctionAttributes.ArgumentList) == 0) {
for (int i = firstArg; i < args.Length; i++) {
ReturnFixer rf = CompilerHelpers.EmitArgument(cg, args[i]);
if (rf != null) fixers.Add(rf);
}
cg.EmitCall(typeof(Ops), "Call", CompilerHelpers.MakeRepeatedArray(typeof(object), nargs + 1));
} else if ((functionAttributes & IronPython.Compiler.Ast.FunctionAttributes.ArgumentList) == 0) {
cg.EmitObjectArray(nargs, delegate(int index) {
ReturnFixer rf = CompilerHelpers.EmitArgument(cg, args[index + firstArg]);
if (rf != null) fixers.Add(rf);
});
cg.EmitCall(typeof(Ops), "Call", new Type[] { typeof(object), typeof(object[]) });
} else {
cg.EmitObjectArray(nargs - 1, delegate(int index) {
ReturnFixer rf = CompilerHelpers.EmitArgument(cg, args[index + firstArg]);
if (rf != null) fixers.Add(rf);
});
args[args.Length - 1].EmitGet(cg);
cg.EmitCall(typeof(Tuple), "Make");
cg.EmitCall(typeof(Ops), "CallWithArgsTuple");
}
foreach (ReturnFixer rf in fixers) {
rf.FixReturn(cg);
}
cg.EmitReturnFromObject();
}
private void CreateVTableMethodOverride(MethodInfo mi, VTableEntry methField) {
CodeGen cg = tg.DefineMethodOverride(mi);
Label instanceCall = cg.DefineLabel();
Slot callTarget = cg.GetLocalTmp(typeof(object));
typeField.EmitGet(cg);
cg.EmitThis();
dictField.EmitGet(cg);
cg.EmitSymbolId(methField.name);
cg.EmitInt(methField.index);
callTarget.EmitGetAddr(cg);
// out param populated w/ bound method on success
cg.EmitCall(typeof(UserType), "TryGetNonInheritedMethodHelper");
cg.Emit(OpCodes.Brtrue, instanceCall);
EmitBaseMethodDispatch(mi, cg);
cg.MarkLabel(instanceCall);
int argStart = 0;
Ast.FunctionAttributes attrs = Ast.FunctionAttributes.None;
ParameterInfo[] pis = mi.GetParameters();
if (pis.Length > 0) {
if (pis[0].ParameterType == typeof(ICallerContext))
argStart = 1;
if (pis[pis.Length - 1].IsDefined(typeof(ParamArrayAttribute), false)) {
attrs |= Ast.FunctionAttributes.ArgumentList;
}
}
EmitCallFromClrToPython(cg, callTarget, argStart, attrs);
cg.Finish();
}
private void CreateVirtualMethodOverride(MethodInfo mi, Slot methField) {
CodeGen cg = tg.DefineMethodOverride(mi);
Label baseCall = cg.DefineLabel();
methField.EmitGet(cg);
cg.EmitCall(typeof(MethodWrapper), "IsBuiltinMethod");
cg.Emit(OpCodes.Brtrue, baseCall);
methField.EmitGet(cg);
cg.EmitThis();
for (int i = 0; i < mi.GetParameters().Length; i++) {
cg.EmitArgGet(i);
cg.EmitConvertToObject(mi.GetParameters()[i].ParameterType);
}
if (mi.GetParameters().Length > 3) {
throw new NotImplementedException("OverrideBaseMethod: " + mi);
}
cg.EmitCall(typeof(MethodWrapper), "Invoke", CompilerHelpers.MakeRepeatedArray(typeof(object), 1 + mi.GetParameters().Length));
MethodInfo object_ToString = typeof(object).GetMethod("ToString");
if (mi.MethodHandle != object_ToString.MethodHandle) {
cg.EmitReturnFromObject();
} else {
cg.EmitCall(typeof(UserType), "ToStringReturnHelper");
cg.EmitReturn();
}
cg.MarkLabel(baseCall);
if (mi.MethodHandle == object_ToString.MethodHandle) {
// object.ToString() displays the CLI type name. However, __class__ is the real type for Python type instances
cg.EmitThis();
cg.EmitCall(typeof(UserType).GetMethod("ToStringHelper"));
cg.EmitReturn();
} else if (mi.IsAbstract) {
EmitBadCallThrow(cg, mi, "must override abstract method"); //@todo better exception
} else {
// Just forward to the base method implementation
EmitBaseMethodDispatch(mi, cg);
}
cg.Finish();
}
internal static CodeGen CreateVirtualMethodHelper(TypeGen tg, MethodInfo mi) {
ParameterInfo[] parms = mi.GetParameters();
Type[] types = CompilerHelpers.GetTypes(parms);
string[] paramNames = new string[parms.Length];
Type miType = mi.DeclaringType;
for (int i = 0; i < types.Length; i++) {
paramNames[i] = parms[i].Name;
if (types[i] == miType) {
types[i] = tg.myType;
}
}
CodeGen cg = tg.DefineMethod(MethodAttributes.Public | MethodAttributes.HideBySig,
"#base#" + mi.Name, mi.ReturnType, types, paramNames);
EmitBaseMethodDispatch(mi, cg);
cg.Finish();
return cg;
}
}
class ReturnFixer {
private Slot argSlot, refSlot;
public ReturnFixer(Slot refSlot, Slot argSlot) {
Debug.Assert(refSlot.Type.IsGenericType && refSlot.Type.GetGenericTypeDefinition() == typeof(ClrModule.Reference<>));
Debug.Assert(argSlot.Type.IsByRef);
this.refSlot = refSlot;
this.argSlot = argSlot;
}
public void FixReturn(CodeGen cg) {
argSlot.EmitGet(cg);
refSlot.EmitGet(cg);
cg.EmitCall(refSlot.Type.GetProperty("Value").GetGetMethod());
cg.EmitStoreValueIndirect(argSlot.Type.GetElementType());
}
internal Slot RefSlot {
get {
return refSlot;
}
}
}
class VTableEntry {
public readonly string name;
public readonly int index;
public VTableEntry(string name, int index) {
this.name = name;
this.index = index;
}
}
}
See more files for this project here