2017-05-21 14 views
4

필자는 올바르게 빌드하고 실행되는 컴파일러를 가지고 있지만 PEVerify는 특정 시점에서이를 확인할 수 없다고합니다. 문제의 지점에 대한 오류, 해당 소스 코드 및 ILDasm 출력을주의 깊게 살펴본 후 .NET 및 Mono 버전을 제외하고 PEVerify의 버그를 의심할만한 지점에서 문제를 찾을 수 없습니다. 같은 장소에서 같은 오류를보고합니다. 다음이 코드가 확인되지 않는 이유는 무엇입니까?

The problematic method0x11 Offsest

[IL]: Error: [D:\SDL-1.3.0-4423\boo\build\Boo.Lang.Compiler.dll : Boo.Lang.Compiler.TypeSystem.AsyncHelper::InAsyncMethod][offset 0x00000011][found ref 'Boo.Lang.Compiler.Ast.Node'][expected ref Boo.Lang.Compiler.Ast.INodeWithBody'] Unexpected type on the stack.

?? 식의 후반부에 해당로서

internal static bool InAsyncMethod(Expression value) 
{ 
    INodeWithBody ancestor = value.GetAncestor<BlockExpression>() ?? (INodeWithBody) value.GetAncestor<Method>(); 
    return ContextAnnotations.IsAsync(ancestor); 
} 

오류가보고된다. ILDASM에서 :

.method assembly hidebysig static bool InAsyncMethod(class Boo.Lang.Compiler.Ast.Expression 'value') cil managed 
{ 
    // Code size  29 (0x1d) 
    .maxstack 2 
    .locals init ([0] class Boo.Lang.Compiler.Ast.INodeWithBody ancestor, 
      [1] bool CS$1$0000) 
    IL_0000: nop 
    IL_0001: ldarg.0 
    IL_0002: callvirt instance !!0 Boo.Lang.Compiler.Ast.Node::GetAncestor<class Boo.Lang.Compiler.Ast.BlockExpression>() 
    IL_0007: dup 
    IL_0008: brtrue.s IL_0011 
    IL_000a: pop 
    IL_000b: ldarg.0 
    IL_000c: callvirt instance !!0 Boo.Lang.Compiler.Ast.Node::GetAncestor<class Boo.Lang.Compiler.Ast.Method>() 
    IL_0011: stloc.0 
    IL_0012: ldloc.0 
    IL_0013: call  bool Boo.Lang.Compiler.Steps.ContextAnnotations::IsAsync(class Boo.Lang.Compiler.Ast.INodeWithBody) 
    IL_0018: stloc.1 
    IL_0019: br.s  IL_001b 
    IL_001b: ldloc.1 
    IL_001c: ret 
} // end of method AsyncHelper::InAsyncMethod 

Boo.Lang.Compiler.Ast.Node 클래스는 모든 AST 노드의 기본 클래스입니다. BlockExpressionMethod은 각각 람다 및 메소드에 대한 노드 클래스이며, 둘 다 INodeWithBody 인터페이스를 구현합니다. 모든 것이 C# (형식 문제가있는 경우 빌드되지 않음)과 IL (000c에서 GetAncestor<Method> 호출의 반환 형식이 메서드 호출의 첫 번째 형식 매개 변수 인 경우)에 나타납니다.

PEVerify가 정확히 Method 값을 가지고있을 때 Node 유형의 값을 처리한다고 생각하게하는 원인은 무엇입니까? 그리고 그것을 고칠 수있는 방법이 있습니까?

+1

몇 년 동안 나 자신을 여러 번 디버깅 한 결과, 거의 항상 '? :'조건부 연산자. 검증자는 수년에 걸쳐 조건을 처리하기위한 전략을 변경했으며 매우 혼란 스러울 수 있습니다. –

+0

@EricLippert 이번에는 '??'연산자였습니다. 자세한 내용은 대답을 참조하십시오. 코드를 고정 된 두 개의 별도 문으로 다시 작성하면 코드가 정확히 같아야하므로 약간 이상합니다. 나는'csc'가 검증 할 수없는 일리노이를 생성하는 것을보고 깜짝 놀랐습니다! –

+1

'?? '연산자는 그냥 멋진 드레스에서'? :'연산자입니다. ('&&'와'||'및'? .')'csc'가 이것을 생성하면 (1) 여러분의 csc 버전이 검증 자의 다른 버전에 대해 테스트되었습니다. (2) csc는 조건부 코드 경로에서 또 다른 사기성 버그를 겪었습니다. 보고 해주십시오. 이번에 고칠 필요가 없기 때문에 기쁩니다. 또는 (3) 둘 다. –

답변

3

What is causing PEVerify to think it's dealing with a value of type Node here when it clearly has a value of type Method ?

로는 분기의 대상으로 IL_0011에 도달 개의 코드 경로들이 존재뿐만 스테판 DELCROIX 지적. 분명히 유형이 Method 일 필요는 없습니다.

GetAncestor<TAncestor>TAncestor을 반환합니다. GetAncestor<BlockExpression> 결과 또는 GetAncestor<Method> 결과가 있으므로 BlockExpression 또는 Method 중 하나입니다. 둘 다 INodeWithBody을 구현하므로 논리적으로 코드는 여전히 유효합니다.

"BlockExpression 또는 Method"은 확인하기에는 너무 많습니다. 이것은 Node (공통 기본)으로 간소화되며, 이 아니며 INodeWithBody을 구현합니다. ECMA-335 §III.1.8.1.3를 참조하십시오 : 당신은 C# 컴파일러가 무엇을 선택하면, 당신은 그것이 dup 이전 유형 INodeWithBody의 지역에 stloc.0/ldloc.0 조합을 방출하는 것을 볼 수 있습니다

The merged type, U , shall be computed as follows (recall that S := T is the compatibility function defined in §III.1.8.1.2.2):

  1. if S := T then U=S

  2. Otherwise, if T := S then U=T

  3. Otherwise, if S and T are both object types, then let V be the closest common supertype of S and T then U=V .

  4. Otherwise, the merge shall fail.

. 공통 유형 인 INodeWithBodyMethodINodeWithBody이므로 모든 것이 작동합니다.

+0

와우, 나는 그 조건부 점프를 완전히 놓쳤다! 불행하게도, 캐스팅을 다른 쪽으로 옮기거나 두 가지 모두에 넣는 것 역시 동일한 일리가있는 일리노이를 만듭니다. 어떻게이 문제를 올바르게 해결할 수 있습니까? 아니면'csc'에 버그로보고해야합니까? –

+0

@MasonWheeler 잠깐 -이 일리노이가 csc에서 나왔어? 나는 당신이 자신의 컴파일러에서 이것을 얻고 있다고 생각했습니다. 오해해서 미안합니다. csc가 이것을 생성해서는 안되며 마지막 단락에서 C# 컴파일에서 본 동작을 설명했습니다. Microsoft.Net.Compilers 패키지를 설치하여 강제로 최신 컴파일러를 사용하도록 시도 할 수 있으며, 작동하지 않는 경우 실제로 버그로보고하십시오. – hvd

+0

예, 이것은 Visual Studio 2013의 일반 C# 컴파일러에서 나옵니다. Mono의 C# 컴파일러는 분명히 동일한 문제가되는 출력을 생성합니다. –

1

0x11의 첫 번째 또는 오른쪽 부분으로 인해 0x08이 (으)로 분기되어 있기 때문에 그것이 분명한 지 여부는 확실하지 않습니다.

내가 생각에 기울고있다 GetAncestor<>Node을 반환하고 ??의 왼쪽 부분에는 명시 적 캐스트가 없습니다.

+1

이것은 합리적인 추측이지만'GetAncestor '는'TAncestor'를 반환하고'BlockExpression'과'Method'는 모두'Node'에서 파생되고'INodeWithBody'를 구현합니다. – hvd