public override DynamicMetaObject /*!*/ FallbackGetMember(DynamicMetaObject /*!*/ target, DynamicMetaObject errorSuggestion) { if (_unmangled != null) { // TODO: errorSuggestion? return(_unmangled.Bind(target, DynamicMetaObject.EmptyMetaObjects)); } #if !SILVERLIGHT DynamicMetaObject result; if (Microsoft.Scripting.ComInterop.ComBinder.TryBindGetMember(this, target, out result)) { return(result); } #endif var metaBuilder = new MetaObjectBuilder(target); var callArgs = new CallArguments(_context, target, DynamicMetaObject.EmptyMetaObjects, _CallInfo); if (!RubyCallAction.BuildAccess(metaBuilder, _originalName ?? Name, callArgs, errorSuggestion == null, true)) { Debug.Assert(errorSuggestion != null); metaBuilder.SetMetaResult(errorSuggestion, false); } return(metaBuilder.CreateMetaObject(this)); }
public override DynamicMetaObject /*!*/ FallbackGetMember(DynamicMetaObject /*!*/ target, DynamicMetaObject errorSuggestion) { #if !SILVERLIGHT DynamicMetaObject result; if (Microsoft.Scripting.ComInterop.ComBinder.TryBindGetMember(this, target, out result)) { return(result); } #endif var metaBuilder = new MetaObjectBuilder(target); var callArgs = new CallArguments(_context, target, DynamicMetaObject.EmptyMetaObjects, _CallInfo); // See InvokeMember binder for explanation. bool tryAlternateBinding = _unmangled != null && errorSuggestion == null; if (!RubyCallAction.BuildAccess(metaBuilder, _originalName ?? Name, callArgs, errorSuggestion == null && !tryAlternateBinding, true)) { Debug.Assert(errorSuggestion != null || tryAlternateBinding); if (tryAlternateBinding) { metaBuilder.SetMetaResult(target.BindGetMember(_unmangled), true); } else { // method wasn't found so we didn't do any operation with arguments that would require restrictions converted to conditions: metaBuilder.SetMetaResult(errorSuggestion, false); } } return(metaBuilder.CreateMetaObject(this)); }
internal static DynamicMetaObject FallbackInvokeMember(IInteropBinder /*!*/ binder, string /*!*/ methodName, CallInfo /*!*/ callInfo, DynamicMetaObject /*!*/ target, DynamicMetaObject /*!*/[] /*!*/ args, DynamicMetaObject errorSuggestion, InvokeMember alternateBinder) { var metaBuilder = new MetaObjectBuilder(binder, target, args); var callArgs = new CallArguments(binder.Context, target, args, callInfo); // // If we are called with no errorSuggestion we attempt to bind the alternate name since the current binding failed // (unless we successfully bind to a COM object method or Ruby/CLR method defined on the target meta-object). // If we already have an errorSuggestion we use it as it represents a valid binding and no alternate name lookups are thus necessary. // // For example, DynamicObject InvokeMember calls our FallbackInvokeMember 4 times: // // 1) binder.fallback(..., errorSuggestion: null) // -> DynamicObject.BindInvokeMember(altBinder) // 2) altBinder.fallback(..., errorSuggestion: null) // -> [[ error ]] // // 3) altBinder.fallback(..., errorSuggestion: [[ // TryInvokeMember(altName, out result) // ? result // : TryGetMember(altName, out result) // ? altBinder.FallbackInvoke(result) // : [[ error ]] // ]]) // -> errorSuggestion // // 4) binder.fallback(..., errorSuggestion: [[ // TryInvokeMember(name, out result) // ? result // : TryGetMember(name, out result) // ? binder.FallbackInvoke(result) // TryInvokeMember(altName, out result) // ? result // : TryGetMember(altName, out result) // ? altBinder.FallbackInvoke(result) // : [[ error ]] // // ]]) // -> errorSuggestion // bool tryAlternateBinding = alternateBinder != null && errorSuggestion == null; if (!RubyCallAction.BuildCall(metaBuilder, methodName, callArgs, errorSuggestion == null && !tryAlternateBinding, true)) { Debug.Assert(errorSuggestion != null || tryAlternateBinding); if (tryAlternateBinding) { metaBuilder.SetMetaResult(target.BindInvokeMember(alternateBinder, args), true); } else { // method wasn't found so we didn't do any operation with arguments that would require restrictions converted to conditions: metaBuilder.SetMetaResult(errorSuggestion, false); } } return(metaBuilder.CreateMetaObject((DynamicMetaObjectBinder)binder)); }
public RubyCallAction /*!*/ Call(string /*!*/ methodName, RubyCallSignature signature) { var key = Key.Create(methodName, signature); lock (_callActions) { RubyCallAction result; if (!_callActions.TryGetValue(key, out result)) { _callActions.Add(key, result = new RubyCallAction(_context, methodName, signature)); } return(result); } }
public static MetaObject TryBind(RubyContext /*!*/ context, GetMemberBinder /*!*/ binder, MetaObject /*!*/ target) { Assert.NotNull(context, target); var metaBuilder = new MetaObjectBuilder(); var contextExpression = Ast.Constant(context); metaBuilder.AddTargetTypeTest(target.Value, target.Expression, context, contextExpression); RubyMemberInfo method = context.ResolveMethod(target.Value, binder.Name, true).InvalidateSitesOnOverride(); if (method != null && RubyModule.IsMethodVisible(method, false)) { // we need to create a bound member: metaBuilder.Result = Ast.Constant(new RubyMethod(target.Value, method, binder.Name)); } else { // TODO: // We need to throw an exception if we don't find method_missing so that our version update optimization works: // This limits interop with other languages. // // class B CLR type with method 'foo' // class C < B Ruby class // x = C.new // // 1. x.GET("foo") from Ruby // No method found or CLR method found -> fallback to Python // Python might see its method foo or might just fallback to .NET, // in any case it will add rule [1] with restriction on type of C w/o Ruby version check. // 2. B.define_method("foo") // This doesn't update C due to the optimization (there is no overridden method foo in C). // 3. x.GET("foo") from Ruby // This will not invoke the binder since the rule [1] is still valid. // object symbol = SymbolTable.StringToId(binder.Name); RubyCallAction.BindToMethodMissing(metaBuilder, binder.Name, new CallArguments( new MetaObject(contextExpression, Restrictions.Empty, context), new[] { target, new MetaObject(Ast.Constant(symbol), Restrictions.Empty, symbol) }, RubyCallSignature.Simple(1) ), method != null ); } // TODO: we should return null if we fail, we need to throw exception for now: return(metaBuilder.CreateMetaObject(binder, MetaObject.EmptyMetaObjects)); }
internal static DynamicMetaObject FallbackInvokeMember(IInteropBinder /*!*/ binder, string /*!*/ methodName, CallInfo /*!*/ callInfo, DynamicMetaObject /*!*/ target, DynamicMetaObject /*!*/[] /*!*/ args, DynamicMetaObject errorSuggestion) { var metaBuilder = new MetaObjectBuilder(binder, target, args); var callArgs = new CallArguments(binder.Context, target, args, callInfo); if (!RubyCallAction.BuildCall(metaBuilder, methodName, callArgs, errorSuggestion == null, true)) { Debug.Assert(errorSuggestion != null); // method wasn't found so we didn't do any operation with arguments that would require restrictions converted to conditions: metaBuilder.SetMetaResult(errorSuggestion, false); } return(metaBuilder.CreateMetaObject((DynamicMetaObjectBinder)binder)); }
public static DynamicMetaObject /*!*/ Bind(DynamicMetaObject /*!*/ context, GetIndexBinder /*!*/ binder, DynamicMetaObject /*!*/ target, DynamicMetaObject /*!*/[] /*!*/ indexes, Func <DynamicMetaObject, DynamicMetaObject[], DynamicMetaObject> /*!*/ fallback) { Debug.Assert(fallback != null); var callArgs = new CallArguments(context, target, indexes, RubyCallSignature.Interop(indexes.Length)); var metaBuilder = new MetaObjectBuilder(target, indexes); if (!RubyCallAction.BuildCall(metaBuilder, "[]", callArgs, false, false)) { metaBuilder.SetMetaResult(fallback(target, indexes), false); } return(metaBuilder.CreateMetaObject(binder)); }
public static DynamicMetaObject /*!*/ Bind(DynamicMetaObject /*!*/ context, SetMemberBinder /*!*/ binder, DynamicMetaObject /*!*/ target, DynamicMetaObject /*!*/ value, Func <DynamicMetaObject, DynamicMetaObject, DynamicMetaObject> /*!*/ fallback) { Debug.Assert(fallback != null); var args = new[] { value }; var callArgs = new CallArguments(context, target, args, RubyCallSignature.Interop(1)); var metaBuilder = new MetaObjectBuilder(target, args); if (!RubyCallAction.BuildCall(metaBuilder, binder.Name + "=", callArgs, false, false)) { metaBuilder.SetMetaResult(fallback(target, value), false); } return(metaBuilder.CreateMetaObject(binder)); }
public static DynamicMetaObject /*!*/ Bind(DynamicMetaObject /*!*/ context, GetMemberBinder /*!*/ binder, DynamicMetaObject /*!*/ target, Func <DynamicMetaObject, DynamicMetaObject> /*!*/ fallback) { Debug.Assert(fallback != null); var callArgs = new CallArguments(context, target, DynamicMetaObject.EmptyMetaObjects, RubyCallSignature.Interop(0)); var metaBuilder = new MetaObjectBuilder(target); if (!RubyCallAction.BuildAccess(metaBuilder, binder.Name, callArgs, false, false)) { // TODO: error suggestion? metaBuilder.SetMetaResult(fallback(target), false); } return(metaBuilder.CreateMetaObject(binder)); }
public static DynamicMetaObject /*!*/ Bind(DynamicMetaObject /*!*/ context, string /*!*/ methodName, CallInfo /*!*/ callInfo, DynamicMetaObjectBinder /*!*/ binder, DynamicMetaObject /*!*/ target, DynamicMetaObject /*!*/[] /*!*/ args, Func <DynamicMetaObject, DynamicMetaObject[], DynamicMetaObject> /*!*/ fallback) { Debug.Assert(fallback != null); var callArgs = new CallArguments(context, target, args, RubyCallSignature.Interop(callInfo.ArgumentCount)); var metaBuilder = new MetaObjectBuilder(target, args); if (!RubyCallAction.BuildCall(metaBuilder, methodName, callArgs, false, false)) { // TODO: error suggestion? metaBuilder.SetMetaResult(fallback(target, args), false); } return(metaBuilder.CreateMetaObject(binder)); }
public static MetaObject TryBind(RubyContext /*!*/ context, InvokeMemberBinder /*!*/ binder, MetaObject /*!*/ target, MetaObject /*!*/[] /*!*/ args) { Assert.NotNull(context, target); var metaBuilder = new MetaObjectBuilder(); RubyCallAction.Bind(metaBuilder, binder.Name, new CallArguments( new MetaObject(Ast.Constant(context), Restrictions.Empty, context), target, args, RubyCallSignature.Simple(binder.Arguments.Count) ) ); // TODO: we should return null if we fail, we need to throw exception for now: return(metaBuilder.CreateMetaObject(binder, MetaObject.EmptyMetaObjects)); }
public static MetaObject TryBind(RubyContext /*!*/ context, CreateInstanceBinder /*!*/ binder, MetaObject /*!*/ target, MetaObject /*!*/[] /*!*/ args) { Assert.NotNull(context, binder, target, args); var metaBuilder = new MetaObjectBuilder(); RubyCallAction.Bind(metaBuilder, "new", new CallArguments( new MetaObject(Ast.Constant(context), Restrictions.Empty, context), target, args, RubyCallSignature.Simple(args.Length) ) ); // TODO: we should return null if we fail, we need to throw exception due to version update optimization: return(metaBuilder.CreateMetaObject(binder, MetaObject.EmptyMetaObjects)); }
public static DynamicMetaObject /*!*/ Bind(DynamicMetaObject /*!*/ context, SetIndexBinder /*!*/ binder, DynamicMetaObject /*!*/ target, DynamicMetaObject /*!*/[] /*!*/ indexes, DynamicMetaObject /*!*/ value, Func <DynamicMetaObject, DynamicMetaObject[], DynamicMetaObject, DynamicMetaObject> /*!*/ fallback) { Debug.Assert(fallback != null); var args = ArrayUtils.Append(indexes, value); var callArgs = new CallArguments(context, target, args, new RubyCallSignature(indexes.Length, RubyCallFlags.IsInteropCall | RubyCallFlags.HasRhsArgument) ); var metaBuilder = new MetaObjectBuilder(target, args); if (!RubyCallAction.BuildCall(metaBuilder, "[]=", callArgs, false, false)) { metaBuilder.SetMetaResult(fallback(target, indexes, value), false); } return(metaBuilder.CreateMetaObject(binder)); }
internal void SetRule(MetaObjectBuilder /*!*/ metaBuilder, CallArguments /*!*/ args) { Assert.NotNull(metaBuilder, args); Debug.Assert(args.SimpleArgumentCount == 0 && !args.Signature.HasBlock && !args.Signature.HasSplattedArgument && !args.Signature.HasRhsArgument); Debug.Assert(args.Signature.HasScope); var ec = args.RubyContext; // implicit conversions should only depend on a static type: if (TryImplicitConversion(metaBuilder, args)) { if (args.Target == null) { metaBuilder.AddRestriction(Ast.Equal(args.TargetExpression, Ast.Constant(null, args.TargetExpression.Type))); } else { metaBuilder.AddTypeRestriction(args.Target.GetType(), args.TargetExpression); } return; } // check for type version: metaBuilder.AddTargetTypeTest(args); string toMethodName = ToMethodName; Expression targetClassNameConstant = Ast.Constant(ec.GetClassOf(args.Target).Name); // Kernel#respond_to? method is not overridden => we can optimize RubyMemberInfo respondToMethod = ec.ResolveMethod(args.Target, Symbols.RespondTo, true).InvalidateSitesOnOverride(); if (respondToMethod == null || // the method is defined in library, hasn't been replaced by user defined method (TODO: maybe we should make this check better) (respondToMethod.DeclaringModule == ec.KernelModule && respondToMethod is RubyMethodGroupInfo)) { RubyMemberInfo conversionMethod = ec.ResolveMethod(args.Target, toMethodName, false).InvalidateSitesOnOverride(); if (conversionMethod == null) { // error: SetError(metaBuilder, targetClassNameConstant, args); return; } else { // invoke target.to_xxx() and validate it; returns an instance of TTargetType: conversionMethod.BuildCall(metaBuilder, args, toMethodName); if (!metaBuilder.Error && ConversionResultValidator != null) { metaBuilder.Result = ConversionResultValidator.OpCall(targetClassNameConstant, AstFactory.Box(metaBuilder.Result)); } return; } } else if (!RubyModule.IsMethodVisible(respondToMethod, false)) { // respond_to? is private: SetError(metaBuilder, targetClassNameConstant, args); return; } // slow path: invoke respond_to?, to_xxx and result validation: var conversionCallSite = Ast.Dynamic( RubyCallAction.Make(toMethodName, RubyCallSignature.WithScope(0)), typeof(object), args.ScopeExpression, args.TargetExpression ); Expression opCall; metaBuilder.Result = Ast.Condition( // If // respond_to?() Methods.IsTrue.OpCall( Ast.Dynamic( RubyCallAction.Make(Symbols.RespondTo, RubyCallSignature.WithScope(1)), typeof(object), args.ScopeExpression, args.TargetExpression, Ast.Constant(SymbolTable.StringToId(toMethodName)) ) ), // Then // to_xxx(): opCall = (ConversionResultValidator == null) ? conversionCallSite : ConversionResultValidator.OpCall(targetClassNameConstant, conversionCallSite), // Else AstUtils.Convert( (ConversionResultValidator == null) ? args.TargetExpression : Ast.Convert( Ast.Throw(Methods.CreateTypeConversionError.OpCall(targetClassNameConstant, Ast.Constant(TargetTypeName))), typeof(object) ), opCall.Type ) ); }
protected override bool Build(MetaObjectBuilder /*!*/ metaBuilder, CallArguments /*!*/ args, bool defaultFallback) { RubyModule currentDeclaringModule; string currentMethodName; var scope = args.Scope; var scopeExpr = AstUtils.Convert(args.MetaScope.Expression, typeof(RubyScope)); RubyScope targetScope; int scopeNesting = scope.GetSuperCallTarget(out currentDeclaringModule, out currentMethodName, out targetScope); if (scopeNesting == -1) { metaBuilder.AddCondition(Methods.IsSuperOutOfMethodScope.OpCall(scopeExpr)); metaBuilder.SetError(Methods.MakeTopLevelSuperException.OpCall()); return(true); } object target = targetScope.SelfObject; var targetExpression = metaBuilder.GetTemporary(typeof(object), "#super-self"); var assignTarget = Ast.Assign( targetExpression, Methods.GetSuperCallTarget.OpCall(scopeExpr, AstUtils.Constant(scopeNesting)) ); if (_signature.HasImplicitArguments && targetScope.Kind == ScopeKind.BlockMethod) { metaBuilder.AddCondition(Ast.NotEqual(assignTarget, Ast.Field(null, Fields.NeedsUpdate))); metaBuilder.SetError(Methods.MakeImplicitSuperInBlockMethodError.OpCall()); return(true); } // If we need to update we return RubyOps.NeedsUpdate instance that will cause the subsequent conditions to fail: metaBuilder.AddInitialization(assignTarget); args.SetTarget(targetExpression, target); Debug.Assert(currentDeclaringModule != null); RubyMemberInfo method; RubyMemberInfo methodMissing = null; // MRI bug: Uses currentDeclaringModule for method look-up so we can end up with an instance method of class C // called on a target of another class. See http://redmine.ruby-lang.org/issues/show/2419. // we need to lock the hierarchy of the target class: var targetClass = scope.RubyContext.GetImmediateClassOf(target); using (targetClass.Context.ClassHierarchyLocker()) { // initialize all methods in ancestors: targetClass.InitializeMethodsNoLock(); // target is stored in a local, therefore it cannot be part of the restrictions: metaBuilder.TreatRestrictionsAsConditions = true; metaBuilder.AddTargetTypeTest(target, targetClass, targetExpression, args.MetaContext, new[] { Symbols.MethodMissing } // currentMethodName is resolved for super, which cannot be an instance singleton ); metaBuilder.TreatRestrictionsAsConditions = false; method = targetClass.ResolveSuperMethodNoLock(currentMethodName, currentDeclaringModule).InvalidateSitesOnOverride().Info; if (_signature.ResolveOnly) { metaBuilder.Result = AstUtils.Constant(method != null); return(true); } if (method == null) { // MRI: method_missing is called for the targetClass, not for the super: methodMissing = targetClass.ResolveMethodMissingForSite(currentMethodName, RubyMethodVisibility.None); } } if (method != null) { method.BuildSuperCall(metaBuilder, args, currentMethodName, currentDeclaringModule); } else { return(RubyCallAction.BuildMethodMissingCall(metaBuilder, args, currentMethodName, methodMissing, RubyMethodVisibility.None, true, defaultFallback)); } return(true); }