Почему инструкция вызова приводит к дестабилизации времени выполнения? - PullRequest
1 голос
/ 20 ноября 2010

Следующий код выдает исключение, которое говорит о том, что оно может дестабилизировать среду выполнения.

var method = new DynamicMethod("CallObjectToString", typeof(string),new[]{ typeof(object)});
var ilgen =method.GetILGenerator();
ilgen.Emit(OpCodes.Ldarg_0);
ilgen.Emit(OpCodes.Call, typeof(object).GetMethod("ToString"));
ilgen.Emit(OpCodes.Ret);
var @delegate = method.CreateDelegate(typeof(Func<object, string>)) as Func<object,string>;
@delegate(new object());

Изменение Opcodes.Call на Opcodes.CallVirt решает проблему.Это все хорошо, но я могу использовать typebuilder для создания динамического типа, который имеет статический метод, построенный с использованием (MethodBuilder), который имеет точно того же IL, затем использовать CreateDelegate, и он не вызовет это исключение,Фактически, используя methodbuilder, вы можете заставить что-то вызывать виртуальные методы совершенно другого объекта, даже не того, от которого этот тип наследует.

Еще более удивительно, что если я сделаю что-то вроде new object().ToString(), это такжевыдает инструкцию call в IL, а не callvirt.

Если незаконно вызывать виртуальный метод, используя call, за исключением случая вызова базового метода в переопределении экземпляра,тогда почему CLR позволяет делать такой метод статически для другого типа?Или разрешить отправку call инструкций для известных ненулевых объектов?

Или это проблема только для кода, сгенерированного с помощью DynamicMethod?

Редактировать: я не спрашиваю ус точки зрения производительности, мне действительно все равно.Приведенный ниже код, хотя и немного более сложный, создает делегата, который выполняет дестабилизирующее действие без дестабилизации среды выполнения.Это весь вопрос.Почему один законный, а другой незаконный?

var assemblyBuilder = AppDomain.CurrentDomain.DefineDynamicAssembly(new AssemblyName("SomeAssembly"), AssemblyBuilderAccess.Run) ;
var moduleBuilder = assemblyBuilder.DefineDynamicModule("SomeModule");
var typeBuilder = moduleBuilder.DefineType("SomeType");
var methodBuilder = typeBuilder.DefineMethod("SomeStaticMethod",MethodAttributes.Public | MethodAttributes.Static,typeof(string),new[]{typeof(object)});
var ilgen = methodBuilder.GetILGenerator();
ilgen.Emit(OpCodes.Ldarg_0);
ilgen.Emit(OpCodes.Call, typeof(object).GetMethod("ToString"));
ilgen.Emit(OpCodes.Ret);
var newType = typeBuilder.CreateType();
var @delegate = Delegate.CreateDelegate(typeof(Func<object, string>),newType.GetMethod("SomeStaticMethod")) as Func<object,string>;
@delegate(new object());

1 Ответ

1 голос
/ 20 ноября 2010

Он пытается защитить вас от неправильных действий. Вызов здесь неправильный, потому что конкретный тип объекта не может быть известен. Компилятор, который юридически знает, что объект не может быть ничем иным, кроме того, что он видит (например, он видит запечатанный класс или просто создает экземпляр объекта), может сойти с рук, потому что он знает, что это безопасно, но в вашем случае все, что вы видите «объект» передается в качестве аргумента, поэтому это преобразование небезопасно. В случае «new object (). ToString ()» тип объекта абсолютно точно известен как object, поэтому нет необходимости в виртуальном вызове, однако в вашем случае объект входит в качестве аргумента, так что у вас нет абсолютно никакой возможности узнать, каков его конкретный тип. Вы не должны беспокоиться слишком много, хотя. JITer может знать даже лучше, чем вы, когда дело доходит до JIT этого кода, и может в любом случае превратить callvirt в прямой вызов, но с другой стороны он может сделать обратное и принудительно вызвать вызов в косвенный вызов, так что на самом деле очень не о чем беспокоиться.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...