[Ironruby-core] Code Review: MethodBinder23

Tomas Matousek Tomas.Matousek at microsoft.com
Wed Sep 10 14:23:15 EDT 2008

tfpt review "/shelveset:MethodBinder23;REDMOND\tomat"

Ruby changes

This change significantly affects Ruby method definitions in C# libraries:
1) CodeContext hidden parameter is replaced with RubyScope or RubyContext hidden parameters depending on what the method needs.
CodeContext is still supported but will eventually be dropped. Methods shouldn't use any special parameters if they don't need to. Methods should use RubyContext if they only need to get the current runtime context (note that you can get to the runtime context also from non-null BlockParam, RubyClass, RubyModule, and other runtime-bound Ruby objects). Currently we have 2 contexts: RubyExecutionContext and RubyContext. RubyExecutionContext is going to be merged into RubyContext soon. For now you can get it from RubyContext using ExecutionContext property. Methods that need to access the current scope or need to call a dynamic site need RubyScope. RubyScope holds on RubyContext/RubyExecutionContext so it is not necessary for a method to take both RubyScope and RubyContext parameters. In future dynamic sites will be passed in a special hidden parameter(s).

2) The shelveset introduces DefaultProtocol attribute that can be applied on parameters of type MutableString and Int32 (more types will be supported in future). This feature removes the need to explicitly call Protocols.CastToString and Protocols.CastToFixnum as the binder performs the conversions for you. The only method that uses the attribute so far is String#center. However, most of the methods that use CastToString/Fixnum will be switched to DefaultProtocol eventually.

3) BlockParam parameter now precedes self parameter and [NotNull] attribute is used for overload resolution if applied on BlockParam. A method that doesn't include BlockParam can still be called with a block. The block is ignored in this case.

If the method overload doesn't have a BlockParam parameter, we inject MissingBlockParam parameter and arg builder.
The parameter is treated as a regular explicit mandatory parameter.

The argument builder provides no value for the actual argument expression, which makes the default binder to skip it
when emitting a tree for the actual method call (this is necessary since the method doesn't in fact have the parameter).

By injecting the missing block parameter we achieve that all overloads have either BlockParam, [NotNull]BlockParam or
MissingBlockParam parameter. MissingBlockParam (MBP) and BlockParam (BP) are convertible to each other. Default binder prefers
those overloads where no conversion needs to happen, which ensures the desired semantics:

Conversions with desired priority (the less number the higher priority):

Parameters:                call w/o block      call with non-null block       call with null block
 (implicit, MBP, ... )      MBP -> MBP (1)            BP -> MBP (3)               BP -> MBP (2)
 (implicit, BP,  ... )      MBP -> BP  (2)            BP -> BP  (2)               BP -> BP  (1)
 (implicit, BP!, ... )          N/A                   BP -> BP! (1)                  N/A

4) Methods designated by RubyConstructor attribute must have RubyClass self parameter. The class object being instantiated is passed in it.

5) A RubyConstant  attribute can now be applied on a method. The method is called during class initialization to produce the value of the constant.

Many library methods were adjusted to match the binder changes.
Adds more checks to class-init generator to enforce correct method signatures.
Also adds library data dictionary to RubyContext - libraries can add objects to the dictionary that should be associated with the context.

Fixes bugs in Struct:
- Struct defines a singleton method "new" that is used to create new structures (classes).
- If Singleton(Struct)#new method is removed, Class#new is invoked instead, which raises missing allocator error.
- A class created by Singleton(Struct)#new is a structure class that defines accessors specified in struct constructor and its singleton class defines "[]", "members" and "new" methods. "new" create an instance of the structure given values for the struct's attributes.
- Structures can be derived from and duplicated.

DLR changes

