에 필요한 MethodInfo 및 인스턴스 유형을 제공 OpCodes.Callvirt와 OpCodes.Constrained을 방출하는 방법 il : ILGenerator
함수에 글로벌과 exp
는 형식 검사 구문 분석 된 언어를 나타내는 판별 조합입니다 emit : Map<string,LocalBuilder> -> exp -> unit
대소 문자가 InstanceCall of exp * MethodInfo * exp list * Type
이고 Type
인 경우는 표현식의 유형을 나타내는 exp
의 속성입니다.내가 재귀 함수를 가지고 손
다음 코드에서 instance.Type
이 ValueType
일 수도 있고 아닐 수도있는 인스턴스 호출에 대해 IL 연산 코드를 내보내려고합니다. 그래서 OpCodes.Constrained
을 사용하여 참조, 값 및 열거 형에 대해 유연하고 효율적으로 가상 호출을 수행 할 수 있음을 알고 있습니다. Reflection에 익숙하지 않습니다. 일반적으로 기계 언어와 언어를 삭제하면 OpCodes.Constrained
에 대한 링크 된 문서를 이해하는 것이 중요하지 않습니다.
가 여기 내 시도하지만하는 VerificationException
결과 "작업 런타임을 불안정하게 할 수있다."
let rec emit lenv ast =
match ast with
...
| InstanceCall(instance,methodInfo,args,_) ->
instance::args |> List.iter (emit lenv)
il.Emit(OpCodes.Constrained, instance.Type)
il.Emit(OpCodes.Callvirt, methodInfo)
...
이 문서를 보면, 내가 키가 될 수있다 생각한다 "A는, 포인터, PTR 관리 ptr의 유형은 thisType에 대한 관리 포인터 (&) 여야합니다.이 유형의 참조를 기대하는 접두어가없는 callvirt 명령어의 경우와는 다릅니다. "
업데이트는 당신에게 @Tomas 감사 OpCodes.Constrained
를 사용하는 경우 @desco, 지금 (instance.Type
이 치 형이지만, methodInfo.DeclaringType
는 참조 형식입니다) 이해합니다.
하지만 아직 그 사건을 고려할 필요가 없다는 것이 드러났습니다. 실제 문제는 스택의 인스턴스 인수였습니다. 값 대신 주소가 필요하다는 것을 배우기까지 6 시간 밖에 걸리지 않았습니다. DLR 소스 코드에서 단서를 제공 한 다음 간단한 C# 프로그램에서 ilasm.exe를 사용하여 명확하게 나타 냈습니다. 난 당신이 질문의 마지막에 인용 된 문서의 비트가 문제의 원인이라고 생각
let rec emit lenv ast =
match ast with
| Int32(x,_) ->
il.Emit(OpCodes.Ldc_I4, x)
...
| InstanceCall(instance,methodInfo,args,_) ->
emit lenv instance
//if value type, pop, put in field, then load the field address
if instance.Type.IsValueType then
let loc = il.DeclareLocal(instance.Type)
il.Emit(OpCodes.Stloc, loc)
il.Emit(OpCodes.Ldloca, loc)
for arg in args do emit lenv arg
if instance.Type.IsValueType then
il.Emit(OpCodes.Call, methodInfo)
else
il.Emit(OpCodes.Callvirt, methodInfo)
...