Skip to content

Commit

Permalink
Merge pull request #376 from TheLastRar/hotfix/SingletonLambda
Browse files Browse the repository at this point in the history
Return same Lambda where Lambda doesn't have captures
  • Loading branch information
wasabii authored Jul 5, 2023
2 parents 7184c44 + 63e891c commit db191d4
Showing 1 changed file with 38 additions and 7 deletions.
45 changes: 38 additions & 7 deletions src/IKVM.Runtime/LambdaMetafactory.cs
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ namespace IKVM.Internal
sealed class LambdaMetafactory
{

private MethodBuilder ctor;
private MethodBuilder getInstance;

internal static bool Emit(DynamicTypeWrapper.FinishContext context, ClassFile classFile, int constantPoolIndex, ClassFile.ConstantPoolItemInvokeDynamic cpi, CodeEmitter ilgen)
{
Expand All @@ -57,7 +57,7 @@ internal static bool Emit(DynamicTypeWrapper.FinishContext context, ClassFile cl
return false;
}
LambdaMetafactory lmf = context.GetValue<LambdaMetafactory>(constantPoolIndex);
if (lmf.ctor == null && !lmf.EmitImpl(context, classFile, cpi, bsm, ilgen))
if (lmf.getInstance == null && !lmf.EmitImpl(context, classFile, cpi, bsm, ilgen))
{
#if IMPORTER
if (context.TypeWrapper.GetClassLoader().DisableDynamicBinding)
Expand All @@ -67,9 +67,7 @@ internal static bool Emit(DynamicTypeWrapper.FinishContext context, ClassFile cl
#endif
return false;
}
ilgen.Emit(OpCodes.Newobj, lmf.ctor);
// the CLR verification rules about type merging mean we have to explicitly cast to the interface type here
ilgen.Emit(OpCodes.Castclass, cpi.GetRetType().TypeAsBaseType);
ilgen.Emit(OpCodes.Call, lmf.getInstance);
return true;
}

Expand Down Expand Up @@ -201,7 +199,7 @@ private bool EmitImpl(DynamicTypeWrapper.FinishContext context, ClassFile classF
{
tb.AddInterfaceImplementation(marker.TypeAsBaseType);
}
ctor = CreateConstructorAndDispatch(context, cpi, tb, methods, implParameters, samMethodType, implMethod, instantiatedMethodType, serializable);
getInstance = CreateConstructorAndDispatch(context, cpi, tb, methods, implParameters, samMethodType, implMethod, instantiatedMethodType, serializable);
AddDefaultInterfaceMethods(context, methodList, tb);
return true;
}
Expand Down Expand Up @@ -434,6 +432,39 @@ private static MethodBuilder CreateConstructorAndDispatch(DynamicTypeWrapper.Fin
ilgen.Emit(OpCodes.Ret);
ilgen.DoEmit();

// instance getter
MethodBuilder getInstance = tb.DefineMethod("__<GetInstance>", MethodAttributes.Assembly | MethodAttributes.Static, cpi.GetRetType().TypeAsBaseType, capturedTypes);
CodeEmitter ilgenGet = CodeEmitter.Create(getInstance);

if (capturedTypes.Length == 0)
{
// use singleton for lambdas with no captures
FieldBuilder instField = tb.DefineField("inst", tb, FieldAttributes.Private | FieldAttributes.Static);

// static constructor
MethodBuilder cctor = ReflectUtil.DefineTypeInitializer(tb, context.TypeWrapper.GetClassLoader());
CodeEmitter ilgenCCtor = CodeEmitter.Create(cctor);
ilgenCCtor.Emit(OpCodes.Newobj, ctor);
ilgenCCtor.Emit(OpCodes.Stsfld, instField);
ilgenCCtor.Emit(OpCodes.Ret);
ilgenCCtor.DoEmit();

// singleton instance
ilgenGet.Emit(OpCodes.Ldsfld, instField);
}
else
{
// new instance
for (int i = 0; i < capturedTypes.Length; i++)
ilgenGet.EmitLdarg(i);
ilgenGet.Emit(OpCodes.Newobj, ctor);
}

// the CLR verification rules about type merging mean we have to explicitly cast to the interface type here
ilgenGet.Emit(OpCodes.Castclass, cpi.GetRetType().TypeAsBaseType);
ilgenGet.Emit(OpCodes.Ret);
ilgenGet.DoEmit();

// dispatch methods
foreach (MethodWrapper mw in methods)
{
Expand Down Expand Up @@ -487,7 +518,7 @@ private static MethodBuilder CreateConstructorAndDispatch(DynamicTypeWrapper.Fin
}
}

return ctor;
return getInstance;
}

private static void EmitDispatch(DynamicTypeWrapper.FinishContext context, TypeWrapper[] args, TypeBuilder tb, MethodWrapper interfaceMethod, TypeWrapper[] implParameters,
Expand Down

0 comments on commit db191d4

Please sign in to comment.