static void DefineCtors(TypeBuilder proxyTB,
Type superClass,
string initName,
string postInitName,
ISeq ctorsTypes,
FieldBuilder initFB,
FieldBuilder postInitFB,
FieldBuilder stateFB,
string factoryName)
{
for (ISeq s = ctorsTypes; s != null; s = s.next())
{
IMapEntry me = (IMapEntry)s.first();
ISeq thisParamTypesV = (ISeq)me.key();
ISeq baseParamTypesV = (ISeq)me.val();
Type[] thisParamTypes = CreateTypeArray(thisParamTypesV);
Type[] baseParamTypes = CreateTypeArray(baseParamTypesV);
BindingFlags flags = BindingFlags.CreateInstance| BindingFlags.NonPublic| BindingFlags.Public| BindingFlags.Instance;
ConstructorInfo superCtor = superClass.GetConstructor(flags,null,baseParamTypes,null);
if (superCtor == null || superCtor.IsPrivate)
throw new InvalidOperationException("Base class constructor missing or private");
ConstructorBuilder cb = proxyTB.DefineConstructor(MethodAttributes.Public, CallingConventions.HasThis, thisParamTypes);
ILGen gen = new ILGen(cb.GetILGenerator());
Label noInitLabel = gen.DefineLabel();
Label noPostInitLabel = gen.DefineLabel();
Label endPostInitLabel = gen.DefineLabel();
Label endLabel = gen.DefineLabel();
LocalBuilder locSuperArgs = gen.DeclareLocal(typeof(object));
LocalBuilder locInitVal = gen.DeclareLocal(typeof(object));
if (initFB != null)
{
// init supplied
EmitGetVar(gen, initFB);
gen.Emit(OpCodes.Dup);
gen.Emit(OpCodes.Brfalse_S, noInitLabel);
gen.Emit(OpCodes.Castclass, typeof(IFn));
// box init args
for (int i = 0; i < thisParamTypes.Length; i++)
{
gen.EmitLoadArg(i + 1); // gen.Emit(OpCodes.Ldarg, i + 1);
if (thisParamTypes[i].IsValueType)
gen.Emit(OpCodes.Box,thisParamTypes[i]);
}
gen.EmitCall(Compiler.Methods_IFn_invoke[thisParamTypes.Length]); // gen.Emit(OpCodes.Call, Compiler.Methods_IFn_invoke[thisParamTypes.Length]);
// Expecting: [[super-ctor-args...] state]
// store the init return in a local
gen.Emit(OpCodes.Dup);
gen.Emit(OpCodes.Stloc,locInitVal);
// store the first element in a local
gen.EmitInt(0); // gen.Emit(OpCodes.Ldc_I4_0);
gen.EmitCall(Method_RT_nth); // gen.Emit(OpCodes.Call, Method_RT_nth);
gen.Emit(OpCodes.Stloc, locSuperArgs);
// Stack this + super-ctor-args + call base-class ctor.
gen.EmitLoadArg(0); // gen.Emit(OpCodes.Ldarg_0);
for (int i = 0; i < baseParamTypes.Length; i++)
{
gen.Emit(OpCodes.Ldloc, locSuperArgs);
gen.EmitInt(i); // gen.Emit(OpCodes.Ldc_I4, i);
gen.EmitCall(Method_RT_nth); // gen.Emit(OpCodes.Call, Method_RT_nth);
if (baseParamTypes[i].IsValueType)
gen.Emit(OpCodes.Unbox_Any, baseParamTypes[i]);
else
gen.Emit(OpCodes.Castclass, baseParamTypes[i]);
}
gen.Emit(OpCodes.Call, superCtor);
if (stateFB != null)
{
gen.EmitLoadArg(0); // gen.Emit(OpCodes.Ldarg_0);
gen.Emit(OpCodes.Ldloc, locInitVal);
gen.EmitInt(1); // gen.Emit(OpCodes.Ldc_I4_1);
gen.EmitCall(Method_RT_nth); // gen.Emit(OpCodes.Call, Method_RT_nth);
gen.Emit(OpCodes.Castclass, typeof(object));
gen.EmitFieldSet(stateFB); // gen.Emit(OpCodes.Stfld, stateFB);
}
gen.Emit(OpCodes.Br_S, endLabel);
// No init found
gen.MarkLabel(noInitLabel);
gen.Emit(OpCodes.Pop);
EmitUnsupported(gen, initName);
gen.MarkLabel(endLabel);
}
else // no InitFB supplied.
{
bool ok = thisParamTypes.Length == baseParamTypes.Length;
for (int i = 0; ok && i < thisParamTypes.Length; i++)
ok = baseParamTypes[i].IsAssignableFrom(thisParamTypes[i]);
if (!ok)
throw new InvalidOperationException(":init not specified, but ctor and super ctor args differ");
gen.EmitLoadArg(0); // gen.Emit(OpCodes.Ldarg_0);
for ( int i=0; i< thisParamTypes.Length; i++ )
{
gen.EmitLoadArg(i + 1); // gen.Emit(OpCodes.Ldarg, i + 1);
if (baseParamTypes[i] != thisParamTypes[i])
gen.Emit(OpCodes.Castclass, baseParamTypes[i]);
}
gen.Emit(OpCodes.Call, superCtor);
}
if (postInitFB != null)
{
// post-init supplied
EmitGetVar(gen, postInitFB);
gen.Emit(OpCodes.Dup);
gen.Emit(OpCodes.Brfalse_S, noPostInitLabel);
gen.Emit(OpCodes.Castclass, typeof(IFn));
// box init args
gen.EmitLoadArg(0); // gen.Emit(OpCodes.Ldarg_0);
for (int i = 0; i < thisParamTypes.Length; i++)
{
gen.EmitLoadArg(i + 1); // gen.Emit(OpCodes.Ldarg, i + 1);
if (thisParamTypes[i].IsValueType)
gen.Emit(OpCodes.Box, thisParamTypes[i]);
gen.Emit(OpCodes.Castclass, thisParamTypes[i]);
}
gen.EmitCall(Compiler.Methods_IFn_invoke[thisParamTypes.Length + 1]); // gen.Emit(OpCodes.Call, Compiler.Methods_IFn_invoke[thisParamTypes.Length + 1]);
gen.Emit(OpCodes.Pop);
gen.Emit(OpCodes.Br_S, endPostInitLabel);
// no post-init found
gen.MarkLabel(noPostInitLabel);
gen.Emit(OpCodes.Pop);
EmitUnsupported(gen,postInitName + " not defined");
gen.MarkLabel(endPostInitLabel);
}
gen.Emit(OpCodes.Ret);
if (!String.IsNullOrEmpty(factoryName))
{
MethodBuilder factoryMB = proxyTB.DefineMethod(factoryName, MethodAttributes.Public | MethodAttributes.Static, CallingConventions.Standard, proxyTB, thisParamTypes);
ILGen genf = new ILGen(factoryMB.GetILGenerator());
LocalBuilder[] locals = new LocalBuilder[thisParamTypes.Length];
for (int i = 0; i < thisParamTypes.Length; i++)
{
locals[i] = genf.DeclareLocal(thisParamTypes[i]);
genf.EmitLoadArg(i); // genf.Emit(OpCodes.Ldarg, i);
genf.Emit(OpCodes.Stloc, locals[i]);
}
for (int i = 0; i < thisParamTypes.Length; i++)
genf.EmitLoadArg(i); // genf.Emit(OpCodes.Ldarg, i);
genf.EmitNew(cb); // genf.Emit(OpCodes.Newobj, cb);
genf.Emit(OpCodes.Ret);
}
}
}