2010-12-12 27 views
5

Câu hỏi này sẽ mất một chút giới thiệu.Hành vi ủy nhiệm của CIL với xung đột "tĩnh" của phương pháp đích

Tôi đang làm việc trên một dự án bảo mật sẽ phân tích các cụm CIL và từ chối những người thực hiện những điều "xấu" nhất định đồng thời cho phép ứng dụng lưu trữ cung cấp "cổng" cho một số phương pháp. (Đây là một tập con nhỏ của chức năng của dự án, nhưng đó là phần tôi sẽ hỏi về đây.)

Dự án quét tất cả các hướng dẫn trong mọi phương pháp trong hội đồng, và tìm kiếm cuộc gọi, callvirt, ldftn, ldvirtftn, và newobj opcodes, vì đây là những opcodes duy nhất mà cuối cùng có thể dẫn đến một cuộc gọi phương thức. Opcodes ldftn được sử dụng khi xây dựng các đại biểu, như vậy:

ldarg.1 
ldftn instance bool string::EndsWith(string) 
newobj instance void class [System.Core]System.Func`2<string, bool>::'.ctor'(object, native int) 

Vào cuối của dãy này, một Func<string, bool> là trên đỉnh của ngăn xếp.

Giả sử tôi muốn chặn tất cả các cuộc gọi đến String.EndsWith(String). Đối với cuộc gọi và callvirt, tôi chỉ có thể thay thế cuộc gọi cá thể bằng một cuộc gọi tĩnh của chữ ký Boolean(String,String) - đối số đầu tiên sẽ là cá thể chuỗi mà phương thức ban đầu được gọi. Ở mức CIL, hành vi sẽ rõ ràng và được xác định rõ ràng, vì đây là cách các phương thức tĩnh được gọi.

Nhưng đối với ldftn? Tôi đã cố gắng chỉ cần thay thế các toán hạng của lệnh ldftn với phương pháp tĩnh cùng sử dụng để thay thế toán hạng cuộc gọi/callvirt của:

ldarg.1 
ldftn bool class Prototype.Program::EndsWithGate(string, string) 
newobj instance void class [System.Core]System.Func`2<string, bool>::'.ctor'(object, native int) 

Tôi đã hoàn toàn mong đợi này thất bại, vì các đại biểu được đưa ra một đối tượng mục tiêu (không null) trong khi đưa một con trỏ phương thức tĩnh. Trước sự ngạc nhiên của tôi, điều này thực sự hoạt động trên cả Microsoft .NET runtime và Mono. Tôi hiểu rằng tham số đích/tham số này chỉ là tham số đầu tiên của phương thức và được ẩn cho các phương thức ví dụ. (Dự án dựa trên kiến ​​thức này.) Nhưng thực tế là các đại biểu thực sự làm việc trong những trường hợp này là một chút khó hiểu với tôi.

Vì vậy, câu hỏi của tôi: là hành vi được xác định và được ghi lại? Sẽ đại biểu, khi được gọi, luôn luôn đẩy mục tiêu của họ vào ngăn xếp nếu nó không null? Nó sẽ được tốt hơn để xây dựng một lớp đóng cửa mà sẽ nắm bắt các mục tiêu và "đúng" gọi phương pháp tĩnh, mặc dù điều này sẽ phức tạp hơn nhiều và gây phiền nhiễu?

+2

Nếu không có hành vi này, bạn không thể tạo đại biểu từ một phương thức tiện ích mở rộng giống như từ một phương pháp thể hiện. – CodesInChaos

Trả lời

5

ECMA 335 spec part 2 14.6.2 có một đoạn về điều này: Quy ước gọi điện của T và D phải khớp chính xác, bỏ qua sự khác biệt giữa phương pháp tĩnh và ví dụ. (tức là tham số này nếu có, không được xử lý đặc biệt).

Điều này nghe có vẻ với tôi như vậy cho các phương pháp tĩnh sẽ được cho phép trong hai biến thể:

  • Nếu không có này, trong trường hợp NULL nên được thông qua
  • Với các tham số đầu tiên thêm, giả sử các trận đấu loại những gì đã được đưa vào cuộc gọi newobj.
1

Vâng, tôi không nghĩ rằng tôi muốn được trả lời câu hỏi đầu tiên của tôi bản thân mình ...

Một đồng nghiệp trong #mono (Ck) đã thông báo với tôi về hành vi liên quan của Delegate.CreateDelegate: (tôi nhấn mạnh)

Nếu firstArgument được cung cấp, nó được chuyển đến phương thức mỗi lần ủy nhiệm được gọi; firstArgument được cho là bị ràng buộc với người được ủy quyền, và người được ủy quyền được cho là bị đóng trong đối số đầu tiên của nó. Nếu phương thức là tĩnh (Được chia sẻ trong Visual Basic), danh sách đối số được cung cấp khi gọi đại biểu bao gồm tất cả các tham số ngoại trừ đầu tiên; nếu phương thức là một phương thức thể hiện, thì firstArgument được truyền vào tham số cá thể ẩn (được biểu diễn bằng cách này trong C# hoặc bởi Me trong Visual Basic).

Có vẻ hợp lý khi kết luận từ tài liệu này, sử dụng ldftn trong quá trình xây dựng đại biểu, kết hợp với mục tiêu không null, thực sự được xác định rõ ràng.

3

Điều đáng nói là đây không phải là lạm dụng. Đó là một kỹ thuật được gọi là "đại biểu cà ri". Nó xuất phát từ một kỹ thuật tổng quát hơn gọi là "currying" trong ngôn ngữ lập trình chức năng, nơi một hàm với N + 1 đối số được chuyển thành một chức năng với N arguments.The C# tương đương sẽ giống như thế:

Func<T2, R> CurryFirst<T1, T2, R>(
    Func<T1, T2, R> f, 
    T1 arg 
) 
{ 
    return (x) => f(arg, x); 
} 

Func<T1, R> CurrySecond<T1, T2, R>(
    Func<T1, T2, R> f, 
    T2 arg 
) 
{ 
    return (x) => f(x, arg); 
} 

Các CLR cung cấp sự hỗ trợ đặc biệt cho trường hợp "cà ri đầu tiên", chủ yếu là do ở mức mã máy, lời gọi phương thức tĩnh được gọi gần như chính xác như lời gọi phương thức thể hiện (tham số this được truyền vào như đối số đầu tiên ngầm định).

Điều đó làm cho việc triển khai thực hiện currying delegate khá hiệu quả. Ban đầu nó được triển khai, cùng với DynamicMethod hỗ trợ Iron Python. Nó cũng được sử dụng cho các mục đích khác, chẳng hạn như cho phép các đại biểu tham chiếu đến các phương thức mở rộng một cách minh bạch.

+0

Tính năng này có thực sự chỉ được triển khai cùng với DLR không? –

+0

Tôi tin rằng nó đã được thực hiện để hỗ trợ python sắt, mà trước DLR ít nhất 2 năm hoặc lâu hơn. –

Các vấn đề liên quan