1031 lines
32 KiB
C#
1031 lines
32 KiB
C#
/*
|
|
Copyright (C) 2014 Jeroen Frijters
|
|
|
|
This software is provided 'as-is', without any express or implied
|
|
warranty. In no event will the authors be held liable for any damages
|
|
arising from the use of this software.
|
|
|
|
Permission is granted to anyone to use this software for any purpose,
|
|
including commercial applications, and to alter it and redistribute it
|
|
freely, subject to the following restrictions:
|
|
|
|
1. The origin of this software must not be misrepresented; you must not
|
|
claim that you wrote the original software. If you use this software
|
|
in a product, an acknowledgment in the product documentation would be
|
|
appreciated but is not required.
|
|
2. Altered source versions must be plainly marked as such, and must not be
|
|
misrepresented as being the original software.
|
|
3. This notice may not be removed or altered from any source distribution.
|
|
|
|
Jeroen Frijters
|
|
jeroen@frijters.net
|
|
|
|
*/
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.Diagnostics;
|
|
#if STATIC_COMPILER
|
|
using IKVM.Reflection;
|
|
using IKVM.Reflection.Emit;
|
|
using Type = IKVM.Reflection.Type;
|
|
#else
|
|
using System.Reflection;
|
|
using System.Reflection.Emit;
|
|
#endif
|
|
|
|
namespace IKVM.Internal
|
|
{
|
|
sealed class LambdaMetafactory
|
|
{
|
|
private MethodBuilder ctor;
|
|
|
|
internal static bool Emit(DynamicTypeWrapper.FinishContext context, ClassFile classFile, int constantPoolIndex, ClassFile.ConstantPoolItemInvokeDynamic cpi, CodeEmitter ilgen)
|
|
{
|
|
ClassFile.BootstrapMethod bsm = classFile.GetBootstrapMethod(cpi.BootstrapMethod);
|
|
if (!IsLambdaMetafactory(classFile, bsm) && !IsLambdaAltMetafactory(classFile, bsm))
|
|
{
|
|
return false;
|
|
}
|
|
LambdaMetafactory lmf = context.GetValue<LambdaMetafactory>(constantPoolIndex);
|
|
if (lmf.ctor == null && !lmf.EmitImpl(context, classFile, cpi, bsm, ilgen))
|
|
{
|
|
#if STATIC_COMPILER
|
|
if (context.TypeWrapper.GetClassLoader().DisableDynamicBinding)
|
|
{
|
|
StaticCompiler.IssueMessage(Message.UnableToCreateLambdaFactory);
|
|
}
|
|
#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);
|
|
return true;
|
|
}
|
|
|
|
private bool EmitImpl(DynamicTypeWrapper.FinishContext context, ClassFile classFile, ClassFile.ConstantPoolItemInvokeDynamic cpi, ClassFile.BootstrapMethod bsm, CodeEmitter ilgen)
|
|
{
|
|
if (HasUnloadable(cpi))
|
|
{
|
|
Fail("cpi has unloadable");
|
|
return false;
|
|
}
|
|
bool serializable = false;
|
|
TypeWrapper[] markers = TypeWrapper.EmptyArray;
|
|
ClassFile.ConstantPoolItemMethodType[] bridges = null;
|
|
if (bsm.ArgumentCount > 3)
|
|
{
|
|
AltFlags flags = (AltFlags)classFile.GetConstantPoolConstantInteger(bsm.GetArgument(3));
|
|
serializable = (flags & AltFlags.Serializable) != 0;
|
|
int argpos = 4;
|
|
if ((flags & AltFlags.Markers) != 0)
|
|
{
|
|
markers = new TypeWrapper[classFile.GetConstantPoolConstantInteger(bsm.GetArgument(argpos++))];
|
|
for (int i = 0; i < markers.Length; i++)
|
|
{
|
|
if ((markers[i] = classFile.GetConstantPoolClassType(bsm.GetArgument(argpos++))).IsUnloadable)
|
|
{
|
|
Fail("unloadable marker");
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
if ((flags & AltFlags.Bridges) != 0)
|
|
{
|
|
bridges = new ClassFile.ConstantPoolItemMethodType[classFile.GetConstantPoolConstantInteger(bsm.GetArgument(argpos++))];
|
|
for (int i = 0; i < bridges.Length; i++)
|
|
{
|
|
bridges[i] = classFile.GetConstantPoolConstantMethodType(bsm.GetArgument(argpos++));
|
|
if (HasUnloadable(bridges[i]))
|
|
{
|
|
Fail("unloadable bridge");
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
ClassFile.ConstantPoolItemMethodType samMethodType = classFile.GetConstantPoolConstantMethodType(bsm.GetArgument(0));
|
|
ClassFile.ConstantPoolItemMethodHandle implMethod = classFile.GetConstantPoolConstantMethodHandle(bsm.GetArgument(1));
|
|
ClassFile.ConstantPoolItemMethodType instantiatedMethodType = classFile.GetConstantPoolConstantMethodType(bsm.GetArgument(2));
|
|
if (HasUnloadable(samMethodType)
|
|
|| HasUnloadable((ClassFile.ConstantPoolItemMI)implMethod.MemberConstantPoolItem)
|
|
|| HasUnloadable(instantiatedMethodType))
|
|
{
|
|
Fail("bsm args has unloadable");
|
|
return false;
|
|
}
|
|
TypeWrapper interfaceType = cpi.GetRetType();
|
|
MethodWrapper[] methodList;
|
|
if (!CheckSupportedInterfaces(context.TypeWrapper, interfaceType, markers, bridges, out methodList))
|
|
{
|
|
Fail("unsupported interface");
|
|
return false;
|
|
}
|
|
if (serializable && Array.Exists(methodList, delegate(MethodWrapper mw) { return mw.Name == "writeReplace" && mw.Signature == "()Ljava.lang.Object;"; }))
|
|
{
|
|
Fail("writeReplace");
|
|
return false;
|
|
}
|
|
if (!IsSupportedImplMethod(implMethod, context.TypeWrapper, cpi.GetArgTypes(), instantiatedMethodType))
|
|
{
|
|
Fail("implMethod " + implMethod.MemberConstantPoolItem.Class + "::" + implMethod.MemberConstantPoolItem.Name + implMethod.MemberConstantPoolItem.Signature);
|
|
return false;
|
|
}
|
|
TypeWrapper[] implParameters = GetImplParameters(implMethod);
|
|
CheckConstraints(instantiatedMethodType, samMethodType, cpi.GetArgTypes(), implParameters);
|
|
if (bridges != null)
|
|
{
|
|
foreach (ClassFile.ConstantPoolItemMethodType bridge in bridges)
|
|
{
|
|
if (bridge.Signature == samMethodType.Signature)
|
|
{
|
|
Fail("bridge signature matches sam");
|
|
return false;
|
|
}
|
|
if (!CheckConstraints(instantiatedMethodType, bridge, cpi.GetArgTypes(), implParameters))
|
|
{
|
|
Fail("bridge constraints");
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
if (instantiatedMethodType.GetRetType() != PrimitiveTypeWrapper.VOID)
|
|
{
|
|
TypeWrapper Rt = instantiatedMethodType.GetRetType();
|
|
TypeWrapper Ra = GetImplReturnType(implMethod);
|
|
if (Ra == PrimitiveTypeWrapper.VOID || !IsAdaptable(Ra, Rt, true))
|
|
{
|
|
Fail("The return type Rt is void, or the return type Ra is not void and is adaptable to Rt");
|
|
return false;
|
|
}
|
|
}
|
|
MethodWrapper interfaceMethod = null;
|
|
List<MethodWrapper> methods = new List<MethodWrapper>();
|
|
foreach (MethodWrapper mw in methodList)
|
|
{
|
|
if (mw.Name == cpi.Name && mw.Signature == samMethodType.Signature)
|
|
{
|
|
interfaceMethod = mw;
|
|
methods.Add(mw);
|
|
}
|
|
else if (mw.IsAbstract && !IsObjectMethod(mw))
|
|
{
|
|
methods.Add(mw);
|
|
}
|
|
}
|
|
if (interfaceMethod == null || !interfaceMethod.IsAbstract || IsObjectMethod(interfaceMethod) || !MatchSignatures(interfaceMethod, samMethodType))
|
|
{
|
|
Fail("interfaceMethod");
|
|
return false;
|
|
}
|
|
|
|
TypeBuilder tb = context.DefineAnonymousClass();
|
|
// we're not implementing the interfaces recursively (because we don't care about .NET Compact anymore),
|
|
// but should we decide to do that, we'd need to somehow communicate to AnonymousTypeWrapper what the 'real' interface is
|
|
tb.AddInterfaceImplementation(interfaceType.TypeAsBaseType);
|
|
if (serializable && Array.IndexOf(markers, CoreClasses.java.io.Serializable.Wrapper) == -1)
|
|
{
|
|
tb.AddInterfaceImplementation(CoreClasses.java.io.Serializable.Wrapper.TypeAsBaseType);
|
|
}
|
|
foreach (TypeWrapper marker in markers)
|
|
{
|
|
tb.AddInterfaceImplementation(marker.TypeAsBaseType);
|
|
}
|
|
ctor = CreateConstructorAndDispatch(context, cpi, tb, methods, implParameters, samMethodType, implMethod, instantiatedMethodType, serializable);
|
|
AddDefaultInterfaceMethods(context, methodList, tb);
|
|
return true;
|
|
}
|
|
|
|
[Conditional("TRACE_LAMBDA_METAFACTORY")]
|
|
private static void Fail(string msg)
|
|
{
|
|
Console.WriteLine("Fail: " + msg);
|
|
}
|
|
|
|
private static bool CheckConstraints(ClassFile.ConstantPoolItemMethodType instantiatedMethodType, ClassFile.ConstantPoolItemMethodType methodType, TypeWrapper[] args, TypeWrapper[] implParameters)
|
|
{
|
|
if (!IsSubTypeOf(instantiatedMethodType, methodType))
|
|
{
|
|
Fail("instantiatedMethodType <= methodType");
|
|
return false;
|
|
}
|
|
if (args.Length + methodType.GetArgTypes().Length != implParameters.Length)
|
|
{
|
|
Fail("K + N = M");
|
|
return false;
|
|
}
|
|
for (int i = 0, K = args.Length; i < K; i++)
|
|
{
|
|
if (args[i] == implParameters[i])
|
|
{
|
|
// ok
|
|
}
|
|
else if (args[i].IsPrimitive || implParameters[i].IsPrimitive || !args[i].IsSubTypeOf(implParameters[i]))
|
|
{
|
|
Fail("For i=1..K, Di = Ai");
|
|
return false;
|
|
}
|
|
}
|
|
for (int i = 0, N = methodType.GetArgTypes().Length, k = args.Length; i < N; i++)
|
|
{
|
|
if (!IsAdaptable(instantiatedMethodType.GetArgTypes()[i], implParameters[i + k], false))
|
|
{
|
|
Fail("For i=1..N, Ti is adaptable to Aj, where j=i+k");
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
private static TypeWrapper[] GetImplParameters(ClassFile.ConstantPoolItemMethodHandle implMethod)
|
|
{
|
|
MethodWrapper mw = (MethodWrapper)implMethod.Member;
|
|
TypeWrapper[] parameters = mw.GetParameters();
|
|
if (mw.IsStatic || mw.IsConstructor)
|
|
{
|
|
return parameters;
|
|
}
|
|
return ArrayUtil.Concat(mw.DeclaringType, parameters);
|
|
}
|
|
|
|
private static TypeWrapper GetImplReturnType(ClassFile.ConstantPoolItemMethodHandle implMethod)
|
|
{
|
|
return implMethod.Kind == ClassFile.RefKind.newInvokeSpecial
|
|
? implMethod.Member.DeclaringType
|
|
: ((MethodWrapper)implMethod.Member).ReturnType;
|
|
}
|
|
|
|
private static bool IsAdaptable(TypeWrapper Q, TypeWrapper S, bool isReturn)
|
|
{
|
|
if (Q == S)
|
|
{
|
|
return true;
|
|
}
|
|
|
|
if (Q.IsPrimitive)
|
|
{
|
|
if (S.IsPrimitive)
|
|
{
|
|
// Q can be converted to S via a primitive widening conversion
|
|
switch (Q.SigName[0] | S.SigName[0] << 8)
|
|
{
|
|
case 'B' | 'S' << 8:
|
|
case 'B' | 'I' << 8:
|
|
case 'B' | 'J' << 8:
|
|
case 'B' | 'F' << 8:
|
|
case 'B' | 'D' << 8:
|
|
case 'S' | 'I' << 8:
|
|
case 'S' | 'J' << 8:
|
|
case 'S' | 'F' << 8:
|
|
case 'S' | 'D' << 8:
|
|
case 'C' | 'I' << 8:
|
|
case 'C' | 'J' << 8:
|
|
case 'C' | 'F' << 8:
|
|
case 'C' | 'D' << 8:
|
|
case 'I' | 'J' << 8:
|
|
case 'I' | 'F' << 8:
|
|
case 'I' | 'D' << 8:
|
|
case 'J' | 'F' << 8:
|
|
case 'J' | 'D' << 8:
|
|
case 'F' | 'D' << 8:
|
|
return true;
|
|
default:
|
|
return false;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// S is a supertype of the Wrapper(Q)
|
|
return GetWrapper(Q).IsAssignableTo(S);
|
|
}
|
|
}
|
|
else if (isReturn)
|
|
{
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
if (S.IsPrimitive)
|
|
{
|
|
// If Q is a primitive wrapper, check that Primitive(Q) can be widened to S
|
|
TypeWrapper primitive = GetPrimitiveFromWrapper(Q);
|
|
return primitive != null && IsAdaptable(primitive, S, isReturn);
|
|
}
|
|
else
|
|
{
|
|
// for parameter types: S is a supertype of Q
|
|
return Q.IsAssignableTo(S);
|
|
}
|
|
}
|
|
}
|
|
|
|
private static TypeWrapper GetWrapper(TypeWrapper primitive)
|
|
{
|
|
Debug.Assert(primitive.IsPrimitive);
|
|
switch (primitive.SigName[0])
|
|
{
|
|
case 'Z':
|
|
return ClassLoaderWrapper.LoadClassCritical("java.lang.Boolean");
|
|
case 'B':
|
|
return ClassLoaderWrapper.LoadClassCritical("java.lang.Byte");
|
|
case 'S':
|
|
return ClassLoaderWrapper.LoadClassCritical("java.lang.Short");
|
|
case 'C':
|
|
return ClassLoaderWrapper.LoadClassCritical("java.lang.Character");
|
|
case 'I':
|
|
return ClassLoaderWrapper.LoadClassCritical("java.lang.Integer");
|
|
case 'J':
|
|
return ClassLoaderWrapper.LoadClassCritical("java.lang.Long");
|
|
case 'F':
|
|
return ClassLoaderWrapper.LoadClassCritical("java.lang.Float");
|
|
case 'D':
|
|
return ClassLoaderWrapper.LoadClassCritical("java.lang.Double");
|
|
default:
|
|
throw new InvalidOperationException();
|
|
}
|
|
}
|
|
|
|
private static TypeWrapper GetPrimitiveFromWrapper(TypeWrapper wrapper)
|
|
{
|
|
switch (wrapper.Name)
|
|
{
|
|
case "java.lang.Boolean":
|
|
return PrimitiveTypeWrapper.BOOLEAN;
|
|
case "java.lang.Byte":
|
|
return PrimitiveTypeWrapper.BYTE;
|
|
case "java.lang.Short":
|
|
return PrimitiveTypeWrapper.SHORT;
|
|
case "java.lang.Character":
|
|
return PrimitiveTypeWrapper.CHAR;
|
|
case "java.lang.Integer":
|
|
return PrimitiveTypeWrapper.INT;
|
|
case "java.lang.Long":
|
|
return PrimitiveTypeWrapper.LONG;
|
|
case "java.lang.Float":
|
|
return PrimitiveTypeWrapper.FLOAT;
|
|
case "java.lang.Double":
|
|
return PrimitiveTypeWrapper.DOUBLE;
|
|
default:
|
|
return null;
|
|
}
|
|
}
|
|
|
|
private static bool IsSubTypeOf(ClassFile.ConstantPoolItemMethodType instantiatedMethodType, ClassFile.ConstantPoolItemMethodType samMethodType)
|
|
{
|
|
TypeWrapper[] T = instantiatedMethodType.GetArgTypes();
|
|
TypeWrapper[] U = samMethodType.GetArgTypes();
|
|
if (T.Length != U.Length)
|
|
{
|
|
return false;
|
|
}
|
|
for (int i = 0; i < T.Length; i++)
|
|
{
|
|
if (!T[i].IsAssignableTo(U[i]))
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
TypeWrapper Rt = instantiatedMethodType.GetRetType();
|
|
TypeWrapper Ru = samMethodType.GetRetType();
|
|
return Rt.IsAssignableTo(Ru);
|
|
}
|
|
|
|
private static MethodBuilder CreateConstructorAndDispatch(DynamicTypeWrapper.FinishContext context, ClassFile.ConstantPoolItemInvokeDynamic cpi, TypeBuilder tb,
|
|
List<MethodWrapper> methods, TypeWrapper[] implParameters, ClassFile.ConstantPoolItemMethodType samMethodType, ClassFile.ConstantPoolItemMethodHandle implMethod,
|
|
ClassFile.ConstantPoolItemMethodType instantiatedMethodType, bool serializable)
|
|
{
|
|
TypeWrapper[] args = cpi.GetArgTypes();
|
|
|
|
// captured values
|
|
Type[] capturedTypes = new Type[args.Length];
|
|
FieldBuilder[] capturedFields = new FieldBuilder[capturedTypes.Length];
|
|
for (int i = 0; i < capturedTypes.Length; i++)
|
|
{
|
|
capturedTypes[i] = args[i].TypeAsSignatureType;
|
|
FieldAttributes attr = FieldAttributes.Private;
|
|
if (i > 0 || !args[0].IsGhost)
|
|
{
|
|
attr |= FieldAttributes.InitOnly;
|
|
}
|
|
capturedFields[i] = tb.DefineField("arg$" + (i + 1), capturedTypes[i], attr);
|
|
}
|
|
|
|
// constructor
|
|
MethodBuilder ctor = ReflectUtil.DefineConstructor(tb, MethodAttributes.Assembly, capturedTypes);
|
|
CodeEmitter ilgen = CodeEmitter.Create(ctor);
|
|
ilgen.Emit(OpCodes.Ldarg_0);
|
|
ilgen.Emit(OpCodes.Call, Types.Object.GetConstructor(Type.EmptyTypes));
|
|
for (int i = 0; i < capturedTypes.Length; i++)
|
|
{
|
|
ilgen.EmitLdarg(0);
|
|
ilgen.EmitLdarg(i + 1);
|
|
ilgen.Emit(OpCodes.Stfld, capturedFields[i]);
|
|
}
|
|
ilgen.Emit(OpCodes.Ret);
|
|
ilgen.DoEmit();
|
|
|
|
// dispatch methods
|
|
foreach (MethodWrapper mw in methods)
|
|
{
|
|
EmitDispatch(context, args, tb, mw, implParameters, implMethod, instantiatedMethodType, capturedFields);
|
|
}
|
|
|
|
// writeReplace method
|
|
if (serializable)
|
|
{
|
|
MethodBuilder writeReplace = tb.DefineMethod("writeReplace", MethodAttributes.Private, Types.Object, Type.EmptyTypes);
|
|
ilgen = CodeEmitter.Create(writeReplace);
|
|
context.TypeWrapper.EmitClassLiteral(ilgen);
|
|
ilgen.Emit(OpCodes.Ldstr, cpi.GetRetType().Name.Replace('.', '/'));
|
|
ilgen.Emit(OpCodes.Ldstr, cpi.Name);
|
|
ilgen.Emit(OpCodes.Ldstr, samMethodType.Signature.Replace('.', '/'));
|
|
ilgen.EmitLdc_I4((int)implMethod.Kind);
|
|
ilgen.Emit(OpCodes.Ldstr, implMethod.Class.Replace('.', '/'));
|
|
ilgen.Emit(OpCodes.Ldstr, implMethod.Name);
|
|
ilgen.Emit(OpCodes.Ldstr, implMethod.Signature.Replace('.', '/'));
|
|
ilgen.Emit(OpCodes.Ldstr, instantiatedMethodType.Signature.Replace('.', '/'));
|
|
ilgen.EmitLdc_I4(capturedFields.Length);
|
|
ilgen.Emit(OpCodes.Newarr, Types.Object);
|
|
for (int i = 0; i < capturedFields.Length; i++)
|
|
{
|
|
ilgen.Emit(OpCodes.Dup);
|
|
ilgen.EmitLdc_I4(i);
|
|
ilgen.EmitLdarg(0);
|
|
ilgen.Emit(OpCodes.Ldfld, capturedFields[i]);
|
|
if (args[i].IsPrimitive)
|
|
{
|
|
Boxer.EmitBox(ilgen, args[i]);
|
|
}
|
|
else if (args[i].IsGhost)
|
|
{
|
|
args[i].EmitConvSignatureTypeToStackType(ilgen);
|
|
}
|
|
ilgen.Emit(OpCodes.Stelem, Types.Object);
|
|
}
|
|
MethodWrapper ctorSerializedLambda = ClassLoaderWrapper.LoadClassCritical("java.lang.invoke.SerializedLambda").GetMethodWrapper(StringConstants.INIT,
|
|
"(Ljava.lang.Class;Ljava.lang.String;Ljava.lang.String;Ljava.lang.String;ILjava.lang.String;Ljava.lang.String;Ljava.lang.String;Ljava.lang.String;[Ljava.lang.Object;)V", false);
|
|
ctorSerializedLambda.Link();
|
|
ctorSerializedLambda.EmitNewobj(ilgen);
|
|
ilgen.Emit(OpCodes.Ret);
|
|
ilgen.DoEmit();
|
|
|
|
if (!context.TypeWrapper.GetClassLoader().NoAutomagicSerialization)
|
|
{
|
|
// add .NET serialization interop support
|
|
Serialization.MarkSerializable(tb);
|
|
Serialization.AddGetObjectData(tb);
|
|
}
|
|
}
|
|
|
|
return ctor;
|
|
}
|
|
|
|
private static void EmitDispatch(DynamicTypeWrapper.FinishContext context, TypeWrapper[] args, TypeBuilder tb, MethodWrapper interfaceMethod, TypeWrapper[] implParameters,
|
|
ClassFile.ConstantPoolItemMethodHandle implMethod, ClassFile.ConstantPoolItemMethodType instantiatedMethodType, FieldBuilder[] capturedFields)
|
|
{
|
|
MethodBuilder mb = interfaceMethod.GetDefineMethodHelper().DefineMethod(context.TypeWrapper, tb, interfaceMethod.Name, MethodAttributes.Public | MethodAttributes.Virtual | MethodAttributes.NewSlot | MethodAttributes.Final);
|
|
if (interfaceMethod.Name != interfaceMethod.RealName)
|
|
{
|
|
tb.DefineMethodOverride(mb, (MethodInfo)interfaceMethod.GetMethod());
|
|
}
|
|
CodeEmitter ilgen = CodeEmitter.Create(mb);
|
|
for (int i = 0; i < capturedFields.Length; i++)
|
|
{
|
|
ilgen.EmitLdarg(0);
|
|
OpCode opc = OpCodes.Ldfld;
|
|
if (i == 0 && args[0].IsGhost)
|
|
{
|
|
switch (implMethod.Kind)
|
|
{
|
|
case ClassFile.RefKind.invokeInterface:
|
|
case ClassFile.RefKind.invokeVirtual:
|
|
case ClassFile.RefKind.invokeSpecial:
|
|
opc = OpCodes.Ldflda;
|
|
break;
|
|
}
|
|
}
|
|
ilgen.Emit(opc, capturedFields[i]);
|
|
}
|
|
for (int i = 0, count = interfaceMethod.GetParameters().Length, k = capturedFields.Length; i < count; i++)
|
|
{
|
|
ilgen.EmitLdarg(i + 1);
|
|
TypeWrapper Ui = interfaceMethod.GetParameters()[i];
|
|
TypeWrapper Ti = instantiatedMethodType.GetArgTypes()[i];
|
|
TypeWrapper Aj = implParameters[i + k];
|
|
if (Ui == PrimitiveTypeWrapper.BYTE)
|
|
{
|
|
ilgen.Emit(OpCodes.Conv_I1);
|
|
}
|
|
if (Ti != Ui)
|
|
{
|
|
if (Ti.IsGhost)
|
|
{
|
|
Ti.EmitConvStackTypeToSignatureType(ilgen, Ui);
|
|
}
|
|
else if (Ui.IsGhost)
|
|
{
|
|
Ui.EmitConvSignatureTypeToStackType(ilgen);
|
|
}
|
|
else
|
|
{
|
|
Ti.EmitCheckcast(ilgen);
|
|
}
|
|
}
|
|
if (Ti != Aj)
|
|
{
|
|
if (Ti.IsPrimitive && !Aj.IsPrimitive)
|
|
{
|
|
Boxer.EmitBox(ilgen, Ti);
|
|
}
|
|
else if (!Ti.IsPrimitive && Aj.IsPrimitive)
|
|
{
|
|
TypeWrapper primitive = GetPrimitiveFromWrapper(Ti);
|
|
Boxer.EmitUnbox(ilgen, primitive, false);
|
|
if (primitive == PrimitiveTypeWrapper.BYTE)
|
|
{
|
|
ilgen.Emit(OpCodes.Conv_I1);
|
|
}
|
|
}
|
|
else if (Aj == PrimitiveTypeWrapper.LONG)
|
|
{
|
|
ilgen.Emit(OpCodes.Conv_I8);
|
|
}
|
|
else if (Aj == PrimitiveTypeWrapper.FLOAT)
|
|
{
|
|
ilgen.Emit(OpCodes.Conv_R4);
|
|
}
|
|
else if (Aj == PrimitiveTypeWrapper.DOUBLE)
|
|
{
|
|
ilgen.Emit(OpCodes.Conv_R8);
|
|
}
|
|
}
|
|
}
|
|
switch (implMethod.Kind)
|
|
{
|
|
case ClassFile.RefKind.invokeVirtual:
|
|
case ClassFile.RefKind.invokeInterface:
|
|
((MethodWrapper)implMethod.Member).EmitCallvirt(ilgen);
|
|
break;
|
|
case ClassFile.RefKind.newInvokeSpecial:
|
|
((MethodWrapper)implMethod.Member).EmitNewobj(ilgen);
|
|
break;
|
|
case ClassFile.RefKind.invokeStatic:
|
|
case ClassFile.RefKind.invokeSpecial:
|
|
((MethodWrapper)implMethod.Member).EmitCall(ilgen);
|
|
break;
|
|
default:
|
|
throw new InvalidOperationException();
|
|
}
|
|
TypeWrapper Ru = interfaceMethod.ReturnType;
|
|
TypeWrapper Ra = GetImplReturnType(implMethod);
|
|
TypeWrapper Rt = instantiatedMethodType.GetRetType();
|
|
if (Ra == PrimitiveTypeWrapper.BYTE)
|
|
{
|
|
ilgen.Emit(OpCodes.Conv_I1);
|
|
}
|
|
if (Ra != Ru)
|
|
{
|
|
if (Ru == PrimitiveTypeWrapper.VOID)
|
|
{
|
|
ilgen.Emit(OpCodes.Pop);
|
|
}
|
|
else if (Ra.IsGhost)
|
|
{
|
|
Ra.EmitConvSignatureTypeToStackType(ilgen);
|
|
}
|
|
else if (Ru.IsGhost)
|
|
{
|
|
Ru.EmitConvStackTypeToSignatureType(ilgen, Ra);
|
|
}
|
|
}
|
|
if (Ra != Rt)
|
|
{
|
|
if (Rt.IsPrimitive)
|
|
{
|
|
if (Rt == PrimitiveTypeWrapper.VOID)
|
|
{
|
|
// already popped
|
|
}
|
|
else if (!Ra.IsPrimitive)
|
|
{
|
|
TypeWrapper primitive = GetPrimitiveFromWrapper(Ra);
|
|
if (primitive != null)
|
|
{
|
|
Boxer.EmitUnbox(ilgen, primitive, false);
|
|
}
|
|
else
|
|
{
|
|
// If Q is not a primitive wrapper, cast Q to the base Wrapper(S); for example Number for numeric types
|
|
EmitConvertingUnbox(ilgen, Rt);
|
|
}
|
|
}
|
|
else if (Rt == PrimitiveTypeWrapper.LONG)
|
|
{
|
|
ilgen.Emit(OpCodes.Conv_I8);
|
|
}
|
|
else if (Rt == PrimitiveTypeWrapper.FLOAT)
|
|
{
|
|
ilgen.Emit(OpCodes.Conv_R4);
|
|
}
|
|
else if (Rt == PrimitiveTypeWrapper.DOUBLE)
|
|
{
|
|
ilgen.Emit(OpCodes.Conv_R8);
|
|
}
|
|
}
|
|
else if (Ra.IsPrimitive)
|
|
{
|
|
Boxer.EmitBox(ilgen, GetPrimitiveFromWrapper(Rt));
|
|
}
|
|
else
|
|
{
|
|
Rt.EmitCheckcast(ilgen);
|
|
}
|
|
}
|
|
ilgen.EmitTailCallPrevention();
|
|
ilgen.Emit(OpCodes.Ret);
|
|
ilgen.DoEmit();
|
|
}
|
|
|
|
private static void EmitConvertingUnbox(CodeEmitter ilgen, TypeWrapper tw)
|
|
{
|
|
switch (tw.SigName[0])
|
|
{
|
|
case 'Z':
|
|
case 'C':
|
|
Boxer.EmitUnbox(ilgen, tw, true);
|
|
break;
|
|
case 'B':
|
|
EmitUnboxNumber(ilgen, "byteValue", "()B");
|
|
break;
|
|
case 'S':
|
|
EmitUnboxNumber(ilgen, "shortValue", "()S");
|
|
break;
|
|
case 'I':
|
|
EmitUnboxNumber(ilgen, "intValue", "()I");
|
|
break;
|
|
case 'J':
|
|
EmitUnboxNumber(ilgen, "longValue", "()J");
|
|
break;
|
|
case 'F':
|
|
EmitUnboxNumber(ilgen, "floatValue", "()F");
|
|
break;
|
|
case 'D':
|
|
EmitUnboxNumber(ilgen, "doubleValue", "()D");
|
|
break;
|
|
default:
|
|
throw new InvalidOperationException();
|
|
}
|
|
}
|
|
|
|
private static void EmitUnboxNumber(CodeEmitter ilgen, string methodName, string methodSig)
|
|
{
|
|
TypeWrapper tw = ClassLoaderWrapper.LoadClassCritical("java.lang.Number");
|
|
tw.EmitCheckcast(ilgen);
|
|
MethodWrapper mw = tw.GetMethodWrapper(methodName, methodSig, false);
|
|
mw.Link();
|
|
mw.EmitCallvirt(ilgen);
|
|
}
|
|
|
|
private static void AddDefaultInterfaceMethods(DynamicTypeWrapper.FinishContext context, MethodWrapper[] methodList, TypeBuilder tb)
|
|
{
|
|
// we use special name to hide these from Java reflection
|
|
const MethodAttributes attr = MethodAttributes.Public | MethodAttributes.Virtual | MethodAttributes.NewSlot | MethodAttributes.Final | MethodAttributes.SpecialName;
|
|
TypeWrapperFactory factory = context.TypeWrapper.GetClassLoader().GetTypeWrapperFactory();
|
|
foreach (MethodWrapper mw in methodList)
|
|
{
|
|
if (!mw.IsAbstract)
|
|
{
|
|
MethodBuilder mb = mw.GetDefineMethodHelper().DefineMethod(factory, tb, mw.Name, attr);
|
|
if (mw.Name != mw.RealName)
|
|
{
|
|
tb.DefineMethodOverride(mb, (MethodInfo)mw.GetMethod());
|
|
}
|
|
DynamicTypeWrapper.FinishContext.EmitCallDefaultInterfaceMethod(mb, mw);
|
|
}
|
|
else if (IsObjectMethod(mw))
|
|
{
|
|
MethodBuilder mb = mw.GetDefineMethodHelper().DefineMethod(factory, tb, mw.Name, attr);
|
|
if (mw.Name != mw.RealName)
|
|
{
|
|
tb.DefineMethodOverride(mb, (MethodInfo)mw.GetMethod());
|
|
}
|
|
CodeEmitter ilgen = CodeEmitter.Create(mb);
|
|
for (int i = 0, count = mw.GetParameters().Length; i <= count; i++)
|
|
{
|
|
ilgen.EmitLdarg(i);
|
|
}
|
|
CoreClasses.java.lang.Object.Wrapper.GetMethodWrapper(mw.Name, mw.Signature, false).EmitCallvirt(ilgen);
|
|
ilgen.Emit(OpCodes.Ret);
|
|
ilgen.DoEmit();
|
|
}
|
|
}
|
|
}
|
|
|
|
private static bool IsSupportedImplMethod(ClassFile.ConstantPoolItemMethodHandle implMethod, TypeWrapper caller, TypeWrapper[] captured, ClassFile.ConstantPoolItemMethodType instantiatedMethodType)
|
|
{
|
|
switch (implMethod.Kind)
|
|
{
|
|
case ClassFile.RefKind.invokeVirtual:
|
|
case ClassFile.RefKind.invokeInterface:
|
|
case ClassFile.RefKind.newInvokeSpecial:
|
|
case ClassFile.RefKind.invokeStatic:
|
|
case ClassFile.RefKind.invokeSpecial:
|
|
break;
|
|
default:
|
|
return false;
|
|
}
|
|
MethodWrapper mw = (MethodWrapper)implMethod.Member;
|
|
if (mw == null || mw.HasCallerID || DynamicTypeWrapper.RequiresDynamicReflectionCallerClass(mw.DeclaringType.Name, mw.Name, mw.Signature))
|
|
{
|
|
return false;
|
|
}
|
|
TypeWrapper instance;
|
|
if (mw.IsConstructor)
|
|
{
|
|
instance = mw.DeclaringType;
|
|
}
|
|
else if (mw.IsStatic)
|
|
{
|
|
instance = null;
|
|
}
|
|
else
|
|
{
|
|
// if implMethod is an instance method, the type of the first captured value must be subtype of implMethod.DeclaringType
|
|
instance = captured.Length == 0 ? instantiatedMethodType.GetArgTypes()[0] : captured[0];
|
|
if (!instance.IsAssignableTo(mw.DeclaringType))
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
if (!mw.IsAccessibleFrom(mw.DeclaringType, caller, instance))
|
|
{
|
|
return false;
|
|
}
|
|
mw.Link();
|
|
return true;
|
|
}
|
|
|
|
private static bool IsSupportedInterface(TypeWrapper tw, TypeWrapper caller)
|
|
{
|
|
return tw.IsInterface
|
|
&& !tw.IsGhost
|
|
&& tw.IsAccessibleFrom(caller)
|
|
&& !Serialization.IsISerializable(tw);
|
|
}
|
|
|
|
private static bool CheckSupportedInterfaces(TypeWrapper caller, TypeWrapper tw, TypeWrapper[] markers, ClassFile.ConstantPoolItemMethodType[] bridges, out MethodWrapper[] methodList)
|
|
{
|
|
// we don't need to check for unloadable, because we already did that while validating the invoke signature
|
|
if (!IsSupportedInterface(tw, caller))
|
|
{
|
|
methodList = null;
|
|
return false;
|
|
}
|
|
Dictionary<MethodKey, MethodWrapper> methods = new Dictionary<MethodKey,MethodWrapper>();
|
|
int abstractMethodCount = 0;
|
|
int bridgeMethodCount = 0;
|
|
if (GatherAllInterfaceMethods(tw, bridges, methods, ref abstractMethodCount, ref bridgeMethodCount) && abstractMethodCount == 1)
|
|
{
|
|
foreach (TypeWrapper marker in markers)
|
|
{
|
|
if (!IsSupportedInterface(marker, caller))
|
|
{
|
|
methodList = null;
|
|
return false;
|
|
}
|
|
if (!GatherAllInterfaceMethods(marker, null, methods, ref abstractMethodCount, ref bridgeMethodCount) || abstractMethodCount != 1)
|
|
{
|
|
methodList = null;
|
|
return false;
|
|
}
|
|
}
|
|
if (bridges != null && bridgeMethodCount != bridges.Length)
|
|
{
|
|
methodList = null;
|
|
return false;
|
|
}
|
|
methodList = new MethodWrapper[methods.Count];
|
|
methods.Values.CopyTo(methodList, 0);
|
|
return true;
|
|
}
|
|
methodList = null;
|
|
return false;
|
|
}
|
|
|
|
private static bool GatherAllInterfaceMethods(TypeWrapper tw, ClassFile.ConstantPoolItemMethodType[] bridges, Dictionary<MethodKey, MethodWrapper> methods,
|
|
ref int abstractMethodCount, ref int bridgeMethodCount)
|
|
{
|
|
foreach (MethodWrapper mw in tw.GetMethods())
|
|
{
|
|
if (mw.IsVirtual)
|
|
{
|
|
MirandaMethodWrapper mmw = mw as MirandaMethodWrapper;
|
|
if (mmw != null)
|
|
{
|
|
if (mmw.Error != null)
|
|
{
|
|
return false;
|
|
}
|
|
continue;
|
|
}
|
|
MethodKey key = new MethodKey("", mw.Name, mw.Signature);
|
|
MethodWrapper current;
|
|
if (methods.TryGetValue(key, out current))
|
|
{
|
|
if (!MatchSignatures(mw, current))
|
|
{
|
|
// linkage error (or unloadable type)
|
|
return false;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
methods.Add(key, mw);
|
|
if (mw.IsAbstract && !IsObjectMethod(mw))
|
|
{
|
|
if (bridges != null && IsBridge(mw, bridges))
|
|
{
|
|
bridgeMethodCount++;
|
|
}
|
|
else
|
|
{
|
|
abstractMethodCount++;
|
|
}
|
|
}
|
|
}
|
|
mw.Link();
|
|
if (mw.GetMethod() == null)
|
|
{
|
|
return false;
|
|
}
|
|
if (current != null && mw.RealName != current.RealName)
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
foreach (TypeWrapper tw1 in tw.Interfaces)
|
|
{
|
|
if (!GatherAllInterfaceMethods(tw1, bridges, methods, ref abstractMethodCount, ref bridgeMethodCount))
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
private static bool IsBridge(MethodWrapper mw, ClassFile.ConstantPoolItemMethodType[] bridges)
|
|
{
|
|
foreach (ClassFile.ConstantPoolItemMethodType bridge in bridges)
|
|
{
|
|
if (bridge.Signature == mw.Signature)
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
private static bool IsObjectMethod(MethodWrapper mw)
|
|
{
|
|
MethodWrapper objectMethod;
|
|
return (objectMethod = CoreClasses.java.lang.Object.Wrapper.GetMethodWrapper(mw.Name, mw.Signature, false)) != null
|
|
&& objectMethod.IsPublic;
|
|
}
|
|
|
|
private static bool MatchSignatures(MethodWrapper interfaceMethod, ClassFile.ConstantPoolItemMethodType samMethodType)
|
|
{
|
|
return interfaceMethod.ReturnType == samMethodType.GetRetType()
|
|
&& MatchTypes(interfaceMethod.GetParameters(), samMethodType.GetArgTypes());
|
|
}
|
|
|
|
private static bool MatchSignatures(MethodWrapper mw1, MethodWrapper mw2)
|
|
{
|
|
return mw1.ReturnType == mw2.ReturnType
|
|
&& MatchTypes(mw1.GetParameters(), mw2.GetParameters());
|
|
}
|
|
|
|
private static bool MatchTypes(TypeWrapper[] ar1, TypeWrapper[] ar2)
|
|
{
|
|
if (ar1.Length != ar2.Length)
|
|
{
|
|
return false;
|
|
}
|
|
for (int i = 0; i < ar1.Length; i++)
|
|
{
|
|
if (ar1[i] != ar2[i])
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
private static bool IsLambdaMetafactory(ClassFile classFile, ClassFile.BootstrapMethod bsm)
|
|
{
|
|
ClassFile.ConstantPoolItemMethodHandle mh;
|
|
return bsm.ArgumentCount == 3
|
|
&& classFile.GetConstantPoolConstantType(bsm.GetArgument(0)) == ClassFile.ConstantType.MethodType
|
|
&& classFile.GetConstantPoolConstantType(bsm.GetArgument(1)) == ClassFile.ConstantType.MethodHandle
|
|
&& classFile.GetConstantPoolConstantType(bsm.GetArgument(2)) == ClassFile.ConstantType.MethodType
|
|
&& (mh = classFile.GetConstantPoolConstantMethodHandle(bsm.BootstrapMethodIndex)).Kind == ClassFile.RefKind.invokeStatic
|
|
&& mh.Member != null
|
|
&& IsLambdaMetafactory(mh.Member);
|
|
}
|
|
|
|
private static bool IsLambdaMetafactory(MemberWrapper mw)
|
|
{
|
|
return mw.Name == "metafactory"
|
|
&& mw.Signature == "(Ljava.lang.invoke.MethodHandles$Lookup;Ljava.lang.String;Ljava.lang.invoke.MethodType;Ljava.lang.invoke.MethodType;Ljava.lang.invoke.MethodHandle;Ljava.lang.invoke.MethodType;)Ljava.lang.invoke.CallSite;"
|
|
&& mw.DeclaringType.Name == "java.lang.invoke.LambdaMetafactory";
|
|
}
|
|
|
|
[Flags]
|
|
enum AltFlags
|
|
{
|
|
Serializable = 1,
|
|
Markers = 2,
|
|
Bridges = 4,
|
|
Mask = Serializable | Markers | Bridges
|
|
}
|
|
|
|
private static bool IsLambdaAltMetafactory(ClassFile classFile, ClassFile.BootstrapMethod bsm)
|
|
{
|
|
ClassFile.ConstantPoolItemMethodHandle mh;
|
|
AltFlags flags;
|
|
int argpos = 4;
|
|
return bsm.ArgumentCount >= 4
|
|
&& (mh = classFile.GetConstantPoolConstantMethodHandle(bsm.BootstrapMethodIndex)).Kind == ClassFile.RefKind.invokeStatic
|
|
&& mh.Member != null
|
|
&& IsLambdaAltMetafactory(mh.Member)
|
|
&& classFile.GetConstantPoolConstantType(bsm.GetArgument(0)) == ClassFile.ConstantType.MethodType
|
|
&& classFile.GetConstantPoolConstantType(bsm.GetArgument(1)) == ClassFile.ConstantType.MethodHandle
|
|
&& classFile.GetConstantPoolConstantType(bsm.GetArgument(2)) == ClassFile.ConstantType.MethodType
|
|
&& classFile.GetConstantPoolConstantType(bsm.GetArgument(3)) == ClassFile.ConstantType.Integer
|
|
&& ((flags = (AltFlags)classFile.GetConstantPoolConstantInteger(bsm.GetArgument(3))) & ~AltFlags.Mask) == 0
|
|
&& ((flags & AltFlags.Markers) == 0 || CheckOptionalArgs(classFile, bsm, ClassFile.ConstantType.Class, ref argpos))
|
|
&& ((flags & AltFlags.Bridges) == 0 || CheckOptionalArgs(classFile, bsm, ClassFile.ConstantType.MethodType, ref argpos))
|
|
&& argpos == bsm.ArgumentCount;
|
|
}
|
|
|
|
private static bool IsLambdaAltMetafactory(MemberWrapper mw)
|
|
{
|
|
return mw.Name == "altMetafactory"
|
|
&& mw.Signature == "(Ljava.lang.invoke.MethodHandles$Lookup;Ljava.lang.String;Ljava.lang.invoke.MethodType;[Ljava.lang.Object;)Ljava.lang.invoke.CallSite;"
|
|
&& mw.DeclaringType.Name == "java.lang.invoke.LambdaMetafactory";
|
|
}
|
|
|
|
private static bool CheckOptionalArgs(ClassFile classFile, ClassFile.BootstrapMethod bsm, ClassFile.ConstantType type, ref int argpos)
|
|
{
|
|
if (bsm.ArgumentCount - argpos < 1)
|
|
{
|
|
return false;
|
|
}
|
|
if (classFile.GetConstantPoolConstantType(bsm.GetArgument(argpos)) != ClassFile.ConstantType.Integer)
|
|
{
|
|
return false;
|
|
}
|
|
int count = classFile.GetConstantPoolConstantInteger(bsm.GetArgument(argpos++));
|
|
if (count < 0 || bsm.ArgumentCount - argpos < count)
|
|
{
|
|
return false;
|
|
}
|
|
for (int i = 0; i < count; i++)
|
|
{
|
|
if (classFile.GetConstantPoolConstantType(bsm.GetArgument(argpos++)) != type)
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
private static bool HasUnloadable(ClassFile.ConstantPoolItemInvokeDynamic cpi)
|
|
{
|
|
return HasUnloadable(cpi.GetArgTypes()) || cpi.GetRetType().IsUnloadable;
|
|
}
|
|
|
|
private static bool HasUnloadable(ClassFile.ConstantPoolItemMethodType cpi)
|
|
{
|
|
return HasUnloadable(cpi.GetArgTypes()) || cpi.GetRetType().IsUnloadable;
|
|
}
|
|
|
|
private static bool HasUnloadable(ClassFile.ConstantPoolItemMI cpi)
|
|
{
|
|
return HasUnloadable(cpi.GetArgTypes()) || cpi.GetRetType().IsUnloadable;
|
|
}
|
|
|
|
private static bool HasUnloadable(TypeWrapper[] wrappers)
|
|
{
|
|
foreach (TypeWrapper tw in wrappers)
|
|
{
|
|
if (tw.IsUnloadable)
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
}
|
|
}
|