Renames MethodBinderContext to ParameterBinder and removes a dependency on CodeContext from it. A subclass of ParameterBinder, ParameterBinderWithCodeContext adds CodeContext expression to ParameterBinder. The idea is that this class will eventually move to Python and become PythonParameterBinder and the default binder won't depend on CodeContext. Methods on ActionBinder that deal with parameters could also be moved to ParameterBinder then (e.g. GetByRefArrayExpression, ParametersEquivalent, CanConvertFrom, BindSpecialArgument, PrepareArgumentBinding). These methods cannot be moved now since the parameter binder is not flown to all places where it would be needed. Adds RubyParameterBinder which implements custom parameter conversions for Ruby library methods.

Adds more methods to ActionBinder (should move on ParameterBinder in future):

-       PrepareArgumentBinding, BindSpecialArgument - used in MethodBinder when the method candidates are built up. These extension points enable to preprocess parameter infos and produce language specific ParameterWrappers and ArgBuilders and skip initial language specific hidden arguments. Ruby uses this to process hidden arguments of types RubyScope, RubyContext and CodeContext (which is deprecated and will be gradually removed from libraries). Also passing blocks to library methods are handled in a special way: Most of the library methods can accept a block even though they ignore it. It would be tedious to use the block parameter when not needed and therefore the Ruby binder injects it automatically to the ParameterWrappers and ArgBuilders if not specified. The injected parameter is of type MissingBlockParam and implicit conversions are defined from BlockParam to MissingBlockParam and vice versa. Therefore, for the purpose of overload resolution, each method has a block parameter. If no block is specified at call-site overloads with MissingBlockParam are preferred, followed by nullable BlockParam ([NotNull]BlockParam overloads are not applicable). If a block is specified but is null then nullable BlockParam has precedence over MissingBlockParam ([NotNull]BlockParam overloads are not applicable). If a block is specified and is not null then the order is [NotNull]BlockParam > BlockParam > MissingBlockParam.

-       CanConvertFrom - moved here from ParameterWrapper and changed the parameter to ParameterWrappers instead of Type. This allows to customize conversions based upon attributes and other modifiers applied on the parameters in addition to the type of the parameter.

-       ParametersEquivalent - used from ParameterWrapper to detect whether parameters are equivalent in parameter ordering used for overload resolution.

Adds Candidate enum with values { Equivalent, One, Two} and refactors various CompareTo, PreferConvert, SelectBestConversionFrom methods to return values of Candidate enum instead of integers.

Adds ParameterInfo property to both ParameterWrapper and ArgBuilder. The value is optional and some kinds of parameters and arg builders might not be associated with parameter-info. The purpose is to allow parameter comparison and conversion methods to make decisions based upon parameter-info properties when available.

Deletes Dynamic class. It's not used anywhere.

Minor changes:
-       Refactored MethodTarget.MakeExpression.
-       Renamed PrepareArgumentBinding to PrepareParameterBinding, BindSpecialArgument to BindSpecialParameter.
-       Renamed CompareTo methods on ParameterWrapper to GetPreferredParameter(s), made them static and renamed their arguments so that the value of Candidate enum expresses the preference that is made by the method.
-       Refactored usage of nullable Candidate enum, replaced it by Candidate.Ambiguous enum value. I've also added extension methods for the enum that makes the code working with Candidate enum more readable.
-       Removed type parameter from *ArgBuilder ctors where possible, add ctor overloads for callers that pass no parameter info.
-       Removed #if BUG - I forgot that in the source code.
-       Added comments where missing.

In addition, I've moved classes that are related to overload resolution implementation in default binder from Generation into Actions.Calls namespace.

Python changes

Pass parameter binder with code context to default  binder instead of code context.

-------------- next part --------------
A non-text attachment was scrubbed...
Name: MethodBinder23.diff
Type: application/octet-stream
Size: 903123 bytes
Desc: MethodBinder23.diff
URL: <http://rubyforge.org/pipermail/ironruby-core/attachments/20080910/aea141e2/attachment-0001.obj>

More information about the Ironruby-core mailing list