Example #1
0
        private static bool DoStubCall(int callerIndex, bool staticMethod, bool onValueType, GenericShape typeGenericShape, GenericShape methodGenericShape)
        {
            string        callerNameSeed = Config.InstantiatingStubPrefix + "Caller" + callerIndex; // Use a consistent seed value here so that the various various of unboxing/instantiating stubs are generated with the same arg shape
            string        callerName     = callerNameSeed + (staticMethod ? "Static" : "Instance") + (onValueType ? "Class" : "ValueType") + typeGenericShape.ToString() + methodGenericShape.ToString();
            Random        rand           = new Random(GetSeed(callerNameSeed));
            List <TypeEx> pms;

            do
            {
                pms = RandomParameters(s_allTypes, rand);
            } while (pms.Count > 16);

            Type delegateType = GetDelegateType(pms, typeof(int));

            Callee callee = new Callee(callerName + "Callee", pms);// CreateCallee(Config.PInvokeePrefix + calleeIndex, s_allTypes);

            callee.Emit();

            Delegate calleeDelegate = callee.Method.CreateDelegate(delegateType);

            int newStubCount = Interlocked.Increment(ref s_stubTypesCreated);

            if ((s_stubTypesModule == null) || (newStubCount % 1000) == 0)
            {
                AssemblyBuilder stubsAssembly = AssemblyBuilder.DefineDynamicAssembly(new AssemblyName("ABIStress_Stubs" + newStubCount), AssemblyBuilderAccess.RunAndCollect);
                s_stubTypesModule = stubsAssembly.DefineDynamicModule("ABIStress_Stubs" + newStubCount);
            }

            // This code is based on DelegateHelpers.cs in System.Linq.Expressions.Compiler
            TypeBuilder tb =
                s_stubTypesModule.DefineType(
                    $"{callerName}_GenericTarget",
                    TypeAttributes.Class | TypeAttributes.Public | TypeAttributes.Sealed | TypeAttributes.AutoClass,
                    onValueType ? typeof(object) : typeof(ValueType));

            GenericTypeParameterBuilder[] typeParamsType = null;
            if (typeGenericShape != GenericShape.NotGeneric)
            {
                typeParamsType = tb.DefineGenericParameters(new string[] { "T" });
            }

            FieldInfo fieldDeclaration = tb.DefineField("MagicValue", typeof(int), FieldAttributes.Public);

            Type      typeofInstantiatedType;
            FieldInfo fieldInfoMagicValueField;

            if (typeGenericShape == GenericShape.NotGeneric)
            {
                typeofInstantiatedType   = tb;
                fieldInfoMagicValueField = fieldDeclaration;
            }
            else
            {
                typeofInstantiatedType   = tb.MakeGenericType(typeParamsType[0]);
                fieldInfoMagicValueField = TypeBuilder.GetField(typeofInstantiatedType, fieldDeclaration);
            }

            ConstructorBuilder cb = tb.DefineConstructor(
                MethodAttributes.Public | MethodAttributes.HideBySig | MethodAttributes.RTSpecialName,
                CallingConventions.Standard,
                new Type[] { typeof(int) });

            cb.SetImplementationFlags(MethodImplAttributes.Managed);

            ILGenerator g = cb.GetILGenerator();

            g.Emit(OpCodes.Ldarg, 0);
            g.Emit(OpCodes.Call, typeof(object).GetConstructor(Array.Empty <Type>()));
            g.Emit(OpCodes.Ldarg, 0);
            g.Emit(OpCodes.Ldarg_1);
            g.Emit(OpCodes.Stfld, fieldInfoMagicValueField);
            g.Emit(OpCodes.Ret);

            MethodAttributes methodAttributes = MethodAttributes.Public | MethodAttributes.HideBySig;

            if (staticMethod)
            {
                methodAttributes |= MethodAttributes.Static;
            }

            MethodBuilder mbInstance = tb.DefineMethod(
                "Method",
                methodAttributes,
                callee.Method.ReturnType,
                callee.Parameters.Select(t => t.Type).ToArray());

            GenericTypeParameterBuilder[] typeParamsMethod = null;
            if (methodGenericShape != GenericShape.NotGeneric)
            {
                typeParamsMethod = mbInstance.DefineGenericParameters(new string[] { "T" });
            }

            mbInstance.SetImplementationFlags(MethodImplAttributes.Managed);

            int magicNumberEmbeddedInObject = rand.Next();

            GCHandle gchCallee       = GCHandle.Alloc(callee.Method.CreateDelegate(delegateType));
            IntPtr   gchCalleeIntPtr = GCHandle.ToIntPtr(gchCallee);

            g = mbInstance.GetILGenerator();

            if (!staticMethod)
            {
                // Verify random number made it intact, and this parameter was handled correctly

                g.Emit(OpCodes.Ldarg_0);
                g.Emit(OpCodes.Ldfld, fieldInfoMagicValueField);
                g.Emit(OpCodes.Ldc_I4, magicNumberEmbeddedInObject);
                g.Emit(OpCodes.Call, s_compareNumbers);
            }

            // Verify generic args are as expected
            EmitTypeHandleCheck(g, typeGenericShape, typeParamsType, "type");
            EmitTypeHandleCheck(g, methodGenericShape, typeParamsMethod, "method");

            // Make the call to callee
            LocalBuilder gcHandleLocal = g.DeclareLocal(typeof(GCHandle));

            // Load GCHandle of callee delegate
            g.Emit(OpCodes.Ldc_I8, (long)gchCalleeIntPtr);
            g.Emit(OpCodes.Conv_I);
            g.Emit(OpCodes.Call, s_gcHandleFromIntPtr);
            g.Emit(OpCodes.Stloc, gcHandleLocal);
            // Resolve to target
            g.Emit(OpCodes.Ldloca, gcHandleLocal);
            g.Emit(OpCodes.Call, s_gcHandle_getTarget);
            // Cast to delegate type
            g.Emit(OpCodes.Castclass, delegateType);
            // Load all args
            int argOffset = 1;

            if (staticMethod)
            {
                argOffset = 0;
            }
            for (int i = 0; i < pms.Count; i++)
            {
                g.Emit(OpCodes.Ldarg, argOffset + i);
            }

            // Call delegate invoke method
            g.Emit(OpCodes.Callvirt, delegateType.GetMethod("Invoke"));
            // ret
            g.Emit(OpCodes.Ret);
            Type calleeTypeOpen = tb.CreateType();
            Type calleeType;

            switch (typeGenericShape)
            {
            case GenericShape.NotGeneric:
                calleeType = calleeTypeOpen;
                break;

            case GenericShape.GenericOverReferenceType:
                calleeType = calleeTypeOpen.MakeGenericType(typeof(object));
                break;

            case GenericShape.GenericOverValueType:
                calleeType = calleeTypeOpen.MakeGenericType(typeof(int));
                break;

            default:
                throw new Exception("Unknown case");
            }

            MethodInfo targetMethodOpen = calleeType.GetMethod("Method");
            MethodInfo targetMethod;

            switch (methodGenericShape)
            {
            case GenericShape.NotGeneric:
                targetMethod = targetMethodOpen;
                break;

            case GenericShape.GenericOverReferenceType:
                targetMethod = targetMethodOpen.MakeGenericMethod(typeof(object));
                break;

            case GenericShape.GenericOverValueType:
                targetMethod = targetMethodOpen.MakeGenericMethod(typeof(int));
                break;

            default:
                throw new Exception("Unknown case");
            }

            Delegate targetMethodToCallDel;

            if (staticMethod)
            {
                targetMethodToCallDel = targetMethod.CreateDelegate(delegateType);
            }
            else
            {
                targetMethodToCallDel = targetMethod.CreateDelegate(delegateType, Activator.CreateInstance(calleeType, magicNumberEmbeddedInObject));
            }

            GCHandle gchTargetMethod = GCHandle.Alloc(targetMethodToCallDel);

            // CALLER Dynamic method
            DynamicMethod caller = new DynamicMethod(
                callerName, typeof(int), pms.Select(t => t.Type).ToArray(), typeof(Program).Module);

            g = caller.GetILGenerator();

            // Create the args to pass to the callee from the caller.
            List <Value> args = GenCallerToCalleeArgs(pms, callee.Parameters, rand);

            if (Config.Verbose)
            {
                EmitDumpValues("Caller's incoming args", g, pms.Select((p, i) => new ArgValue(p, i)));
            }

            if (Config.Verbose)
            {
                EmitDumpValues($"Caller's args to {callerName} call", g, args);
            }

            gcHandleLocal = g.DeclareLocal(typeof(GCHandle));
            g.Emit(OpCodes.Ldc_I8, (long)GCHandle.ToIntPtr(gchTargetMethod));
            g.Emit(OpCodes.Conv_I);
            g.Emit(OpCodes.Call, s_gcHandleFromIntPtr);
            g.Emit(OpCodes.Stloc, gcHandleLocal);
            // Resolve to target
            g.Emit(OpCodes.Ldloca, gcHandleLocal);
            g.Emit(OpCodes.Call, s_gcHandle_getTarget);
            // Cast to delegate type
            g.Emit(OpCodes.Castclass, delegateType);

            foreach (Value v in args)
            {
                v.Emit(g);
            }

            // Call delegate invoke method
            g.Emit(OpCodes.Callvirt, delegateType.GetMethod("Invoke"));
            // ret
            g.Emit(OpCodes.Ret);

            (object callerResult, object calleeResult) = InvokeCallerCallee(caller, pms, callee.Method, args, rand);

            gchCallee.Free();
            gchTargetMethod.Free();
            if (callerResult.Equals(calleeResult))
            {
                return(true);
            }

            Console.WriteLine("Mismatch in stub call: expected {0}, got {1}", calleeResult, callerResult);
            Console.WriteLine(callerName);
            WriteSignature(caller);
            WriteSignature(callee.Method);
            return(false);
        }
