UserTypeGenerator.cs from p4shelf at Krugle
Show UserTypeGenerator.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.Generic;
using System.Collections.Specialized;
using System.Text;
using System.Diagnostics;
using System.Reflection;
using System.Reflection.Emit;
using IronPython.Runtime;
using IronPython.Runtime.Calls;
using IronPython.Runtime.Types;
using IronPython.Compiler.Ast;
using IronPython.CodeDom;
using IronPython.Runtime.Operations;
namespace IronPython.Compiler.Generation {
/// <summary>
/// Create's static .NET types from Python types. Python types must have __slots__
/// definition to get this concrete form created. These types then disallow arbitrary
/// instance assignment.
/// </summary>
static class UserTypeGenerator {
public static TypeGen DoStaticCompilation(GlobalSuite gs, CodeGen cg) {
GeneratorWalker gw = new GeneratorWalker(cg);
gs.Walk(gw);
return gw.FinishedType;
}
private class GeneratorWalker : AstWalkerNonRecursive {
CompilerContext compctx;
Stack<StackInfo> stack = new Stack<StackInfo>();
List<Dictionary<string, TypeGen>> declaredTypes = new List<Dictionary<string, TypeGen>>();
CodeGen moduleInit;
TypeGen outerScope;
TypeGen finished;
AssemblyGen assembly;
List<string> namespaces;
List<Assembly> assemblies;
public GeneratorWalker(CodeGen codeGen) {
Debug.Assert(codeGen.typeGen != null);
assembly = codeGen.typeGen.myAssembly;
compctx = codeGen.Context;
moduleInit = codeGen;
outerScope = codeGen.typeGen;
namespaces = new List<string>();
//!!! need to get type names already declared...
PushNewType(codeGen.typeGen, codeGen, null, null);
assemblies = new List<Assembly>();
if (RemoteCompiler.Instance != null) {
StringCollection files = RemoteCompiler.Instance.References;
foreach (string file in files) {
Assembly asm = IronPython.Hosting.PythonCompiler.LoadAssembly(file);
assemblies.Add(asm);
}
}
}
public TypeGen FinishedType {
get {
return finished;
}
}
#region IAstWalker Members
/// <summary>
/// Starts a class definition. This will create the new type,
/// the static constructor & emit the initialization into it,
/// and then push the type onto the stack.
///
/// Later the walker will emit all of the statements in the class
/// definition into the static constructor.
/// </summary>
public override bool Walk(ClassDefinition cd) {
// class definition - could either by a real type, or
// a namespace. If it only contains class defintions
// and no slots then it's a namespace.
if (IsNamespace(cd)) {
namespaces.Add(cd.Name.GetString());
return true;
}
// - emit all class initialization inside of
// static ctor.
SlotFinder sf = new SlotFinder();
cd.Walk(sf);
TypeGen baseTg;
List<Type> baseTypes = GetBaseType(cd, out baseTg);
if (baseTypes == null) {
// we don't know the base types, just emit it the old way...
namespaces.Add(cd.Name.GetString());
return true;
}
TypeGen newType = DefineNewType(cd, baseTypes);
CodeGen getCompiledType = AddIDynamicObject(newType, newType.AddField(typeof(IDictionary<SymbolId, object>), "__dict__"));
CodeGen ctorBuilder = AddDefaultCtor(newType, sf.Slots);
CodeGen classInit = DefineClassInit(cd, newType);
PushNewType(newType, classInit, ctorBuilder, baseTypes);
stack.Peek().GetCompiledType = getCompiledType;
// continue the walk on just the body (returning
// true would have us walk the bases & the body).
cd.Body.Walk(this);
return false;
}
/// <summary>
/// Pops the last class defintion from the stack, updating
/// the available type names so other classes can derive
/// from them
/// </summary>
public override void PostWalk(ClassDefinition cd) {
TypeGen baseTg;
List<Type> baseTypes = GetBaseType(cd, out baseTg);
if (baseTypes == null || IsNamespace(cd)) {
namespaces.RemoveAt(namespaces.Count - 1);
if (stack.Count == 1 && namespaces.Count == 0) {
// outer item is a namespace, we need to load it.
CodeGen cg = stack.Peek().StaticConstructor;
cg.EmitCallerContext();
cg.EmitCall(typeof(Assembly), "GetExecutingAssembly");
cg.EmitString(cd.Name.GetString());
cg.EmitCall(typeof(Ops), "GetNamespace");
cg.EmitSet(cd.Name);
}
return;
}
StackInfo info = stack.Pop();
finished = info.Type;
info.StaticConstructor.Emit(OpCodes.Ret);
declaredTypes.RemoveAt(declaredTypes.Count - 1);
declaredTypes[declaredTypes.Count - 1][cd.Name.GetString()] = finished;
Type baseType = baseTypes[0];
info.DefaultConstructor.Emit(OpCodes.Ldarg_0);
ConstructorInfo baseCtor;
if (baseTg != null) {
baseCtor = baseTg.DefaultConstructor;
} else {
baseCtor = baseType.GetConstructor(new Type[0]);
}
Debug.Assert(baseCtor != null, "baseCtor is null", String.Format("type: {0} base: {1}", info.Type.myType.Name, baseType.FullName));
info.DefaultConstructor.Emit(OpCodes.Call, baseCtor);
info.DefaultConstructor.EmitReturn();
if (stack.Count == 1) {
info.Type.FinishType();
CodeGen cg = stack.Peek().StaticConstructor;
if (namespaces.Count == 0) {
cg.EmitCall(info.GetCompiledType.MethodInfo);
cg.EmitSet(cd.Name);
}
}
}
public override bool Walk(FunctionDefinition node) {
CodeGen typeCctor = stack.Peek().StaticConstructor;
Debug.Assert(typeCctor != null);
if (stack.Count == 1) {
// global function, just emit the node...
node.Emit(typeCctor);
return false;
}
string strName = node.Name.GetString();
SignatureInfo sigInfo = node.GetSignature(typeCctor);
CodeGen icg = CreateNewMethod(node, typeCctor, sigInfo);
icg.SetCustomAttribute(new CustomAttributeBuilder(
typeof(PythonNameAttribute).GetConstructor(new Type[] { typeof(string) }),
new object[] { strName }));
string docStr = node.Body.Documentation;
if (docStr != null) {
icg.SetCustomAttribute(new CustomAttributeBuilder(
typeof(DocumentationAttribute).GetConstructor(new Type[] { typeof(string) }),
new object[] { docStr }));
}
// Generate the function implementation
node.EmitFunctionImplementation(icg, typeCctor);
icg.Finish();
stack.Peek().Methods.Add(icg);
return false;
}
public override bool Walk(SuiteStatement node) {
return true;
}
public override bool Walk(GlobalSuite node) {
node.CreateGlobalSlots(stack.Peek().StaticConstructor);
return true;
}
// anything else gets emitted directly
// into the current initialization function. If this
// is something like a conditional block that includes
// a function definition it will not be visible at .NET
// scope (but will still work).
public override bool Walk(AssignStatement node) {
if (stack.Count == 1) {
node.Emit(stack.Peek().StaticConstructor);
return false;
}
CallExpression rhsCall = node.Right as CallExpression;
if (rhsCall != null) {
bool fCantEmit = false;
NameExpression ne = rhsCall.Target as NameExpression;
string get = null, set = null;
if (ne != null) {
if (ne.Name.GetString() == "property") {
// property definition...
fCantEmit = GetPropertyAccessors(rhsCall, ref ne, ref get, ref set);
}
}
if (!fCantEmit) {
PropertyBuilder pb = stack.Peek().Type.DefineProperty(((NameExpression)node.Left[0]).Name.GetString(), PropertyAttributes.None, typeof(object));
foreach (CodeGen cg in stack.Peek().Methods) {
if (get != null && cg.methodInfo.Name == get) {
pb.SetGetMethod((MethodBuilder)cg.methodInfo);
} else if (set != null && cg.methodInfo.Name == set) {
pb.SetSetMethod((MethodBuilder)cg.methodInfo);
}
}
return false;
}
}
ConstantExpression rhsConstant = node.Right as ConstantExpression;
NameExpression rhsName;
if (rhsConstant != null) {
if (rhsConstant.Value != null) {
stack.Peek().Type.AddStaticField(rhsConstant.Value.GetType(), ((NameExpression)node.Left[0]).Name.GetString());
} else {
stack.Peek().Type.AddStaticField(typeof(object), ((NameExpression)node.Left[0]).Name.GetString());
}
} else if ((rhsName = node.Right as NameExpression) != null &&
(rhsName.Name.GetString() == "True" || rhsName.Name.GetString() == "False")) {
stack.Peek().Type.AddStaticField(typeof(bool), ((NameExpression)node.Left[0]).Name.GetString());
} else {
Emit(node);
}
return false;
}
public override bool Walk(AssertStatement node) { Emit(node); return false; }
public override bool Walk(AugAssignStatement node) { Emit(node); return false; }
public override bool Walk(BreakStatement node) { Emit(node); return false; }
public override bool Walk(ContinueStatement node) { Emit(node); return false; }
public override bool Walk(DelStatement node) { Emit(node); return false; }
public override bool Walk(ExecStatement node) { Emit(node); return false; }
public override bool Walk(ExpressionStatement node) { Emit(node); return false; }
public override bool Walk(ForStatement node) { Emit(node); return false; }
public override bool Walk(FromImportStatement node) { Emit(node); return false; }
public override bool Walk(GlobalStatement node) { Emit(node); return false; }
public override bool Walk(IfStatement node) { Emit(node); return false; }
public override bool Walk(ImportStatement node) { Emit(node); return false; }
public override bool Walk(PassStatement node) { Emit(node); return false; }
public override bool Walk(PrintStatement node) { Emit(node); return false; }
public override bool Walk(RaiseStatement node) { Emit(node); return false; }
public override bool Walk(ReturnStatement node) { Emit(node); return false; }
public override bool Walk(TryFinallyStatement node) { Emit(node); return false; }
public override bool Walk(TryStatement node) { Emit(node); return false; }
public override bool Walk(WhileStatement node) { Emit(node); return false; }
public override bool Walk(YieldStatement node) { Emit(node); return false; }
#endregion
#region Private implementation details
private static bool GetPropertyAccessors(CallExpression rhsCall, ref NameExpression ne, ref string get, ref string set) {
bool fCantEmit = false;
for (int i = 0; i < rhsCall.Args.Count; i++) {
// fget, fset, fdel, doc
if (rhsCall.Args[i].Name != SymbolTable.Empty) {
switch (rhsCall.Args[i].Name.GetString()) {
case "fget":
ne = rhsCall.Args[i].Expression as NameExpression;
if (ne == null) { fCantEmit = true; break; }
get = ne.Name.GetString();
break;
case "fset":
ne = rhsCall.Args[i].Expression as NameExpression;
if (ne == null) { fCantEmit = true; break; }
set = ne.Name.GetString();
break;
default:
fCantEmit = true;
break;
}
} else {
switch (i) {
case 0:
ne = rhsCall.Args[i].Expression as NameExpression;
if (ne == null) { fCantEmit = true; break; }
get = ne.Name.GetString();
break;
case 1:
ne = rhsCall.Args[i].Expression as NameExpression;
if (ne == null) { fCantEmit = true; break; }
set = ne.Name.GetString();
break;
default:
fCantEmit = true;
break;
}
}
}
return fCantEmit;
}
private void Emit(Statement s) {
s.Emit(stack.Peek().StaticConstructor);
}
private static bool IsNamespace(ClassDefinition cd) {
SlotFinder sf = new SlotFinder();
cd.Walk(sf);
if (sf.HasSubTypes && !sf.HasFunctions && !sf.FoundSlots && cd.Bases.Count == 0) {
return true;
}
return false;
}
private CodeGen CreateNewMethod(FunctionDefinition node, CodeGen typeCctor, SignatureInfo sigInfo) {
string strName = node.Name.GetString();
MethodAttributes attrs = GetMethodAttributes(node, strName);
int offset = (sigInfo.HasContext ? 2 : 1);
if ((attrs & MethodAttributes.Static) != 0) offset--;
if (node.Parameters.Count == 0 && (attrs & MethodAttributes.Static) == 0)
throw new CompilerException(String.Format("defining non-static method {0} with no parameters. Add self or @staticmethod decorator", node.Name), node, this.compctx.SourceFile);
Debug.Assert(sigInfo.ParamNames.Length >= offset,
"less param names then offset",
String.Format("Params: {0} Offset: {1}: Context: {2} Attrs: {3} Name: {4}",
sigInfo.ParamNames.Length, offset, sigInfo.HasContext, attrs, strName));
string[] paramNames = new string[sigInfo.ParamNames.Length - offset];
for (int i = 0; i < paramNames.Length; i++)
paramNames[i] = sigInfo.ParamNames[i + offset].GetString();
Type[] typeArr;
CustomAttributeBuilder[] cabs;
GetTypesAndAttrs(node, sigInfo, offset, out typeArr, out cabs);
object[] funcDefaults = GetStaticDefaults(node, paramNames.Length);
MethodInfo baseMethod = GetMethodOverload(strName, attrs);
Type retType;
if (baseMethod == null) {
// Check whether the method has a return statement, to decide whether it should
// return void or object
ReturnStatementFinder finder = new ReturnStatementFinder(node);
node.Walk(finder);
retType = finder.FoundReturnStatement ? typeof(object) : typeof(void);
} else {
// Get the return and param types from the base method
typeArr = CompilerHelpers.GetTypes(baseMethod.GetParameters());
retType = baseMethod.ReturnType;
attrs |= MethodAttributes.Virtual;
}
CodeGen icg = typeCctor.typeGen.DefineMethod(attrs,
strName,
retType,
typeArr,
paramNames,
funcDefaults,
cabs);
icg.Context = compctx;
if (baseMethod != null) icg.methodToOverride = baseMethod;
icg.Names = CodeGen.CreateLocalNamespace(icg);
for (int arg = offset; arg < sigInfo.ParamNames.Length; arg++) {
icg.Names.SetSlot(sigInfo.ParamNames[arg], icg.GetArgumentSlot(arg - offset));
}
if ((attrs & MethodAttributes.Static) == 0) {
icg.Names.SetSlot(sigInfo.ParamNames[offset - 1], new ArgSlot(0, typeCctor.typeGen.myType, icg));
}
icg.ContextSlot = stack.Peek().Type.moduleSlot;
EmitArgsToTuple(node, icg, sigInfo, paramNames.Length);
return icg;
}
private MethodInfo GetMethodOverload(string strName, MethodAttributes attrs) {
MethodInfo baseMethod = null;
if ((attrs & MethodAttributes.Static) == 0) {
// see if we're overriding anything in a base
List<Type> bases = stack.Peek().BaseTypes;
for (int i = 0; i < bases.Count; i++) {
MethodInfo[] mis = bases[i].GetMethods(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance);
baseMethod = PickBestOverload(strName, mis);
if (baseMethod != null) {
break;
}
}
}
return baseMethod;
}
private static MethodAttributes GetMethodAttributes(FunctionDefinition node, string strName) {
MethodAttributes attrs = MethodAttributes.Public;
if (strName == "__new__") {
attrs |= MethodAttributes.Static;
} else {
CallExpression decExpr = node.Decorators as CallExpression;
while (decExpr != null) {
NameExpression ne = decExpr.Target as NameExpression;
if (ne != null && ne.Name.GetString() == "staticmethod") {
attrs |= MethodAttributes.Static;
}
decExpr = decExpr.Args[0].Expression as CallExpression;
}
}
return attrs;
}
private static MethodInfo PickBestOverload(string name, MethodInfo[] mis) {
for (int i = 0; i < mis.Length; i++) {
if (mis[i].Name == name) {
return mis[i];
}
}
return null;
}
private static void EmitArgsToTuple(FunctionDefinition fd, CodeGen icg, SignatureInfo sigInfo, int cnt) {
if ((fd.Flags & FunctionAttributes.ArgumentList) != 0) {
// transform params object[] into tuple on call...
LocalBuilder lb = icg.DeclareLocal(typeof(object));
Slot argsSlot = new LocalSlot(lb, icg);
int index;
if ((fd.Flags & FunctionAttributes.KeywordDictionary) != 0) {
index = sigInfo.ParamNames.Length - 2;
icg.EmitArgGet(cnt - 2);
} else {
index = sigInfo.ParamNames.Length - 1;
icg.EmitArgGet(cnt - 1);
}
icg.EmitCall(typeof(Tuple), "MakeTuple");
argsSlot.EmitSet(icg);
lb.SetLocalSymInfo(sigInfo.ParamNames[index].GetString());
icg.Names.SetSlot(sigInfo.ParamNames[index], argsSlot);
}
}
private static object[] GetStaticDefaults(FunctionDefinition fd, int count) {
object[] funcDefaults = CompilerHelpers.MakeRepeatedArray<object>(DBNull.Value, count);
if (fd.Defaults != null) {
for (int i = 0; i < fd.Defaults.Count; i++) {
ConstantExpression ce = fd.Defaults[i] as ConstantExpression;
if (ce != null) {
funcDefaults[i + (count - fd.Defaults.Count)] = ce.Value;
} else {
throw new InvalidOperationException(String.Format("can't handle default: {0}", funcDefaults[i]));
}
}
}
return funcDefaults;
}
private static void GetTypesAndAttrs(FunctionDefinition node, SignatureInfo sigInfo, int offset, out Type[] typeArr, out CustomAttributeBuilder[] cabs) {
typeArr = CompilerHelpers.MakeRepeatedArray(typeof(object), sigInfo.ParamNames.Length - offset);
cabs = null;
int curIndex = typeArr.Length - 1;
if ((node.Flags & FunctionAttributes.KeywordDictionary) != 0) {
typeArr[curIndex] = typeof(Dict);
cabs = new CustomAttributeBuilder[typeArr.Length];
cabs[curIndex] = new CustomAttributeBuilder(typeof(ParamDictAttribute).GetConstructor(new Type[0]), Ops.EMPTY);
curIndex--;
}
if ((node.Flags & FunctionAttributes.ArgumentList) != 0) {
if (cabs == null) cabs = new CustomAttributeBuilder[typeArr.Length];
typeArr[curIndex] = typeof(object[]);
cabs[curIndex] = new CustomAttributeBuilder(typeof(ParamArrayAttribute).GetConstructor(new Type[0]), Ops.EMPTY);
curIndex--;
}
}
/// <summary>
/// Pushes a new type onto the stack for cases where we have
/// nested class definitions.
/// </summary>
private void PushNewType(TypeGen newType, CodeGen classInit, CodeGen init, List<Type> baseTypes) {
stack.Push(new StackInfo(newType, classInit, init, baseTypes));
declaredTypes.Add(new Dictionary<string, TypeGen>());
}
private CodeGen DefineClassInit(ClassDefinition cd, TypeGen newType) {
CodeGen classInit = newType.GetOrMakeInitializer();
EmitModuleInitialization(newType, classInit);
classInit.Context = compctx;
classInit.Names = CodeGen.CreateStaticFieldNamespace(newType);
// prepare the class for compilation, this binds
// all of our names into classInit's scope. We'll then
// recurse and add everything by hand.
cd.PrepareForEmit(stack.Peek().StaticConstructor, classInit);
return classInit;
}
/// <summary>
/// Propagates the module field to the inner type, or runs the
/// module initialization if it hasn't already been run.
/// </summary>
private void EmitModuleInitialization(TypeGen newType, CodeGen classInit) {
Debug.Assert(classInit != null);
Label noModule = classInit.DefineLabel();
Label done = classInit.DefineLabel();
outerScope.moduleSlot.EmitGet(classInit);
classInit.Emit(OpCodes.Ldnull);
classInit.Emit(OpCodes.Beq, noModule);
outerScope.moduleSlot.EmitGet(classInit);
newType.moduleSlot.EmitSet(classInit);
classInit.Emit(OpCodes.Br, done);
classInit.MarkLabel(noModule);
// module slot is un-initialized, we must
// have been reloaded by some .NET code.
// We'll create a new module now.
Slot instance = new LocalSlot(classInit.DeclareLocal(outerScope.myType), classInit);
List<string> refs = null;
if (RemoteCompiler.Instance != null) {
refs = new List<string>(RemoteCompiler.Instance.References.Count);
for (int i = 0; i < RemoteCompiler.Instance.References.Count; i++) {
string refName = RemoteCompiler.Instance.References[i];
if (refName.ToLower().EndsWith(".dll"))
refName = refName.Substring(0, refName.Length - 4);
if (refName.IndexOf('\\') != -1) {
refs.Add(refName.Substring(refName.LastIndexOf('\\') + 1));
} else
refs.Add(refName);
}
}
OutputGenerator.EmitModuleConstruction(outerScope, classInit, outerScope.myType.Name, instance, refs);
// module ctor leaves PythonModule on the stack.
classInit.Emit(OpCodes.Dup);
// store the new module in both locations
outerScope.moduleSlot.EmitSet(classInit);
newType.moduleSlot.EmitSet(classInit);
// and finally run the modules initialize method
instance.EmitGet(classInit);
classInit.EmitCall(moduleInit.MethodInfo);
classInit.MarkLabel(done);
}
/// <summary>
/// Creates the new type, sets PythonTypeAttribute on it, and sets the documentation string
/// </summary>
private TypeGen DefineNewType(ClassDefinition cd, List<Type> baseTypes) {
Debug.Assert(baseTypes.Count > 0, "type has no bases", String.Format("for class {0}", cd.Name.ToString()));
StringBuilder typeName = new StringBuilder();
for (int i = 0; i < namespaces.Count; i++) {
typeName.Append(namespaces[i]);
typeName.Append('.');
}
typeName.Append(cd.Name.GetString());
TypeGen newType = DefineTypeInParent(baseTypes, typeName);
List<MethodInfo> overridden = new List<MethodInfo>();
List<CodeGen> overrides = new List<CodeGen>();
MethodInfo[] methods = baseTypes[0].GetMethods(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance);
for (int i = 0; i < methods.Length; i++) {
if (methods[i].IsPrivate) continue;
if (methods[i].IsVirtual || methods[i].IsAbstract) {
overrides.Add(NewTypeMaker.CreateVirtualMethodHelper(newType, methods[i]));
overridden.Add(methods[i]);
}
}
for (int i = 1; i < baseTypes.Count; i++) {
Debug.Assert(baseTypes[i].IsInterface, "expected interface", String.Format("type is not an interface: {0}", baseTypes[i].FullName));
newType.myType.AddInterfaceImplementation(baseTypes[i]);
}
if (overridden.Count != 0) {
newType.myType.AddInterfaceImplementation(typeof(ICustomBaseAccess));
CodeGen baseAccess = newType.DefineMethodOverride(typeof(ICustomBaseAccess).GetMethod("TryGetBaseAttr"));
Label next = baseAccess.DefineLabel();
for (int i = 0; i < overridden.Count; i++) {
baseAccess.EmitSymbolIdInt(overridden[i].Name);
baseAccess.EmitArgAddr(1);
baseAccess.EmitFieldGet(typeof(SymbolId), "Id");
baseAccess.Emit(OpCodes.Bne_Un, next);
// return a ReflectedMethod for this method...
baseAccess.EmitArgGet(2);
baseAccess.EmitString(overridden[i].Name);
baseAccess.Emit(OpCodes.Ldtoken, overrides[i].MethodInfo);
baseAccess.EmitCall(typeof(MethodBase), "GetMethodFromHandle", new Type[] { typeof(RuntimeMethodHandle) });
baseAccess.Emit(OpCodes.Castclass, typeof(MethodInfo));
baseAccess.EmitInt((int)FunctionType.Method);
baseAccess.EmitCall(typeof(BuiltinFunction).GetMethod("MakeMethod",
new Type[] { typeof(string), typeof(MethodInfo), typeof(FunctionType) }));
baseAccess.EmitCall(typeof(BuiltinFunction), "GetDescriptor");
baseAccess.Emit(OpCodes.Stind_Ref);
baseAccess.EmitRawConstant(true);
baseAccess.EmitReturn();
baseAccess.MarkLabel(next);
next = baseAccess.DefineLabel();
}
baseAccess.EmitRawConstant(false);
baseAccess.EmitReturn();
}
newType.SetCustomAttribute(typeof(PythonTypeAttribute), new object[] { cd.Name.GetString() });
string doc = cd.Documentation;
if (doc != null) {
newType.SetCustomAttribute(typeof(DocumentationAttribute), new object[] { doc });
}
return newType;
}
private TypeGen DefineTypeInParent(List<Type> baseTypes, StringBuilder typeName) {
TypeGen newType;
if (stack.Count == 1) {
//!!! if typeName == moduleName we need to do something special (PythonNameAttribute ?)
newType = assembly.DefinePublicType(typeName.ToString(), baseTypes[0]);
newType.AddModuleField(typeof(PythonModule));
} else {
newType = stack.Peek().Type.DefineNestedType(typeName.ToString(), baseTypes[0]);
}
return newType;
}
private static CodeGen AddIDynamicObject(TypeGen newType, Slot dictSlot) {
Slot classSlot = newType.AddStaticField(typeof(DynamicType), FieldAttributes.Private, "$class");
newType.myType.AddInterfaceImplementation(typeof(ISuperDynamicObject));
/* GetDynamicType */
CodeGen gdtCg = newType.DefineMethodOverride(typeof(IDynamicObject).GetMethod("GetDynamicType"));
CodeGen cg = newType.DefineUserHiddenMethod(MethodAttributes.FamORAssem | MethodAttributes.Static,
"$$GetOrMakeDynamicType", typeof(DynamicType), new Type[0]);
DoLazyInitCheck(cg, classSlot,
delegate() {
// if not null, just return the value.
classSlot.EmitGet(cg);
cg.EmitReturn();
},
delegate() {
// initialization code
// CompiledType GetTypeForType(Type t)
cg.EmitType(newType.myType);
cg.EmitCall(typeof(CompiledType), "GetTypeForType");
cg.Emit(OpCodes.Dup);
classSlot.EmitSet(cg);
cg.EmitReturn();
});
cg.Finish();
CodeGen ret = cg;
// just a call to the helper method
gdtCg.EmitThis();
gdtCg.EmitCall(typeof(object), "GetType");
gdtCg.EmitType(newType.myType);
Label differ = gdtCg.DefineLabel();
gdtCg.Emit(OpCodes.Bne_Un, differ);
gdtCg.EmitCall(cg.MethodInfo);
gdtCg.EmitReturn();
gdtCg.MarkLabel(differ);
gdtCg.EmitThis();
gdtCg.EmitCall(typeof(object), "GetType");
gdtCg.EmitCall(typeof(Ops), "GetDynamicTypeFromType");
gdtCg.EmitReturn();
gdtCg.Finish();
/* GetDict */
cg = newType.DefineMethodOverride(typeof(ISuperDynamicObject).GetMethod("GetDict"));
DoLazyInitCheck(cg, dictSlot,
delegate() {
// if not null, just return the value.
dictSlot.EmitGet(cg);
cg.EmitReturn();
},
delegate() {
// initialization code - dictSlot = new CustomOldClassDict()
cg.EmitNew(typeof(CustomOldClassDict).GetConstructor(new Type[0]));
cg.Emit(OpCodes.Dup);
dictSlot.EmitSet(cg);
cg.EmitReturn();
});
cg.Finish();
/* SetDict */
cg = newType.DefineMethodOverride(typeof(ISuperDynamicObject).GetMethod("SetDict"));
cg.EmitString("SetDict");
cg.EmitInt(0);
cg.Emit(OpCodes.Newarr, typeof(object));
cg.EmitCall(typeof(Ops), "NotImplementedError");
cg.Emit(OpCodes.Throw);
/* SetDynamicType */
cg = newType.DefineMethodOverride(typeof(ISuperDynamicObject).GetMethod("SetDynamicType"));
cg.EmitString("SetDynamicType");
cg.EmitInt(0);
cg.Emit(OpCodes.Newarr, typeof(object));
cg.EmitCall(typeof(Ops), "NotImplementedError");
cg.Emit(OpCodes.Throw);
return ret;
}
delegate void LazyInitHelper();
private static void DoLazyInitCheck(CodeGen cg, Slot val, LazyInitHelper ifNotNull, LazyInitHelper initCode) {
Label initType = cg.DefineLabel();
val.EmitGet(cg);
cg.Emit(OpCodes.Ldnull);
cg.Emit(OpCodes.Beq, initType);
ifNotNull();
cg.MarkLabel(initType);
initCode();
}
private static CodeGen AddDefaultCtor(TypeGen newType, string[] slots) {
CodeGen cg = newType.DefineConstructor(new Type[0]);
if (slots != null) {
for (int i = 0; i < slots.Length; i++) {
Slot fld;
// getting other types would be nice here...
if (slots[i].StartsWith("__") && !slots[i].EndsWith("__")) {
fld = newType.AddField(typeof(object), "_" + newType.myType.Name + slots[i]);
} else {
fld = newType.AddField(typeof(object), slots[i]);
}
cg.EmitUninitialized();
fld.EmitSet(cg);
}
newType.DefaultConstructor = cg.methodInfo as ConstructorBuilder;
slots = null;
}
newType.DefaultConstructor = cg.methodInfo as ConstructorBuilder;
return cg;
}
/// <summary>
/// Gets the basetype for the class.
/// </summary>
private List<Type> GetBaseType(ClassDefinition cd, out TypeGen tg) {
//!!! need to handle types declared before us
// but within our module's scope, and what do
// we do if we don't know the base type?
List<Type> types = new List<Type>(cd.Bases.Count);
tg = null;
if (cd.Bases.Count == 0) {
types.Add(typeof(object));
return types;
}
Type baseType = typeof(object);
string baseName = null;
for (int i = 0; i < cd.Bases.Count; i++) {
NameExpression ne = cd.Bases[i] as NameExpression;
FieldExpression fe;
if (ne != null) {
baseName = ne.Name.GetString();
} else if ((fe = cd.Bases[i] as FieldExpression) != null) {
baseName = CodeDom.CodeWalker.GetFieldString(fe);
} else {
throw new NotImplementedException(String.Format("non-name expr base type {0}", cd.Bases[i].GetType()));
}
if (baseName == null) throw new InvalidOperationException("couldn't find basetype");
if (baseName != "object" && baseName != "System.Object") {
for (int j = declaredTypes.Count - 1; j >= 0; j--) {
Dictionary<string, TypeGen> curDict = declaredTypes[j];
if (curDict.TryGetValue(baseName, out tg)) {
baseType = tg.myType;
break;
}
}
if (baseType == typeof(object) && RemoteCompiler.Instance != null) {
List<Assembly> assms = this.assemblies;
for (int j = 0; j < assms.Count; j++) {
Debug.Assert(assms[j] != null);
Type t = assms[j].GetType(baseName);
if (t != null) {
baseType = t;
break;
}
}
}
if (baseType == typeof(object) && Ops.compiledEngine != null) {
foreach (Assembly asm in Ops.compiledEngine.Sys.TopPackage.LoadedAssemblies.Keys) {
Type t = asm.GetType(baseName);
if (t != null) {
baseType = t;
break;
}
}
}
Debug.Assert(baseType != null, "Failed to find type for " + baseName);
if (baseType.IsInterface) {
types.Add(baseType);
} else if (baseType != typeof(object)) {
Debug.Assert(types.Count == 0 || types[0].IsInterface, "adding multiple classes",
String.Format("Had: {0}\r\nAdding{1} {2}", types.Count == 0 ? "" : types[0].FullName, baseType, baseName));
types.Insert(0, baseType);
} else {
return null;
}
} else {
types.Add(typeof(object));
}
baseType = typeof(object);
}
// if we only got interfaces object is our base
if (types[0].IsInterface) {
types.Insert(0, typeof(object));
}
return types;
}
#endregion
private class StackInfo {
TypeGen type;
CodeGen cctor, ctor, getCompiledType;
List<Type> bases;
List<CodeGen> methods;
public StackInfo(TypeGen typeGen, CodeGen classInit, CodeGen defaultCtor, List<Type> baseTypes) {
type = typeGen;
cctor = classInit;
ctor = defaultCtor;
bases = baseTypes;
methods = new List<CodeGen>();
}
public TypeGen Type {
get {
return type;
}
}
public List<CodeGen> Methods {
get {
return methods;
}
}
public List<Type> BaseTypes {
get {
return bases;
}
}
public CodeGen StaticConstructor {
get {
return cctor;
}
}
public CodeGen DefaultConstructor {
get {
return ctor;
}
}
public CodeGen GetCompiledType {
get {
return getCompiledType;
}
set {
getCompiledType = value;
}
}
}
}
/// <summary>
/// Determines if the class has a __slots__ field which would
/// allow us to
/// </summary>
private class SlotFinder : AstWalkerNonRecursive {
public bool FoundSlots = false;
public bool HasFunctions = false;
public bool HasSubTypes = false;
private int depth = 0;
private string[] slots;
#region AstWalkerNonRecursive Method Overrides
public override bool Walk(AssignStatement node) {
if (!FoundSlots && node.Left.Count == 1) {
NameExpression ne = node.Left[0] as NameExpression;
if (ne != null && ne.Name.GetString() == "__slots__") {
ListExpression le = node.Right as ListExpression;
if (le != null) {
string[] slotRes = new string[le.Items.Count];
for (int i = 0; i < le.Items.Count; i++) {
ConstantExpression ce = le.Items[i] as ConstantExpression;
if (ce == null) {
slotRes = null;
break;
}
string slotStr = ce.Value as string;
if (slotStr == null) {
slotRes = null;
break;
}
slotRes[i] = slotStr;
}
slots = slotRes;
}
FoundSlots = true;
}
}
return false;
}
public override bool Walk(ClassDefinition node) {
depth++;
if (depth > 1) HasSubTypes = true;
if (depth == 1) {
return !FoundSlots;
}
return false;
}
public override void PostWalk(ClassDefinition node) {
depth--;
}
public override bool Walk(FunctionDefinition node) {
if (depth == 1) {
HasFunctions = true;
}
return false;
}
public override bool Walk(SuiteStatement node) {
return !FoundSlots;
}
#endregion
public string[] Slots {
get {
return slots;
}
}
}
private class ReturnStatementFinder : AstWalkerNonRecursive {
private FunctionDefinition _funcDef;
public bool FoundReturnStatement = false;
public ReturnStatementFinder(FunctionDefinition funcDef) { _funcDef = funcDef; }
// Only recurse on constructs that can contain return statements
public override bool Walk(SuiteStatement node) { return true; }
public override bool Walk(ForStatement node) { return true; }
public override bool Walk(IfStatement node) { return true; }
public override bool Walk(WhileStatement node) { return true; }
public override bool Walk(TryFinallyStatement node) { return true; }
public override bool Walk(TryStatement node) { return true; }
// Only recurse on the function itself, but not any nested ones
public override bool Walk(FunctionDefinition node) { return (node == _funcDef); }
public override bool Walk(ReturnStatement node) {
FoundReturnStatement = true;
return false;
}
}
}
}
See more files for this project here