tfpt review "/shelveset:EMS10;REDMOND\tomat"
Comment :
Implements support for extension methods in IronRuby.
DLR:
- Factors out reusable parts of type inferer to ReflectionUtils and simplifies
the inferer a bit. We need to bind generic parameters when dealing with
extension methods whose first parameter is an open constructed type.
- Moves around a few methods in ReflectionUtils so they are better organized
to regions.
- Implements extension method reflection services in ReflectionUtils. The
implementation will improve when we''ll get our new metadata reader
checked-in. We''ll also need a slightly different extension method
enumeration scheme for Python. I''ll also rename ReflectionUtils to
ReflectionServices when they use the new metadata reader and move it out of
Utils directory. They are becoming more than just simple utils.
- Updates OverloadResolver to prefer non-extension methods over extension
methods if it can''t decide which overload is better based on other
criteria.
- Implements a workaround for a bug in MethodInfo.GetBaseMethod in
CompilerHelpers.TryGetCallableMethod. The API might return a generic method
definition even if called on a method instantiation.
- Small improvements to a couple of ArrayUtils.
Ruby:
- Adds method Kernel#using_clr_extensions that takes a namespace and activates
all extension methods defined on classes defined in that namespace
(doesn''t include sub-namespaces) regardless of the assembly they are
defined in. All assemblies already loaded to the current ScriptRuntime are
reflected for extension methods and any assembly that is loaded in the future
gets reflected as well. This means that additional extension methods might
appear when a new assembly is loaded. For each affected type the loaded
extension methods are added to the overload sets of existing methods (if there
are any). Thus if class C defines an instance method foo() and an extension
method foo(this C, int) is loaded the next call of foo on an instance of C will
trigger overload resolution algorithm to choose among overloads { foo() and
foo(int) }. If the resolver can''t chose based upon the call-site
arguments the non-extension methods are preferred.
- Generic extension methods might be defined, for example like so:
public static void Foo<S, T>(this Dictionary<S, T> x, T y) where S
: T { }
This defined Foo on all Dictionaries whose key type is the same or derives
from the type of the values stored in the dictionary. This is supported to the
extent that all involved constraints can be instantiated using the type of the
first argument to the call-site. We do ignore constraints that can''t.
For example:
public static void Foo<S, T>(this S x, T y) where S : T { }
In this case the methods will be available on all types in the system. However
if the method is invoked with an argument that is not compatible with the
constraint the overload can''t be selected and the method call might
fail.
If the first parameter of an extension method is not a constructed type (i.e.
it is a plain generic parameter like in the example above) the method is
supposed to be defined on every type in the system that satisfies the constrains
of the generic parameter. In the current implementation IronRuby considers such
method (say Foo) defined on Object and available to all applicable types via
inheritance. Thus a Ruby method Foo or foo defined on some class A <: Object
hides the extension method Foo. This means that the extension method
won''t be available from any subclass of A until the Ruby method on A
gets removed. Ideally the extension method should appear to be defined directly
on every type instead of just Object. However this brings additional complexity
to the current IronRuby type system. Since defining an extension method on every
single type is rather a corner case I''ve decided for the simple
approach.
- We intentionally don''t enable extension methods on built-in types
(such as Array or Hash) for two reasons:
o There is high potential for name conflicts. For example,
IEnumerable<T> LINQ extensions include methods like First, Zip, Count,
Max, Min, Sum, etc. some of these are already methods on Ruby''s
Enumerable and other might easily be in future. We could use the policy of
non-mangling names so that they could be called only using their CLR name and
not the Ruby equivalent (Zip vs zip). However, then writing LINQ queries that
work for all data sources would require to always use CLR names otherwise it
wouldn''t work for builtins.
o Built-ins hide CLR interface they implement to be fully compatible with Ruby
built-ins. For example, although Array implements IList<object>
Array.included_mixins == [Enumerable]. Thus it would need a bit of magical
behavior to make the extension methods appear on Array.
It would actually be much cleaner if a separate non-built-in class was defined
that wraps Ruby array and implements IEnumerable<T>. Then any time you
want to use an array as LINQ data source you just wrap it into this enumerable
implementation.
- An example of using LINQ extension methods:
load_assembly "System.Core"
using_clr_extensions System::Linq
p System::String.to_clr_type.get_methods.
where(System::Func[Object, System::Boolean].new { |m| m.name[0, 2] ==
''To'' }).
select(System::Func[Object, Object].new { |m| m.name }).
to_a
# => [''ToCharArray'', ''ToCharArray'',
''ToLower'', ''ToLower'',
''ToLowerInvariant'', ''ToUpper'',
''ToUpper'', ''ToUpperInvariant'',
''ToString'', ''ToString'']
Currently IronRuby''s type inference doesn''t infer the
generic parameters of Where and Select methods so we need to help it by
specifying the types explicitly.
- Fixes name mangling of "or" word.
- Fixes cross-runtime class hierarchy locking bug in constant resolution. It
is no longer allowed to open a foreign module or class via module/class keyword.
Tomas
-------------- next part --------------
A non-text attachment was scrubbed...
Name: EMS10.diff
Type: application/octet-stream
Size: 111916 bytes
Desc: EMS10.diff
URL:
<http://rubyforge.org/pipermail/ironruby-core/attachments/20100629/8c47d5d5/attachment-0001.obj>