Example #2
0
        private static bool DoTailCall(int callerIndex)
        {
            // We pregenerate tail callee parameter lists because we want to be able to select
            // a callee with less arg stack space than this caller.
            if (s_tailCallees == null)
            {
                s_tailCallees =
                    Enumerable.Range(0, Config.NumCallees)
                    .Select(i => CreateCallee(Config.TailCalleePrefix + i, s_tailCalleeCandidateArgTypes))
                    .ToList();
            }

            string        callerName = Config.TailCallerPrefix + callerIndex;
            Random        rand       = new Random(GetSeed(callerName));
            List <TypeEx> callerParams;
            List <Callee> callable;

            do
            {
                callerParams = RandomParameters(s_tailCalleeCandidateArgTypes, rand);
                int argStackSizeApprox = s_abi.ApproximateArgStackAreaSize(callerParams);
                callable = s_tailCallees.Where(t => t.ArgStackSizeApprox < argStackSizeApprox).ToList();
            } while (callable.Count <= 0);

            int    calleeIndex = rand.Next(callable.Count);
            Callee callee      = callable[calleeIndex];

            callee.Emit();

            DynamicMethod caller = new DynamicMethod(
                callerName, typeof(int), callerParams.Select(t => t.Type).ToArray(), typeof(Program).Module);

            ILGenerator g = caller.GetILGenerator();

            // Create the args to pass to the callee from the caller.
            List <Value> args = GenCallerToCalleeArgs(callerParams, callee.Parameters, rand);

            if (Config.Verbose)
            {
                EmitDumpValues("Caller's incoming args", g, callerParams.Select((p, i) => new ArgValue(p, i)));
                EmitDumpValues("Caller's args to tailcall", g, args);
            }

            foreach (Value v in args)
            {
                v.Emit(g);
            }

            g.Emit(OpCodes.Tailcall);
            g.EmitCall(OpCodes.Call, callee.Method, null);
            g.Emit(OpCodes.Ret);

            (object callerResult, object calleeResult) = InvokeCallerCallee(caller, callerParams, callee.Method, args, rand);

            if (callerResult.Equals(calleeResult))
            {
                return(true);
            }

            Console.WriteLine("Mismatch in tailcall: expected {0}, got {1}", calleeResult, callerResult);
            WriteSignature(caller);
            WriteSignature(callee.Method);
            return(false);
        }