edit: $/Dev10/feature/vs_langs01_s/Merlin/Main/Languages/IronPython/IronPython/IronPython.Build.csproj;C815265 File: IronPython.Build.csproj =================================================================== --- $/Dev10/feature/vs_langs01_s/Merlin/Main/Languages/IronPython/IronPython/IronPython.Build.csproj;C815265 (server) 4/6/2009 10:17 AM +++ Shelved Change: $/Dev10/feature/vs_langs01_s/Merlin/Main/Languages/IronPython/IronPython/IronPython.Build.csproj;NewMethodBinder @@ -230,6 +230,7 @@ + @@ -254,6 +255,7 @@ + @@ -280,7 +282,9 @@ + + @@ -289,6 +293,7 @@ + @@ -297,6 +302,7 @@ + =================================================================== edit: $/Dev10/feature/vs_langs01_s/Merlin/Main/Languages/IronPython/IronPython/IronPython.csproj;C817637 File: IronPython.csproj =================================================================== --- $/Dev10/feature/vs_langs01_s/Merlin/Main/Languages/IronPython/IronPython/IronPython.csproj;C817637 (server) 4/6/2009 10:17 AM +++ Shelved Change: $/Dev10/feature/vs_langs01_s/Merlin/Main/Languages/IronPython/IronPython/IronPython.csproj;NewMethodBinder @@ -2,7 +2,7 @@ Debug AnyCPU - 9.0.30729 + 9.0.21022 2.0 {95289EA9-5778-489D-AB48-F81F2CE2DA32} Library @@ -103,6 +103,11 @@ + + + + + @@ -243,6 +248,7 @@ + =================================================================== branch, edit: $/Dev10/feature/vs_langs01_s/Merlin/Main/Languages/IronPython/IronPython/Runtime/SiteLocalStorage.cs File: SiteLocalStorage.cs =================================================================== --- SiteLocalStorage.cs (server) 4/6/2009 10:17 AM +++ Shelved Change: $/Dev10/feature/vs_langs01_s/Merlin/Main/Languages/IronPython/IronPython/Runtime/SiteLocalStorage.cs;NewMethodBinder @@ -13,7 +13,7 @@ * * ***************************************************************************/ -namespace Microsoft.Scripting.Generation { +namespace IronPython.Runtime { public abstract class SiteLocalStorage { } =================================================================== edit: $/Dev10/feature/vs_langs01_s/Merlin/Main/Languages/IronPython/IronPython/Runtime/Binding/CompatibilityInvokeBinder.cs;C791094 File: CompatibilityInvokeBinder.cs =================================================================== --- $/Dev10/feature/vs_langs01_s/Merlin/Main/Languages/IronPython/IronPython/Runtime/Binding/CompatibilityInvokeBinder.cs;C791094 (server) 4/6/2009 10:17 AM +++ Shelved Change: $/Dev10/feature/vs_langs01_s/Merlin/Main/Languages/IronPython/IronPython/Runtime/Binding/CompatibilityInvokeBinder.cs;NewMethodBinder @@ -62,10 +62,10 @@ } internal DynamicMetaObject/*!*/ InvokeFallback(DynamicMetaObject/*!*/ target, DynamicMetaObject/*!*/[]/*!*/ args, CallSignature sig) { - var parameterBinder = new ParameterBinderWithCodeContext(Binder.Binder, AstUtils.Constant(_state.Context)); - return PythonProtocol.Call(this, target, args) ?? - Binder.Binder.Create(sig, parameterBinder, target, args) ?? - Binder.Binder.Call(sig, parameterBinder, target, args); + return + PythonProtocol.Call(this, target, args) ?? + Binder.Binder.Create(sig, target, args, AstUtils.Constant(_state.Context)) ?? + Binder.Binder.Call(sig, target, args, AstUtils.Constant(_state.Context)); } public override int GetHashCode() { =================================================================== branch, edit: $/Dev10/feature/vs_langs01_s/Merlin/Main/Languages/IronPython/IronPython/Runtime/Binding/ContextArgBuilder.cs File: ContextArgBuilder.cs =================================================================== --- ContextArgBuilder.cs (server) 4/6/2009 10:17 AM +++ Shelved Change: $/Dev10/feature/vs_langs01_s/Merlin/Main/Languages/IronPython/IronPython/Runtime/Binding/ContextArgBuilder.cs;NewMethodBinder @@ -19,11 +19,10 @@ using System.Reflection; using System; using System.Dynamic; +using Microsoft.Scripting.Actions.Calls; -namespace Microsoft.Scripting.Actions.Calls { +namespace IronPython.Runtime.Binding { - // TODO: Move to Pyhton - /// /// ArgBuilder which provides the CodeContext parameter to a method. /// @@ -37,15 +36,15 @@ get { return -1; } } - internal protected override Expression ToExpression(ParameterBinder parameterBinder, IList parameters, bool[] hasBeenUsed) { - return ((ParameterBinderWithCodeContext)parameterBinder).ContextExpression; + protected override Expression ToExpression(MethodBinder methodBinder, IList parameters, bool[] hasBeenUsed) { + return ((PythonMethodBinder)methodBinder).ContextExpression; } - protected internal override Func ToDelegate(ParameterBinder parameterBinder, IList knownTypes, bool[] hasBeenUsed) { + protected override Func ToDelegate(MethodBinder methodBinder, IList knownTypes, bool[] hasBeenUsed) { return _readFunc; } - internal override bool CanGenerateDelegate { + protected override bool CanGenerateDelegate { get { return true; } =================================================================== edit: $/Dev10/feature/vs_langs01_s/Merlin/Main/Languages/IronPython/IronPython/Runtime/Binding/MetaBuiltinFunction.cs;C791094 File: MetaBuiltinFunction.cs =================================================================== --- $/Dev10/feature/vs_langs01_s/Merlin/Main/Languages/IronPython/IronPython/Runtime/Binding/MetaBuiltinFunction.cs;C791094 (server) 4/6/2009 10:17 AM +++ Shelved Change: $/Dev10/feature/vs_langs01_s/Merlin/Main/Languages/IronPython/IronPython/Runtime/Binding/MetaBuiltinFunction.cs;NewMethodBinder @@ -119,20 +119,17 @@ (newArgs) => { BindingTarget target; var binder = BinderState.GetBinderState(call).Binder; - DynamicMetaObject res = binder.CallMethod( - new ParameterBinderWithCodeContext(binder, codeContext), - Value.Targets, + + var methodBinder = new PythonMethodBinder( + binder, newArgs, BindingHelpers.GetCallSignature(call), - selfRestrict, PythonNarrowing.None, - Value.IsBinaryOperator ? - PythonNarrowing.BinaryOperator : - NarrowingLevel.All, - Value.Name, - out target + Value.IsBinaryOperator ? PythonNarrowing.BinaryOperator : NarrowingLevel.All, + codeContext ); + DynamicMetaObject res = binder.CallMethod(methodBinder, Value.Targets, selfRestrict, Value.Name, out target); return new BuiltinFunction.BindingResult(target, res); } ); @@ -173,38 +170,29 @@ DynamicMetaObject res; BinderState state = BinderState.GetBinderState(call); BindingTarget target; - var mc = new ParameterBinderWithCodeContext(state.Binder, codeContext); + PythonMethodBinder methodBinder; if (Value.IsReversedOperator) { - res = state.Binder.CallMethod( - mc, - Value.Targets, + methodBinder = new PythonMethodBinder( + state.Binder, newArgs, GetReversedSignature(signature), - self.Restrictions, NarrowingLevel.None, - Value.IsBinaryOperator ? - PythonNarrowing.BinaryOperator : - NarrowingLevel.All, - Value.Name, - out target + Value.IsBinaryOperator ? PythonNarrowing.BinaryOperator : NarrowingLevel.All, + codeContext ); } else { - res = state.Binder.CallInstanceMethod( - mc, - Value.Targets, + methodBinder = new PythonMethodBinder( + state.Binder, self, args, signature, - self.Restrictions, NarrowingLevel.None, - Value.IsBinaryOperator ? - PythonNarrowing.BinaryOperator : - NarrowingLevel.All, - Value.Name, - out target + Value.IsBinaryOperator ? PythonNarrowing.BinaryOperator : NarrowingLevel.All, + codeContext ); } + res = state.Binder.CallMethod(methodBinder, Value.Targets, self.Restrictions, Value.Name, out target); return new BuiltinFunction.BindingResult(target, res); } ); =================================================================== edit: $/Dev10/feature/vs_langs01_s/Merlin/Main/Languages/IronPython/IronPython/Runtime/Binding/MetaBuiltinMethodDescriptor.cs;C791094 File: MetaBuiltinMethodDescriptor.cs =================================================================== --- $/Dev10/feature/vs_langs01_s/Merlin/Main/Languages/IronPython/IronPython/Runtime/Binding/MetaBuiltinMethodDescriptor.cs;C791094 (server) 4/6/2009 10:17 AM +++ Shelved Change: $/Dev10/feature/vs_langs01_s/Merlin/Main/Languages/IronPython/IronPython/Runtime/Binding/MetaBuiltinMethodDescriptor.cs;NewMethodBinder @@ -87,19 +87,21 @@ selfRestrict, (newArgs) => { BindingTarget target; - BinderState state = BinderState.GetBinderState(call); + var methodBinder = new PythonMethodBinder( + state.Binder, + newArgs, + signature, + NarrowingLevel.None, + Value.Template.IsBinaryOperator ? PythonNarrowing.BinaryOperator : NarrowingLevel.All, + codeContext + ); + DynamicMetaObject res = state.Binder.CallMethod( - new ParameterBinderWithCodeContext(state.Binder, codeContext), + methodBinder, Value.Template.Targets, - newArgs, - signature, selfRestrict, - NarrowingLevel.None, - Value.Template.IsBinaryOperator ? - PythonNarrowing.BinaryOperator : - NarrowingLevel.All, Value.Template.Name, out target ); =================================================================== edit: $/Dev10/feature/vs_langs01_s/Merlin/Main/Languages/IronPython/IronPython/Runtime/Binding/MetaPythonType.Calls.cs;C791094 File: MetaPythonType.Calls.cs =================================================================== --- $/Dev10/feature/vs_langs01_s/Merlin/Main/Languages/IronPython/IronPython/Runtime/Binding/MetaPythonType.Calls.cs;C791094 (server) 4/6/2009 10:17 AM +++ Shelved Change: $/Dev10/feature/vs_langs01_s/Merlin/Main/Languages/IronPython/IronPython/Runtime/Binding/MetaPythonType.Calls.cs;NewMethodBinder @@ -86,10 +86,13 @@ if (ctors.Length > 0) { return state.Binder.CallMethod( - new ParameterBinderWithCodeContext(state.Binder, codeContext), + new PythonMethodBinder( + state.Binder, + args, + signature, + codeContext + ), ctors, - args, - signature, Restrictions.Merge(BindingRestrictions.GetInstanceRestriction(Expression, Value)) ); } else { @@ -375,27 +378,14 @@ } public override DynamicMetaObject/*!*/ GetExpression(DefaultBinder/*!*/ binder) { - var mc = new ParameterBinderWithCodeContext(binder, CodeContext); - + PythonMethodBinder methodBinder; if (_creating.IsSystemType) { - return binder.CallMethod( - mc, - _creating.UnderlyingSystemType.GetConstructors(), - new DynamicMetaObject[0], - new CallSignature(0), - BindingRestrictions.Empty, - _creating.Name - ); + methodBinder = new PythonMethodBinder(binder, DynamicMetaObject.EmptyMetaObjects, new CallSignature(0), CodeContext); + } else { + methodBinder = new PythonMethodBinder(binder, new[] { Arguments.Self }, new CallSignature(1), CodeContext); } - return binder.CallMethod( - mc, - _creating.UnderlyingSystemType.GetConstructors(), - new DynamicMetaObject[] { Arguments.Self }, - new CallSignature(1), - BindingRestrictions.Empty, - _creating.Name - ); + return binder.CallMethod(methodBinder, _creating.UnderlyingSystemType.GetConstructors(), BindingRestrictions.Empty, _creating.Name); } } @@ -408,24 +398,27 @@ } public override DynamicMetaObject/*!*/ GetExpression(DefaultBinder/*!*/ binder) { - var mc = new ParameterBinderWithCodeContext(binder, CodeContext); + PythonMethodBinder methodBinder; if (_creating.IsSystemType) { - return binder.CallMethod( - mc, - _creating.UnderlyingSystemType.GetConstructors(), - Arguments.Arguments, - Arguments.Signature, - Arguments.Self.Restrictions, - _creating.Name + methodBinder = new PythonMethodBinder( + binder, + Arguments.Arguments, + Arguments.Signature, + CodeContext ); + } else { + methodBinder = new PythonMethodBinder( + binder, + ArrayUtils.Insert(Arguments.Self, Arguments.Arguments), + GetDynamicNewSignature(), + CodeContext + ); } return binder.CallMethod( - mc, + methodBinder, _creating.UnderlyingSystemType.GetConstructors(), - ArrayUtils.Insert(Arguments.Self, Arguments.Arguments), - GetDynamicNewSignature(), Arguments.Self.Restrictions, _creating.Name ); @@ -443,14 +436,14 @@ } public override DynamicMetaObject/*!*/ GetExpression(DefaultBinder/*!*/ binder) { - var mc = new ParameterBinderWithCodeContext(binder, CodeContext); - - CallSignature sig = Arguments.Signature.InsertArgument(new Argument(ArgumentType.Simple)); return binder.CallMethod( - mc, + new PythonMethodBinder( + binder, + ArrayUtils.Insert(Arguments.Self, Arguments.Arguments), + Arguments.Signature.InsertArgument(new Argument(ArgumentType.Simple)), + CodeContext + ), _ctor.Targets, - ArrayUtils.Insert(Arguments.Self, Arguments.Arguments), - sig, _creating.Name ); } @@ -553,12 +546,15 @@ return createExpr; } - return binder.CallInstanceMethod( - new ParameterBinderWithCodeContext(binder, CodeContext), + return binder.CallMethod( + new PythonMethodBinder( + binder, + createExpr, + Arguments.Arguments, + Arguments.Signature, + CodeContext + ), _method.Targets, - createExpr, - Arguments.Arguments, - Arguments.Signature, Arguments.Self.Restrictions ); } =================================================================== branch, edit: $/Dev10/feature/vs_langs01_s/Merlin/Main/Languages/IronPython/IronPython/Runtime/Binding/PythonBinder.Create.cs File: PythonBinder.Create.cs =================================================================== --- DefaultBinder.Create.cs (server) 4/6/2009 4:28 PM +++ Shelved Change: $/Dev10/feature/vs_langs01_s/Merlin/Main/Languages/IronPython/IronPython/Runtime/Binding/PythonBinder.Create.cs;NewMethodBinder @@ -21,12 +21,15 @@ using Microsoft.Scripting.Runtime; using Microsoft.Scripting.Utils; using Microsoft.Scripting.Actions.Calls; +using Microsoft.Scripting.Actions; -namespace Microsoft.Scripting.Actions { +namespace IronPython.Runtime.Binding { using Ast = System.Linq.Expressions.Expression; - public partial class DefaultBinder : ActionBinder { - public DynamicMetaObject Create(CallSignature signature, ParameterBinderWithCodeContext parameterBinder, DynamicMetaObject target, DynamicMetaObject[] args) { + public sealed partial class PythonBinder : DefaultBinder { + // TODO: move to IronPython + public DynamicMetaObject Create(CallSignature signature, DynamicMetaObject target, DynamicMetaObject[] args, Expression contextExpression) { + Type t = GetTargetType(target.Value); if (t != null) { @@ -36,12 +39,19 @@ // BinderOps.CreateDelegate(CodeContext context, object callable); return new DynamicMetaObject( - Ast.Call(null, dc, parameterBinder.ContextExpression, args[0].Expression), + Ast.Call(null, dc, contextExpression, args[0].Expression), target.Restrictions.Merge(BindingRestrictions.GetInstanceRestriction(target.Expression, target.Value)) ); } - return CallMethod(parameterBinder, CompilerHelpers.GetConstructors(t, PrivateBinding), args, signature); + var methodBinder = new PythonMethodBinder( + this, + args, + signature, + contextExpression + ); + + return CallMethod(methodBinder, CompilerHelpers.GetConstructors(t, PrivateBinding), BindingRestrictions.Empty); } return null; =================================================================== edit: $/Dev10/feature/vs_langs01_s/Merlin/Main/Languages/IronPython/IronPython/Runtime/Binding/PythonBinder.cs;C791094 File: PythonBinder.cs =================================================================== --- $/Dev10/feature/vs_langs01_s/Merlin/Main/Languages/IronPython/IronPython/Runtime/Binding/PythonBinder.cs;C791094 (server) 4/6/2009 10:17 AM +++ Shelved Change: $/Dev10/feature/vs_langs01_s/Merlin/Main/Languages/IronPython/IronPython/Runtime/Binding/PythonBinder.cs;NewMethodBinder @@ -20,6 +20,7 @@ using System.Dynamic; using System.Linq.Expressions; using System.Reflection; +using System.Runtime.CompilerServices; using System.Threading; using IronPython.Runtime.Operations; using IronPython.Runtime.Types; @@ -35,9 +36,8 @@ namespace IronPython.Runtime.Binding { using Ast = System.Linq.Expressions.Expression; using AstUtils = Microsoft.Scripting.Ast.Utils; - using System.Runtime.CompilerServices; - public class PythonBinder : DefaultBinder { + public sealed partial class PythonBinder : DefaultBinder { private PythonContext/*!*/ _context; private SlotCache/*!*/ _typeMembers = new SlotCache(); private SlotCache/*!*/ _resolvedMembers = new SlotCache(); @@ -177,10 +177,6 @@ return Converter.PreferConvert(t1, t2); } - public override Expression GetByRefArrayExpression(Expression argumentArrayExpression) { - return Ast.Call(typeof(PythonOps).GetMethod("MakeTuple"), argumentArrayExpression); - } - public override ErrorInfo MakeConversionError(Type toType, Expression value) { return ErrorInfo.FromException( Ast.Call( @@ -230,7 +226,7 @@ #region .NET member binding - protected override string GetTypeName(Type t) { + public override string GetTypeName(Type t) { return DynamicHelpers.GetPythonTypeFromType(t).Name; } @@ -645,10 +641,6 @@ return AstUtils.Constant(DynamicHelpers.GetPythonTypeFromType(memberTracker.Type)); } - protected override bool AllowKeywordArgumentSetting(MethodBase method) { - return CompilerHelpers.IsConstructor(method) && !method.DeclaringType.IsDefined(typeof(PythonTypeAttribute), true); - } - internal ScriptDomainManager/*!*/ DomainManager { get { return _context.DomainManager; =================================================================== branch, edit: $/Dev10/feature/vs_langs01_s/Merlin/Main/Languages/IronPython/IronPython/Runtime/Binding/PythonBinder.Invoke.cs File: PythonBinder.Invoke.cs =================================================================== --- DefaultBinder.Invoke.cs (server) 4/6/2009 4:31 PM +++ Shelved Change: $/Dev10/feature/vs_langs01_s/Merlin/Main/Languages/IronPython/IronPython/Runtime/Binding/PythonBinder.Invoke.cs;NewMethodBinder @@ -16,87 +16,55 @@ using System; using System.Collections.Generic; using System.Diagnostics; +using System.Dynamic; using System.Linq.Expressions; using System.Reflection; -using System.Dynamic; +using Microsoft.Scripting; +using Microsoft.Scripting.Actions; +using Microsoft.Scripting.Actions.Calls; using Microsoft.Scripting.Generation; using Microsoft.Scripting.Runtime; using Microsoft.Scripting.Utils; -using Microsoft.Scripting.Actions.Calls; using AstUtils = Microsoft.Scripting.Ast.Utils; -namespace Microsoft.Scripting.Actions { +namespace IronPython.Runtime.Binding { using Ast = System.Linq.Expressions.Expression; - - public partial class DefaultBinder : ActionBinder { - + + public sealed partial class PythonBinder : DefaultBinder { /// /// Provides default binding for performing a call on the specified meta objects. /// /// The signature describing the call - /// The object to be called - /// - /// Additional meta objects are the parameters for the call as specified by the CallSignature in the CallAction. - /// - /// A MetaObject representing the call or the error. - public DynamicMetaObject Call(CallSignature signature, DynamicMetaObject target, params DynamicMetaObject[] args) { - return Call(signature, new ParameterBinder(this), target, args); - } - - /// - /// Provides default binding for performing a call on the specified meta objects. - /// - /// The signature describing the call /// The meta object to be called. /// /// Additional meta objects are the parameters for the call as specified by the CallSignature in the CallAction. /// - /// ParameterBinder used to map arguments to parameters. + /// CodeContext expression. /// A MetaObject representing the call or the error. - public DynamicMetaObject Call(CallSignature signature, ParameterBinder parameterBinder, DynamicMetaObject target, params DynamicMetaObject[] args) { + public DynamicMetaObject Call(CallSignature signature, DynamicMetaObject target, DynamicMetaObject[] args, Expression contextExpression) { ContractUtils.RequiresNotNullItems(args, "args"); - ContractUtils.RequiresNotNull(parameterBinder, "parameterBinder"); + ContractUtils.RequiresNotNull(contextExpression, "contextExpression"); TargetInfo targetInfo = GetTargetInfo(signature, target, args); if (targetInfo != null) { // we're calling a well-known MethodBase - return MakeMetaMethodCall(signature, parameterBinder, targetInfo); + BindingRestrictions restrictions = BindingRestrictions.Combine(targetInfo.Arguments).Merge(targetInfo.Restrictions); + PythonMethodBinder methodBinder; + if (targetInfo.Instance != null) { + restrictions = targetInfo.Instance.Restrictions.Merge(restrictions); + methodBinder = new PythonMethodBinder(this, targetInfo.Instance, targetInfo.Arguments, signature, contextExpression); + } else { + methodBinder = new PythonMethodBinder(this, targetInfo.Arguments, signature, contextExpression); + } + + return CallMethod(methodBinder, targetInfo.Targets, restrictions); } else { // we can't call this object return MakeCannotCallRule(target, target.GetLimitType()); } } - #region Method Call Rule - - private DynamicMetaObject MakeMetaMethodCall(CallSignature signature, ParameterBinder parameterBinder, TargetInfo targetInfo) { - BindingRestrictions restrictions = BindingRestrictions.Combine(targetInfo.Arguments).Merge(targetInfo.Restrictions); - if (targetInfo.Instance != null) { - restrictions = targetInfo.Instance.Restrictions.Merge(restrictions); - } - - if (targetInfo.Instance != null) { - return CallInstanceMethod( - parameterBinder, - targetInfo.Targets, - targetInfo.Instance, - targetInfo.Arguments, - signature, - restrictions - ); - } - - return CallMethod( - parameterBinder, - targetInfo.Targets, - targetInfo.Arguments, - signature, - restrictions); - } - - #endregion - #region Target acquisition /// =================================================================== edit: $/Dev10/feature/vs_langs01_s/Merlin/Main/Languages/IronPython/IronPython/Runtime/Binding/PythonInvokeBinder.cs;C791094 File: PythonInvokeBinder.cs =================================================================== --- $/Dev10/feature/vs_langs01_s/Merlin/Main/Languages/IronPython/IronPython/Runtime/Binding/PythonInvokeBinder.cs;C791094 (server) 4/6/2009 10:17 AM +++ Shelved Change: $/Dev10/feature/vs_langs01_s/Merlin/Main/Languages/IronPython/IronPython/Runtime/Binding/PythonInvokeBinder.cs;NewMethodBinder @@ -121,8 +121,8 @@ } return PythonProtocol.Call(this, target, args) ?? - Binder.Binder.Create(Signature, new ParameterBinderWithCodeContext(Binder.Binder, codeContext), target, args) ?? - Binder.Binder.Call(Signature, new ParameterBinderWithCodeContext(Binder.Binder, codeContext), target, args); + Binder.Binder.Create(Signature, target, args, codeContext) ?? + Binder.Binder.Call(Signature, target, args, codeContext); } #endregion =================================================================== branch, edit: $/Dev10/feature/vs_langs01_s/Merlin/Main/Languages/IronPython/IronPython/Runtime/Binding/PythonMethodBinder.cs File: PythonMethodBinder.cs =================================================================== --- ParameterBinderWithCodeContext.cs (server) 4/6/2009 4:45 PM +++ Shelved Change: $/Dev10/feature/vs_langs01_s/Merlin/Main/Languages/IronPython/IronPython/Runtime/Binding/PythonMethodBinder.cs;NewMethodBinder @@ -14,28 +14,79 @@ * ***************************************************************************/ using System; +using System.Collections.Generic; using System.Dynamic; using System.Linq.Expressions; using System.Reflection; +using Microsoft.Scripting.Actions; +using Microsoft.Scripting.Actions.Calls; using Microsoft.Scripting.Utils; +using Microsoft.Scripting.Generation; +using IronPython.Runtime.Operations; +using Microsoft.Scripting.Runtime; -namespace Microsoft.Scripting.Actions.Calls { - - // TODO: this should move to Python - public class ParameterBinderWithCodeContext : ParameterBinder { +namespace IronPython.Runtime.Binding { + public sealed class PythonMethodBinder : DefaultMethodBinder { private readonly Expression _context; public Expression ContextExpression { get { return _context; } } - public ParameterBinderWithCodeContext(ActionBinder actionBinder, Expression codeContext) - : base(actionBinder) { - Assert.NotNull(actionBinder, codeContext); + // instance method call: + public PythonMethodBinder(ActionBinder binder, DynamicMetaObject instance, IList args, CallSignature signature, + Expression codeContext) + : base(binder, instance, args, signature) { + Assert.NotNull(codeContext); + _context = codeContext; + } + // instance method call: + public PythonMethodBinder(ActionBinder binder, DynamicMetaObject instance, IList args, CallSignature signature, + NarrowingLevel minLevel, NarrowingLevel maxLevel, Expression codeContext) + : base(binder, instance, args, signature, minLevel, maxLevel) { + Assert.NotNull(codeContext); _context = codeContext; } + // method call: + public PythonMethodBinder(ActionBinder binder, IList args, CallSignature signature, + NarrowingLevel minLevel, NarrowingLevel maxLevel, Expression codeContext) + : base(binder, args, signature, minLevel, maxLevel) { + Assert.NotNull(codeContext); + _context = codeContext; + } + + // method call: + public PythonMethodBinder(ActionBinder binder, IList args, CallSignature signature, Expression codeContext) + : base(binder, args, signature) { + Assert.NotNull(codeContext); + _context = codeContext; + } + + protected override bool BindSpecialParameter(ParameterInfo parameterInfo, List arguments, + List parameters, ref int index) { + + // CodeContext is implicitly provided at runtime, the user cannot provide it. + if (parameterInfo.ParameterType == typeof(CodeContext) && arguments.Count == 0) { + arguments.Add(new ContextArgBuilder(parameterInfo)); + return true; + } else if (parameterInfo.ParameterType.IsSubclassOf(typeof(SiteLocalStorage))) { + arguments.Add(new SiteLocalStorageBuilder(parameterInfo)); + return true; + } + + return base.BindSpecialParameter(parameterInfo, arguments, parameters, ref index); + } + + protected override Expression GetByRefArrayExpression(Expression argumentArrayExpression) { + return Expression.Call(typeof(PythonOps).GetMethod("MakeTuple"), argumentArrayExpression); + } + + protected override bool AllowKeywordArgumentSetting(MethodBase method) { + return CompilerHelpers.IsConstructor(method) && !method.DeclaringType.IsDefined(typeof(PythonTypeAttribute), true); + } + public override Expression ConvertExpression(Expression expr, ParameterInfo info, Type toType) { return Binder.ConvertExpression(expr, toType, ConversionResultKind.ExplicitCast, _context); } =================================================================== edit: $/Dev10/feature/vs_langs01_s/Merlin/Main/Languages/IronPython/IronPython/Runtime/Binding/PythonProtocol.Operations.cs;C791094 File: PythonProtocol.Operations.cs =================================================================== --- $/Dev10/feature/vs_langs01_s/Merlin/Main/Languages/IronPython/IronPython/Runtime/Binding/PythonProtocol.Operations.cs;C791094 (server) 4/6/2009 10:17 AM +++ Shelved Change: $/Dev10/feature/vs_langs01_s/Merlin/Main/Languages/IronPython/IronPython/Runtime/Binding/PythonProtocol.Operations.cs;NewMethodBinder @@ -1669,16 +1669,21 @@ Assert.NotNullItems(args); BindingTarget target; - - DynamicMetaObject res = Binder.CallInstanceMethod( - new ParameterBinderWithCodeContext(Binder, AstUtils.Constant(BinderState.Context)), - _bf.Targets, + + var methodBinder = new PythonMethodBinder( + Binder, args[0], ArrayUtils.RemoveFirst(args), new CallSignature(args.Length - 1), - BindingRestrictions.Combine(args), PythonNarrowing.None, PythonNarrowing.IndexOperator, + AstUtils.Constant(BinderState.Context) + ); + + DynamicMetaObject res = Binder.CallMethod( + methodBinder, + _bf.Targets, + BindingRestrictions.Combine(args), _bf.Name, out target ); @@ -1691,7 +1696,7 @@ ); } } else if (customFailure == null || (res = customFailure()) == null) { - res = DefaultBinder.MakeError(Binder.MakeInvalidParametersError(target), BindingRestrictions.Combine(ConvertArgs(args))); + res = DefaultBinder.MakeError(methodBinder.MakeInvalidParametersError(target), BindingRestrictions.Combine(ConvertArgs(args))); } return res; =================================================================== branch, edit: $/Dev10/feature/vs_langs01_s/Merlin/Main/Languages/IronPython/IronPython/Runtime/Binding/SiteLocalStorageBuilder.cs File: SiteLocalStorageBuilder.cs =================================================================== --- SiteLocalStorageBuilder.cs (server) 4/6/2009 10:17 AM +++ Shelved Change: $/Dev10/feature/vs_langs01_s/Merlin/Main/Languages/IronPython/IronPython/Runtime/Binding/SiteLocalStorageBuilder.cs;NewMethodBinder @@ -19,8 +19,9 @@ using System.Linq.Expressions; using System.Reflection; using AstUtils = Microsoft.Scripting.Ast.Utils; +using Microsoft.Scripting.Actions.Calls; -namespace Microsoft.Scripting.Actions.Calls { +namespace IronPython.Runtime.Binding { public sealed class SiteLocalStorageBuilder : ArgBuilder { public override int Priority { get { return -1; } @@ -30,17 +31,17 @@ : base(info) { } - internal protected override Expression ToExpression(ParameterBinder parameterBinder, IList parameters, bool[] hasBeenUsed) { + protected override Expression ToExpression(MethodBinder methodBinder, IList parameters, bool[] hasBeenUsed) { return AstUtils.Constant(Activator.CreateInstance(ParameterInfo.ParameterType)); } - internal override bool CanGenerateDelegate { + protected override bool CanGenerateDelegate { get { return true; } } - protected internal override Func ToDelegate(ParameterBinder parameterBinder, IList knownTypes, bool[] hasBeenUsed) { + protected override Func ToDelegate(MethodBinder methodBinder, IList knownTypes, bool[] hasBeenUsed) { object value = Activator.CreateInstance(ParameterInfo.ParameterType); return (args) => value; } =================================================================== edit: $/Dev10/feature/vs_langs01_s/Merlin/Main/Languages/IronPython/IronPython/Runtime/Binding/SlotOrFunction.cs;C791094 File: SlotOrFunction.cs =================================================================== --- $/Dev10/feature/vs_langs01_s/Merlin/Main/Languages/IronPython/IronPython/Runtime/Binding/SlotOrFunction.cs;C791094 (server) 4/6/2009 10:17 AM +++ Shelved Change: $/Dev10/feature/vs_langs01_s/Merlin/Main/Languages/IronPython/IronPython/Runtime/Binding/SlotOrFunction.cs;NewMethodBinder @@ -226,7 +226,14 @@ xBf = null; } - var mc = new ParameterBinderWithCodeContext(state.Binder, AstUtils.Constant(state.Context)); + var mc = new PythonMethodBinder( + state.Binder, + types, + new CallSignature(types.Length), + PythonNarrowing.None, + PythonNarrowing.BinaryOperator, + AstUtils.Constant(state.Context) + ); if (xBf == null) { if (yBf == null) { @@ -234,30 +241,12 @@ bt = null; } else { declaringType = DynamicHelpers.GetPythonTypeFromType(yBf.DeclaringType); - binder = state.Binder.CallMethod( - mc, - yBf.Targets, - types, - new CallSignature(types.Length), - BindingRestrictions.Empty, - PythonNarrowing.None, - PythonNarrowing.BinaryOperator, - out bt - ); + binder = state.Binder.CallMethod(mc, yBf.Targets, BindingRestrictions.Empty, null, out bt); } } else { if (yBf == null) { declaringType = DynamicHelpers.GetPythonTypeFromType(xBf.DeclaringType); - binder = state.Binder.CallMethod( - mc, - xBf.Targets, - types, - new CallSignature(types.Length), - BindingRestrictions.Empty, - PythonNarrowing.None, - PythonNarrowing.BinaryOperator, - out bt - ); + binder = state.Binder.CallMethod(mc, xBf.Targets, BindingRestrictions.Empty, null, out bt); } else { List targets = new List(); targets.AddRange(xBf.Targets); @@ -265,16 +254,7 @@ if (!ContainsMethodSignature(targets, mb)) targets.Add(mb); } - binder = state.Binder.CallMethod( - mc, - targets.ToArray(), - types, - new CallSignature(types.Length), - BindingRestrictions.Empty, - PythonNarrowing.None, - PythonNarrowing.BinaryOperator, - out bt - ); + binder = state.Binder.CallMethod(mc, targets.ToArray(), BindingRestrictions.Empty, null, out bt); foreach (MethodBase mb in yBf.Targets) { if (bt.Method == mb) { =================================================================== edit: $/Dev10/feature/vs_langs01_s/Merlin/Main/Languages/IronPython/IronPython/Runtime/Types/BuiltinFunction.cs;C812927 File: BuiltinFunction.cs =================================================================== --- $/Dev10/feature/vs_langs01_s/Merlin/Main/Languages/IronPython/IronPython/Runtime/Types/BuiltinFunction.cs;C812927 (server) 4/6/2009 10:17 AM +++ Shelved Change: $/Dev10/feature/vs_langs01_s/Merlin/Main/Languages/IronPython/IronPython/Runtime/Types/BuiltinFunction.cs;NewMethodBinder @@ -1070,7 +1070,6 @@ private KeyValuePair MakeCall(PythonInvokeBinder binder, object[] args) where T : class { Expression codeContext = Expression.Parameter(typeof(CodeContext), "context"); var pybinder = BinderState.GetBinderState(binder).Binder; - var paramBinder = new ParameterBinderWithCodeContext(pybinder, codeContext); KeyValuePair call; if (IsUnbound) { call = MakeBuiltinFunctionDelegate( @@ -1079,22 +1078,19 @@ GetMetaObjects(args), false, // no self (newArgs) => { - BindingTarget target; - DynamicMetaObject res = pybinder.CallMethod( - paramBinder, - Targets, + var methodBinder = new PythonMethodBinder( + pybinder, newArgs, BindingHelpers.GetCallSignature(binder), - BindingRestrictions.Empty, PythonNarrowing.None, - IsBinaryOperator ? - PythonNarrowing.BinaryOperator : - NarrowingLevel.All, - Name, - out target + IsBinaryOperator ? PythonNarrowing.BinaryOperator : NarrowingLevel.All, + codeContext ); - return new BuiltinFunction.BindingResult(target, res, target.Success ? target.MakeDelegate(paramBinder) : null); + BindingTarget target; + DynamicMetaObject res = pybinder.CallMethod(methodBinder, Targets, BindingRestrictions.Empty, Name, out target); + + return new BuiltinFunction.BindingResult(target, res, target.Success ? target.MakeDelegate() : null); } ); } else { @@ -1109,38 +1105,31 @@ CallSignature signature = BindingHelpers.GetCallSignature(binder); DynamicMetaObject res; BindingTarget target; + PythonMethodBinder methodBinder; + if (IsReversedOperator) { - res = pybinder.CallMethod( - paramBinder, - Targets, + methodBinder = new PythonMethodBinder( + pybinder, newArgs, MetaBuiltinFunction.GetReversedSignature(signature), - self.Restrictions, NarrowingLevel.None, - IsBinaryOperator ? - PythonNarrowing.BinaryOperator : - NarrowingLevel.All, - Name, - out target + IsBinaryOperator ? PythonNarrowing.BinaryOperator : NarrowingLevel.All, + codeContext ); } else { - res = pybinder.CallInstanceMethod( - paramBinder, - Targets, + methodBinder = new PythonMethodBinder( + pybinder, self, metaArgs, signature, - self.Restrictions, - NarrowingLevel.None, - IsBinaryOperator ? - PythonNarrowing.BinaryOperator : - NarrowingLevel.All, - Name, - out target + NarrowingLevel.None, + IsBinaryOperator ? PythonNarrowing.BinaryOperator : NarrowingLevel.All, + codeContext ); } - return new BuiltinFunction.BindingResult(target, res, target.Success ? target.MakeDelegate(paramBinder) : null); + res = pybinder.CallMethod(methodBinder, Targets, self.Restrictions, Name, out target); + return new BuiltinFunction.BindingResult(target, res, target.Success ? target.MakeDelegate() : null); } ); } =================================================================== edit: $/Dev10/feature/vs_langs01_s/Merlin/Main/Languages/IronPython/IronPython/Runtime/Types/InstanceCreator.cs;C791094 File: InstanceCreator.cs =================================================================== --- $/Dev10/feature/vs_langs01_s/Merlin/Main/Languages/IronPython/IronPython/Runtime/Types/InstanceCreator.cs;C791094 (server) 4/6/2009 10:19 AM +++ Shelved Change: $/Dev10/feature/vs_langs01_s/Merlin/Main/Languages/IronPython/IronPython/Runtime/Types/InstanceCreator.cs;NewMethodBinder @@ -172,7 +172,7 @@ Interlocked.CompareExchange( ref _ctorSite0, CallSite>.Create( - PythonContext.GetContext(context).DefaultBinderState.InvokeOne + PythonContext.GetContext(context).DefaultBinderState.InvokeNone ), null ); @@ -187,7 +187,7 @@ ref _ctorSite1, CallSite>.Create( PythonContext.GetContext(context).DefaultBinderState.Invoke( - new CallSignature(2) + new CallSignature(1) ) ), null @@ -203,7 +203,7 @@ ref _ctorSite2, CallSite>.Create( PythonContext.GetContext(context).DefaultBinderState.Invoke( - new CallSignature(3) + new CallSignature(2) ) ), null @@ -219,7 +219,7 @@ ref _ctorSite3, CallSite>.Create( PythonContext.GetContext(context).DefaultBinderState.Invoke( - new CallSignature(4) + new CallSignature(3) ) ), null =================================================================== edit: $/Dev10/feature/vs_langs01_s/Merlin/Main/Languages/IronPython/Tests/interop/net/method/test_arguments.py;C791094 File: test_arguments.py =================================================================== --- $/Dev10/feature/vs_langs01_s/Merlin/Main/Languages/IronPython/Tests/interop/net/method/test_arguments.py;C791094 (server) 4/6/2009 1:11 PM +++ Shelved Change: $/Dev10/feature/vs_langs01_s/Merlin/Main/Languages/IronPython/Tests/interop/net/method/test_arguments.py;NewMethodBinder @@ -102,15 +102,15 @@ f = o.M203 f() - AssertErrorWithMessage(TypeError, "M203() takes at least 0 arguments (1 given)", lambda: f(1)) #CP 18379 - AssertErrorWithMessage(TypeError, "M203() takes at least 0 arguments (1 given)", lambda: f({'a':1})) #CP 18379 + AssertErrorWithMessage(TypeError, "M203() takes no arguments (1 given)", lambda: f(1)) + AssertErrorWithMessage(TypeError, "M203() takes no arguments (1 given)", lambda: f({'a':1})) f(a=1) f(a=1, b=2) f(**{}) f(**{'a':2, 'b':3}) f(a=1, **{'b':2, 'c':5}) AssertErrorWithMessage(TypeError, "M203() got multiple values for keyword argument 'a'", lambda: f(a=1, **{'a':2, 'c':5})) - AssertErrorWithMessage(TypeError, "M203() takes at least 0 arguments (3 given)", lambda: f(*(1,2,3))) + AssertErrorWithMessage(TypeError, "M203() takes no arguments (3 given)", lambda: f(*(1,2,3))) def test_optional(): @@ -231,7 +231,7 @@ #public void M351(int x, [ParamDictionary] IAttributesCollection arg) { Flag.Set(arg); } f = o.M351 - AssertErrorWithMessage(TypeError, "M351() takes at least 1 argument (0 given)", lambda: f()) + AssertErrorWithMessage(TypeError, "M351() takes exactly 1 argument (0 given)", lambda: f()) f(1); AreEqual(Flag[object].Value1, {}) f(1, a=3); AreEqual(Flag[object].Value1, {'a':3}) f(1, a=3,b=4); AreEqual(Flag[object].Value1, {'a':3, 'b':4}) @@ -523,6 +523,14 @@ AreEqual(x.Value, 30) AreEqual(y.Value, 40) Flag.Check(2) - + +def test_splatting_errors(): + # public void M200(int arg) { Flag.Reset(); Flag.Set(arg); } + f = o.M200 + + AssertErrorWithMessage(TypeError, "M200() argument after * must be a sequence, not NoneType", lambda: f(*None)) + AssertErrorWithMessage(TypeError, "M200() argument after * must be a sequence, not str", lambda: f(*'x')) + AssertErrorWithMessage(TypeError, "M200() argument after * must be a sequence, not int", lambda: f(*1)) + run_test(__name__) =================================================================== edit: $/Dev10/feature/vs_langs01_s/Merlin/Main/Languages/Ruby/IronRuby.Tests/IronRuby.Tests.Build.csproj;C806904 File: IronRuby.Tests.Build.csproj =================================================================== --- $/Dev10/feature/vs_langs01_s/Merlin/Main/Languages/Ruby/IronRuby.Tests/IronRuby.Tests.Build.csproj;C806904 (server) 4/6/2009 10:17 AM +++ Shelved Change: $/Dev10/feature/vs_langs01_s/Merlin/Main/Languages/Ruby/IronRuby.Tests/IronRuby.Tests.Build.csproj;NewMethodBinder @@ -74,6 +74,7 @@ + =================================================================== edit: $/Dev10/feature/vs_langs01_s/Merlin/Main/Languages/Ruby/IronRuby.Tests/IronRuby.Tests.csproj;C806904 File: IronRuby.Tests.csproj =================================================================== --- $/Dev10/feature/vs_langs01_s/Merlin/Main/Languages/Ruby/IronRuby.Tests/IronRuby.Tests.csproj;C806904 (server) 4/6/2009 10:17 AM +++ Shelved Change: $/Dev10/feature/vs_langs01_s/Merlin/Main/Languages/Ruby/IronRuby.Tests/IronRuby.Tests.csproj;NewMethodBinder @@ -2,7 +2,7 @@ Debug AnyCPU - 9.0.30729 + 9.0.21022 2.0 {8103D91B-89D8-4A18-9A40-426992602EA2} Exe @@ -74,6 +74,7 @@ + =================================================================== edit: $/Dev10/feature/vs_langs01_s/Merlin/Main/Languages/Ruby/IronRuby.Tests/RubyTests.cs;C806904 File: RubyTests.cs =================================================================== --- $/Dev10/feature/vs_langs01_s/Merlin/Main/Languages/Ruby/IronRuby.Tests/RubyTests.cs;C806904 (server) 4/6/2009 10:17 AM +++ Shelved Change: $/Dev10/feature/vs_langs01_s/Merlin/Main/Languages/Ruby/IronRuby.Tests/RubyTests.cs;NewMethodBinder @@ -39,7 +39,8 @@ Scenario_RubyNameMangling1, Scenario_RubyNameMangling2, - OverloadResolution_Block, + OverloadResolution_Block1, + OverloadResolution_ParamArrays1, AmbiguousMatch, Scenario_RubySimpleCall1, @@ -280,6 +281,10 @@ Scenario_RubyArgSplatting1, Scenario_RubyArgSplatting2, Scenario_RubyArgSplatting3, + Scenario_RubyArgSplatting4, + Scenario_RubyArgSplatting5, + Scenario_RubyArgSplatting6, + Scenario_CaseSplatting1, Scenario_RubyBoolExpressions1, Scenario_RubyBoolExpressions2, Scenario_RubyBoolExpressions3, @@ -442,6 +447,7 @@ SuperParameterless2, SuperParameterless3, Super2, + SuperToAttribute1, SuperAndMethodMissing1, SuperAndMethodMissing2, SuperCaching1, =================================================================== edit: $/Dev10/feature/vs_langs01_s/Merlin/Main/Languages/Ruby/IronRuby.Tests/Runtime/ClrTests.cs;C806880 File: ClrTests.cs =================================================================== --- $/Dev10/feature/vs_langs01_s/Merlin/Main/Languages/Ruby/IronRuby.Tests/Runtime/ClrTests.cs;C806880 (server) 4/6/2009 10:17 AM +++ Shelved Change: $/Dev10/feature/vs_langs01_s/Merlin/Main/Languages/Ruby/IronRuby.Tests/Runtime/ClrTests.cs;NewMethodBinder @@ -560,7 +560,7 @@ M1() M1() M1() -# +# # " ); =================================================================== edit: $/Dev10/feature/vs_langs01_s/Merlin/Main/Languages/Ruby/IronRuby.Tests/Runtime/MiscTests.cs;C802353 File: MiscTests.cs =================================================================== --- $/Dev10/feature/vs_langs01_s/Merlin/Main/Languages/Ruby/IronRuby.Tests/Runtime/MiscTests.cs;C802353 (server) 4/6/2009 10:17 AM +++ Shelved Change: $/Dev10/feature/vs_langs01_s/Merlin/Main/Languages/Ruby/IronRuby.Tests/Runtime/MiscTests.cs;NewMethodBinder @@ -20,6 +20,13 @@ using IronRuby.Compiler.Ast; using IronRuby.Builtins; using Microsoft.Scripting.Math; +using System.Reflection; +using System.Reflection.Emit; +using Microsoft.Scripting.Utils; +using Microsoft.Scripting.Generation; +using System.Collections.Generic; +using IronRuby.Runtime; +using IronRuby.Runtime.Calls; namespace IronRuby.Tests { @@ -756,51 +763,7 @@ }, "foo"); } - - public void Scenario_RubyArgSplatting1() { - AssertOutput(delegate() { - CompilerTest(@" -def foo(a,b,c) - print a,b,c -end - -foo(*[1,2,3]) -"); - }, @"123"); - } - - public void Scenario_RubyArgSplatting2() { - AssertOutput(delegate() { - CompilerTest(@" -class C - def []=(a,b,c) - print a,b,c - end -end - -x = [1,2] -C.new[*x] = 3 -C.new[1, *[2]] = 3 -"); - }, @"123123"); - } - - public void Scenario_RubyArgSplatting3() { - AssertOutput(delegate() { - CompilerTest(@" -def foo(a,b,c) - print a,b,c - puts -end - -foo(1,2,*3) -foo(1,2,*nil) -"); - }, @" -123 -12nil"); - } - + public void ClassVariables1() { AssertOutput(delegate() { CompilerTest(@" =================================================================== edit: $/Dev10/feature/vs_langs01_s/Merlin/Main/Languages/Ruby/IronRuby.Tests/Runtime/OverloadResolutionTests.cs;C806880 File: OverloadResolutionTests.cs =================================================================== --- $/Dev10/feature/vs_langs01_s/Merlin/Main/Languages/Ruby/IronRuby.Tests/Runtime/OverloadResolutionTests.cs;C806880 (server) 4/6/2009 10:17 AM +++ Shelved Change: $/Dev10/feature/vs_langs01_s/Merlin/Main/Languages/Ruby/IronRuby.Tests/Runtime/OverloadResolutionTests.cs;NewMethodBinder @@ -15,45 +15,58 @@ using System; using System.Collections.Generic; -using System.Text; +using System.Dynamic; +using System.Reflection; +using IronRuby.Builtins; using IronRuby.Runtime; +using IronRuby.Runtime.Calls; using Microsoft.Scripting.Runtime; -using IronRuby.Runtime.Calls; -using System.Dynamic; - -using MSA = System.Linq.Expressions; using Ast = System.Linq.Expressions.Expression; -using IronRuby.Builtins; namespace IronRuby.Tests { public partial class Tests { + private static DynamicMetaObject/*!*/ MO(object value) { + return new DynamicMetaObject(Ast.Constant(value), BindingRestrictions.Empty, value); + } + + private static MethodInfo/*!*/[]/*!*/ GetStaticMethods(Type/*!*/ type, string/*!*/ name) { + return Array.ConvertAll(type.GetMember(name, BindingFlags.Public | BindingFlags.Static), (mi) => (MethodInfo)mi); + } + #region Block - - public void OverloadResolution_Block() { - var t = GetType(); - var gse = new RubyGlobalScope(Context, new Scope(), new object(), true); - var scope = new RubyTopLevelScope(gse, null, new SymbolDictionary()); + public class OverloadsWithBlock { + public static object Times1(BlockParam block, int self) { + return 1; + } + + public static object Times2(int self) { + return 2; + } + + public static object Times3([NotNull]BlockParam/*!*/ block, int self) { + return 3; + } + + public static object Times4(RubyContext/*!*/ context, BlockParam block, object self) { + return 4; + } + } + + public void OverloadResolution_Block1() { + var scope = Context.EmptyScope; var proc = new Proc(ProcKind.Proc, null, scope, new BlockDispatcher0((x, y) => null, BlockSignatureAttributes.None)); - var scopeArg = new DynamicMetaObject(Ast.Constant(proc.LocalScope), BindingRestrictions.Empty, proc.LocalScope); - var contextArg = new DynamicMetaObject(Ast.Constant(Context), BindingRestrictions.Empty, Context); - var instanceInt = new DynamicMetaObject(Ast.Constant(1), BindingRestrictions.Empty, 1); - var str = "foo"; - var instanceStr = new DynamicMetaObject(Ast.Constant(str), BindingRestrictions.Empty, str); - var procArg = new DynamicMetaObject(Ast.Constant(proc), BindingRestrictions.Empty, proc); - var nullArg = new DynamicMetaObject(Ast.Constant(Ast.Constant(null)), BindingRestrictions.Empty, null); - var arguments = new[] { // 1.times - new CallArguments(Context, scopeArg, new[] { instanceInt }, RubyCallSignature.WithScope(0)), + new CallArguments(Context, MO(scope), new[] { MO(1) }, RubyCallSignature.WithScope(0)), // 1.times &nil - new CallArguments(Context, scopeArg, new[] { instanceInt, nullArg }, RubyCallSignature.WithScopeAndBlock(0)), + new CallArguments(Context, MO(scope), new[] { MO(1), MO(null) }, RubyCallSignature.WithScopeAndBlock(0)), // 1.times &p - new CallArguments(Context, instanceInt, new[] { procArg }, RubyCallSignature.WithBlock(0)), + new CallArguments(Context, MO(1), new[] { MO(proc) }, RubyCallSignature.WithBlock(0)), // obj.times &p - new CallArguments(Context, instanceStr, new[] { procArg }, RubyCallSignature.WithBlock(0)), + new CallArguments(Context, MO("foo"), new[] { MO(proc) }, RubyCallSignature.WithBlock(0)), }; var results = new[] { @@ -63,42 +76,90 @@ "Times4", }; + var metaBuilder = new MetaObjectBuilder(null); for (int i = 0; i < arguments.Length; i++) { - var bindingTarget = RubyMethodGroupInfo.ResolveOverload("times", new[] { - t.GetMethod("Times1"), - t.GetMethod("Times2"), - t.GetMethod("Times3"), - t.GetMethod("Times4"), - }, arguments[i], SelfCallConvention.SelfIsParameter); + RubyMethodBinder parameterBinder; + var bindingTarget = RubyMethodGroupInfo.ResolveOverload( + metaBuilder, arguments[i], "times", GetStaticMethods(typeof(OverloadsWithBlock), "Times*"), SelfCallConvention.SelfIsParameter, + out parameterBinder + ); Assert(bindingTarget.Success); Assert(bindingTarget.Method.Name == results[i]); } } - public static object Times1(BlockParam block, int self) { - return 1; - } - - public static object Times2(int self) { - return 2; - } - - public static object Times3([NotNull]BlockParam/*!*/ block, int self) { - return 3; - } - - public static object Times4(RubyContext/*!*/ context, BlockParam block, object self) { - return 4; - } - #endregion + #region Param Arrays + + public class MethodsWithParamArrays { + public static KeyValuePair F1(int p1, params int[] ps) { + return new KeyValuePair(2, new object[] { p1, ps }); + } + + public static KeyValuePair F2(int p1) { + return new KeyValuePair(2, new object[] { p1 }); + } + + public static KeyValuePair F3(int p1, int p2) { + return new KeyValuePair(3, new object[] { p1, p2 }); + } + + public static KeyValuePair F4(double p1, int p2, params int[] ps) { + return new KeyValuePair(4, new object[] { p1, p2, ps }); + } + + public static KeyValuePair F5(bool p1, int p2, int p3, params int[] ps) { + return new KeyValuePair(5, new object[] { p1, p2, p3, ps }); + } + + internal static List/*!*/ GetMethods() { + var methods = new List(); + methods.AddRange(GetStaticMethods(typeof(MethodsWithParamArrays), "F*")); + methods.Add(CreateParamsArrayMethod("F0", new[] { typeof(int), typeof(int[]), typeof(string), typeof(int) }, 1, 0)); + return methods; + } + } + + public void OverloadResolution_ParamArrays1() { + } + + #endregion + + #region Failures + + public class AmbiguousOverloads { + public static int F(params object[] p) { + return 1; + } + + public static int F(object p) { + return 1; + } + + public static int F(int p) { + return 2; + } + + public static int F(string p) { + return 3; + } + } + public void AmbiguousMatch() { - AssertExceptionThrown(() => CompilerTest(@" -require 'mscorlib' -System::Console.WriteLine(nil) -")); + Context.SetGlobalConstant("C", Context.GetClass(typeof(AmbiguousOverloads))); + AssertOutput(() => CompilerTest(@" +[1, nil, 'foo'].each do |x| + puts C.f(x) rescue p $!.class +end +"), @" +2 +System::Reflection::AmbiguousMatchException +3 +"); } + + #endregion } } =================================================================== branch, edit: $/Dev10/feature/vs_langs01_s/Merlin/Main/Languages/Ruby/IronRuby.Tests/Runtime/SplattingTests.cs File: SplattingTests.cs =================================================================== --- MiscTests.cs (server) 4/6/2009 10:17 AM +++ Shelved Change: $/Dev10/feature/vs_langs01_s/Merlin/Main/Languages/Ruby/IronRuby.Tests/Runtime/SplattingTests.cs;NewMethodBinder @@ -14,749 +14,52 @@ * ***************************************************************************/ using System; -using Microsoft.Scripting; -using Microsoft.Scripting.Runtime; -using IronRuby.Compiler; -using IronRuby.Compiler.Ast; +using System.Collections.Generic; +using System.Reflection; +using System.Reflection.Emit; using IronRuby.Builtins; -using Microsoft.Scripting.Math; +using IronRuby.Runtime.Calls; +using Microsoft.Scripting.Generation; +using Microsoft.Scripting.Utils; namespace IronRuby.Tests { public partial class Tests { - public void Scenario_Globals1() { - Context.DefineGlobalVariable("x", 123); - CompilerTest(@"$z = $x"); - object z = Context.GetGlobalVariable("z"); - AssertEquals(z, 123); - } - public void Scenario_RubyLocals1() { - AssertOutput(delegate() { - CompilerTest(@" -def foo() - x = 1 - y = 2 - puts x + y -end + internal static MethodInfo/*!*/ CreateParamsArrayMethod(string/*!*/ name, Type/*!*/[]/*!*/ paramTypes, int paramsArrayIndex, int returnValue) { + var tb = Snippets.Shared.DefineType("", typeof(object), false, false).TypeBuilder; + var mb = tb.DefineMethod(name, CompilerHelpers.PublicStatic, typeof(KeyValuePair), paramTypes); + var pb = mb.DefineParameter(1 + paramsArrayIndex, ParameterAttributes.None, "ps"); + pb.SetCustomAttribute(new CustomAttributeBuilder(typeof(ParamArrayAttribute).GetConstructor(Type.EmptyTypes), ArrayUtils.EmptyObjects)); -foo -"); - }, "3"); - } + var il = mb.GetILGenerator(); + il.Emit(OpCodes.Ldc_I4, returnValue); + il.Emit(OpCodes.Ldarg, paramsArrayIndex); + il.Emit(OpCodes.Newobj, typeof(KeyValuePair).GetConstructor(new[] { typeof(int), typeof(Array) })); + il.Emit(OpCodes.Ret); + return tb.CreateType().GetMethod(name, BindingFlags.Public | BindingFlags.Static); - public void Scenario_UninitializedVars1() { - AssertOutput(delegate() { - CompilerTest(@" -def foo - x = 1 if false - puts x -end +#if FALSE + var array = il.DeclareLocal(typeof(object[])); + il.Emit(OpCodes.Ldc_I4, paramTypes.Length); + il.Emit(OpCodes.Newarr, typeof(object[])); + il.Emit(OpCodes.Stloc, array); -foo -"); - }, "nil"); - } + for (int i = 0; i < paramTypes.Length; i++) { + il.Emit(OpCodes.Ldloc, array); + il.Emit(OpCodes.Ldc_I4, i); + il.Emit(OpCodes.Ldarg, i); + il.Emit(OpCodes.Box, typeof(object)); + il.Emit(OpCodes.Stelem, typeof(object)); + } - public void Scenario_UninitializedVars2() { - AssertExceptionThrown(delegate() { - CompilerTest(@" -def foo - puts x -end - -foo -"); - }); + il.Emit(OpCodes.Ldloc, array); + il.Emit(OpCodes.Ldc_I4, returnValue); + il.Emit(OpCodes.Newobj, typeof(KeyValuePair).GetConstructor(new[] { typeof(Array), typeof(int) })); + il.Emit(OpCodes.Ret); +#endif } - // Fixnum doesn't have identity in Ruby - public void InstanceVariables1() { - AssertOutput(delegate() { - CompilerTest(@" -class Fixnum - def foo= a - @x = a - end - def foo - @x - end -end - -a = 1 -b = 1 -c = 2 - -a.foo = 1 -b.foo = 2 -c.foo = 3 -puts a.foo, b.foo, c.foo -"); - }, - @" -2 -2 -3"); - } - - // Float has an identity in Ruby - public void InstanceVariables2() { - AssertOutput(delegate() { - CompilerTest(@" -class Float - def foo= a - @x = a - end - def foo - @x - end -end - -a = 1.0 -b = 1.0 - -a.foo = 1 -b.foo = 2 -puts a.foo, b.foo -"); - }, - @" -1 -2"); - } - - - public void Scenario_RubyParams1() { - AssertOutput(delegate() { - CompilerTest(@" -def foo(a,b) - puts a + b -end - -puts foo(1,2) -"); - }, - @" -3 -nil"); - } - - public void Scenario_RubyParams2() { - AssertOutput(delegate() { - CompilerTest(@" -def foo(a, b, c = a + b, d = c + 1) - puts a,b,c,d -end - -foo(1,2) -"); - }, - @" -1 -2 -3 -4 -"); - } - - public void Scenario_RubyMethodMissing1() { - AssertOutput(delegate { - CompilerTest(@" -class C - def method_missing(name, *a) - puts name, a - end -end - -C.new.foo 1,2,3 -"); - }, @" -foo -1 -2 -3 -"); - - } - - public void Scenario_RubyMethodMissing2() { - AssertExceptionThrown(delegate { - CompilerTest(@"unknown_method"); - }, delegate(MissingMethodException e) { - return e.Message.StartsWith("undefined method"); - }); - } - - public void Scenario_RubySingletonConstants1() { - AssertOutput(delegate { - CompilerTest(@" -puts nil.to_s -puts true.to_s -puts false.to_s - -puts nil | false -puts true & [] -puts false ^ [] -puts nil.nil? -puts nil.to_i -puts nil.to_f -"); - }, @" - -true -false -false -true -true -true -0 -0.0 -"); - - } - - public void Scenario_RubyMath1() { - AssertOutput(delegate { - CompilerTest(@" -puts Math.acos(1.0) -puts Math.acos(1) -"); - }, @" -0.0 -0.0"); - - } - - public void Scenario_RubyScopeParsing() { - LoggingErrorSink log = new LoggingErrorSink(); - - SourceUnitTree p; - SourceUnit unit; - - unit = Context.CreateSnippet(@" - class c << G - end - ", SourceCodeKind.File); - - p = new Parser().Parse(unit, new RubyCompilerOptions(), log); - Assert(p == null && log.FatalErrorCount == 1); - log.ClearCounters(); - - unit = Context.CreateSnippet(@" - def goo(&b) - end - - class C - def foo() - x.goo() { - goo() { - class << M - def bar() - goo() { - } - end - end - } - } - end - end - - BEGIN { goo1() { } } - END { goo2() { } } - ", SourceCodeKind.File); - - p = new Parser().Parse(unit, new RubyCompilerOptions(), ErrorSink.Null); - Assert(p != null && !log.AnyError); - log.ClearCounters(); - - unit = Context.CreateSnippet(@" - for x in array do - goo() - end - ", SourceCodeKind.File); - - p = new Parser().Parse(unit, new RubyCompilerOptions(), ErrorSink.Null); - Assert(p != null && !log.AnyError); - log.ClearCounters(); - } - - /// - /// Tests that class lexical scopes are applied to method definitions. - /// - public void Scenario_RubyScopes1() { - AssertOutput(delegate() { - CompilerTest(@" -class A; def foo; print 'A'; end; end -class B; def foo; print 'B'; end; end -A.new.foo -B.new.foo -"); - }, @"AB"); - } - - /// - /// Uninitialized local variables. - /// - public void Scenario_RubyScopes2A() { - AssertOutput(delegate() { - CompilerTest(@" -def foo - x = 1 if false - p x -end - -foo -"); - }, @"nil"); - } - - /// - /// Uninitialized local variables in closure. - /// - public void Scenario_RubyScopes2B() { - AssertOutput(delegate() { - CompilerTest(@" -def foo - x = 1 if false - 1.times { p x } -end - -foo -"); - }, @"nil"); - } - - /// - /// Tests that variables defined in module/class locals scope are not visible outside. - /// - public void Scenario_RubyScopes3() { - AssertOutput(delegate() { - CompilerTest(@" -y = 'var' -class C - x = 'var' -end - -def x; 'method'; end -def y; 'method'; end - -puts x -puts y -"); - }, @" -method -var"); - } - - public void Scenario_RubyScopes4() { - AssertOutput(delegate() { - CompilerTest(@" -module Bar - class C - module M - puts self - end - end -end -"); - }, @" -Bar::C::M -"); - } - - public void Scenario_RubyScopes5() { - AssertOutput(delegate() { - CompilerTest(@" -module M - module Z - E = self - end -end - -include M::Z - -puts E -"); - }, @" -M::Z -"); - } - - /// - /// Nested module scopes - they don't have DLR tuples and therefore need to be skipped when looking up a storage from closure. - /// - public void Scenario_RubyScopes6() { - CompilerTest(@" -1.times { - module M - module N - z = 1 - 1.times { - x = z - } - end - end -} -"); - } - - public void NumericLiterals1() { - AssertOutput(delegate() { - CompilerTest(@" -puts(1) -puts(-1) -puts(+1) -puts(1.1) -puts(-1.1) -puts(+1.1) - -x = 2.0 -puts x -puts 2.0 -puts 2.1560 -"); - }, @" -1 --1 -1 -1.1 --1.1 -1.1 -2.0 -2.0 -2.156 -"); - } - - public void NumericOps1() { - // overflow tests: - Assert((BigInteger)ClrInteger.Minus(Int32.MinValue) == -(BigInteger)Int32.MinValue); - Assert((BigInteger)ClrInteger.Abs(Int32.MinValue) == -(BigInteger)Int32.MinValue); - - Assert((BigInteger)ClrInteger.Divide(Int32.MinValue, -1) == -(BigInteger)Int32.MinValue); - Assert(ClrInteger.Modulo(Int32.MinValue, -1) == 0); - - var dm = ClrInteger.DivMod(Int32.MinValue, -1); - Assert((BigInteger)dm[0] == -(BigInteger)Int32.MinValue); - Assert((int)dm[1] == 0); - - Assert((int)ClrInteger.LeftShift(1, Int32.MinValue) == 0); - AssertExceptionThrown(() => ClrInteger.RightShift(1, Int32.MinValue)); - } - - public void Scenario_RubyInclusions1() { - AssertOutput(delegate() { - CompilerTest(@" -module M - def foo - puts 'foo' - end -end - -class C - include M -end - -C.new.foo -"); - }, "foo"); - } - - public void Scenario_RubyClassVersions1() { - AssertOutput(delegate() { - CompilerTest(@" -class C - def m - puts 'v1' - end -end - -C.new.m - -class C - def m - puts 'v2' - end -end - -C.new.m -"); - }, -@" -v1 -v2 -"); - } - - public void Scenario_RubyClassVersions2() { - AssertOutput(delegate() { - CompilerTest(@" -module M - def m; puts 'v1'; end -end - -module N - def m; puts 'v2'; end -end - -class C - include M -end - -class D < C -end - -D.new.m - -class D - include N -end - -D.new.m -"); - }, -@" -v1 -v2 -"); - } - - public void InvokeMemberCache1() { - AssertOutput(delegate() { - CompilerTest(@" -def try_call - B.m rescue puts 'error' -end - -class B; end - -try_call # m not defined - -class Class - def m; puts 'ok' end -end - -try_call # m defined on Class -"); - }, -@" -error -ok -"); - } - - public void Scenario_RubyBlockExpressions1() { - AssertOutput(delegate() { - CompilerTest(@" -puts class Foo - 'A' -end - -puts def bar - 'B' -end - -puts module M - 'C' -end - -puts begin - 'D' -end - -x = ('E'; 'F') -puts x -"); - }, @" -A -nil -C -D -F"); - } - - public void Scenario_ClassVariables1() { - AssertOutput(delegate() { - CompilerTest(@" -module M - @@m = 1 - - def goo - @@n = 2 - end -end - -class C - include M - - def foo - @@a = 1 - end - - @@b = 2 - @@c = 1 -end - -class D < C - def bar - @@c = 3 - end - - @@d = 4 -end - -C.new.foo -D.new.bar -C.new.goo - -p C.class_variables.sort -p D.class_variables.sort -p M.class_variables.sort"); - }, @" -[""@@a"", ""@@b"", ""@@c"", ""@@m"", ""@@n""] -[""@@a"", ""@@b"", ""@@c"", ""@@d"", ""@@m"", ""@@n""] -[""@@m"", ""@@n""] -"); - } - - public void Scenario_ClassVariables2() { - AssertOutput(delegate() { - CompilerTest(@" -class C - @@x = 1 -end - -class D < C - remove_class_variable :@@x rescue puts 'Error' - @@y = 'foo' - puts remove_class_variable(:@@y) -end -"); - }, @" -Error -foo -"); - } - - public void Scenario_RubyReturnValues1() { - AssertOutput(delegate() { - CompilerTest(@" -x = while true do - break 1,2,3 -end -puts x -"); - }, @" -1 -2 -3 -"); - } - - public void Scenario_RubyReturnValues2() { - AssertOutput(delegate() { - CompilerTest(@" -x = while true do - break 1 => 2, 3 => 4 -end -puts x -"); - }, @"1234"); - } - - public void Scenario_RubyReturnValues3() { - AssertOutput(delegate() { - CompilerTest(@" -x = while true do - break 'a', 'b', 1 => 2, 3 => 4 -end -puts x -"); - }, @" -a -b -1234"); - } - - public void Scenario_RubyReturnValues4() { - AssertOutput(delegate() { - CompilerTest(@" -x = while true do - break 'a', 'b', 1 => 2, 3 => 4, *['A', 'B'] -end -puts x -"); - }, @" -a -b -1234 -A -B"); - } - - public void Scenario_RubyReturnValues5() { - AssertOutput(delegate() { - CompilerTest(@" -def foo - return 1,2,3, 4 => 5, *[6,7] -end -puts foo -"); - }, @" -1 -2 -3 -45 -6 -7 -"); - } - - public void Scenario_RubyReturnValues6() { - AssertOutput(delegate() { - CompilerTest(@" -def foo - return *$x = [1,2] -end -$y = foo - -puts $x.object_id == $y.object_id -puts $x.inspect -"); - }, @" -true -[1, 2]"); - } - - public void Scenario_RubyClosures1() { - AssertOutput(delegate() { - CompilerTest(@" -def foo - y = 10 - 3.times { |x| puts x + y } -end - -foo -"); - }, @" -10 -11 -12"); - } - - public void Scenario_RubyReturn1() { - AssertOutput(delegate() { - CompilerTest(@" -def foo() - return 'foo' -end - -puts foo -"); - }, - "foo"); - } - public void Scenario_RubyArgSplatting1() { AssertOutput(delegate() { CompilerTest(@" @@ -801,162 +104,178 @@ 12nil"); } - public void ClassVariables1() { + /// + /// Splat anything that's IList (including arrays and values passed via out parameters). + /// + public void Scenario_RubyArgSplatting4() { AssertOutput(delegate() { CompilerTest(@" -module M - @@m = 3 - def moo - puts @@m - puts @@a rescue puts '!a' - end -end +a,b,c = System::Array[Fixnum].new([1,2,3]) +p [a,b,c] -class A - @@a = 1 +def y1; yield System::Array[Fixnum].new([4]); end +def y2; yield System::Array[Fixnum].new([4,5]); end +def y3; yield System::Array[Fixnum].new([4,5,6]); end +def y10; yield System::Array[Fixnum].new([1,2,3,4,5,6,7,8,9,10]); end - include M +y1 { |x| p [x] } +y2 { |x,y| p [x,y] } +y3 { |x,y,z| p [x,y,z] } +y10 { |a1,a2,a3,a4,a5,a6,a7,a8,a9,a10| p [a1,a2,a3,a4,a5,a6,a7,a8,a9,a10] } - class B - @@b = 2 - def foo - puts @@b - end - end - - def foo - puts @@a - end -end - -x = A.new -y = A::B.new -x.foo -x.moo -y.foo +dict = System::Collections::Generic::Dictionary[Fixnum, Fixnum].new +dict.add(1,1) +has_value, value = dict.try_get_value(1) +p [has_value, value] "); }, @" -1 -3 -!a -2 +[1, 2, 3] +[[4]] +[4, 5] +[4, 5, 6] +[1, 2, 3, 4, 5, 6, 7, 8, 9, 10] +[true, 1] "); } - public void Scenario_RubyThreads1() { + /// + /// Splat anything that implements to_ary. + /// + public void Scenario_RubyArgSplatting5() { + // TODO: +// XAssertOutput(delegate() { +// CompilerTest(@" +//"); +// }, @" +//"); + } + + public void Scenario_RubyArgSplatting6() { + var c = Context.GetClass(typeof(MethodsWithParamArrays)); + Context.SetGlobalConstant("C", new MethodsWithParamArrays()); + + // The post-param-array arguments might decide which overload to call: + c.SetLibraryMethod("bar", new RubyMethodGroupInfo(new[] { + CreateParamsArrayMethod("B0", new[] { typeof(int), typeof(int), typeof(int[]), typeof(bool) }, 2, 0), + CreateParamsArrayMethod("B1", new[] { typeof(int), typeof(int[]), typeof(int), typeof(int) }, 1, 1), + }, c, null, true), true); + AssertOutput(delegate() { CompilerTest(@" -t = Thread.new 1,2,3 do |a,b,c| - puts a,b,c -end -t.join -puts 4 +x = C.bar(*[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,true]) +p x.key, x.value +x = C.bar(*[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15]) +p x.key, x.value "); }, @" +0 +[2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14] 1 -2 -3 -4 +[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13] "); - } - public void Scenario_YieldCodeGen() { + // Huge splattees. AssertOutput(delegate() { CompilerTest(@" -def foo - puts 1, yield +[ +[1], +[1] * 2, +[1] * 3, +[1] * 4, +[1] * 5, +[1] * 10003 + [true], +[1] * 10003, +].each do |s| + begin + x = C.bar(*s) + puts ""B#{x.key} -> #{x.value.length}"" + rescue + p $! + end end -foo { 2 } "); }, @" -1 -2 +# +# +B1 -> 0 +B1 -> 1 +B1 -> 2 +B0 -> 10001 +B1 -> 10000 "); - } - public void Scenario_MainSingleton() { + // Overloads might differ only in the element types of params-array. + // If binder decision is not based upon all splatted item types + c.SetLibraryMethod("baz", new RubyMethodGroupInfo(new[] { + CreateParamsArrayMethod("Z0", new[] { typeof(int), typeof(object[]) }, 1, 0), + CreateParamsArrayMethod("Z1", new[] { typeof(int), typeof(MutableString[]) }, 1, 1), + CreateParamsArrayMethod("Z2", new[] { typeof(int), typeof(int[]) }, 1, 2), + }, c, null, true), true); + AssertOutput(delegate() { CompilerTest(@" -require 'mscorlib' -include System::Collections -puts ArrayList +[ +[1] * 20 + ['x'] + [1] * 20, +[1] * 10001, +[1] * 10000 + [true], +[1] + ['x'] * 10000, +].each do |s| + x = C.baz(*s) + puts ""Z#{x.key} -> #{x.value.length}"" +end "); - }, @"System::Collections::ArrayList"); - } + }, @" +Z0 -> 40 +Z2 -> 10000 +Z0 -> 10000 +Z1 -> 10000 +"); - public void Scenario_ModuleOps_Methods() { + // Tests error handling and caching. + c.SetLibraryMethod("error", new RubyMethodGroupInfo(new[] { + CreateParamsArrayMethod("E1", new[] { typeof(int), typeof(MutableString[]) }, 1, 1), + CreateParamsArrayMethod("E2", new[] { typeof(int), typeof(int[]) }, 1, 2), + }, c, null, true), true); + AssertOutput(delegate() { CompilerTest(@" -class C - def ifoo - puts 'ifoo' +[ +[1] + [2] * 10000, +[1] * 20 + ['zzz'] + [1] * 20, +[1] + ['x'] * 10000, +].each do |s| + begin + x = C.error(*s) + puts ""Z#{x.key} -> #{x.value.length}"" + rescue + p $! end end - -class << C - $C1 = self - - def foo - puts 'foo' - end -end - -class C - alias_method(:bar,:foo) rescue puts 'Error 1' - instance_method(:foo) rescue puts 'Error 2' - puts method_defined?(:foo) - foo - - alias_method(:ibar,:ifoo) - instance_method(:ifoo) - puts method_defined?(:ifoo) - ifoo rescue puts 'Error 3' - - remove_method(:ifoo) -end - -C.new.ifoo rescue puts 'Error 4' -C.new.ibar "); }, @" -Error 1 -Error 2 -false -foo -true -Error 3 -Error 4 -ifoo +Z2 -> 10000 +# +Z1 -> 10000 "); + + // TODO: test GetPreferredParameters with collapsed arguments } - public void Methods1() { - AssertOutput(delegate() { - CompilerTest(@" -class C - def foo a,b - puts a + b + public void Scenario_CaseSplatting1() { + AssertOutput(() => CompilerTest(@" +[0,2,5,8,6,7,9,4].each do |x| + case x + when 0,1,*[2,3,4]: print 0 + when *[5]: print 1 + when *[6,7]: print 2 + when *8: print 3 + when *System::Array[Fixnum].new([9]): print 4 end end - -class D < C -end - -c = C.new -p m = c.method(:foo) -p u = m.unbind -p n = u.bind(D.new) - -m[1,2] -n[1,2] +"), @" +00132240 "); - }, @" -# -# -# -3 -3 -"); } + } } =================================================================== edit: $/Dev10/feature/vs_langs01_s/Merlin/Main/Languages/Ruby/IronRuby.Tests/Runtime/SuperTests.cs;C791094 File: SuperTests.cs =================================================================== --- $/Dev10/feature/vs_langs01_s/Merlin/Main/Languages/Ruby/IronRuby.Tests/Runtime/SuperTests.cs;C791094 (server) 4/6/2009 10:17 AM +++ Shelved Change: $/Dev10/feature/vs_langs01_s/Merlin/Main/Languages/Ruby/IronRuby.Tests/Runtime/SuperTests.cs;NewMethodBinder @@ -197,6 +197,35 @@ "); } + public void SuperToAttribute1() { + AssertOutput(() => CompilerTest(@" +class C + attr_accessor :foo +end + +class D < C + def foo + super + end + + def foo= v + super + end +end + +d = D.new +p d.foo = 123 +p d.foo +p d.foo {} +p d.foo(1) rescue p $! +"), @" +123 +123 +123 +# +"); + } + /// /// Super calls method_missing. /// =================================================================== edit: $/Dev10/feature/vs_langs01_s/Merlin/Main/Languages/Ruby/Libraries.LCA_RESTRICTED/Builtins/ArrayOps.cs;C806880 File: ArrayOps.cs =================================================================== --- $/Dev10/feature/vs_langs01_s/Merlin/Main/Languages/Ruby/Libraries.LCA_RESTRICTED/Builtins/ArrayOps.cs;C806880 (server) 4/6/2009 10:17 AM +++ Shelved Change: $/Dev10/feature/vs_langs01_s/Merlin/Main/Languages/Ruby/Libraries.LCA_RESTRICTED/Builtins/ArrayOps.cs;NewMethodBinder @@ -102,11 +102,11 @@ return self; } - private static object CreateArray([NotNull]BlockParam/*!*/ block, int size) { + private static object CreateArray(BlockParam/*!*/ block, int size) { return Reinitialize(block, new RubyArray(), size); } - private static object Reinitialize([NotNull]BlockParam/*!*/ block, RubyArray/*!*/ self, int size) { + private static object Reinitialize(BlockParam/*!*/ block, RubyArray/*!*/ self, int size) { if (size < 0) { throw RubyExceptions.CreateArgumentError("negative array size"); } =================================================================== edit: $/Dev10/feature/vs_langs01_s/Merlin/Main/Languages/Ruby/Ruby/Builtins/RubyClass.cs;C806880 File: RubyClass.cs =================================================================== --- $/Dev10/feature/vs_langs01_s/Merlin/Main/Languages/Ruby/Ruby/Builtins/RubyClass.cs;C806880 (server) 4/6/2009 10:17 AM +++ Shelved Change: $/Dev10/feature/vs_langs01_s/Merlin/Main/Languages/Ruby/Ruby/Builtins/RubyClass.cs;NewMethodBinder @@ -735,12 +735,11 @@ } private MemberInfo[]/*!*/ GetDeclaredClrMethods(Type/*!*/ type, BindingFlags bindingFlags, string/*!*/ name) { - // GetMember uses prefix matching if the name ends with '*': - if (name.LastCharacter() != '*') { - return type.GetMember(name, MemberTypes.Method, bindingFlags | BindingFlags.InvokeMethod); - } else { - return new MemberInfo[0]; + // GetMember uses prefix matching if the name ends with '*', add another * to match the original name: + if (name.LastCharacter() == '*') { + name += "*"; } + return type.GetMember(name, MemberTypes.Method, bindingFlags | BindingFlags.InvokeMethod); } // Returns the number of methods newly added to the dictionary. @@ -1042,13 +1041,11 @@ )); } } else { - var actualArgs = RubyMethodGroupBase.NormalizeArguments(metaBuilder, args, SelfCallConvention.NoSelf, false, false); - if (actualArgs.Length == 1) { + var actualArgs = RubyMethodBinder.NormalizeArguments(metaBuilder, args, 1, 1); + if (!metaBuilder.Error) { var convertBinder = args.RubyContext.CreateConvertBinder(type, true); var converted = convertBinder.Bind(actualArgs[0], DynamicMetaObject.EmptyMetaObjects); metaBuilder.SetMetaResult(converted, args); - } else { - metaBuilder.SetWrongNumberOfArgumentsError(actualArgs.Length, 1); } } } =================================================================== edit: $/Dev10/feature/vs_langs01_s/Merlin/Main/Languages/Ruby/Ruby/Builtins/RubyStruct.cs;C791094 File: RubyStruct.cs =================================================================== --- $/Dev10/feature/vs_langs01_s/Merlin/Main/Languages/Ruby/Ruby/Builtins/RubyStruct.cs;C791094 (server) 4/6/2009 10:17 AM +++ Shelved Change: $/Dev10/feature/vs_langs01_s/Merlin/Main/Languages/Ruby/Ruby/Builtins/RubyStruct.cs;NewMethodBinder @@ -142,7 +142,7 @@ cls.SingletonClass.DefineRuleGenerator("new", (int)RubyMethodAttributes.PublicSingleton, newInstance); cls.SingletonClass.DefineLibraryMethod("members", (int)RubyMethodAttributes.PublicSingleton, - new Func>(GetMembers) + new Func(GetMembers) ); for (int i = 0; i < structMembers.Length; i++) { @@ -165,16 +165,14 @@ private static RuleGenerator/*!*/ CreateGetter(int index) { return delegate(MetaObjectBuilder/*!*/ metaBuilder, CallArguments/*!*/ args, string/*!*/ name) { - - var actualArgs = RubyMethodGroupInfo.NormalizeArguments(metaBuilder, args, SelfCallConvention.NoSelf, false, false); - if (actualArgs.Length == 0) { + + var actualArgs = RubyMethodBinder.NormalizeArguments(metaBuilder, args, 0, 0); + if (!metaBuilder.Error) { metaBuilder.Result = Ast.Call( Ast.Convert(args.TargetExpression, typeof(RubyStruct)), Methods.RubyStruct_GetValue, AstUtils.Constant(index) ); - } else { - metaBuilder.SetWrongNumberOfArgumentsError(actualArgs.Length, 0); } }; } @@ -182,16 +180,14 @@ private static RuleGenerator/*!*/ CreateSetter(int index) { return delegate(MetaObjectBuilder/*!*/ metaBuilder, CallArguments/*!*/ args, string/*!*/ name) { - var actualArgs = RubyMethodGroupInfo.NormalizeArguments(metaBuilder, args, SelfCallConvention.NoSelf, false, false); - if (actualArgs.Length == 1) { + var actualArgs = RubyMethodBinder.NormalizeArguments(metaBuilder, args, 1, 1); + if (!metaBuilder.Error) { metaBuilder.Result = Ast.Call( Ast.Convert(args.TargetExpression, typeof(RubyStruct)), Methods.RubyStruct_SetValue, AstUtils.Constant(index), AstFactory.Box(actualArgs[0].Expression) ); - } else { - metaBuilder.SetWrongNumberOfArgumentsError(actualArgs.Length, 1); } }; } =================================================================== edit: $/Dev10/feature/vs_langs01_s/Merlin/Main/Languages/Ruby/Ruby/Compiler/ReflectionCache.Generated.cs;C802353 File: ReflectionCache.Generated.cs =================================================================== --- $/Dev10/feature/vs_langs01_s/Merlin/Main/Languages/Ruby/Ruby/Compiler/ReflectionCache.Generated.cs;C802353 (server) 4/6/2009 10:17 AM +++ Shelved Change: $/Dev10/feature/vs_langs01_s/Merlin/Main/Languages/Ruby/Ruby/Compiler/ReflectionCache.Generated.cs;NewMethodBinder @@ -382,6 +382,8 @@ private static MethodInfo _TraceMethodCall; public static MethodInfo/*!*/ TraceMethodReturn { get { return _TraceMethodReturn ?? (_TraceMethodReturn = GetMethod(typeof(RubyOps), "TraceMethodReturn")); } } private static MethodInfo _TraceMethodReturn; + public static MethodInfo/*!*/ ExistsUnsplat { get { return _ExistsUnsplat ?? (_ExistsUnsplat = GetMethod(typeof(RubyOps), "ExistsUnsplat")); } } + private static MethodInfo _ExistsUnsplat; public static MethodInfo/*!*/ TryGetClassVariable { get { return _TryGetClassVariable ?? (_TryGetClassVariable = GetMethod(typeof(RubyOps), "TryGetClassVariable")); } } private static MethodInfo _TryGetClassVariable; public static MethodInfo/*!*/ TryGetObjectClassVariable { get { return _TryGetObjectClassVariable ?? (_TryGetObjectClassVariable = GetMethod(typeof(RubyOps), "TryGetObjectClassVariable")); } } =================================================================== edit: $/Dev10/feature/vs_langs01_s/Merlin/Main/Languages/Ruby/Ruby/Compiler/Ast/Expressions/CaseExpression.cs;C806880 File: CaseExpression.cs =================================================================== --- $/Dev10/feature/vs_langs01_s/Merlin/Main/Languages/Ruby/Ruby/Compiler/Ast/Expressions/CaseExpression.cs;C806880 (server) 4/6/2009 10:17 AM +++ Shelved Change: $/Dev10/feature/vs_langs01_s/Merlin/Main/Languages/Ruby/Ruby/Compiler/Ast/Expressions/CaseExpression.cs;NewMethodBinder @@ -13,18 +13,18 @@ * * ***************************************************************************/ -using System.Collections; +using System; using System.Collections.Generic; +using System.Runtime.CompilerServices; +using IronRuby.Runtime.Calls; using Microsoft.Scripting; -using Microsoft.Scripting.Actions; using Microsoft.Scripting.Utils; using AstUtils = Microsoft.Scripting.Ast.Utils; using MSA = System.Linq.Expressions; -using IronRuby.Runtime.Calls; namespace IronRuby.Compiler.Ast { using Ast = System.Linq.Expressions.Expression; - + public partial class CaseExpression : Expression { // case value @@ -79,10 +79,9 @@ // when // generates into: // RubyOps.IsTrue() if the case has no value, otherise: - // RubyOps.IsTrue(InvokeMember("===", , )) - private static MSA.Expression/*!*/ MakeTest(AstGenerator/*!*/ gen, MSA.Expression/*!*/ expr, MSA.Expression/*!*/ value) { + // RubyOps.IsTrue(Call("===", , )) + private static MSA.Expression/*!*/ MakeTest(AstGenerator/*!*/ gen, MSA.Expression/*!*/ expr, MSA.Expression value) { if (value != null) { - // InvokeMember("===", , ) expr = Ast.Dynamic(RubyCallAction.Make(gen.Context, "===", RubyCallSignature.WithScope(1)), typeof(object), gen.CurrentScopeVariable, expr, @@ -93,46 +92,12 @@ } // when [, ...] * - // - // generates this code: - // - // IEnumerator/*!*/ enumVar = RubyOps.Unsplat().GetEnumerator(); - // bool result = false; - // while (enumVar.MoveNext()) { - // if ((enumVar.Current)) { - // result = true; - // break; - // } - // } private static MSA.Expression/*!*/ MakeArrayTest(AstGenerator/*!*/ gen, MSA.Expression/*!*/ array, MSA.Expression value) { - MSA.Expression enumVariable = gen.CurrentScope.DefineHiddenVariable("#case-enumerator", typeof(IEnumerator)); - MSA.Expression resultVariable = gen.CurrentScope.DefineHiddenVariable("#case-compare-result", typeof(bool)); - - MSA.LabelTarget label = Ast.Label(); - return AstFactory.Block( - Ast.Assign(enumVariable, Ast.Call( - Methods.Unsplat.OpCall(AstFactory.Box(array)), - Methods.IEnumerable_Of_Object_GetEnumerator - )), - - Ast.Assign(resultVariable, AstUtils.Constant(false)), - - AstUtils.While( - Ast.Call(enumVariable, Methods.IEnumerator_MoveNext), - AstUtils.If( - MakeTest(gen, Ast.Call(enumVariable, Methods.IEnumerator_get_Current), value), - Ast.Block( - Ast.Assign(resultVariable, AstUtils.Constant(true)), - Ast.Break(label), - AstUtils.Empty() - ) - ), - null, - label, - null - ), - resultVariable - ); + return Methods.ExistsUnsplat.OpCall(Ast.Constant( + CallSite>.Create( + RubyCallAction.Make(gen.Context, "===", RubyCallSignature.WithImplicitSelf(2)) + ) + ), AstFactory.Box(array), AstFactory.Box(value)); } // when , ... [*] =================================================================== edit: $/Dev10/feature/vs_langs01_s/Merlin/Main/Languages/Ruby/Ruby/Compiler/Ast/LeftValues/CompoundLeftValue.cs;C791094 File: CompoundLeftValue.cs =================================================================== --- $/Dev10/feature/vs_langs01_s/Merlin/Main/Languages/Ruby/Ruby/Compiler/Ast/LeftValues/CompoundLeftValue.cs;C791094 (server) 4/6/2009 10:17 AM +++ Shelved Change: $/Dev10/feature/vs_langs01_s/Merlin/Main/Languages/Ruby/Ruby/Compiler/Ast/LeftValues/CompoundLeftValue.cs;NewMethodBinder @@ -25,6 +25,7 @@ namespace IronRuby.Compiler.Ast { using Ast = System.Linq.Expressions.Expression; + using System.Collections; public partial class CompoundLeftValue : LeftValue { /// @@ -157,10 +158,10 @@ ]; int writeIndex = 0; - MSA.Expression result = gen.CurrentScope.DefineHiddenVariable("#rhs", typeof(List)); + MSA.Expression result = gen.CurrentScope.DefineHiddenVariable("#rhs", typeof(IList)); writes[writeIndex++] = Ast.Assign(result, resultExpression); - MethodInfo itemGetter = typeof(List).GetMethod("get_Item"); + MethodInfo itemGetter = typeof(IList).GetMethod("get_Item"); for (int i = 0; i < _leftValues.Count; i++) { MSA.Expression rvalue; =================================================================== edit: $/Dev10/feature/vs_langs01_s/Merlin/Main/Languages/Ruby/Ruby/Runtime/BlockParam.cs;C791094 File: BlockParam.cs =================================================================== --- $/Dev10/feature/vs_langs01_s/Merlin/Main/Languages/Ruby/Ruby/Runtime/BlockParam.cs;C791094 (server) 4/6/2009 10:17 AM +++ Shelved Change: $/Dev10/feature/vs_langs01_s/Merlin/Main/Languages/Ruby/Ruby/Runtime/BlockParam.cs;NewMethodBinder @@ -43,13 +43,24 @@ } internal sealed class MissingBlockParam { - internal static readonly DynamicMetaObject MetaObject; + /// + /// LimitType must be MissingBlockParam (overload resolution, ). + /// Restriction should be empty: used only for !HasBlock call-sites => the site will never be reused for a call with a block. + /// + internal sealed class Meta : DynamicMetaObject, IRestrictedMetaObject { + internal static readonly DynamicMetaObject Instance = new Meta(); - static MissingBlockParam() { - var value = new MissingBlockParam(); - var constant = Ast.Constant(value); - MetaObject = new DynamicMetaObject(constant, BindingRestrictions.GetTypeRestriction(constant, typeof(MissingBlockParam)), value); + private Meta() + : base(AstUtils.Constant(null, typeof(MissingBlockParam)), BindingRestrictions.Empty) { + Debug.Assert(LimitType == typeof(MissingBlockParam)); + } + + public DynamicMetaObject Restrict(Type/*!*/ type) { + Debug.Assert(type == typeof(BlockParam) || type == typeof(MissingBlockParam)); + return this; + } } + } public sealed partial class BlockParam { =================================================================== edit: $/Dev10/feature/vs_langs01_s/Merlin/Main/Languages/Ruby/Ruby/Runtime/RubyOps.cs;C812203 File: RubyOps.cs =================================================================== --- $/Dev10/feature/vs_langs01_s/Merlin/Main/Languages/Ruby/Ruby/Runtime/RubyOps.cs;C812203 (server) 4/6/2009 10:17 AM +++ Shelved Change: $/Dev10/feature/vs_langs01_s/Merlin/Main/Languages/Ruby/Ruby/Runtime/RubyOps.cs;NewMethodBinder @@ -813,10 +813,14 @@ #region Array [Emitted] - public static List/*!*/ SplatAppend(List/*!*/ array, object splattee) { - List list = splattee as List; - if (list != null) { - array.AddRange(list); + public static IList/*!*/ SplatAppend(IList/*!*/ array, object splattee) { + IEnumerable objList; + IEnumerable iList; + + if ((objList = splattee as IEnumerable) != null) { + array.AddRange(objList); + } else if ((iList = splattee as IEnumerable) != null) { + array.AddRange(iList); } else { array.Add(splattee); } @@ -825,7 +829,7 @@ [Emitted] public static object Splat(object/*!*/ value) { - List list = value as List; + var list = value as IList; if (list == null) { return value; } @@ -839,7 +843,7 @@ [Emitted] public static object SplatPair(object value, object array) { - List list = array as List; + var list = array as IList; if (list != null) { if (list.Count == 0) { return value; @@ -855,23 +859,39 @@ } [Emitted] - public static RubyArray/*!*/ Unsplat(object/*!*/ value) { - RubyArray list = value as RubyArray; + public static IList/*!*/ Unsplat(object/*!*/ splattee) { + var list = splattee as IList; if (list == null) { list = new RubyArray(1); - list.Add(value); + list.Add(splattee); } return list; } + // CaseExpression + [Emitted] + public static bool ExistsUnsplat(CallSite>/*!*/ comparisonSite, object splattee, object value) { + var list = splattee as IList; + if (list != null) { + foreach (var item in list) { + if (IsTrue(comparisonSite.Target(comparisonSite, item, value))) { + return true; + } + } + return false; + } else { + return IsTrue(comparisonSite.Target(comparisonSite, splattee, value)); + } + } + [Emitted] // parallel assignment: - public static object GetArrayItem(List/*!*/ array, int index) { + public static object GetArrayItem(IList/*!*/ array, int index) { Debug.Assert(index >= 0); return index < array.Count ? array[index] : null; } [Emitted] // parallel assignment: - public static List/*!*/ GetArraySuffix(List/*!*/ array, int startIndex) { + public static RubyArray/*!*/ GetArraySuffix(IList/*!*/ array, int startIndex) { int size = array.Count - startIndex; if (size > 0) { RubyArray result = new RubyArray(size); =================================================================== edit: $/Dev10/feature/vs_langs01_s/Merlin/Main/Languages/Ruby/Ruby/Runtime/Utils.cs;C791094 File: Utils.cs =================================================================== --- $/Dev10/feature/vs_langs01_s/Merlin/Main/Languages/Ruby/Ruby/Runtime/Utils.cs;C791094 (server) 4/6/2009 10:17 AM +++ Shelved Change: $/Dev10/feature/vs_langs01_s/Merlin/Main/Languages/Ruby/Ruby/Runtime/Utils.cs;NewMethodBinder @@ -18,6 +18,8 @@ using System.Diagnostics; using System.Text; using System.Reflection; +using System.Collections; +using System.Collections.Generic; namespace IronRuby.Runtime { public static class Utils { @@ -316,6 +318,32 @@ return result; } + internal static void AddRange(this IList/*!*/ list, IEnumerable/*!*/ range) { + Assert.NotNull(list, range); + + List objList; + if ((objList = list as List) != null) { + objList.AddRange(range); + } else { + foreach (var item in range) { + list.Add(item); + } + } + } + + internal static void AddRange(this IList/*!*/ list, IEnumerable/*!*/ range) { + Assert.NotNull(list, range); + + List objList; + if ((objList = list as List) != null) { + objList.AddRange(range); + } else { + foreach (var item in range) { + list.Add(item); + } + } + } + [Conditional("DEBUG")] public static void Log(string/*!*/ message, string/*!*/ category) { #if !SILVERLIGHT =================================================================== edit: $/Dev10/feature/vs_langs01_s/Merlin/Main/Languages/Ruby/Ruby/Runtime/Calls/ArgsBuilder.cs;C791094 File: ArgsBuilder.cs =================================================================== --- $/Dev10/feature/vs_langs01_s/Merlin/Main/Languages/Ruby/Ruby/Runtime/Calls/ArgsBuilder.cs;C791094 (server) 4/6/2009 10:17 AM +++ Shelved Change: $/Dev10/feature/vs_langs01_s/Merlin/Main/Languages/Ruby/Ruby/Runtime/Calls/ArgsBuilder.cs;NewMethodBinder @@ -26,6 +26,7 @@ namespace IronRuby.Runtime.Calls { using Ast = System.Linq.Expressions.Expression; + using System.Collections; public sealed class ArgsBuilder { private readonly Expression[]/*!*/ _arguments; @@ -149,7 +150,7 @@ Add( Ast.Call( listVariable, - typeof(List).GetMethod("get_Item"), + typeof(IList).GetMethod("get_Item"), AstUtils.Constant(i) ) ); =================================================================== edit: $/Dev10/feature/vs_langs01_s/Merlin/Main/Languages/Ruby/Ruby/Runtime/Calls/BlockDispatcher.cs;C792525 File: BlockDispatcher.cs =================================================================== --- $/Dev10/feature/vs_langs01_s/Merlin/Main/Languages/Ruby/Ruby/Runtime/Calls/BlockDispatcher.cs;C792525 (server) 4/6/2009 10:17 AM +++ Shelved Change: $/Dev10/feature/vs_langs01_s/Merlin/Main/Languages/Ruby/Ruby/Runtime/Calls/BlockDispatcher.cs;NewMethodBinder @@ -27,6 +27,7 @@ namespace IronRuby.Runtime.Calls { using Ast = System.Linq.Expressions.Expression; using AstFactory = IronRuby.Compiler.Ast.AstFactory; + using System.Collections; [Flags] public enum BlockSignatureAttributes { @@ -129,7 +130,7 @@ int i = Math.Min(initializedArgCount, parameterCount); int j = 0; - var list = splattee as List; + var list = splattee as IList; if (list != null) { while (i < parameterCount && j < list.Count) { args[i++] = list[j++]; @@ -300,7 +301,7 @@ // return the only item of the array: return Ast.Call( listVariable, - typeof(List).GetMethod("get_Item"), + typeof(IList).GetMethod("get_Item"), AstUtils.Constant(0) ); } else { =================================================================== edit: $/Dev10/feature/vs_langs01_s/Merlin/Main/Languages/Ruby/Ruby/Runtime/Calls/BlockDispatchers.cs;C791094 File: BlockDispatchers.cs =================================================================== --- $/Dev10/feature/vs_langs01_s/Merlin/Main/Languages/Ruby/Ruby/Runtime/Calls/BlockDispatchers.cs;C791094 (server) 4/6/2009 10:17 AM +++ Shelved Change: $/Dev10/feature/vs_langs01_s/Merlin/Main/Languages/Ruby/Ruby/Runtime/Calls/BlockDispatchers.cs;NewMethodBinder @@ -18,6 +18,7 @@ using System.Collections.Generic; using System.Diagnostics; using IronRuby.Builtins; +using System.Collections; namespace IronRuby.Runtime.Calls { // L(0, -) @@ -180,7 +181,7 @@ // R(0, *) public override object InvokeSplat(BlockParam/*!*/ param, object self, object splattee) { - var list = splattee as List; + var list = splattee as IList; if (list != null) { switch (list.Count) { case 0: @@ -208,7 +209,7 @@ // R(1, *) public override object InvokeSplat(BlockParam/*!*/ param, object self, object arg1, object splattee) { - var list = splattee as List; + var list = splattee as IList; if (list == null) { if (!HasSingleCompoundParameter) { param.MultipleValuesForBlockParameterWarning(2); @@ -337,7 +338,7 @@ } private object InvokeSplatInternal(BlockParam/*!*/ param, object self, object splattee) { - var list = splattee as List; + var list = splattee as IList; if (list != null) { switch (list.Count) { case 0: return _block(param, self, null, null); @@ -351,7 +352,7 @@ // R(1, *) public override object InvokeSplat(BlockParam/*!*/ param, object self, object arg1, object splattee) { - var list = splattee as List; + var list = splattee as IList; if (list != null) { switch (list.Count) { case 0: return _block(param, self, arg1, null); @@ -446,7 +447,7 @@ } private object InvokeSplatInternal(BlockParam/*!*/ param, object self, object splattee) { - var list = splattee as List; + var list = splattee as IList; if (list != null) { switch (list.Count) { case 0: return _block(param, self, null, null, null); @@ -461,7 +462,7 @@ // R(1, *) public override object InvokeSplat(BlockParam/*!*/ param, object self, object arg1, object splattee) { - var list = splattee as List; + var list = splattee as IList; if (list != null) { switch (list.Count) { case 0: return _block(param, self, arg1, null, null); @@ -475,7 +476,7 @@ // R(2, *) public override object InvokeSplat(BlockParam/*!*/ param, object self, object arg1, object arg2, object splattee) { - var list = splattee as List; + var list = splattee as IList; if (list != null) { switch (list.Count) { case 0: return _block(param, self, arg1, arg2, null); @@ -565,7 +566,7 @@ } private object InvokeSplatInternal(BlockParam/*!*/ param, object self, object splattee) { - var list = splattee as List; + var list = splattee as IList; if (list != null) { switch (list.Count) { case 0: return _block(param, self, null, null, null, null); @@ -581,7 +582,7 @@ // R(1, *) public override object InvokeSplat(BlockParam/*!*/ param, object self, object arg1, object splattee) { - var list = splattee as List; + var list = splattee as IList; if (list != null) { switch (list.Count) { case 0: return _block(param, self, arg1, null, null, null); @@ -596,7 +597,7 @@ // R(2, *) public override object InvokeSplat(BlockParam/*!*/ param, object self, object arg1, object arg2, object splattee) { - var list = splattee as List; + var list = splattee as IList; if (list != null) { switch (list.Count) { case 0: return _block(param, self, arg1, arg2, null, null); @@ -610,7 +611,7 @@ // R(3, *) public override object InvokeSplat(BlockParam/*!*/ param, object self, object arg1, object arg2, object arg3, object splattee) { - var list = splattee as List; + var list = splattee as IList; if (list != null) { switch (list.Count) { case 0: return _block(param, self, arg1, arg2, arg3, null); =================================================================== edit: $/Dev10/feature/vs_langs01_s/Merlin/Main/Languages/Ruby/Ruby/Runtime/Calls/BlockDispatcherUnsplatN.cs;C791094 File: BlockDispatcherUnsplatN.cs =================================================================== --- $/Dev10/feature/vs_langs01_s/Merlin/Main/Languages/Ruby/Ruby/Runtime/Calls/BlockDispatcherUnsplatN.cs;C791094 (server) 4/6/2009 10:17 AM +++ Shelved Change: $/Dev10/feature/vs_langs01_s/Merlin/Main/Languages/Ruby/Ruby/Runtime/Calls/BlockDispatcherUnsplatN.cs;NewMethodBinder @@ -19,6 +19,7 @@ using Microsoft.Scripting.Utils; using System.Diagnostics; using IronRuby.Builtins; +using System.Collections; namespace IronRuby.Runtime.Calls { // L(n, *) @@ -132,7 +133,7 @@ // R(N, *, =) public override object InvokeSplatRhs(BlockParam/*!*/ param, object self, object[]/*!*/ args, object splattee, object rhs) { - var list = splattee as List; + var list = splattee as IList; if (list != null) { var l = new RubyArray(list.Count + 1); l.AddRange(list); @@ -146,10 +147,10 @@ } private object InvokeSplatInternal(BlockParam/*!*/ param, object self, object[]/*!*/ args, object splattee) { - return InvokeSplatInternal(param, self, args, splattee, splattee as List); + return InvokeSplatInternal(param, self, args, splattee, splattee as IList); } - private object InvokeSplatInternal(BlockParam/*!*/ param, object self, object[]/*!*/ args, object splattee, List list) { + private object InvokeSplatInternal(BlockParam/*!*/ param, object self, object[]/*!*/ args, object splattee, IList list) { int argsLength = args.Length; int nextArg, nextItem; =================================================================== edit: $/Dev10/feature/vs_langs01_s/Merlin/Main/Languages/Ruby/Ruby/Runtime/Calls/MetaObjectBuilder.cs;C806880 File: MetaObjectBuilder.cs =================================================================== --- $/Dev10/feature/vs_langs01_s/Merlin/Main/Languages/Ruby/Ruby/Runtime/Calls/MetaObjectBuilder.cs;C806880 (server) 4/6/2009 10:17 AM +++ Shelved Change: $/Dev10/feature/vs_langs01_s/Merlin/Main/Languages/Ruby/Ruby/Runtime/Calls/MetaObjectBuilder.cs;NewMethodBinder @@ -26,6 +26,7 @@ using Microsoft.Scripting.Utils; using Ast = System.Linq.Expressions.Expression; using AstUtils = Microsoft.Scripting.Ast.Utils; +using System.Collections; namespace IronRuby.Runtime.Calls { public sealed class MetaObjectBuilder { @@ -112,11 +113,14 @@ } public ParameterExpression/*!*/ GetTemporary(Type/*!*/ type, string/*!*/ name) { + return AddTemporary(Ast.Variable(type, name)); + } + + private ParameterExpression/*!*/ AddTemporary(ParameterExpression/*!*/ variable) { if (_temps == null) { _temps = new List(); } - var variable = Ast.Variable(type, name); _temps.Add(variable); return variable; } @@ -186,6 +190,14 @@ } } + public void AddRestriction(BindingRestrictions/*!*/ restriction) { + if (_treatRestrictionsAsConditions) { + AddCondition(restriction.ToExpression()); + } else { + Add(restriction); + } + } + private void Add(BindingRestrictions/*!*/ restriction) { Debug.Assert(!_treatRestrictionsAsConditions); _restrictions = _restrictions.Merge(restriction); @@ -323,13 +335,13 @@ // test exact type: AddTypeRestriction(value.GetType(), expression); - List list = value as List; + IList list = value as IList; if (list != null) { - Type type = typeof(List); + Type type = typeof(IList); listLength = list.Count; listVariable = GetTemporary(type, "#list"); AddCondition(Ast.Equal( - Ast.Property(Ast.Assign(listVariable, Ast.Convert(expression, type)), type.GetProperty("Count")), + Ast.Property(Ast.Assign(listVariable, Ast.Convert(expression, type)), typeof(ICollection).GetProperty("Count")), AstUtils.Constant(list.Count)) ); return true; =================================================================== edit: $/Dev10/feature/vs_langs01_s/Merlin/Main/Languages/Ruby/Ruby/Runtime/Calls/ProtocolConversionAction.cs;C806880 File: ProtocolConversionAction.cs =================================================================== --- $/Dev10/feature/vs_langs01_s/Merlin/Main/Languages/Ruby/Ruby/Runtime/Calls/ProtocolConversionAction.cs;C806880 (server) 4/6/2009 10:17 AM +++ Shelved Change: $/Dev10/feature/vs_langs01_s/Merlin/Main/Languages/Ruby/Ruby/Runtime/Calls/ProtocolConversionAction.cs;NewMethodBinder @@ -58,26 +58,22 @@ } protected override DynamicMetaObject/*!*/ InteropBind(MetaObjectBuilder/*!*/ metaBuilder, CallArguments/*!*/ args) { - // TODO: pass block as the last parameter (before RHS arg?): - var normalizedArgs = RubyMethodGroupBase.NormalizeArguments(metaBuilder, args, SelfCallConvention.NoSelf, false, false); - - MethodInfo postConverter; - ConvertBinder interopBinder = GetInteropBinder(args.RubyContext, out postConverter); - - if (normalizedArgs.Length == 0) { + var normalizedArgs = RubyMethodBinder.NormalizeArguments(metaBuilder, args, 0, 0); + if (!metaBuilder.Error) { + MethodInfo postConverter; + ConvertBinder interopBinder = GetInteropBinder(args.RubyContext, out postConverter); + // TODO: the type of result.Expression (and LimitType) should be the type of the conversion. - var result = interopBinder.Bind(args.MetaTarget, normalizedArgs); + var result = interopBinder.Bind(args.MetaTarget, ArrayUtils.MakeArray(normalizedArgs)); metaBuilder.SetMetaResult(result, args); if (postConverter != null) { metaBuilder.Result = postConverter.OpCall(AstUtils.Convert(metaBuilder.Result, interopBinder.Type)); } - - } else { - metaBuilder.SetWrongNumberOfArgumentsError(normalizedArgs.Length, 0); + return metaBuilder.CreateMetaObject(interopBinder); } - return metaBuilder.CreateMetaObject(interopBinder); + return metaBuilder.CreateMetaObject(this); } public static RubyConversionAction TryGetDefaultConversionAction(RubyContext/*!*/ context, Type/*!*/ parameterType) { =================================================================== edit: $/Dev10/feature/vs_langs01_s/Merlin/Main/Languages/Ruby/Ruby/Runtime/Calls/RubyAccessorInfo.cs;C806880 File: RubyAccessorInfo.cs =================================================================== --- $/Dev10/feature/vs_langs01_s/Merlin/Main/Languages/Ruby/Ruby/Runtime/Calls/RubyAccessorInfo.cs;C806880 (server) 4/6/2009 10:17 AM +++ Shelved Change: $/Dev10/feature/vs_langs01_s/Merlin/Main/Languages/Ruby/Ruby/Runtime/Calls/RubyAccessorInfo.cs;NewMethodBinder @@ -51,15 +51,13 @@ } internal override void BuildCallNoFlow(MetaObjectBuilder/*!*/ metaBuilder, CallArguments/*!*/ args, string/*!*/ name) { - var actualArgs = RubyMethodGroupInfo.NormalizeArguments(metaBuilder, args, SelfCallConvention.NoSelf, false, false); - if (actualArgs.Length == 0) { + var actualArgs = RubyMethodBinder.NormalizeArguments(metaBuilder, args, 0, 0); + if (!metaBuilder.Error) { metaBuilder.Result = Methods.GetInstanceVariable.OpCall( AstUtils.Convert(args.MetaScope.Expression, typeof(RubyScope)), AstFactory.Box(args.TargetExpression), AstUtils.Constant(InstanceVariableName) ); - } else { - metaBuilder.SetWrongNumberOfArgumentsError(actualArgs.Length, 0); } } @@ -78,16 +76,14 @@ } internal override void BuildCallNoFlow(MetaObjectBuilder/*!*/ metaBuilder, CallArguments/*!*/ args, string/*!*/ name) { - var actualArgs = RubyMethodGroupInfo.NormalizeArguments(metaBuilder, args, SelfCallConvention.NoSelf, false, false); - if (actualArgs.Length == 1) { + var actualArgs = RubyMethodBinder.NormalizeArguments(metaBuilder, args, 1, 1); + if (!metaBuilder.Error) { metaBuilder.Result = Methods.SetInstanceVariable.OpCall( AstFactory.Box(args.TargetExpression), AstFactory.Box(actualArgs[0].Expression), AstUtils.Convert(args.MetaScope.Expression, typeof(RubyScope)), AstUtils.Constant(InstanceVariableName) ); - } else { - metaBuilder.SetWrongNumberOfArgumentsError(actualArgs.Length, 1); } } =================================================================== edit: $/Dev10/feature/vs_langs01_s/Merlin/Main/Languages/Ruby/Ruby/Runtime/Calls/RubyBinder.cs;C806880 File: RubyBinder.cs =================================================================== --- $/Dev10/feature/vs_langs01_s/Merlin/Main/Languages/Ruby/Ruby/Runtime/Calls/RubyBinder.cs;C806880 (server) 4/6/2009 10:17 AM +++ Shelved Change: $/Dev10/feature/vs_langs01_s/Merlin/Main/Languages/Ruby/Ruby/Runtime/Calls/RubyBinder.cs;NewMethodBinder @@ -47,251 +47,13 @@ _context = context; } - internal static readonly DynamicMetaObject NullMetaBlockParam = - new DynamicMetaObject( - AstUtils.Constant(null, typeof(BlockParam)), - BindingRestrictions.GetTypeRestriction(AstUtils.Constant(null, typeof(BlockParam)), typeof(BlockParam)), - null - ); - - protected override string GetTypeName(Type t) { + public override string GetTypeName(Type t) { RubyModule module; return _context.TryGetModule(t, out module) ? module.Name : RubyUtils.GetQualifiedName(t); } - public override Actions.ErrorInfo MakeInvalidParametersError(BindingTarget target) { - Expression exceptionValue; - switch (target.Result) { - case BindingResult.AmbiguousMatch: - exceptionValue = MakeAmbiguousCallError(target); - break; - case BindingResult.IncorrectArgumentCount: - exceptionValue = MakeIncorrectArgumentCountError(target); - break; - case BindingResult.CallFailure: - exceptionValue = MakeCallFailureError(target); - break; - default: throw new InvalidOperationException(); - } - return Actions.ErrorInfo.FromException(exceptionValue); - } - private Expression MakeAmbiguousCallError(BindingTarget target) { - StringBuilder sb = new StringBuilder(string.Format("Found multiple methods for '{0}': ", target.Name)); - string outerComma = ""; - foreach (MethodTarget mt in target.AmbiguousMatches) { - Type[] types = mt.GetParameterTypes(); - string innerComma = ""; - sb.Append(outerComma); - sb.Append(target.Name); - sb.Append('('); - foreach (Type t in types) { - sb.Append(innerComma); - sb.Append(GetTypeName(t)); - innerComma = ", "; - } - - sb.Append(')'); - outerComma = ", "; - } - - return Methods.MakeAmbiguousMatchError.OpCall(AstUtils.Constant(sb.ToString())); - } - - private Expression MakeIncorrectArgumentCountError(BindingTarget target) { - int minArgs = Int32.MaxValue; - int maxArgs = Int32.MinValue; - foreach (int argCnt in target.ExpectedArgumentCount) { - minArgs = System.Math.Min(minArgs, argCnt); - maxArgs = System.Math.Max(maxArgs, argCnt); - } - - return Methods.MakeWrongNumberOfArgumentsError.OpCall( - AstUtils.Constant(target.ActualArgumentCount), - AstUtils.Constant(minArgs)); - } - - private Expression MakeCallFailureError(BindingTarget target) { - foreach (CallFailure cf in target.CallFailures) { - switch (cf.Reason) { - case CallFailureReason.ConversionFailure: - foreach (ConversionResult cr in cf.ConversionResults) { - if (cr.Failed) { - if (typeof(Proc).IsAssignableFrom(cr.To)) { - return Methods.CreateArgumentsErrorForProc.OpCall(AstUtils.Constant(GetTypeName(cr.From))); - } - - Debug.Assert(typeof(BlockParam).IsSealed); - if (cr.To == typeof(BlockParam)) { - Debug.Assert(cr.From == typeof(MissingBlockParam)); - return Methods.CreateArgumentsErrorForMissingBlock.OpCall(); - } - - return Methods.CreateTypeConversionError.OpCall( - AstUtils.Constant(GetTypeName(cr.From)), - AstUtils.Constant(GetTypeName(cr.To))); - } - } - break; - - case CallFailureReason.DuplicateKeyword: - case CallFailureReason.UnassignableKeyword: - default: throw new InvalidOperationException(); - } - } - throw new InvalidOperationException(); - } - - protected override int PrepareParametersBinding(ParameterInfo/*!*/[]/*!*/ parameterInfos, List/*!*/ arguments, - List/*!*/ parameters, ref int index) { - - var i = 0; - - while (i < parameterInfos.Length - && parameterInfos[i].ParameterType.IsSubclassOf(typeof(RubyCallSiteStorage))) { - - arguments.Add(new RubyCallSiteStorageBuilder(parameterInfos[i])); - i++; - } - - if (i < parameterInfos.Length) { - var parameterInfo = parameterInfos[i]; - - if (parameterInfo.ParameterType == typeof(RubyScope)) { - arguments.Add(new RubyScopeArgBuilder(parameterInfo)); - i++; - } else if (parameterInfo.ParameterType == typeof(RubyContext)) { - arguments.Add(new RubyContextArgBuilder(parameterInfo)); - i++; - } - } - - // 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 and BlockParam 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 - // - if (i >= parameterInfos.Length || parameterInfos[i].ParameterType != typeof(BlockParam)) { - arguments.Add(new MissingBlockArgBuilder(index++)); - parameters.Add(new ParameterWrapper(this, typeof(MissingBlockParam), null, false)); - } - - return i; - } - - protected override bool BindSpecialParameter(ParameterInfo parameterInfo, List arguments, - List parameters, ref int index) { - return false; - } - - internal static void GetParameterCount(ParameterInfo/*!*/[]/*!*/ parameterInfos, out int mandatory, out int optional, out bool acceptsBlock) { - acceptsBlock = false; - mandatory = 0; - optional = 0; - foreach (ParameterInfo parameterInfo in parameterInfos) { - if (IsHiddenParameter(parameterInfo)) { - continue; - } else if (parameterInfo.ParameterType == typeof(BlockParam)) { - acceptsBlock = true; - } else if (CompilerHelpers.IsParamArray(parameterInfo)) { - // TODO: indicate splat args separately? - optional++; - } else if (CompilerHelpers.IsOutParameter(parameterInfo)) { - // Python allows passing of optional "clr.Reference" to capture out parameters - // Ruby should allow similar - optional++; - } else if (CompilerHelpers.IsMandatoryParameter(parameterInfo)) { - mandatory++; - } else { - optional++; - } - } - } - - internal static bool IsHiddenParameter(ParameterInfo/*!*/ parameterInfo) { - return parameterInfo.ParameterType == typeof(RubyScope) - || parameterInfo.ParameterType == typeof(RubyContext) - || parameterInfo.ParameterType.IsSubclassOf(typeof(RubyCallSiteStorage)); - } - - internal sealed class RubyContextArgBuilder : ArgBuilder { - public RubyContextArgBuilder(ParameterInfo/*!*/ info) - : base(info) { - } - - public override int Priority { - get { return -1; } - } - - protected override Expression ToExpression(ParameterBinder/*!*/ parameterBinder, IList/*!*/ parameters, bool[]/*!*/ hasBeenUsed) { - return ((RubyParameterBinder)parameterBinder).ContextExpression; - } - } - - internal sealed class RubyCallSiteStorageBuilder : ArgBuilder { - public RubyCallSiteStorageBuilder(ParameterInfo/*!*/ info) - : base(info) { - } - - public override int Priority { - get { return -1; } - } - - protected override Expression ToExpression(ParameterBinder/*!*/ parameterBinder, IList/*!*/ parameters, bool[]/*!*/ hasBeenUsed) { - return AstUtils.Constant(Activator.CreateInstance(ParameterInfo.ParameterType, ((RubyParameterBinder)parameterBinder).Context)); - } - } - - internal sealed class RubyScopeArgBuilder : ArgBuilder { - public RubyScopeArgBuilder(ParameterInfo/*!*/ info) - : base(info) { - } - - public override int Priority { - get { return -1; } - } - - protected override Expression ToExpression(ParameterBinder/*!*/ parameterBinder, IList/*!*/ parameters, bool[]/*!*/ hasBeenUsed) { - return ((RubyParameterBinder)parameterBinder).ScopeExpression; - } - } - - internal sealed class MissingBlockArgBuilder : SimpleArgBuilder { - public MissingBlockArgBuilder(int index) - : base(typeof(MissingBlockParam), index, false, false) { - } - - public override int Priority { - get { return -1; } - } - - protected override SimpleArgBuilder/*!*/ Copy(int newIndex) { - return new MissingBlockArgBuilder(newIndex); - } - - protected override Expression ToExpression(ParameterBinder/*!*/ parameterBinder, IList/*!*/ parameters, bool[]/*!*/ hasBeenUsed) { - Debug.Assert(Index < parameters.Count); - Debug.Assert(Index < hasBeenUsed.Length); - Debug.Assert(parameters[Index] != null); - hasBeenUsed[Index] = true; - return null; - } - } - - - #if TODO protected override IList/*!*/ GetExtensionTypes(Type/*!*/ t) { Type extentionType = _rubyContext.RubyContext.GetClass(t).ExtensionType; @@ -387,68 +149,6 @@ return Converter.CanConvertFrom(fromType, toType, level); } - public override bool CanConvertFrom(Type/*!*/ fromType, ParameterWrapper/*!*/ toParameter, NarrowingLevel level) { - Type toType = toParameter.Type; - - if (base.CanConvertFrom(fromType, toParameter, level)) { - return true; - } - - // blocks: - if (fromType == typeof(MissingBlockParam)) { - return toType == typeof(BlockParam) && !toParameter.ProhibitNull; - } - - if (fromType == typeof(BlockParam) && toType == typeof(MissingBlockParam)) { - return true; - } - - // protocol conversions: - if (toParameter.ParameterInfo != null && toParameter.ParameterInfo.IsDefined(typeof(DefaultProtocolAttribute), false) && - !toParameter.IsParamsArray && !toParameter.IsParamsDict) { - - // any type is potentially convertible, except for nil if [NotNull] is used or the target type is a value type: - return fromType != typeof(DynamicNull) || !(toParameter.ProhibitNull || toType.IsValueType); - } - - return false; - } - - public override Candidate SelectBestConversionFor(Type/*!*/ actualType, ParameterWrapper/*!*/ candidateOne, ParameterWrapper/*!*/ candidateTwo, NarrowingLevel level) { - Type typeOne = candidateOne.Type; - Type typeTwo = candidateTwo.Type; - - if (actualType == typeof(DynamicNull)) { - // if nil is passed as a block argument prefer BlockParam over missing block; - if (typeOne == typeof(BlockParam) && typeTwo == typeof(MissingBlockParam)) { - return Candidate.One; - } - - if (typeOne == typeof(MissingBlockParam) && typeTwo == typeof(BlockParam)) { - return Candidate.Two; - } - } else { - if (actualType == typeOne && candidateOne.ProhibitNull) { - return Candidate.One; - } - - if (actualType == typeTwo && candidateTwo.ProhibitNull) { - return Candidate.Two; - } - } - - if (actualType == typeOne) { - return Candidate.One; - } - - if (actualType == typeTwo) { - return Candidate.Two; - } - - - return Candidate.Equivalent; - } - public override Candidate PreferConvert(Type t1, Type t2) { return Converter.PreferConvert(t1, t2); // return t1 == t2; =================================================================== edit: $/Dev10/feature/vs_langs01_s/Merlin/Main/Languages/Ruby/Ruby/Runtime/Calls/RubyCallAction.cs;C806880 File: RubyCallAction.cs =================================================================== --- $/Dev10/feature/vs_langs01_s/Merlin/Main/Languages/Ruby/Ruby/Runtime/Calls/RubyCallAction.cs;C806880 (server) 4/6/2009 10:17 AM +++ Shelved Change: $/Dev10/feature/vs_langs01_s/Merlin/Main/Languages/Ruby/Ruby/Runtime/Calls/RubyCallAction.cs;NewMethodBinder @@ -202,13 +202,16 @@ protected override DynamicMetaObject/*!*/ InteropBind(MetaObjectBuilder/*!*/ metaBuilder, CallArguments/*!*/ args) { // TODO: pass block as the last parameter (before RHS arg?): - var normalizedArgs = RubyMethodGroupBase.NormalizeArguments(metaBuilder, args, SelfCallConvention.NoSelf, false, false); - var callInfo = new CallInfo(normalizedArgs.Length); + var normalizedArgs = RubyMethodBinder.NormalizeArguments(metaBuilder, args, 0, Int32.MaxValue); + if (!metaBuilder.Error) { + var callInfo = new CallInfo(normalizedArgs.Count); - var interopBinder = GetInteropBinder(args.RubyContext, callInfo); - var result = interopBinder.Bind(args.MetaTarget, normalizedArgs); - metaBuilder.SetMetaResult(result, args); - return metaBuilder.CreateMetaObject(interopBinder); + var interopBinder = GetInteropBinder(args.RubyContext, callInfo); + var result = interopBinder.Bind(args.MetaTarget, ArrayUtils.MakeArray(normalizedArgs)); + metaBuilder.SetMetaResult(result, args); + return metaBuilder.CreateMetaObject(interopBinder); + } + return metaBuilder.CreateMetaObject(this); } =================================================================== edit: $/Dev10/feature/vs_langs01_s/Merlin/Main/Languages/Ruby/Ruby/Runtime/Calls/RubyMethodGroupBase.cs;C806880 File: RubyMethodGroupBase.cs =================================================================== --- $/Dev10/feature/vs_langs01_s/Merlin/Main/Languages/Ruby/Ruby/Runtime/Calls/RubyMethodGroupBase.cs;C806880 (server) 4/6/2009 10:17 AM +++ Shelved Change: $/Dev10/feature/vs_langs01_s/Merlin/Main/Languages/Ruby/Ruby/Runtime/Calls/RubyMethodGroupBase.cs;NewMethodBinder @@ -16,23 +16,19 @@ using System; using System.Collections.Generic; using System.Diagnostics; +using System.Dynamic; using System.Linq.Expressions; using System.Reflection; -using System.Reflection.Emit; -using System.Dynamic; -using Microsoft.Scripting; -using Microsoft.Scripting.Actions; +using IronRuby.Builtins; +using IronRuby.Compiler; using Microsoft.Scripting.Actions.Calls; -using Microsoft.Scripting.Generation; using Microsoft.Scripting.Runtime; using Microsoft.Scripting.Utils; -using IronRuby.Builtins; -using IronRuby.Compiler.Generation; -using IronRuby.Compiler; +using Ast = System.Linq.Expressions.Expression; using AstFactory = IronRuby.Compiler.Ast.AstFactory; using AstUtils = Microsoft.Scripting.Ast.Utils; -using Ast = System.Linq.Expressions.Expression; +using System.Collections; namespace IronRuby.Runtime.Calls { @@ -86,7 +82,7 @@ foreach (MethodBase method in MethodBases) { int mandatory, optional; bool acceptsBlock; - RubyBinder.GetParameterCount(method.GetParameters(), out mandatory, out optional, out acceptsBlock); + RubyMethodBinder.GetParameterCount(method.GetParameters(), out mandatory, out optional, out acceptsBlock); if (mandatory > 0) { mandatory--; // account for "self" } @@ -150,7 +146,7 @@ private static bool IsOverloadSignature(MethodBase/*!*/ method, Type/*!*/[]/*!*/ parameterTypes) { var infos = method.GetParameters(); int firstInfo = 0; - while (firstInfo < infos.Length && RubyBinder.IsHiddenParameter(infos[firstInfo])) { + while (firstInfo < infos.Length && RubyMethodBinder.IsHiddenParameter(infos[firstInfo])) { firstInfo++; } @@ -200,12 +196,37 @@ BuildCallNoFlow(metaBuilder, args, name, methods, CallConvention); } - internal static BindingTarget/*!*/ ResolveOverload(string/*!*/ name, IList/*!*/ overloads, CallArguments/*!*/ args, - SelfCallConvention callConvention) { + internal static BindingTarget/*!*/ ResolveOverload(MetaObjectBuilder/*!*/ metaBuilder, CallArguments/*!*/ args, string/*!*/ name, + IList/*!*/ overloads, SelfCallConvention callConvention, out RubyMethodBinder/*!*/ methodBinder) { - var methodBinder = MethodBinder.MakeBinder(args.RubyContext.Binder, name, overloads, ArrayUtils.EmptyStrings, NarrowingLevel.None, NarrowingLevel.All); - var argTypes = GetSignatureToMatch(args, callConvention); - return methodBinder.MakeBindingTarget(CallTypes.None, argTypes); + methodBinder = new RubyMethodBinder(metaBuilder, args, callConvention); + var bindingTarget = methodBinder.ResolveOverload(name, overloads); + + bool calleeHasBlockParam = bindingTarget.Success && HasBlockParameter(bindingTarget.Method); + + // At runtime the BlockParam is created with a new RFC instance that identifies the library method frame as + // a proc-converter target of a method unwinder triggered by break from a block. + if (args.Signature.HasBlock) { + var metaBlock = args.GetMetaBlock(); + if (metaBlock.Value != null && calleeHasBlockParam) { + Debug.Assert(metaBuilder.BfcVariable != null); + metaBuilder.ControlFlowBuilder = RuleControlFlowBuilder; + } + + // Overload resolution might not need to distinguish between nil and non-nil block. + // However, we still do since we construct CF only for non-nil blocks. + if (metaBlock.Value == null) { + metaBuilder.AddRestriction(Ast.Equal(metaBlock.Expression, AstUtils.Constant(null))); + } else { + // don't need to test the exact type of the Proc since the code is subclass agnostic: + metaBuilder.AddRestriction(Ast.NotEqual(metaBlock.Expression, AstUtils.Constant(null))); + } + } + + // add restrictions used for overload resolution: + methodBinder.AddArgumentRestrictions(metaBuilder, bindingTarget); + + return bindingTarget; } internal override void BuildCallNoFlow(MetaObjectBuilder/*!*/ metaBuilder, CallArguments/*!*/ args, string/*!*/ name) { @@ -221,37 +242,12 @@ internal static void BuildCallNoFlow(MetaObjectBuilder/*!*/ metaBuilder, CallArguments/*!*/ args, string/*!*/ name, IList/*!*/ overloads, SelfCallConvention callConvention) { - var bindingTarget = ResolveOverload(name, overloads, args, callConvention); - bool calleeHasBlockParam = bindingTarget.Success && HasBlockParameter(bindingTarget.Method); - - // Allocates a variable holding BlockParam. At runtime the BlockParam is created with a new RFC instance that - // identifies the library method frame as a proc-converter target of a method unwinder triggered by break from a block. - if (args.Signature.HasBlock) { - var metaBlock = args.GetMetaBlock(); - if (metaBlock.Value != null && calleeHasBlockParam) { - if (metaBuilder.BfcVariable == null) { - metaBuilder.BfcVariable = metaBuilder.GetTemporary(typeof(BlockParam), "#bfc"); - } - metaBuilder.ControlFlowBuilder = RuleControlFlowBuilder; - } - - // Block test - we need to test for a block regardless of whether it is actually passed to the method or not - // since the information that the block is not null is used for overload resolution. - if (metaBlock.Value == null) { - metaBuilder.AddRestriction(Ast.Equal(metaBlock.Expression, AstUtils.Constant(null))); - } else { - // don't need to test the exact type of the Proc since the code is subclass agnostic: - metaBuilder.AddRestriction(Ast.NotEqual(metaBlock.Expression, AstUtils.Constant(null))); - } - } - - var actualArgs = MakeActualArgs(metaBuilder, args, callConvention, calleeHasBlockParam, true); - + RubyMethodBinder methodBinder; + var bindingTarget = ResolveOverload(metaBuilder, args, name, overloads, callConvention, out methodBinder); if (bindingTarget.Success) { - var parameterBinder = new RubyParameterBinder(args); - metaBuilder.Result = bindingTarget.MakeExpression(parameterBinder, actualArgs); + metaBuilder.Result = bindingTarget.MakeExpression(); } else { - metaBuilder.SetError(args.RubyContext.RubyBinder.MakeInvalidParametersError(bindingTarget).Expression); + metaBuilder.SetError(methodBinder.MakeInvalidParametersError(bindingTarget).Expression); } } @@ -312,234 +308,6 @@ return false; } - // Normalizes arguments: inserts self, expands splats, and inserts rhs arg. - // Adds any restrictions/conditions applied to the arguments to the given meta-builder. - public static DynamicMetaObject[]/*!*/ NormalizeArguments(MetaObjectBuilder/*!*/ metaBuilder, CallArguments/*!*/ args, - SelfCallConvention callConvention, bool calleeHasBlockParam, bool injectMissingBlockParam) { - - var result = new List(); - - // self (instance): - if (callConvention == SelfCallConvention.SelfIsInstance) { - result.Add(args.MetaTarget); - } - - // block: - if (calleeHasBlockParam) { - if (args.Signature.HasBlock) { - if (args.GetMetaBlock() == null) { - // the user explicitly passed nil as a block arg: - result.Add(RubyBinder.NullMetaBlockParam); - } else { - // pass BlockParam: - Debug.Assert(metaBuilder.BfcVariable != null); - result.Add(new DynamicMetaObject(metaBuilder.BfcVariable, BindingRestrictions.Empty)); - } - } else { - // no block passed into a method with a BlockParam: - result.Add(RubyBinder.NullMetaBlockParam); - } - } else if (injectMissingBlockParam) { - // no block passed into a method w/o a BlockParam (we still need to fill the missing block argument): - result.Add(RubyBinder.NullMetaBlockParam); - } - - // self (parameter): - if (callConvention == SelfCallConvention.SelfIsParameter) { - result.Add(args.MetaTarget); - } - - // simple arguments: - for (int i = 0; i < args.SimpleArgumentCount; i++) { - result.Add(args.GetSimpleMetaArgument(i)); - } - - // splat argument: - int listLength; - ParameterExpression listVariable; - if (args.Signature.HasSplattedArgument) { - var splatted = args.GetSplattedMetaArgument(); - - if (metaBuilder.AddSplattedArgumentTest(splatted.Value, splatted.Expression, out listLength, out listVariable)) { - - // AddTestForListArg only returns 'true' if the argument is a List - var list = (List)splatted.Value; - - // get arguments, add tests - for (int j = 0; j < listLength; j++) { - result.Add(DynamicMetaObject.Create( - list[j], - Ast.Call(listVariable, typeof(List).GetMethod("get_Item"), AstUtils.Constant(j)) - )); - } - - } else { - // argument is not an array => add the argument itself: - result.Add(splatted); - } - } - - // rhs argument: - if (args.Signature.HasRhsArgument) { - result.Add(args.GetRhsMetaArgument()); - } - - return result.ToArray(); - } - - // TODO: OBSOLETE - private static Expression[]/*!*/ MakeActualArgs(MetaObjectBuilder/*!*/ metaBuilder, CallArguments/*!*/ args, - SelfCallConvention callConvention, bool calleeHasBlockParam, bool injectMissingBlockParam) { - - var actualArgs = new List(); - - // self (instance): - if (callConvention == SelfCallConvention.SelfIsInstance) { - // test already added by method resolver - Debug.Assert(args.TargetExpression != null); - AddArgument(actualArgs, args.Target, args.TargetExpression); - } - - // block: - if (calleeHasBlockParam) { - if (args.Signature.HasBlock) { - if (args.GetBlock() == null) { - // the user explicitly passed nil as a block arg: - actualArgs.Add(AstUtils.Constant(null)); - } else { - // pass BlockParam: - Debug.Assert(metaBuilder.BfcVariable != null); - actualArgs.Add(metaBuilder.BfcVariable); - } - } else { - // no block passed into a method with a BlockParam: - actualArgs.Add(AstUtils.Constant(null)); - } - } else if (injectMissingBlockParam) { - // no block passed into a method w/o a BlockParam (we still need to fill the missing block argument): - actualArgs.Add(AstUtils.Constant(null)); - } - - // self (non-instance): - if (callConvention == SelfCallConvention.SelfIsParameter) { - // test already added by method resolver - AddArgument(actualArgs, args.Target, args.TargetExpression); - } - - // simple arguments: - for (int i = 0; i < args.SimpleArgumentCount; i++) { - var value = args.GetSimpleArgument(i); - var expr = args.GetSimpleArgumentExpression(i); - - // TODO: overload-resolution restrictions - metaBuilder.AddObjectTypeRestriction(value, expr); - AddArgument(actualArgs, value, expr); - } - - // splat argument: - int listLength; - ParameterExpression listVariable; - if (args.Signature.HasSplattedArgument) { - object splattedArg = args.GetSplattedArgument(); - Expression splattedArgExpression = args.GetSplattedArgumentExpression(); - - if (metaBuilder.AddSplattedArgumentTest(splattedArg, splattedArgExpression, out listLength, out listVariable)) { - - // AddTestForListArg only returns 'true' if the argument is a List - var list = (List)splattedArg; - - // get arguments, add tests - for (int j = 0; j < listLength; j++) { - var value = list[j]; - var expr = Ast.Call(listVariable, typeof(List).GetMethod("get_Item"), AstUtils.Constant(j)); - - // TODO: overload-resolution restrictions - metaBuilder.AddObjectTypeCondition(value, expr); - AddArgument(actualArgs, value, expr); - } - - } else { - // argument is not an array => add the argument itself: - AddArgument(actualArgs, splattedArg, splattedArgExpression); - } - } - - // rhs argument: - if (args.Signature.HasRhsArgument) { - var value = args.GetRhsArgument(); - var expr = args.GetRhsArgumentExpression(); - - // TODO: overload-resolution restrictions - metaBuilder.AddObjectTypeRestriction(value, expr); - AddArgument(actualArgs, value, expr); - } - - return actualArgs.ToArray(); - } - - // TODO: OBSOLETE - private static void AddArgument(List/*!*/ actualArgs, object arg, Expression/*!*/ expr) { - if (arg == null) { - actualArgs.Add(AstUtils.Constant(null)); - } else { - var type = CompilerHelpers.GetVisibleType(arg); - if (type.IsValueType) { - actualArgs.Add(expr); - } else { - actualArgs.Add(AstUtils.Convert(expr, type)); - } - } - } - - // TODO: OBSOLETE - private static Type[]/*!*/ GetSignatureToMatch(CallArguments/*!*/ args, SelfCallConvention callConvention) { - var result = new List(args.ExplicitArgumentCount); - - // self (instance): - if (callConvention == SelfCallConvention.SelfIsInstance) { - result.Add(CompilerHelpers.GetType(args.Target)); - } - - // block: - if (args.Signature.HasBlock) { - // use None to let binder know that [NotNull]BlockParam is not applicable - result.Add(args.GetBlock() != null ? typeof(BlockParam) : typeof(DynamicNull)); - } else { - result.Add(typeof(MissingBlockParam)); - } - - // self (non-instance): - if (callConvention == SelfCallConvention.SelfIsParameter) { - result.Add(CompilerHelpers.GetType(args.Target)); - } - - // simple args: - for (int i = 0; i < args.SimpleArgumentCount; i++) { - result.Add(CompilerHelpers.GetType(args.GetSimpleArgument(i))); - } - - // splat arg: - if (args.Signature.HasSplattedArgument) { - object splattedArg = args.GetSplattedArgument(); - - var list = splattedArg as List; - if (list != null) { - foreach (object obj in list) { - result.Add(CompilerHelpers.GetType(obj)); - } - } else { - result.Add(CompilerHelpers.GetType(splattedArg)); - } - } - - // rhs arg: - if (args.Signature.HasRhsArgument) { - result.Add(CompilerHelpers.GetType(args.GetRhsArgument())); - } - - return result.ToArray(); - } - #endregion } } =================================================================== edit: $/Dev10/feature/vs_langs01_s/Merlin/Main/Languages/Ruby/Ruby/Runtime/Calls/RubyParameterBinder.cs;C806880 File: RubyParameterBinder.cs =================================================================== --- $/Dev10/feature/vs_langs01_s/Merlin/Main/Languages/Ruby/Ruby/Runtime/Calls/RubyParameterBinder.cs;C806880 (server) 4/6/2009 10:17 AM +++ Shelved Change: $/Dev10/feature/vs_langs01_s/Merlin/Main/Languages/Ruby/Ruby/Runtime/Calls/RubyParameterBinder.cs;NewMethodBinder @@ -14,24 +14,33 @@ * ***************************************************************************/ using System; +using System.Collections; using System.Collections.Generic; using System.Diagnostics; +using System.Dynamic; using System.Linq.Expressions; using System.Reflection; using System.Text; using IronRuby.Builtins; -using Microsoft.Scripting; +using IronRuby.Compiler; using Microsoft.Scripting.Actions; using Microsoft.Scripting.Actions.Calls; using Microsoft.Scripting.Generation; +using Microsoft.Scripting.Runtime; using Microsoft.Scripting.Utils; using Ast = System.Linq.Expressions.Expression; using AstUtils = Microsoft.Scripting.Ast.Utils; -using IronRuby.Compiler; namespace IronRuby.Runtime.Calls { - internal sealed class RubyParameterBinder : ParameterBinder { + internal sealed class RubyMethodBinder : MethodBinder { private readonly CallArguments/*!*/ _args; + private readonly MetaObjectBuilder/*!*/ _metaBuilder; + private readonly SelfCallConvention _callConvention; + + private int _firstRestrictedArg; + private int _lastSplattedArg; + private ParameterExpression _listVariable; + private IList _list; public RubyContext/*!*/ Context { get { return _args.RubyContext; } @@ -45,11 +54,333 @@ get { return _args.MetaContext.Expression; } } - public RubyParameterBinder(CallArguments/*!*/ args) - : base(args.RubyContext.Binder) { + public RubyMethodBinder(MetaObjectBuilder/*!*/ metaBuilder, CallArguments/*!*/ args, SelfCallConvention callConvention) + : base(args.RubyContext.Binder, CallTypes.None, NarrowingLevel.None, NarrowingLevel.All) { _args = args; + _metaBuilder = metaBuilder; + _callConvention = callConvention; } + #region Step 1: Special Parameters + + protected override int PrepareParametersBinding(ParameterInfo/*!*/[]/*!*/ parameterInfos, List/*!*/ arguments, + List/*!*/ parameters, ref int index) { + + var i = 0; + + while (i < parameterInfos.Length + && parameterInfos[i].ParameterType.IsSubclassOf(typeof(RubyCallSiteStorage))) { + + arguments.Add(new RubyCallSiteStorageBuilder(parameterInfos[i])); + i++; + } + + if (i < parameterInfos.Length) { + var parameterInfo = parameterInfos[i]; + + if (parameterInfo.ParameterType == typeof(RubyScope)) { + arguments.Add(new RubyScopeArgBuilder(parameterInfo)); + i++; + } else if (parameterInfo.ParameterType == typeof(RubyContext)) { + arguments.Add(new RubyContextArgBuilder(parameterInfo)); + i++; + } + } + + // 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 and BlockParam 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 + // + if (i >= parameterInfos.Length || parameterInfos[i].ParameterType != typeof(BlockParam)) { + arguments.Add(new MissingBlockArgBuilder(index++)); + parameters.Add(new ParameterWrapper(typeof(MissingBlockParam), null, false)); + } + + return i; + } + + internal static void GetParameterCount(ParameterInfo/*!*/[]/*!*/ parameterInfos, out int mandatory, out int optional, out bool acceptsBlock) { + acceptsBlock = false; + mandatory = 0; + optional = 0; + foreach (ParameterInfo parameterInfo in parameterInfos) { + if (IsHiddenParameter(parameterInfo)) { + continue; + } else if (parameterInfo.ParameterType == typeof(BlockParam)) { + acceptsBlock = true; + } else if (CompilerHelpers.IsParamArray(parameterInfo)) { + // TODO: indicate splat args separately? + optional++; + } else if (CompilerHelpers.IsOutParameter(parameterInfo)) { + // Python allows passing of optional "clr.Reference" to capture out parameters + // Ruby should allow similar + optional++; + } else if (CompilerHelpers.IsMandatoryParameter(parameterInfo)) { + mandatory++; + } else { + optional++; + } + } + } + + internal static bool IsHiddenParameter(ParameterInfo/*!*/ parameterInfo) { + return parameterInfo.ParameterType == typeof(RubyScope) + || parameterInfo.ParameterType == typeof(RubyContext) + || parameterInfo.ParameterType.IsSubclassOf(typeof(RubyCallSiteStorage)); + } + + #endregion + + #region Step 2: Actual Arguments + + private static readonly DynamicMetaObject NullMetaBlockParam = + new DynamicMetaObject( + AstUtils.Constant(null, typeof(BlockParam)), + BindingRestrictions.Empty, + null + ); + + // Creates actual/normalized arguments: inserts self, expands splats, and inserts rhs arg. + // Adds any restrictions/conditions applied to the arguments to the given meta-builder. + protected override ActualArguments CreateActualArguments(IList namedArgs, IList argNames, int preSplatLimit, int postSplatLimit) { + var result = new List(); + + // self (instance): + if (_callConvention == SelfCallConvention.SelfIsInstance) { + result.Add(_args.MetaTarget); + } + + if (_args.Signature.HasBlock) { + if (_args.GetBlock() == null) { + // the user explicitly passed nil as a block arg: + result.Add(NullMetaBlockParam); + } else { + // pass BlockParam: + if (_metaBuilder.BfcVariable == null) { + // we add temporary even though we might not us it if the calee doesn't have block param arg: + _metaBuilder.BfcVariable = _metaBuilder.GetTemporary(typeof(BlockParam), "#bfc"); + } + result.Add(new DynamicMetaObject(_metaBuilder.BfcVariable, BindingRestrictions.Empty)); + } + } else { + // no block passed into a method with a BlockParam: + result.Add(MissingBlockParam.Meta.Instance); + } + + // self (parameter): + if (_callConvention == SelfCallConvention.SelfIsParameter) { + result.Add(_args.MetaTarget); + } + + // the next argument is the first one for which we use restrictions coming from overload resolution: + _firstRestrictedArg = result.Count; + + return CreateActualArguments(result, _metaBuilder, _args, preSplatLimit, postSplatLimit, out _lastSplattedArg, out _list, out _listVariable); + } + + public static IList NormalizeArguments(MetaObjectBuilder/*!*/ metaBuilder, CallArguments/*!*/ args, int minCount, int maxCount) { + int lastSplattedArg; + IList list; + ParameterExpression listVariable; + + var actualArgs = CreateActualArguments(new List(), metaBuilder, args, maxCount, maxCount, + out lastSplattedArg, out list, out listVariable); + + int actualCount = actualArgs.Count + actualArgs.CollapsedCount; + + if (actualCount < minCount) { + metaBuilder.SetWrongNumberOfArgumentsError(actualCount, minCount); + return null; + } else if (actualCount > maxCount) { + metaBuilder.SetWrongNumberOfArgumentsError(actualCount, maxCount); + return null; + } + + // any collapsed args are out of limits: + return actualArgs.Arguments; + } + + private static ActualArguments/*!*/ CreateActualArguments(List/*!*/ normalized, MetaObjectBuilder/*!*/ metaBuilder, + CallArguments/*!*/ args, int preSplatLimit, int postSplatLimit, out int lastSplattedArg, out IList list, out ParameterExpression listVariable) { + + int firstSplattedArg, splatIndex, collapsedArgCount; + + // simple arguments: + for (int i = 0; i < args.SimpleArgumentCount; i++) { + normalized.Add(args.GetSimpleMetaArgument(i)); + } + + // splat argument: + list = null; + listVariable = null; + if (args.Signature.HasSplattedArgument) { + firstSplattedArg = normalized.Count; + + int listLength; + var splatted = args.GetSplattedMetaArgument(); + if (metaBuilder.AddSplattedArgumentTest(splatted.Value, splatted.Expression, out listLength, out listVariable)) { + + // AddTestForListArg only returns 'true' if the argument is a List + list = (IList)splatted.Value; + + int i = 0; + while (i < Math.Min(listLength, preSplatLimit - firstSplattedArg)) { + normalized.Add(MakeSplattedItem(list, listVariable, i)); + i++; + } + + // skip items that are not needed for overload resolution + splatIndex = normalized.Count; + + i = Math.Max(i, listLength - (postSplatLimit - (args.Signature.HasRhsArgument ? 1 : 0))); + while (i < listLength) { + normalized.Add(MakeSplattedItem(list, listVariable, i)); + i++; + } + + collapsedArgCount = listLength - (normalized.Count - firstSplattedArg); + } else { + // argument is not an array => add the argument itself: + normalized.Add(splatted); + listLength = 1; + splatIndex = -1; + collapsedArgCount = 0; + } + + lastSplattedArg = normalized.Count - 1; + } else { + splatIndex = firstSplattedArg = lastSplattedArg = -1; + collapsedArgCount = 0; + } + + Debug.Assert(collapsedArgCount >= 0); + + // rhs argument: + if (args.Signature.HasRhsArgument) { + normalized.Add(args.GetRhsMetaArgument()); + } + + return new ActualArguments( + normalized.ToArray(), + DynamicMetaObject.EmptyMetaObjects, + ArrayUtils.EmptyStrings, + collapsedArgCount, + firstSplattedArg, + splatIndex + ); + } + + internal static DynamicMetaObject/*!*/ MakeSplattedItem(IList/*!*/ list, Expression/*!*/ listVariable, int index) { + return DynamicMetaObject.Create( + list[index], + Ast.Call(listVariable, typeof(IList).GetMethod("get_Item"), AstUtils.Constant(index)) + ); + } + + #endregion + + #region Step 3: Restrictions + + internal void AddArgumentRestrictions(MetaObjectBuilder/*!*/ metaBuilder, BindingTarget/*!*/ bindingTarget) { + var args = GetActualArguments(); + var restrictedArgs = bindingTarget.Success ? bindingTarget.RestrictedArguments.Objects : args.Arguments; + + for (int i = _firstRestrictedArg; i < restrictedArgs.Count; i++) { + var arg = (bindingTarget.Success ? restrictedArgs[i] : restrictedArgs[i].Restrict(restrictedArgs[i].GetLimitType())); + + if (i >= args.FirstSplattedArg && i <= _lastSplattedArg) { + metaBuilder.AddCondition(arg.Restrictions.ToExpression()); + } else { + metaBuilder.AddRestriction(arg.Restrictions); + } + } + + // Adds condition for collapsed arguments - it is the same whether we succeed or not: + var splatCondition = GetCollapsedArgsCondition(); + if (splatCondition != null) { + metaBuilder.AddCondition(splatCondition); + } + } + + #endregion + + #region Step 4: Argument Building, Conversions + + public override bool CanConvertFrom(Type/*!*/ fromType, ParameterWrapper/*!*/ toParameter, NarrowingLevel level) { + Type toType = toParameter.Type; + + if (base.CanConvertFrom(fromType, toParameter, level)) { + return true; + } + + // blocks: + if (fromType == typeof(MissingBlockParam)) { + return toType == typeof(BlockParam) && !toParameter.ProhibitNull; + } + + if (fromType == typeof(BlockParam) && toType == typeof(MissingBlockParam)) { + return true; + } + + // protocol conversions: + if (toParameter.ParameterInfo != null && toParameter.ParameterInfo.IsDefined(typeof(DefaultProtocolAttribute), false) && + // default protocol doesn't apply on param-array/dict itself, only on the expanded parameters: + !toParameter.IsParamsArray) { + + // any type is potentially convertible, except for nil if [NotNull] is used or the target type is a value type: + return fromType != typeof(DynamicNull) || !(toParameter.ProhibitNull || toType.IsValueType); + } + + return false; + } + + public override Candidate SelectBestConversionFor(Type/*!*/ actualType, ParameterWrapper/*!*/ candidateOne, ParameterWrapper/*!*/ candidateTwo, NarrowingLevel level) { + Type typeOne = candidateOne.Type; + Type typeTwo = candidateTwo.Type; + + if (actualType == typeof(DynamicNull)) { + // if nil is passed as a block argument prefer BlockParam over missing block; + if (typeOne == typeof(BlockParam) && typeTwo == typeof(MissingBlockParam)) { + return Candidate.One; + } + + if (typeOne == typeof(MissingBlockParam) && typeTwo == typeof(BlockParam)) { + return Candidate.Two; + } + } else { + if (actualType == typeOne && candidateOne.ProhibitNull) { + return Candidate.One; + } + + if (actualType == typeTwo && candidateTwo.ProhibitNull) { + return Candidate.Two; + } + } + + if (actualType == typeOne) { + return Candidate.One; + } + + if (actualType == typeTwo) { + return Candidate.Two; + } + + + return Candidate.Equivalent; + } + public override Expression/*!*/ ConvertExpression(Expression/*!*/ expr, ParameterInfo info, Type/*!*/ toType) { Type fromType = expr.Type; @@ -76,5 +407,182 @@ return Binder.ConvertExpression(expr, toType, ConversionResultKind.ExplicitCast, null); } + + protected override Expression/*!*/ GetSplattedExpression() { + return _listVariable; + } + + protected override object GetSplattedItem(int index) { + return _list[index]; + } + + internal sealed class RubyContextArgBuilder : ArgBuilder { + public RubyContextArgBuilder(ParameterInfo/*!*/ info) + : base(info) { + } + + public override int Priority { + get { return -1; } + } + + protected override Expression ToExpression(MethodBinder/*!*/ methodBinder, IList/*!*/ parameters, bool[]/*!*/ hasBeenUsed) { + return ((RubyMethodBinder)methodBinder).ContextExpression; + } + } + + internal sealed class RubyCallSiteStorageBuilder : ArgBuilder { + public RubyCallSiteStorageBuilder(ParameterInfo/*!*/ info) + : base(info) { + } + + public override int Priority { + get { return -1; } + } + + protected override Expression ToExpression(MethodBinder/*!*/ methodBinder, IList/*!*/ parameters, bool[]/*!*/ hasBeenUsed) { + return AstUtils.Constant(Activator.CreateInstance(ParameterInfo.ParameterType, ((RubyMethodBinder)methodBinder).Context)); + } + } + + internal sealed class RubyScopeArgBuilder : ArgBuilder { + public RubyScopeArgBuilder(ParameterInfo/*!*/ info) + : base(info) { + } + + public override int Priority { + get { return -1; } + } + + protected override Expression ToExpression(MethodBinder/*!*/ methodBinder, IList/*!*/ parameters, bool[]/*!*/ hasBeenUsed) { + return ((RubyMethodBinder)methodBinder).ScopeExpression; + } + } + + internal sealed class MissingBlockArgBuilder : SimpleArgBuilder { + public MissingBlockArgBuilder(int index) + : base(typeof(MissingBlockParam), index, false, false) { + } + + public override int Priority { + get { return -1; } + } + + protected override SimpleArgBuilder/*!*/ Copy(int newIndex) { + return new MissingBlockArgBuilder(newIndex); + } + + protected override Expression ToExpression(MethodBinder/*!*/ methodBinder, IList/*!*/ parameters, bool[]/*!*/ hasBeenUsed) { + Debug.Assert(Index < parameters.Count); + Debug.Assert(Index < hasBeenUsed.Length); + Debug.Assert(parameters[Index] != null); + hasBeenUsed[Index] = true; + return null; + } + } + + #endregion + + #region Setp 5: Errors + + public override Microsoft.Scripting.Actions.ErrorInfo MakeInvalidParametersError(BindingTarget target) { + Expression exceptionValue; + switch (target.Result) { + case BindingResult.AmbiguousMatch: + exceptionValue = MakeAmbiguousCallError(target); + break; + case BindingResult.IncorrectArgumentCount: + exceptionValue = MakeIncorrectArgumentCountError(target); + break; + case BindingResult.CallFailure: + exceptionValue = MakeCallFailureError(target); + break; + default: throw new InvalidOperationException(); + } + return Microsoft.Scripting.Actions.ErrorInfo.FromException(exceptionValue); + } + + private Expression MakeAmbiguousCallError(BindingTarget target) { + StringBuilder sb = new StringBuilder(string.Format("Found multiple methods for '{0}': ", target.Name)); + string outerComma = ""; + foreach (MethodTarget mt in target.AmbiguousMatches) { + Type[] types = mt.GetParameterTypes(); + string innerComma = ""; + + sb.Append(outerComma); + sb.Append(target.Name); + sb.Append('('); + foreach (Type t in types) { + sb.Append(innerComma); + sb.Append(Binder.GetTypeName(t)); + innerComma = ", "; + } + + sb.Append(')'); + outerComma = ", "; + } + + return Methods.MakeAmbiguousMatchError.OpCall(AstUtils.Constant(sb.ToString())); + } + + private Expression MakeIncorrectArgumentCountError(BindingTarget target) { + IList available = target.ExpectedArgumentCount; + int expected; + + if (available.Count > 0) { + int minGreater = Int32.MaxValue; + int maxLesser = Int32.MinValue; + int max = Int32.MinValue; + for (int i = 0; i < available.Count; i++) { + if (available[i] > target.ActualArgumentCount) { + minGreater = Math.Min(minGreater, available[i]); + } else { + maxLesser = Math.Max(maxLesser, available[i]); + } + + max = Math.Max(max, available[i]); + } + + // block parameter is always added to both parameters and arguments (BlockParam or MissingBlockParam): + expected = (target.ActualArgumentCount < maxLesser ? maxLesser : Math.Min(minGreater, max)) - 1; + } else { + // no overload is callable: + expected = 0; + } + + return Methods.MakeWrongNumberOfArgumentsError.OpCall(AstUtils.Constant(target.ActualArgumentCount - 1), AstUtils.Constant(expected)); + } + + private Expression MakeCallFailureError(BindingTarget target) { + foreach (CallFailure cf in target.CallFailures) { + switch (cf.Reason) { + case CallFailureReason.ConversionFailure: + foreach (ConversionResult cr in cf.ConversionResults) { + if (cr.Failed) { + if (typeof(Proc).IsAssignableFrom(cr.To)) { + return Methods.CreateArgumentsErrorForProc.OpCall(AstUtils.Constant(Binder.GetTypeName(cr.From))); + } + + Debug.Assert(typeof(BlockParam).IsSealed); + if (cr.To == typeof(BlockParam)) { + Debug.Assert(cr.From == typeof(MissingBlockParam)); + return Methods.CreateArgumentsErrorForMissingBlock.OpCall(); + } + + return Methods.CreateTypeConversionError.OpCall( + AstUtils.Constant(Binder.GetTypeName(cr.From)), + AstUtils.Constant(Binder.GetTypeName(cr.To))); + } + } + break; + + case CallFailureReason.DuplicateKeyword: + case CallFailureReason.UnassignableKeyword: + default: throw new InvalidOperationException(); + } + } + throw new InvalidOperationException(); + } + + #endregion } } ===================================================================