private void getCallInfo(IntPtr _this, ref CORINFO_RESOLVED_TOKEN pResolvedToken, CORINFO_RESOLVED_TOKEN* pConstrainedResolvedToken, CORINFO_METHOD_STRUCT_* callerHandle, CORINFO_CALLINFO_FLAGS flags, ref CORINFO_CALL_INFO pResult) { #if DEBUG // In debug, write some bogus data to the struct to ensure we have filled everything // properly. fixed (CORINFO_CALL_INFO* tmp = &pResult) MemoryHelper.FillMemory((byte*)tmp, 0xcc, Marshal.SizeOf<CORINFO_CALL_INFO>()); #endif MethodDesc method = HandleToObject(pResolvedToken.hMethod); // Spec says that a callvirt lookup ignores static methods. Since static methods // can't have the exact same signature as instance methods, a lookup that found // a static method would have never found an instance method. if (method.Signature.IsStatic && (flags & CORINFO_CALLINFO_FLAGS.CORINFO_CALLINFO_CALLVIRT) != 0) { throw new BadImageFormatException(); } TypeDesc exactType = HandleToObject(pResolvedToken.hClass); TypeDesc constrainedType = null; if ((flags & CORINFO_CALLINFO_FLAGS.CORINFO_CALLINFO_CALLVIRT) != 0 && pConstrainedResolvedToken != null) { constrainedType = HandleToObject(pConstrainedResolvedToken->hClass); } bool resolvedConstraint = false; bool forceUseRuntimeLookup = false; MethodDesc methodAfterConstraintResolution = method; if (constrainedType == null) { pResult.thisTransform = CORINFO_THIS_TRANSFORM.CORINFO_NO_THIS_TRANSFORM; } else { // We have a "constrained." call. Try a partial resolve of the constraint call. Note that this // will not necessarily resolve the call exactly, since we might be compiling // shared generic code - it may just resolve it to a candidate suitable for // JIT compilation, and require a runtime lookup for the actual code pointer // to call. MethodDesc directMethod = constrainedType.GetClosestMetadataType().TryResolveConstraintMethodApprox(exactType, method, out forceUseRuntimeLookup); if (directMethod != null) { // Either // 1. no constraint resolution at compile time (!directMethod) // OR 2. no code sharing lookup in call // OR 3. we have have resolved to an instantiating stub methodAfterConstraintResolution = directMethod; Debug.Assert(!methodAfterConstraintResolution.OwningType.IsInterface); resolvedConstraint = true; pResult.thisTransform = CORINFO_THIS_TRANSFORM.CORINFO_NO_THIS_TRANSFORM; exactType = constrainedType; } else if (constrainedType.IsValueType) { pResult.thisTransform = CORINFO_THIS_TRANSFORM.CORINFO_BOX_THIS; } else { pResult.thisTransform = CORINFO_THIS_TRANSFORM.CORINFO_DEREF_THIS; } } MethodDesc targetMethod = methodAfterConstraintResolution; // // Determine whether to perform direct call // bool directCall = false; bool resolvedCallVirt = false; if ((flags & CORINFO_CALLINFO_FLAGS.CORINFO_CALLINFO_LDFTN) != 0) { // Delegate targets are always treated as direct calls here. (It would be nice to clean it up...). directCall = true; } else if (targetMethod.Signature.IsStatic) { // Static methods are always direct calls directCall = true; } else if (targetMethod.OwningType.IsInterface) { // Force all interface calls to be interpreted as if they are virtual. directCall = false; } else if ((flags & CORINFO_CALLINFO_FLAGS.CORINFO_CALLINFO_CALLVIRT) == 0 || resolvedConstraint) { directCall = true; } else { if (!targetMethod.IsVirtual || targetMethod.IsFinal || targetMethod.OwningType.GetClosestMetadataType().IsSealed) { resolvedCallVirt = true; directCall = true; } } // TODO: Interface methods if (targetMethod.IsVirtual && targetMethod.OwningType.IsInterface) throw new NotImplementedException("Interface method"); pResult.hMethod = ObjectToHandle(targetMethod); pResult.methodFlags = getMethodAttribsInternal(targetMethod); pResult.classFlags = getClassAttribsInternal(targetMethod.OwningType); Get_CORINFO_SIG_INFO(targetMethod.Signature, out pResult.sig); // Get the required verification information in case it is needed by the verifier later if (pResult.hMethod != pResolvedToken.hMethod) { pResult.verMethodFlags = getMethodAttribsInternal(targetMethod); Get_CORINFO_SIG_INFO(targetMethod.Signature, out pResult.verSig); } else { pResult.verMethodFlags = pResult.methodFlags; pResult.verSig = pResult.sig; } pResult.accessAllowed = CorInfoIsAccessAllowedResult.CORINFO_ACCESS_ALLOWED; pResult.kind = CORINFO_CALL_KIND.CORINFO_CALL; pResult._nullInstanceCheck = (uint)(((flags & CORINFO_CALLINFO_FLAGS.CORINFO_CALLINFO_CALLVIRT) != 0) ? 1 : 0); // TODO: Generics // pResult.contextHandle; // pResult._exactContextNeedsRuntimeLookup // TODO: CORINFO_VIRTUALCALL_STUB // TODO: CORINFO_CALL_CODE_POINTER pResult.codePointerOrStubLookup.constLookup.accessType = InfoAccessType.IAT_VALUE; if (!directCall) { pResult.codePointerOrStubLookup.constLookup.addr = (void*)ObjectToHandle(_compilation.NodeFactory.ReadyToRunHelper(ReadyToRunHelperId.VirtualCall, targetMethod)); if ((pResult.methodFlags & (uint)CorInfoFlag.CORINFO_FLG_DELEGATE_INVOKE) != 0) { pResult._nullInstanceCheck = 1; } } else { if (targetMethod.IsConstructor && targetMethod.OwningType.IsString) { // Calling a string constructor doesn't call the actual constructor. targetMethod = IntrinsicMethods.GetStringInitializer(targetMethod); } pResult.codePointerOrStubLookup.constLookup.addr = (void*)ObjectToHandle(_compilation.NodeFactory.MethodEntrypoint(targetMethod)); pResult.nullInstanceCheck = resolvedCallVirt; } // TODO: Generics // pResult.instParamLookup }
private void getCallInfo(ref CORINFO_RESOLVED_TOKEN pResolvedToken, CORINFO_RESOLVED_TOKEN* pConstrainedResolvedToken, CORINFO_METHOD_STRUCT_* callerHandle, CORINFO_CALLINFO_FLAGS flags, ref CORINFO_CALL_INFO pResult) { #if DEBUG // In debug, write some bogus data to the struct to ensure we have filled everything // properly. fixed (CORINFO_CALL_INFO* tmp = &pResult) MemoryHelper.FillMemory((byte*)tmp, 0xcc, Marshal.SizeOf<CORINFO_CALL_INFO>()); #endif MethodDesc method = HandleToObject(pResolvedToken.hMethod); // Spec says that a callvirt lookup ignores static methods. Since static methods // can't have the exact same signature as instance methods, a lookup that found // a static method would have never found an instance method. if (method.Signature.IsStatic && (flags & CORINFO_CALLINFO_FLAGS.CORINFO_CALLINFO_CALLVIRT) != 0) { throw new BadImageFormatException(); } TypeDesc exactType = HandleToObject(pResolvedToken.hClass); TypeDesc constrainedType = null; if ((flags & CORINFO_CALLINFO_FLAGS.CORINFO_CALLINFO_CALLVIRT) != 0 && pConstrainedResolvedToken != null) { constrainedType = HandleToObject(pConstrainedResolvedToken->hClass); } bool resolvedConstraint = false; bool forceUseRuntimeLookup = false; MethodDesc methodAfterConstraintResolution = method; if (constrainedType == null) { pResult.thisTransform = CORINFO_THIS_TRANSFORM.CORINFO_NO_THIS_TRANSFORM; } else { // We have a "constrained." call. Try a partial resolve of the constraint call. Note that this // will not necessarily resolve the call exactly, since we might be compiling // shared generic code - it may just resolve it to a candidate suitable for // JIT compilation, and require a runtime lookup for the actual code pointer // to call. MethodDesc directMethod = constrainedType.GetClosestDefType().TryResolveConstraintMethodApprox(exactType, method, out forceUseRuntimeLookup); if (directMethod != null) { // Either // 1. no constraint resolution at compile time (!directMethod) // OR 2. no code sharing lookup in call // OR 3. we have have resolved to an instantiating stub methodAfterConstraintResolution = directMethod; Debug.Assert(!methodAfterConstraintResolution.OwningType.IsInterface); resolvedConstraint = true; pResult.thisTransform = CORINFO_THIS_TRANSFORM.CORINFO_NO_THIS_TRANSFORM; exactType = constrainedType; } else if (constrainedType.IsValueType) { pResult.thisTransform = CORINFO_THIS_TRANSFORM.CORINFO_BOX_THIS; } else { pResult.thisTransform = CORINFO_THIS_TRANSFORM.CORINFO_DEREF_THIS; } } MethodDesc targetMethod = methodAfterConstraintResolution; // // Initialize callee context used for inlining and instantiation arguments // if (targetMethod.HasInstantiation) { pResult.contextHandle = contextFromMethod(targetMethod); pResult.exactContextNeedsRuntimeLookup = targetMethod.IsSharedByGenericInstantiations; } else { pResult.contextHandle = contextFromType(exactType); pResult.exactContextNeedsRuntimeLookup = exactType.IsCanonicalSubtype(CanonicalFormKind.Any); } // // Determine whether to perform direct call // bool directCall = false; bool resolvedCallVirt = false; if (targetMethod.Signature.IsStatic) { // Static methods are always direct calls directCall = true; } else if (targetMethod.OwningType.IsInterface) { // Force all interface calls to be interpreted as if they are virtual. directCall = false; } else if ((flags & CORINFO_CALLINFO_FLAGS.CORINFO_CALLINFO_CALLVIRT) == 0 || resolvedConstraint) { directCall = true; } else { if (!targetMethod.IsVirtual || targetMethod.IsFinal || targetMethod.OwningType.IsSealed()) { resolvedCallVirt = true; directCall = true; } } pResult.codePointerOrStubLookup.lookupKind.needsRuntimeLookup = false; if (directCall) { bool allowInstParam = (flags & CORINFO_CALLINFO_FLAGS.CORINFO_CALLINFO_ALLOWINSTPARAM) != 0; Debug.Assert(allowInstParam || !targetMethod.RequiresInstArg(), "Need an instantiating stub"); MethodDesc concreteMethod = targetMethod; targetMethod = targetMethod.GetCanonMethodTarget(CanonicalFormKind.Specific); MethodDesc directMethod = targetMethod; if (targetMethod.IsConstructor && targetMethod.OwningType.IsString) { // Calling a string constructor doesn't call the actual constructor. directMethod = targetMethod.GetStringInitializer(); concreteMethod = directMethod; } pResult.kind = CORINFO_CALL_KIND.CORINFO_CALL; pResult.codePointerOrStubLookup.constLookup.accessType = InfoAccessType.IAT_VALUE; if (pResult.exactContextNeedsRuntimeLookup) { // Nothing to do... The generic handle lookup gets embedded in to the codegen // during the jitting of the call. // (Note: The generic lookup in R2R is performed by a call to a helper at runtime, not by // codegen emitted at crossgen time) MethodDesc contextMethod = methodFromContext(pResolvedToken.tokenContext); // Do not bother capturing the runtime determined context if we're inlining. The JIT is going // to abort the inlining attempt if the inlinee does any generic lookups. bool inlining = contextMethod != MethodBeingCompiled; if (directMethod.IsSharedByGenericInstantiations && !inlining) { MethodDesc runtimeDeterminedMethod = (MethodDesc)GetRuntimeDeterminedObjectForToken(ref pResolvedToken); pResult.codePointerOrStubLookup.constLookup.addr = (void*)ObjectToHandle( _compilation.NodeFactory.RuntimeDeterminedMethod(runtimeDeterminedMethod)); } else { pResult.codePointerOrStubLookup.constLookup.addr = (void*)ObjectToHandle( _compilation.NodeFactory.MethodEntrypoint(directMethod)); } } else { ISymbolNode genericDictionary = null; if (targetMethod.RequiresInstMethodDescArg()) { genericDictionary = _compilation.NodeFactory.MethodGenericDictionary(concreteMethod); } else if (targetMethod.RequiresInstMethodTableArg()) { genericDictionary = _compilation.NodeFactory.TypeGenericDictionary(concreteMethod.OwningType); } if (genericDictionary != null) { pResult.instParamLookup.accessType = InfoAccessType.IAT_VALUE; pResult.instParamLookup.addr = (void*)ObjectToHandle(genericDictionary); pResult.codePointerOrStubLookup.constLookup.addr = (void*)ObjectToHandle( _compilation.NodeFactory.ShadowConcreteMethod(concreteMethod)); } else if (targetMethod.AcquiresInstMethodTableFromThis()) { pResult.codePointerOrStubLookup.constLookup.addr = (void*)ObjectToHandle( _compilation.NodeFactory.ShadowConcreteMethod(concreteMethod)); } else { pResult.codePointerOrStubLookup.constLookup.addr = (void*)ObjectToHandle( _compilation.NodeFactory.MethodEntrypoint(directMethod)); } } pResult.nullInstanceCheck = resolvedCallVirt; } else if ((flags & CORINFO_CALLINFO_FLAGS.CORINFO_CALLINFO_LDFTN) != 0) { pResult.kind = CORINFO_CALL_KIND.CORINFO_VIRTUALCALL_LDVIRTFTN; pResult.codePointerOrStubLookup.constLookup.accessType = InfoAccessType.IAT_VALUE; pResult.codePointerOrStubLookup.constLookup.addr = (void*)ObjectToHandle(_compilation.NodeFactory.ReadyToRunHelper(ReadyToRunHelperId.ResolveVirtualFunction, targetMethod)); // The current CoreRT ReadyToRun helpers do not handle null thisptr - ask the JIT to emit explicit null checks // TODO: Optimize this pResult.nullInstanceCheck = true; } else { // CORINFO_CALL_CODE_POINTER tells the JIT that this is indirect // call that should not be inlined. pResult.kind = CORINFO_CALL_KIND.CORINFO_CALL_CODE_POINTER; if (pResult.exactContextNeedsRuntimeLookup) { pResult.codePointerOrStubLookup.lookupKind.needsRuntimeLookup = true; pResult.codePointerOrStubLookup.lookupKind.runtimeLookupFlags = 0; pResult.codePointerOrStubLookup.runtimeLookup.indirections = CORINFO.USEHELPER; // Do not bother computing the runtime lookup if we are inlining. The JIT is going // to abort the inlining attempt anyway. MethodDesc contextMethod = methodFromContext(pResolvedToken.tokenContext); if (contextMethod != MethodBeingCompiled) { return; } pResult.codePointerOrStubLookup.lookupKind.runtimeLookupKind = GetGenericRuntimeLookupKind(contextMethod); pResult.codePointerOrStubLookup.lookupKind.runtimeLookupFlags = (ushort)ReadyToRunFixupKind.VirtualEntry; } else { pResult.codePointerOrStubLookup.constLookup.accessType = InfoAccessType.IAT_VALUE; pResult.codePointerOrStubLookup.constLookup.addr = (void*)ObjectToHandle(_compilation.NodeFactory.ReadyToRunHelper(ReadyToRunHelperId.VirtualCall, targetMethod)); } // The current CoreRT ReadyToRun helpers do not handle null thisptr - ask the JIT to emit explicit null checks // TODO: Optimize this pResult.nullInstanceCheck = true; } pResult.hMethod = ObjectToHandle(targetMethod); pResult.accessAllowed = CorInfoIsAccessAllowedResult.CORINFO_ACCESS_ALLOWED; // We're pretty much done at this point. Let's grab the rest of the information that the jit is going to // need. pResult.classFlags = getClassAttribsInternal(targetMethod.OwningType); pResult.methodFlags = getMethodAttribsInternal(targetMethod); Get_CORINFO_SIG_INFO(targetMethod, out pResult.sig); if ((flags & CORINFO_CALLINFO_FLAGS.CORINFO_CALLINFO_VERIFICATION) != 0) { if (pResult.hMethod != pResolvedToken.hMethod) { pResult.verMethodFlags = getMethodAttribsInternal(targetMethod); Get_CORINFO_SIG_INFO(targetMethod, out pResult.verSig); } else { pResult.verMethodFlags = pResult.methodFlags; pResult.verSig = pResult.sig; } } pResult._secureDelegateInvoke = 0; }
private void getCallInfo(ref CORINFO_RESOLVED_TOKEN pResolvedToken, CORINFO_RESOLVED_TOKEN* pConstrainedResolvedToken, CORINFO_METHOD_STRUCT_* callerHandle, CORINFO_CALLINFO_FLAGS flags, ref CORINFO_CALL_INFO pResult) { #if DEBUG // In debug, write some bogus data to the struct to ensure we have filled everything // properly. fixed (CORINFO_CALL_INFO* tmp = &pResult) MemoryHelper.FillMemory((byte*)tmp, 0xcc, Marshal.SizeOf<CORINFO_CALL_INFO>()); #endif MethodDesc method = HandleToObject(pResolvedToken.hMethod); // Spec says that a callvirt lookup ignores static methods. Since static methods // can't have the exact same signature as instance methods, a lookup that found // a static method would have never found an instance method. if (method.Signature.IsStatic && (flags & CORINFO_CALLINFO_FLAGS.CORINFO_CALLINFO_CALLVIRT) != 0) { throw new BadImageFormatException(); } TypeDesc exactType = HandleToObject(pResolvedToken.hClass); TypeDesc constrainedType = null; if ((flags & CORINFO_CALLINFO_FLAGS.CORINFO_CALLINFO_CALLVIRT) != 0 && pConstrainedResolvedToken != null) { constrainedType = HandleToObject(pConstrainedResolvedToken->hClass); } bool resolvedConstraint = false; bool forceUseRuntimeLookup = false; MethodDesc methodAfterConstraintResolution = method; if (constrainedType == null) { pResult.thisTransform = CORINFO_THIS_TRANSFORM.CORINFO_NO_THIS_TRANSFORM; } else { // We have a "constrained." call. Try a partial resolve of the constraint call. Note that this // will not necessarily resolve the call exactly, since we might be compiling // shared generic code - it may just resolve it to a candidate suitable for // JIT compilation, and require a runtime lookup for the actual code pointer // to call. MethodDesc directMethod = constrainedType.GetClosestDefType().TryResolveConstraintMethodApprox(exactType, method, out forceUseRuntimeLookup); if (directMethod != null) { // Either // 1. no constraint resolution at compile time (!directMethod) // OR 2. no code sharing lookup in call // OR 3. we have have resolved to an instantiating stub methodAfterConstraintResolution = directMethod; Debug.Assert(!methodAfterConstraintResolution.OwningType.IsInterface); resolvedConstraint = true; pResult.thisTransform = CORINFO_THIS_TRANSFORM.CORINFO_NO_THIS_TRANSFORM; exactType = constrainedType; } else if (constrainedType.IsValueType) { pResult.thisTransform = CORINFO_THIS_TRANSFORM.CORINFO_BOX_THIS; } else { pResult.thisTransform = CORINFO_THIS_TRANSFORM.CORINFO_DEREF_THIS; } } MethodDesc targetMethod = methodAfterConstraintResolution; // TODO: Support Generics if (targetMethod.HasInstantiation) { pResult.contextHandle = contextFromMethod(targetMethod); } else { pResult.contextHandle = contextFromType(targetMethod.OwningType); } pResult._exactContextNeedsRuntimeLookup = 0; // // Determine whether to perform direct call // bool directCall = false; bool resolvedCallVirt = false; if (targetMethod.Signature.IsStatic) { // Static methods are always direct calls directCall = true; } else if (targetMethod.OwningType.IsInterface) { // Force all interface calls to be interpreted as if they are virtual. directCall = false; } else if ((flags & CORINFO_CALLINFO_FLAGS.CORINFO_CALLINFO_CALLVIRT) == 0 || resolvedConstraint) { directCall = true; } else { if (!targetMethod.IsVirtual || targetMethod.IsFinal || targetMethod.OwningType.IsSealed()) { resolvedCallVirt = true; directCall = true; } } pResult.codePointerOrStubLookup.lookupKind.needsRuntimeLookup = false; if (directCall) { MethodDesc directMethod = targetMethod; if (targetMethod.IsConstructor && targetMethod.OwningType.IsString) { // Calling a string constructor doesn't call the actual constructor. directMethod = targetMethod.GetStringInitializer(); } pResult.kind = CORINFO_CALL_KIND.CORINFO_CALL; pResult.codePointerOrStubLookup.constLookup.accessType = InfoAccessType.IAT_VALUE; pResult.codePointerOrStubLookup.constLookup.addr = (void*)ObjectToHandle(_compilation.NodeFactory.MethodEntrypoint(directMethod)); pResult.nullInstanceCheck = resolvedCallVirt; } else if ((flags & CORINFO_CALLINFO_FLAGS.CORINFO_CALLINFO_LDFTN) != 0) { pResult.kind = CORINFO_CALL_KIND.CORINFO_VIRTUALCALL_LDVIRTFTN; pResult.codePointerOrStubLookup.constLookup.accessType = InfoAccessType.IAT_VALUE; pResult.codePointerOrStubLookup.constLookup.addr = (void*)ObjectToHandle(_compilation.NodeFactory.ReadyToRunHelper(ReadyToRunHelperId.ResolveVirtualFunction, targetMethod)); // The current CoreRT ReadyToRun helpers do not handle null thisptr - ask the JIT to emit explicit null checks // TODO: Optimize this pResult.nullInstanceCheck = true; } else { // CORINFO_CALL_CODE_POINTER tells the JIT that this is indirect // call that should not be inlined. pResult.kind = CORINFO_CALL_KIND.CORINFO_CALL_CODE_POINTER; pResult.codePointerOrStubLookup.constLookup.accessType = InfoAccessType.IAT_VALUE; var readyToRunHelper = targetMethod.OwningType.IsInterface ? ReadyToRunHelperId.InterfaceDispatch : ReadyToRunHelperId.VirtualCall; pResult.codePointerOrStubLookup.constLookup.addr = (void*)ObjectToHandle(_compilation.NodeFactory.ReadyToRunHelper(readyToRunHelper, targetMethod)); // The current CoreRT ReadyToRun helpers do not handle null thisptr - ask the JIT to emit explicit null checks // TODO: Optimize this pResult.nullInstanceCheck = true; } pResult.hMethod = ObjectToHandle(targetMethod); pResult.accessAllowed = CorInfoIsAccessAllowedResult.CORINFO_ACCESS_ALLOWED; // We're pretty much done at this point. Let's grab the rest of the information that the jit is going to // need. pResult.classFlags = getClassAttribsInternal(targetMethod.OwningType); pResult.methodFlags = getMethodAttribsInternal(targetMethod); Get_CORINFO_SIG_INFO(targetMethod.Signature, out pResult.sig); if ((flags & CORINFO_CALLINFO_FLAGS.CORINFO_CALLINFO_VERIFICATION) != 0) { if (pResult.hMethod != pResolvedToken.hMethod) { pResult.verMethodFlags = getMethodAttribsInternal(targetMethod); Get_CORINFO_SIG_INFO(targetMethod.Signature, out pResult.verSig); } else { pResult.verMethodFlags = pResult.methodFlags; pResult.verSig = pResult.sig; } } // TODO: Generics // pResult.instParamLookup }
public virtual void getCallInfo_wrapper(IntPtr _this, out IntPtr exception, ref CORINFO_RESOLVED_TOKEN pResolvedToken, CORINFO_RESOLVED_TOKEN* pConstrainedResolvedToken, CORINFO_METHOD_STRUCT_* callerHandle, CORINFO_CALLINFO_FLAGS flags, ref CORINFO_CALL_INFO pResult) { exception = IntPtr.Zero; try { getCallInfo(ref pResolvedToken, pConstrainedResolvedToken, callerHandle, flags, ref pResult); return; } catch (Exception ex) { exception = AllocException(ex); } }
static void _getCallInfo(IntPtr thisHandle, IntPtr* ppException, ref CORINFO_RESOLVED_TOKEN pResolvedToken, CORINFO_RESOLVED_TOKEN* pConstrainedResolvedToken, CORINFO_METHOD_STRUCT_* callerHandle, CORINFO_CALLINFO_FLAGS flags, ref CORINFO_CALL_INFO pResult) { var _this = GetThis(thisHandle); try { _this.getCallInfo(ref pResolvedToken, pConstrainedResolvedToken, callerHandle, flags, ref pResult); } catch (Exception ex) { *ppException = _this.AllocException(ex); } }