linux-packaging-mono/external/ikvm/runtime/LambdaMetafactory.cs
Xamarin Public Jenkins c042cd0c52 Imported Upstream version 4.2.0.179
Former-commit-id: 4610231f55806d2a05ed69e5ff3faa7336cc1479
2015-11-10 15:03:43 +00:00

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;
}
}
}