bool ConvertVirtualToStaticCall(CilType callClass, CilMethod callMethod) { if (callClass.ClassName == callClass.JavaName) { if (callClass.ClassName == "system.FunctionalInterfaceDelegate" && callMethod.Name == "AsInterface") { // a call to system.FunctionalInterfaceDelegate::AsInterface() // should be fixed to expect an 'object' return type, and then // cast that return type to the proper interface var tempMethod = new JavaMethodRef(callMethod.Name, JavaType.ObjectType); code.NewInstruction(0xB6 /* invokevirtual */, callClass, tempMethod); code.NewInstruction(0xC0 /* checkcast */, callMethod.ReturnType, null); stackMap.PopStack(CilMain.Where); stackMap.PushStack(callMethod.ReturnType); return(true); } if (callMethod.IsExternal && NativeMethodClasses.TryGetValue(callClass.ClassName, out var altClassName)) { // a call to an instance method System.NameSpace.Class::Method, // which is implemented only as a native method, is translated // into a static call to some other class where the method is // implemented. such implementing classes are listed in the // NativeMethodClasses dictionary the bottom of this file. var altMethodName = callMethod.Name; int altMethodNameIdx = altMethodName.IndexOf(CilMain.OPEN_PARENS); if (altMethodNameIdx != -1) { altMethodName = altMethodName.Substring(0, altMethodNameIdx); } var tempMethod = new JavaMethodRef(altMethodName, callMethod.ReturnType); var parameters = new List <JavaFieldRef>(callMethod.Parameters); parameters.Insert(0, new JavaFieldRef("this", callClass)); tempMethod.Parameters = parameters; code.NewInstruction(0xB8 /* invokestatic */, new JavaType(0, 0, altClassName), tempMethod); ClearMethodArguments(callMethod, false); PushMethodReturnType(callMethod); return(true); } // don't bother adjusting the call if the program explicitly // refers to the java class rather than the simulated wrapper, // e.g. to java.lang.Throwable rather than System.Exception return(false); } if (callClass.Equals(JavaType.StringType) || callClass.Equals(JavaType.ThrowableType) || (callClass.Equals(JavaType.ObjectType) && callMethod.Name == "GetType" && callMethod.ToDescriptor() == "()Lsystem/Type;")) { // we map some basic .Net types their java counterparts, so we // can't invoke .Net instance methods on them directly. instead, // we invoke a static method on our helper class. for example: // ((System.String)x).CompareTo(y) -> system.String.CompareTo(x,y) var tempMethod = new JavaMethodRef(callMethod.Name, callMethod.ReturnType); var parameters = new List <JavaFieldRef>(callMethod.Parameters); parameters.Insert(0, new JavaFieldRef("this", callClass)); tempMethod.Parameters = parameters; if (callClass.Equals(JavaType.ThrowableType) && callMethod.Name == "system-Exception-GetType") { // undo the effect of CilMethod::MethodIsShadowing upon calling // the virtual/overriding GetType() from System.Exception, and // instead call the static system.Object.GetType() tempMethod.Name = "GetType"; callClass = CilType.From(new JavaType(0, 0, "system.Object")); parameters[0].Type = JavaType.ObjectType; } else { CilMethod.FixNameForVirtualToStaticCall(tempMethod, callClass); } code.NewInstruction(0xB8 /* invokestatic */, new JavaType(0, 0, callClass.JavaName), tempMethod); ClearMethodArguments(callMethod, false); PushMethodReturnType(callMethod); return(true); } if (callClass.JavaName == null && callClass.Equals(JavaType.ClassType) && callMethod.Name == "GetRuntimeType") { // convert virtual call to RuntimeTypeHandle.GetRuntimeType // to a static call to system.RuntimeType code.NewInstruction(0xB8 /* invokestatic */, CilType.SystemRuntimeTypeType, new JavaMethodRef( callMethod.Name, callMethod.ReturnType, JavaType.ObjectType)); ClearMethodArguments(callMethod, false); PushMethodReturnType(callMethod); return(true); } return(false); }
bool Translate_Call(CilMethod callMethod) { var currentClass = method.DeclType; var callClass = callMethod.DeclType; byte op; if (callMethod.IsStatic) { if (ConvertCallToNop()) { return(true); } if (callClass.Equals(JavaType.ObjectType) || callClass.Equals(JavaType.StringType)) { // generally we translate System.Object to java.lang.Object, // and System.String to java.lang.String, // but not in the case of a static method call callClass = CilType.From(new JavaType(0, 0, callClass.JavaName)); } else if (IsSystemTypeOperatorOrIsCallToIsInterface(callClass, callMethod)) { // rename System.Type operators == and != to system.RuntimeType, and // rename call to RuntimeTypeHandle.IsInterface to system.RuntimeType callClass = CilType.SystemRuntimeTypeType; } else if (callMethod.IsExternal && NativeMethodClasses.TryGetValue(callClass.ClassName, out var altClassName)) { // when the call target is a native methods, redirect it callClass = CilType.From(new JavaType(0, 0, altClassName)); } op = 0xB8; // invokestatic } else if (callMethod.IsConstructor || currentClass.IsDerivedFrom(callClass)) { if (callClass.Equals(JavaType.ThrowableType)) { // generally we translate System.Exception to java.lang.Throwable, // but not in the case of a super constructor call callClass = CilType.From(new JavaType(0, 0, callClass.JavaName)); } if (ConvertVirtualToStaticCall(callClass, callMethod)) { return(true); } if (callMethod.IsVirtual && currentClass.Equals(callClass)) { // Android 'D8' does not support 'invokespecial' on a method // on the same class, unless the method is marked final. // if we know the target method is a virtual method, then // it surely is not marked final, so use 'invokevirtual'. op = 0xB6; // invokevirtual } else { op = 0xB7; // invokespecial } if (callMethod.Name == "clone" && callMethod.Parameters.Count == 0) { // if calling clone on the super object, implement Cloneable code.Method.Class.AddInterface("java.lang.Cloneable"); } } else { if (ConvertVirtualToStaticCall(callClass, callMethod)) { return(true); } op = 0xB6; // invokevirtual } if (callMethod.IsArrayMethod) { arrays.Call(callMethod, cilInst); } else { CheckAndBoxArguments(callMethod, (op == 0xB6)); PushGenericArguments(callMethod); code.NewInstruction(op, callClass.AsWritableClass, callMethod.WithGenericParameters); ClearMethodArguments(callMethod, (op == 0xB7)); PushMethodReturnType(callMethod); } return(true); bool IsSystemTypeOperatorOrIsCallToIsInterface(CilType callClass, CilMethod callMethod) { if (callClass.Equals(CilType.SystemTypeType) && ( callMethod.Name == "op_Equality" || callMethod.Name == "op_Inequality")) { return(true); } if (callClass.Equals(JavaType.ClassType) && callMethod.Name == "IsInterface") { return(true); } return(false); } }