Added workaround for .NET C# compiler bug that prevents it from subclassing a Java class that implements a protected abstract method using a public method.

This commit is contained in:
jfrijters 2009-05-12 04:04:52 +00:00
Родитель da7552b32c
Коммит 307ae26b35
1 изменённых файлов: 122 добавлений и 0 удалений

Просмотреть файл

@ -47,12 +47,134 @@ namespace IKVM.Internal
private TypeBuilder typeBuilderGhostInterface;
private Annotation annotation;
private MethodWrapper[] replacedMethods;
private WorkaroundBaseClass workaroundBaseClass;
internal AotTypeWrapper(ClassFile f, CompilerClassLoader loader)
: base(f, loader)
{
}
protected override Type GetBaseTypeForDefineType()
{
TypeWrapper baseTypeWrapper = BaseTypeWrapper;
if (this.IsPublic && this.IsAbstract && baseTypeWrapper.IsPublic && baseTypeWrapper.IsAbstract)
{
// FXBUG
// if the current class widens access on an abstract base class method,
// we need to inject an artificial base class to workaround a C# compiler bug
List<MethodWrapper> methods = null;
foreach (MethodWrapper mw in GetMethods())
{
if (!mw.IsStatic && mw.IsPublic)
{
MethodWrapper baseMethod = baseTypeWrapper.GetMethodWrapper(mw.Name, mw.Signature, true);
if (baseMethod != null && baseMethod.IsAbstract && baseMethod.IsProtected)
{
if (methods == null)
{
methods = new List<MethodWrapper>();
}
methods.Add(baseMethod);
}
}
}
if (methods != null)
{
string name = "__" + Name + "__WorkaroundBaseClass";
while (!classLoader.GetTypeWrapperFactory().ReserveName(name))
{
name += "_";
}
TypeBuilder typeBuilder = classLoader.GetTypeWrapperFactory().ModuleBuilder.DefineType(name, TypeAttributes.Public | TypeAttributes.Abstract, base.GetBaseTypeForDefineType());
AttributeHelper.HideFromJava(typeBuilder);
workaroundBaseClass = new WorkaroundBaseClass(typeBuilder, methods.ToArray());
List<MethodWrapper> constructors = new List<MethodWrapper>();
foreach (MethodWrapper mw in baseTypeWrapper.GetMethods())
{
if (ReferenceEquals(mw.Name, StringConstants.INIT) && mw.IsAccessibleFrom(baseTypeWrapper, this, this))
{
constructors.Add(new ConstructorForwarder(typeBuilder, mw));
}
}
replacedMethods = constructors.ToArray();
return typeBuilder;
}
}
return base.GetBaseTypeForDefineType();
}
internal override void Finish()
{
base.Finish();
lock (this)
{
if (workaroundBaseClass != null)
{
workaroundBaseClass.Finish();
workaroundBaseClass = null;
}
}
}
private sealed class WorkaroundBaseClass
{
private readonly TypeBuilder typeBuilder;
private readonly MethodWrapper[] methods;
internal WorkaroundBaseClass(TypeBuilder typeBuilder, MethodWrapper[] methods)
{
this.typeBuilder = typeBuilder;
this.methods = methods;
}
internal void Finish()
{
foreach (MethodWrapper mw in methods)
{
MethodBuilder mb = typeBuilder.DefineMethod(mw.Name, MethodAttributes.FamORAssem | MethodAttributes.Virtual, mw.ReturnTypeForDefineMethod, mw.GetParametersForDefineMethod());
AttributeHelper.HideFromJava(mb);
CodeEmitter ilgen = CodeEmitter.Create(mb);
EmitHelper.Throw(ilgen, "java.lang.AbstractMethodError");
}
typeBuilder.CreateType();
}
}
private sealed class ConstructorForwarder : MethodWrapper
{
private readonly TypeBuilder typeBuilder;
private readonly MethodWrapper ctor;
private ConstructorBuilder constructorBuilder;
internal ConstructorForwarder(TypeBuilder typeBuilder, MethodWrapper ctor)
: base(ctor.DeclaringType, ctor.Name, ctor.Signature, null, null, null, ctor.Modifiers, MemberFlags.None)
{
this.typeBuilder = typeBuilder;
this.ctor = ctor;
}
protected override void DoLinkMethod()
{
ctor.Link();
Type[] parameters = ctor.GetParametersForDefineMethod();
constructorBuilder = typeBuilder.DefineConstructor(MethodAttributes.PrivateScope, CallingConventions.Standard, parameters);
AttributeHelper.HideFromJava(constructorBuilder);
CodeEmitter ilgen = CodeEmitter.Create(constructorBuilder);
ilgen.Emit(OpCodes.Ldarg_0);
for (int i = 1; i <= parameters.Length; i++)
{
ilgen.Emit(OpCodes.Ldarg_S, (byte)i);
}
ctor.EmitCall(ilgen);
ilgen.Emit(OpCodes.Ret);
}
internal override void EmitCall(CodeEmitter ilgen)
{
ilgen.Emit(OpCodes.Call, constructorBuilder);
}
}
internal override bool IsGhost
{
get