Code Review: ClrMemberEnumeration

tfpt review “/shelveset:ClrMemberEnumeration;REDMOND\tomat”

Implements CLR member enumeration: methods Module#methods,
instance_methods, etc. now include CLR member names.

Adds IronRuby::Clr::Name class, which represents a pair of names of a
CLR method - the primary name is mangled (Ruby name) the alternative
name is the actual CLR name.

Reflection methods like Module#instance_methods return instances of
ClrName whenever a CLR member is encountered that could be called by
both names. ClrName has methods to_s, to_sym, to_str, <=>, inspect,
dump so that it can be used wherever a string can be used. The display
string for the name uses single quotes so that you can easily
distinguish CLR (dual) names from regular names (plain mutable strings).

System::Byte.instance_methods(false)
=> ["-", “%”, “&”, “*”, “**”, “/”, “-@”, “[]”, “^”, “|”, “~”, “+”, “<”,
“<<”, “<=”, “<=>”, “==”, “>”, “>=”, “>>”, “abs”,
“div”, “divmod”, “modulo”, “quo”, “to_f”, “to_s”, “zero?”, “size”,
‘compare_to’, ‘equals’, ‘get_hash_code’, ‘to_string’, ‘get_type_code’]

l = System::Byte.instance_methods(false).last
=> ‘get_type_code’

l.ruby_name
=> “get_type_code”

l.clr_name
=> “GetTypeCode”

Now this works with meta-programming as well:

class System::Decimal
instance_methods(false).each do |name|
mangled = ‘__’ + name

alias_method(mangled, name)
private mangled

define_method(name) do |*args|
  puts "method called: #{name}"
  send mangled, *args
end

end
end

x, y = System::Decimal.new(1), System::Decimal.new(2)
p x + y # => “method called: +”
p x.CompareTo(y) # => “method called: compare_to”

The trick here is in new set of define_method overloads strongly typed
to ClrName that define the real method using the ruby_name and alias it
using the clr_name. So both CompareTo and compare_to calls are
intercepted.

We might add similar overloads to other methods to improve
meta-programming experience for CLR types when needed.

Note that instance_methods never returns any ClrNames for a built-in
class or module, although some built-ins expose CLR members. The reason
is that built-ins are restricted to do no name-mangling in order to
prevent conflicts with Ruby. The CLR methods are only exposed via their
actual names.

Thread.instance_methods(false).sort
=> [“Abort”, “ApartmentState”, “ApartmentState=”, “CurrentCulture”,
“CurrentCulture=”, “CurrentUICulture”, “CurrentUICul
ture=”, “ExecutionContext”, “GetApartmentState”, “GetCompressedStack”,
“GetHashCode”, “Interrupt”, “IsAlive”, “IsBackgro
und”, “IsBackground=”, “IsThreadPoolThread”, “Join”, “ManagedThreadId”,
“Name”, “Name=”, “Priority”, “Priority=”, “Resum
e”, “SetApartmentState”, “SetCompressedStack”, “Start”, “Suspend”,
“ThreadState”, “TrySetApartmentState”, “[]”, “[]=”, "
abort_on_exception", “abort_on_exception=”, “alive?”, “exit”, “group”,
“inspect”, “join”, “key?”, “keys”, “kill”, "raise
", “run”, “status”, “stop?”, “terminate”, “value”, “wakeup”]

Tomas