private static MethodMissingBinding BindToKernelMethodMissing(MetaObjectBuilder /*!*/ metaBuilder, CallArguments /*!*/ args, string /*!*/ methodName, RubyMemberInfo methodMissing, RubyMethodVisibility incompatibleVisibility, bool isSuperCall) { // TODO: better specialization of method_missing methods if (methodMissing == null || methodMissing.DeclaringModule == methodMissing.Context.KernelModule && methodMissing is RubyLibraryMethodInfo) { if (isSuperCall) { metaBuilder.SetError(Methods.MakeMissingSuperException.OpCall(AstUtils.Constant(methodName))); } else if (incompatibleVisibility == RubyMethodVisibility.Private) { metaBuilder.SetError(Methods.MakePrivateMethodCalledError.OpCall( AstUtils.Convert(args.MetaContext.Expression, typeof(RubyContext)), args.TargetExpression, AstUtils.Constant(methodName)) ); } else if (incompatibleVisibility == RubyMethodVisibility.Protected) { metaBuilder.SetError(Methods.MakeProtectedMethodCalledError.OpCall( AstUtils.Convert(args.MetaContext.Expression, typeof(RubyContext)), args.TargetExpression, AstUtils.Constant(methodName)) ); } else { return(MethodMissingBinding.Fallback); } return(MethodMissingBinding.Error); } return(MethodMissingBinding.Custom); }
// Returns true if the call was bound (with success or failure), false if fallback should be performed. internal static bool BuildMethodMissingAccess(MetaObjectBuilder /*!*/ metaBuilder, CallArguments /*!*/ args, string /*!*/ methodName, RubyMemberInfo methodMissing, RubyMethodVisibility incompatibleVisibility, bool isSuperCall, bool defaultFallback) { switch (BindToKernelMethodMissing(metaBuilder, args, methodName, methodMissing, incompatibleVisibility, isSuperCall)) { case MethodMissingBinding.Custom: // we pretend we found the member and return a method that calls method_missing: Debug.Assert(!metaBuilder.Error); metaBuilder.Result = Methods.CreateBoundMissingMember.OpCall( AstUtils.Convert(args.TargetExpression, typeof(object)), Ast.Constant(methodMissing, typeof(RubyMemberInfo)), Ast.Constant(methodName) ); return(true); case MethodMissingBinding.Error: // method_missing is defined in Kernel, error has been reported: return(true); case MethodMissingBinding.Fallback: // method_missing is defined in Kernel: if (defaultFallback) { metaBuilder.SetError(Methods.MakeMissingMemberError.OpCall(Ast.Constant(methodName))); return(true); } return(false); } throw Assert.Unreachable; }
protected override bool Build(MetaObjectBuilder/*!*/ metaBuilder, CallArguments/*!*/ args, bool defaultFallback) { if (TryImplicitConversion(metaBuilder, args)) { metaBuilder.AddObjectTypeRestriction(args.Target, args.TargetExpression); return true; } // TODO: this is our meta object, should we add IRubyMetaConvertible interface instead of using interop-binder? if (args.Target is IDynamicMetaObjectProvider) { metaBuilder.SetMetaResult(args.MetaTarget.BindConvert(new InteropBinder.Convert(args.RubyContext, _type, true)), false); return true; } if (defaultFallback) { metaBuilder.AddObjectTypeRestriction(args.Target, args.TargetExpression); metaBuilder.SetError(Methods.MakeTypeConversionError.OpCall( args.MetaContext.Expression, AstUtils.Convert(args.TargetExpression, typeof(object)), Ast.Constant(_type) )); return true; } return false; }
protected override bool TryImplicitConversion(MetaObjectBuilder /*!*/ metaBuilder, CallArguments /*!*/ args) { object target = args.Target; if (args.Target == null) { metaBuilder.SetError(Methods.CreateTypeConversionError.OpCall(Ast.Constant("nil"), Ast.Constant(TargetTypeName))); return(true); } // TODO: other .NET primitive integer types if (target is int) { metaBuilder.Result = AstUtils.Convert(args.TargetExpression, typeof(int)); return(true); } var bignum = target as BigInteger; if ((object)bignum != null) { metaBuilder.Result = Methods.ConvertBignumToFixnum.OpCall(AstUtils.Convert(args.TargetExpression, typeof(BigInteger))); return(true); } return(false); }
/// <summary> /// Resolves an library method overload and builds call expression. /// The resulting expression on meta-builder doesn't handle block control flow yet. /// </summary> internal static void BuildCallNoFlow(MetaObjectBuilder /*!*/ metaBuilder, CallArguments /*!*/ args, string /*!*/ name, IList <MethodBase> /*!*/ overloads, bool includeSelf, bool selfIsInstance) { var bindingTarget = ResolveOverload(name, overloads, args, includeSelf, selfIsInstance); if (bindingTarget.Success) { bool calleeHasBlockParam = 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. // // NOTE: We check for null block here -> test fore that fact is added in MakeActualArgs if (metaBuilder.BfcVariable == null && args.Signature.HasBlock && args.GetBlock() != null && calleeHasBlockParam) { metaBuilder.BfcVariable = metaBuilder.GetTemporary(typeof(BlockParam), "#bfc"); } var actualArgs = MakeActualArgs(metaBuilder, args, includeSelf, selfIsInstance, calleeHasBlockParam, true); var parameterBinder = new RubyParameterBinder(args.RubyContext.Binder, args.MetaContext.Expression, args.Signature.HasScope); var targetExpression = bindingTarget.MakeExpression(parameterBinder, actualArgs); metaBuilder.Result = targetExpression; } else { metaBuilder.SetError( Methods.MakeInvalidArgumentTypesError.OpCall(Ast.Constant(name)) ); } }
internal override void BuildCallNoFlow(MetaObjectBuilder /*!*/ metaBuilder, CallArguments /*!*/ args, string /*!*/ name) { // TODO: splat, rhs, ... if (args.Signature.ArgumentCount == 0) { if (args.Signature.HasBlock) { metaBuilder.Result = Methods.HookupEvent.OpCall( AstUtils.Constant(this), args.TargetExpression, Ast.Convert(args.GetBlockExpression(), typeof(Proc)) ); } else { metaBuilder.Result = Methods.CreateEvent.OpCall( AstUtils.Constant(this), args.TargetExpression, AstUtils.Constant(name) ); } } else { metaBuilder.SetError(Methods.MakeWrongNumberOfArgumentsError.OpCall(Ast.Constant(args.Signature.ArgumentCount), Ast.Constant(0))); } }
private void SetError(MetaObjectBuilder /*!*/ metaBuilder, Expression /*!*/ targetClassNameConstant, CallArguments /*!*/ args) { if (ConversionResultValidator != null) { metaBuilder.SetError(Methods.CreateTypeConversionError.OpCall(targetClassNameConstant, Ast.Constant(TargetTypeName))); } else { metaBuilder.Result = args.TargetExpression; } }
internal override void BuildCallNoFlow(MetaObjectBuilder /*!*/ metaBuilder, CallArguments /*!*/ args, string /*!*/ name) { Expression expr = null; Expression instance = _fieldInfo.IsStatic ? null : Ast.Convert(args.TargetExpression, _fieldInfo.DeclaringType); if (_isSetter) { // parameters should be: instance/type, value if (args.SimpleArgumentCount == 0 && args.Signature.HasRhsArgument) { expr = Ast.Assign( Ast.Field(instance, _fieldInfo), // TODO: remove args.RubyContext.Binder.ConvertExpression( args.GetRhsArgumentExpression(), _fieldInfo.FieldType, ConversionResultKind.ExplicitCast, args.ScopeExpression ) ); } } else { // parameter should be: instance/type if (args.SimpleArgumentCount == 0) { if (_fieldInfo.IsLiteral) { // TODO: seems like Compiler should correctly handle the literal field case // (if you emit a read to a literal field, you get a NotSupportedExpception from // FieldHandle when we try to emit) expr = Ast.Constant(_fieldInfo.GetValue(null)); } else { expr = Ast.Field(instance, _fieldInfo); } } } if (expr != null) { metaBuilder.Result = expr; } else { metaBuilder.SetError( Methods.MakeInvalidArgumentTypesError.OpCall(Ast.Constant(_isSetter ? name + "=" : name)) ); } }
internal override void BuildCallNoFlow(MetaObjectBuilder /*!*/ metaBuilder, CallArguments /*!*/ args, string /*!*/ name) { var visibleOverloads = GetVisibleOverloads(args, MethodBases, false); if (visibleOverloads.Count == 0) { metaBuilder.SetError(Methods.MakeClrProtectedMethodCalledError.OpCall( args.MetaContext.Expression, args.MetaTarget.Expression, Ast.Constant(name) )); } else { BuildCallNoFlow(metaBuilder, args, name, visibleOverloads, CallConvention, ImplicitProtocolConversions); } }
internal override void BuildSuperCallNoFlow(MetaObjectBuilder /*!*/ metaBuilder, CallArguments /*!*/ args, string /*!*/ name, RubyModule /*!*/ declaringModule) { Assert.NotNull(declaringModule, metaBuilder, args); var visibleOverloads = GetVisibleOverloads(args, MethodBases, true); if (visibleOverloads.Count == 0) { metaBuilder.SetError(Methods.MakeClrVirtualMethodCalledError.OpCall( args.MetaContext.Expression, args.MetaTarget.Expression, Ast.Constant(name) )); } else { BuildCallNoFlow(metaBuilder, args, name, visibleOverloads, CallConvention, ImplicitProtocolConversions); } }
internal void SetRule(MetaObjectBuilder /*!*/ metaBuilder, CallArguments /*!*/ args) { RubyModule currentDeclaringModule; string currentMethodName; var scope = args.Scope; object target; scope.GetSuperCallTarget(out currentDeclaringModule, out currentMethodName, out target); var targetExpression = metaBuilder.GetTemporary(typeof(object), "#super-self"); metaBuilder.AddCondition( Methods.IsSuperCallTarget.OpCall( AstUtils.Convert(args.ScopeExpression, typeof(RubyScope)), Ast.Constant(currentDeclaringModule), AstUtils.Constant(currentMethodName), targetExpression ) ); args.SetTarget(targetExpression, target); Debug.Assert(currentDeclaringModule != null); // target is stored in a local, therefore it cannot be part of the restrictions: metaBuilder.TreatRestrictionsAsConditions = true; metaBuilder.AddTargetTypeTest(target, targetExpression, scope.RubyContext, args.ContextExpression); metaBuilder.TreatRestrictionsAsConditions = false; RubyMemberInfo method = scope.RubyContext.ResolveSuperMethod(target, currentMethodName, currentDeclaringModule); // super calls don't go to method_missing if (method == null) { metaBuilder.SetError(Methods.MakeMissingSuperException.OpCall(Ast.Constant(currentMethodName))); } else { method.InvalidateSitesOnOverride = true; method.BuildSuperCall(metaBuilder, args, currentMethodName, currentDeclaringModule); } }
/// <summary> /// Resolves an library method overload and builds call expression. /// The resulting expression on meta-builder doesn't handle block control flow yet. /// </summary> internal static void BuildCallNoFlow(MetaObjectBuilder /*!*/ metaBuilder, CallArguments /*!*/ args, string /*!*/ name, IList <OverloadInfo> /*!*/ overloads, SelfCallConvention callConvention, bool implicitProtocolConversions) { RubyOverloadResolver resolver; var bindingTarget = ResolveOverload(metaBuilder, args, name, overloads, callConvention, implicitProtocolConversions, out resolver); if (bindingTarget.Success) { // TODO: create a custom overload info: if (ReferenceEquals(bindingTarget.Overload.ReflectionInfo, Methods.CreateDefaultInstance)) { Debug.Assert(args.TargetClass.TypeTracker.Type.IsValueType); metaBuilder.Result = Ast.New(args.TargetClass.TypeTracker.Type); } else if (args.Signature.IsVirtualCall && bindingTarget.Overload.IsVirtual) { // Virtual methods that have been detached from the CLR type and // defined on the corresponding Ruby class or its subclass are not // directly invoked from a dynamic virtual call to prevent recursion. // Instead the base call is performed. // // Example: // class C < ArrayList // define_method(:Add, instance_method(:Add)) // end // // C.new.Add(1) // // C.new.Add dispatches to the virtual ArrayList::Add, which in turn dispatches to the auto-generated override C$1::Add. // That gets here since the defined method is a Ruby method (a detached CLR method group). If we called it directly // it would invoke the C$1::Add override again leading to a stack overflow. So we need to use a base call instead. metaBuilder.Result = Ast.Field(null, Fields.ForwardToBase); } else { metaBuilder.Result = bindingTarget.MakeExpression(); } } else { metaBuilder.SetError(resolver.MakeInvalidParametersError(bindingTarget).Expression); } }
internal override void BuildCallNoFlow(MetaObjectBuilder/*!*/ metaBuilder, CallArguments/*!*/ args, string/*!*/ name) { // TODO: splat, rhs, ... if (args.Signature.ArgumentCount == 0) { if (args.Signature.HasBlock) { metaBuilder.Result = Methods.HookupEvent.OpCall( AstUtils.Constant(this), args.TargetExpression, Ast.Convert(args.GetBlockExpression(), typeof(Proc)) ); } else { metaBuilder.Result = Methods.CreateEvent.OpCall( AstUtils.Constant(this), args.TargetExpression, AstUtils.Constant(name) ); } } else { metaBuilder.SetError(Methods.MakeWrongNumberOfArgumentsError.OpCall(Ast.Constant(args.Signature.ArgumentCount), Ast.Constant(0))); } }
protected override bool TryImplicitConversion(MetaObjectBuilder /*!*/ metaBuilder, CallArguments /*!*/ args) { object target = args.Target; var targetExpression = args.TargetExpression; if (args.Target == null) { metaBuilder.SetError(Methods.CreateTypeConversionError.OpCall(Ast.Constant("nil"), Ast.Constant(TargetTypeName))); return(true); } var str = target as MutableString; if (str != null) { metaBuilder.Result = Methods.ConvertMutableStringToSymbol.OpCall(AstUtils.Convert(targetExpression, typeof(MutableString))); return(true); } var sym = target as string; if (sym != null) { metaBuilder.Result = AstUtils.Convert(targetExpression, typeof(string)); return(true); } if (target is SymbolId) { metaBuilder.Result = Methods.ConvertSymbolIdToSymbol.OpCall(AstUtils.Convert(targetExpression, typeof(SymbolId))); return(true); } if (target is int) { metaBuilder.Result = Methods.ConvertFixnumToSymbol.OpCall(args.ContextExpression, AstUtils.Convert(targetExpression, typeof(int))); return(true); } return(false); }
internal override void BuildCallNoFlow(MetaObjectBuilder/*!*/ metaBuilder, CallArguments/*!*/ args, string/*!*/ name) { Expression expr = null; Expression instance = _fieldInfo.IsStatic ? null : Ast.Convert(args.TargetExpression, _fieldInfo.DeclaringType); if (_isSetter) { // parameters should be: instance/type, value if (args.SimpleArgumentCount == 0 && args.Signature.HasRhsArgument) { expr = Ast.Assign( Ast.Field(instance, _fieldInfo), // TODO: remove args.RubyContext.Binder.ConvertExpression( args.GetRhsArgumentExpression(), _fieldInfo.FieldType, ConversionResultKind.ExplicitCast, args.ScopeExpression ) ); } } else { // parameter should be: instance/type if (args.SimpleArgumentCount == 0) { if (_fieldInfo.IsLiteral) { // TODO: seems like Compiler should correctly handle the literal field case // (if you emit a read to a literal field, you get a NotSupportedExpception from // FieldHandle when we try to emit) expr = Ast.Constant(_fieldInfo.GetValue(null)); } else { expr = Ast.Field(instance, _fieldInfo); } } } if (expr != null) { metaBuilder.Result = expr; } else { metaBuilder.SetError( Methods.MakeInvalidArgumentTypesError.OpCall(Ast.Constant(_isSetter ? name + "=" : name)) ); } }
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; }
internal static bool BindToMethodMissing(MetaObjectBuilder/*!*/ metaBuilder, CallArguments/*!*/ args, string/*!*/ methodName, RubyMemberInfo methodMissing, RubyMethodVisibility incompatibleVisibility, bool isSuperCall, bool defaultFallback) { // Assumption: args already contain method name. // TODO: better check for builtin method if (methodMissing == null || methodMissing.DeclaringModule == methodMissing.Context.KernelModule && methodMissing is RubyLibraryMethodInfo) { if (isSuperCall) { metaBuilder.SetError(Methods.MakeMissingSuperException.OpCall(AstUtils.Constant(methodName))); } else if (incompatibleVisibility == RubyMethodVisibility.Private) { metaBuilder.SetError(Methods.MakePrivateMethodCalledError.OpCall( AstUtils.Convert(args.MetaContext.Expression, typeof(RubyContext)), args.TargetExpression, AstUtils.Constant(methodName)) ); } else if (incompatibleVisibility == RubyMethodVisibility.Protected) { metaBuilder.SetError(Methods.MakeProtectedMethodCalledError.OpCall( AstUtils.Convert(args.MetaContext.Expression, typeof(RubyContext)), args.TargetExpression, AstUtils.Constant(methodName)) ); } else if (defaultFallback) { args.InsertMethodName(methodName); methodMissing.BuildCall(metaBuilder, args, methodName); } else { return false; } } else { args.InsertMethodName(methodName); methodMissing.BuildCall(metaBuilder, args, methodName); } return true; }
private void BuildCall(MetaObjectBuilder/*!*/ metaBuilder, CallArguments/*!*/ args, string/*!*/ name) { var actualArgs = RubyOverloadResolver.NormalizeArguments(metaBuilder, args, 0, Int32.MaxValue); if (metaBuilder.Error) { return; } metaBuilder.AddRestriction( Ast.Equal( Ast.Property(Ast.Convert(args.TargetExpression, typeof(Win32API)), VersionProperty), Ast.Constant(_version) ) ); if (_function == IntPtr.Zero) { metaBuilder.SetError(Ast.Throw(new Func<Exception>(UninitializedFunctionError).Method.OpCall(), typeof(object))); return; } if (_signature.Length != actualArgs.Count) { metaBuilder.SetError(Ast.Throw(new Func<int, int, Exception>(InvalidParameterCountError).Method.OpCall( Ast.Constant(_signature.Length), Ast.Constant(actualArgs.Count)), typeof(object) )); return; } var calliArgs = new AstExpressions(); calliArgs.Add(Ast.Property(Ast.Convert(args.TargetExpression, typeof(Win32API)), FunctionProperty)); for (int i = 0; i < actualArgs.Count; i++) { calliArgs.Add(MarshalArgument(metaBuilder, actualArgs[i], _signature[i])); } metaBuilder.Result = Ast.Call(EmitCalliStub(), calliArgs); // MRI returns 0 if void return type is given: if (_returnType == ArgType.None) { metaBuilder.Result = Ast.Block(metaBuilder.Result, AstUtils.Constant(0)); } }
/// <summary> /// Resolves an library method overload and builds call expression. /// The resulting expression on meta-builder doesn't handle block control flow yet. /// </summary> internal static void BuildCallNoFlow(MetaObjectBuilder/*!*/ metaBuilder, CallArguments/*!*/ args, string/*!*/ name, IList<OverloadInfo>/*!*/ overloads, SelfCallConvention callConvention, bool implicitProtocolConversions) { RubyOverloadResolver resolver; var bindingTarget = ResolveOverload(metaBuilder, args, name, overloads, callConvention, implicitProtocolConversions, out resolver); if (bindingTarget.Success) { // TODO: create a custom overload info: if (ReferenceEquals(bindingTarget.Overload.ReflectionInfo, Methods.CreateDefaultInstance)) { Debug.Assert(args.TargetClass.TypeTracker.Type.IsValueType); metaBuilder.Result = Ast.New(args.TargetClass.TypeTracker.Type); } else if (args.Signature.IsVirtualCall && bindingTarget.Overload.IsVirtual) { // Virtual methods that have been detached from the CLR type and // defined on the corresponding Ruby class or its subclass are not // directly invoked from a dynamic virtual call to prevent recursion. // Instead the base call is performed. // // Example: // class C < ArrayList // define_method(:Add, instance_method(:Add)) // end // // C.new.Add(1) // // C.new.Add dispatches to the virtual ArrayList::Add, which in turn dispatches to the auto-generated override C$1::Add. // That gets here since the defined method is a Ruby method (a detached CLR method group). If we called it directly // it would invoke the C$1::Add override again leading to a stack overflow. So we need to use a base call instead. metaBuilder.Result = Ast.Field(null, Fields.ForwardToBase); } else { metaBuilder.Result = bindingTarget.MakeExpression(); } } else { metaBuilder.SetError(resolver.MakeInvalidParametersError(bindingTarget).Expression); } }
internal override void BuildSuperCallNoFlow(MetaObjectBuilder/*!*/ metaBuilder, CallArguments/*!*/ args, string/*!*/ name, RubyModule/*!*/ declaringModule) { Assert.NotNull(declaringModule, metaBuilder, args); var visibleOverloads = GetVisibleOverloads(args, MethodBases, true); if (visibleOverloads.Count == 0) { metaBuilder.SetError(Methods.MakeClrVirtualMethodCalledError.OpCall( args.MetaContext.Expression, args.MetaTarget.Expression, Ast.Constant(name) )); } else { BuildCallNoFlow(metaBuilder, args, name, visibleOverloads, CallConvention, ImplicitProtocolConversions); } }
public void BuildObjectConstructionNoFlow(MetaObjectBuilder/*!*/ metaBuilder, CallArguments/*!*/ args, string/*!*/ methodName) { Debug.Assert(!IsSingletonClass, "Cannot instantiate singletons"); Type type = GetUnderlyingSystemType(); RubyMemberInfo initializer; using (Context.ClassHierarchyLocker()) { // check version of the class so that we invalidate the rule whenever the initializer changes: metaBuilder.AddVersionTest(this); initializer = ResolveMethodForSiteNoLock(Symbols.Initialize, IgnoreVisibility).Info; // Initializer resolves to Object#initializer unless overridden in a derived class. // We ensure that this method cannot be removed. Debug.Assert(initializer != null); } bool hasRubyInitializer = initializer is RubyMethodInfo; bool hasLibraryInitializer = !hasRubyInitializer && initializer.DeclaringModule != Context.ObjectClass; if (hasRubyInitializer || hasLibraryInitializer && _isRubyClass) { // allocate and initialize: bool allocatorFound = BuildAllocatorCall(metaBuilder, args, () => AstUtils.Constant(Name)); if (metaBuilder.Error) { return; } if (!allocatorFound) { metaBuilder.SetError(Methods.MakeMissingDefaultConstructorError.OpCall( Ast.Convert(args.TargetExpression, typeof(RubyClass)), Ast.Constant(initializer.DeclaringModule.Name) )); return; } if (!initializer.IsEmpty) { BuildOverriddenInitializerCall(metaBuilder, args, initializer); } } else { // construct: MethodBase[] constructionOverloads; SelfCallConvention callConvention = SelfCallConvention.SelfIsParameter; bool implicitProtocolConversions = false; if (typeof(Delegate).IsAssignableFrom(type)) { BuildDelegateConstructorCall(metaBuilder, args, type); return; } else if (type.IsArray && type.GetArrayRank() == 1) { constructionOverloads = ClrVectorFactories; } else if (_structInfo != null) { constructionOverloads = new MethodBase[] { Methods.CreateStructInstance }; } else if (_factories.Length != 0) { constructionOverloads = (MethodBase[])ReflectionUtils.GetMethodInfos(_factories); } else { // TODO: handle protected constructors constructionOverloads = (type == typeof(object) ? typeof(RubyObject) : type).GetConstructors(); if (type.IsValueType) { if (constructionOverloads.Length == 0 || type.GetConstructor(Type.EmptyTypes) == null) { constructionOverloads = ArrayUtils.Append(constructionOverloads, Methods.CreateDefaultInstance); } } else if (constructionOverloads.Length == 0) { metaBuilder.SetError(Methods.MakeAllocatorUndefinedError.OpCall(Ast.Convert(args.TargetExpression, typeof(RubyClass)))); return; } callConvention = SelfCallConvention.NoSelf; implicitProtocolConversions = true; } RubyMethodGroupInfo.BuildCallNoFlow(metaBuilder, args, methodName, constructionOverloads, callConvention, implicitProtocolConversions); // we need to handle break, which unwinds to a proc-converter that could be this method's frame: if (!metaBuilder.Error) { metaBuilder.ControlFlowBuilder = RubyMethodGroupInfo.RuleControlFlowBuilder; } } }
protected override DynamicMetaObject/*!*/ InteropBind(MetaObjectBuilder/*!*/ metaBuilder, CallArguments/*!*/ args) { metaBuilder.SetError(Ast.New( typeof(NotSupportedException).GetConstructor(new[] { typeof(string) }), Ast.Constant("Super call not supported on foreign meta-objects") )); return metaBuilder.CreateMetaObject(this); }
// Returns true if the call was bound (with success or failure), false if fallback should be performed. internal static bool BuildMethodMissingAccess(MetaObjectBuilder/*!*/ metaBuilder, CallArguments/*!*/ args, string/*!*/ methodName, RubyMemberInfo methodMissing, RubyMethodVisibility incompatibleVisibility, bool isSuperCall, bool defaultFallback) { switch (BindToKernelMethodMissing(metaBuilder, args, methodName, methodMissing, incompatibleVisibility, isSuperCall)) { case MethodMissingBinding.Custom: // we pretend we found the member and return a method that calls method_missing: Debug.Assert(!metaBuilder.Error); metaBuilder.Result = Methods.CreateBoundMissingMember.OpCall( AstUtils.Convert(args.TargetExpression, typeof(object)), Ast.Constant(methodMissing, typeof(RubyMemberInfo)), Ast.Constant(methodName) ); return true; case MethodMissingBinding.Error: // method_missing is defined in Kernel, error has been reported: return true; case MethodMissingBinding.Fallback: // method_missing is defined in Kernel: if (defaultFallback) { metaBuilder.SetError(Methods.MakeMissingMemberError.OpCall(Ast.Constant(methodName))); return true; } return false; } throw Assert.Unreachable; }
internal override void BuildCallNoFlow(MetaObjectBuilder/*!*/ metaBuilder, CallArguments/*!*/ args, string/*!*/ name) { var visibleOverloads = GetVisibleOverloads(args, MethodBases, false); if (visibleOverloads.Count == 0) { metaBuilder.SetError(Methods.MakeClrProtectedMethodCalledError.OpCall( args.MetaContext.Expression, args.MetaTarget.Expression, Ast.Constant(name) )); } else { BuildCallNoFlow(metaBuilder, args, name, visibleOverloads, CallConvention, ImplicitProtocolConversions); } }
/// <summary> /// Resolves an library method overload and builds call expression. /// The resulting expression on meta-builder doesn't handle block control flow yet. /// </summary> internal static void BuildCallNoFlow(MetaObjectBuilder/*!*/ metaBuilder, CallArguments/*!*/ args, string/*!*/ name, IList<MethodBase>/*!*/ 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); if (bindingTarget.Success) { var parameterBinder = new RubyParameterBinder(args.RubyContext.Binder, args.MetaContext.Expression, args.Signature.HasScope); metaBuilder.Result = bindingTarget.MakeExpression(parameterBinder, actualArgs); } else { metaBuilder.SetError(args.RubyContext.RubyBinder.MakeInvalidParametersError(bindingTarget).Expression); } }
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); }
internal static bool BuildConversion(MetaObjectBuilder/*!*/ metaBuilder, DynamicMetaObject/*!*/ target, Expression/*!*/ contextExpression, Type/*!*/ toType, bool defaultFallback) { Expression expr = TryImplicitConversion(target, toType); if (expr != null) { metaBuilder.Result = expr; metaBuilder.AddObjectTypeRestriction(target.Value, target.Expression); return true; } if (defaultFallback) { metaBuilder.AddObjectTypeRestriction(target.Value, target.Expression); metaBuilder.SetError(Methods.MakeTypeConversionError.OpCall( contextExpression, AstUtils.Convert(target.Expression, typeof(object)), Ast.Constant(toType) )); return true; } return false; }
/// <summary> /// Resolves an library method overload and builds call expression. /// The resulting expression on meta-builder doesn't handle block control flow yet. /// </summary> internal static void BuildCallNoFlow(MetaObjectBuilder/*!*/ metaBuilder, CallArguments/*!*/ args, string/*!*/ name, IList<MethodBase>/*!*/ overloads, bool includeSelf, bool selfIsInstance) { var bindingTarget = ResolveOverload(name, overloads, args, includeSelf, selfIsInstance); if (bindingTarget.Success) { bool calleeHasBlockParam = 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. // // NOTE: We check for null block here -> test fore that fact is added in MakeActualArgs if (metaBuilder.BfcVariable == null && args.Signature.HasBlock && args.GetBlock() != null && calleeHasBlockParam) { metaBuilder.BfcVariable = metaBuilder.GetTemporary(typeof(BlockParam), "#bfc"); } var actualArgs = MakeActualArgs(metaBuilder, args, includeSelf, selfIsInstance, calleeHasBlockParam, true); var parameterBinder = new RubyParameterBinder(args.RubyContext.Binder, args.MetaContext.Expression, args.Signature.HasScope); var targetExpression = bindingTarget.MakeExpression(parameterBinder, actualArgs); metaBuilder.Result = targetExpression; } else { metaBuilder.SetError( Methods.MakeInvalidArgumentTypesError.OpCall(Ast.Constant(name)) ); } }
private DynamicMetaObject/*!*/ InteropBind(MetaObjectBuilder/*!*/ metaBuilder, CallArguments/*!*/ args) { // TODO: argument count limit depends on the binder! // TODO: pass block as the last (before RHS arg?) parameter/ignore block if args not accepting block: var normalizedArgs = RubyOverloadResolver.NormalizeArguments(metaBuilder, args, 0, Int32.MaxValue); if (!metaBuilder.Error) { MethodInfo postConverter; var interopBinder = GetInteropBinder(args.RubyContext, normalizedArgs, out postConverter); if (interopBinder != null) { Type resultType; var result = interopBinder.Bind(args.MetaTarget, ArrayUtils.MakeArray(normalizedArgs)); metaBuilder.SetMetaResult(result, args); if (postConverter != null) { // TODO: do better? var paramType = postConverter.GetParameters()[0].ParameterType; metaBuilder.Result = Ast.Call(null, postConverter, AstUtils.Convert(metaBuilder.Result, paramType)); resultType = postConverter.ReturnType; } else { resultType = ((IInteropBinder)interopBinder).ResultType; } return metaBuilder.CreateMetaObject(interopBinder, resultType); } else { metaBuilder.SetError(Ast.New( typeof(NotSupportedException).GetConstructor(new[] { typeof(string) }), Ast.Constant(String.Format("{0} not supported on foreign meta-objects", this)) )); } } return metaBuilder.CreateMetaObject(this); }
private static MethodMissingBinding BindToKernelMethodMissing(MetaObjectBuilder/*!*/ metaBuilder, CallArguments/*!*/ args, string/*!*/ methodName, RubyMemberInfo methodMissing, RubyMethodVisibility incompatibleVisibility, bool isSuperCall) { // TODO: better specialization of method_missing methods if (methodMissing == null || methodMissing.DeclaringModule == methodMissing.Context.KernelModule && methodMissing is RubyLibraryMethodInfo) { if (isSuperCall) { metaBuilder.SetError(Methods.MakeMissingSuperException.OpCall(AstUtils.Constant(methodName))); } else if (incompatibleVisibility == RubyMethodVisibility.Private) { metaBuilder.SetError(Methods.MakePrivateMethodCalledError.OpCall( AstUtils.Convert(args.MetaContext.Expression, typeof(RubyContext)), args.TargetExpression, AstUtils.Constant(methodName)) ); } else if (incompatibleVisibility == RubyMethodVisibility.Protected) { metaBuilder.SetError(Methods.MakeProtectedMethodCalledError.OpCall( AstUtils.Convert(args.MetaContext.Expression, typeof(RubyContext)), args.TargetExpression, AstUtils.Constant(methodName)) ); } else { return MethodMissingBinding.Fallback; } return MethodMissingBinding.Error; } return MethodMissingBinding.Custom; }
/// <summary> /// Implements Class#clr_new feature. /// </summary> public void BuildClrObjectConstruction(MetaObjectBuilder/*!*/ metaBuilder, CallArguments/*!*/ args, string/*!*/ methodName) { OverloadInfo[] ctors; if (TypeTracker == null) { metaBuilder.SetError(Methods.MakeNotClrTypeError.OpCall(Ast.Convert(args.TargetExpression, typeof(RubyClass)))); } else if ((ctors = GetConstructors(TypeTracker.Type).ToArray()).Length == 0) { metaBuilder.SetError(Methods.MakeConstructorUndefinedError.OpCall(Ast.Convert(args.TargetExpression, typeof(RubyClass)))); } else { RubyMethodGroupInfo.BuildCallNoFlow(metaBuilder, args, methodName, ctors, SelfCallConvention.NoSelf, true); } }
/// <summary> /// Implements Class#allocate feature. /// </summary> public void BuildObjectAllocation(MetaObjectBuilder/*!*/ metaBuilder, CallArguments/*!*/ args, string/*!*/ methodName) { // check for empty arguments (handles splat correctly): var argsBuilder = new ArgsBuilder(0, 0, 0, 0, false); argsBuilder.AddCallArguments(metaBuilder, args); if (!metaBuilder.Error) { if (!BuildAllocatorCall(metaBuilder, args, () => AstUtils.Constant(Name))) { metaBuilder.SetError(Methods.MakeAllocatorUndefinedError.OpCall(Ast.Convert(args.TargetExpression, typeof(RubyClass)))); } } }
/// <summary> /// Resolves an library method overload and builds call expression. /// The resulting expression on meta-builder doesn't handle block control flow yet. /// </summary> internal static void BuildCallNoFlow(MetaObjectBuilder/*!*/ metaBuilder, CallArguments/*!*/ args, string/*!*/ name, IList<MethodBase>/*!*/ overloads, SelfCallConvention callConvention) { RubyOverloadResolver resolver; var bindingTarget = ResolveOverload(metaBuilder, args, name, overloads, callConvention, out resolver); if (bindingTarget.Success) { metaBuilder.Result = bindingTarget.MakeExpression(); } else { metaBuilder.SetError(resolver.MakeInvalidParametersError(bindingTarget).Expression); } }
public void BuildObjectConstructionNoFlow(MetaObjectBuilder/*!*/ metaBuilder, CallArguments/*!*/ args, string/*!*/ methodName) { if (IsSingletonClass) { metaBuilder.SetError(Methods.MakeVirtualClassInstantiatedError.OpCall()); return; } Type type = GetUnderlyingSystemType(); RubyMemberInfo initializer; using (Context.ClassHierarchyLocker()) { // check version of the class so that we invalidate the rule whenever the initializer changes: metaBuilder.AddVersionTest(this); initializer = ResolveMethodForSiteNoLock(Symbols.Initialize, VisibilityContext.AllVisible).Info; // Initializer resolves to BasicObject#initialize unless overridden in a derived class. // We ensure that initializer cannot be removed/undefined so that we don't ever fall back to method_missing (see RubyModule.RemoveMethodNoEvent). Debug.Assert(initializer != null); } bool isLibraryMethod = initializer is RubyLibraryMethodInfo; bool isRubyInitializer = initializer.IsRubyMember && !isLibraryMethod; bool isLibraryInitializer = isLibraryMethod && !initializer.DeclaringModule.IsObjectClass && !initializer.DeclaringModule.IsBasicObjectClass; if (isRubyInitializer || isLibraryInitializer && _isRubyClass) { // allocate and initialize: bool allocatorFound = BuildAllocatorCall(metaBuilder, args, () => AstUtils.Constant(Name)); if (metaBuilder.Error) { return; } if (!allocatorFound) { metaBuilder.SetError(Methods.MakeMissingDefaultConstructorError.OpCall( Ast.Convert(args.TargetExpression, typeof(RubyClass)), Ast.Constant(initializer.DeclaringModule.Name) )); return; } if (!initializer.IsEmpty) { BuildOverriddenInitializerCall(metaBuilder, args, initializer); } } else { // construct: OverloadInfo[] constructionOverloads; SelfCallConvention callConvention = SelfCallConvention.SelfIsParameter; bool implicitProtocolConversions = false; if (typeof(Delegate).IsAssignableFrom(type)) { BuildDelegateConstructorCall(metaBuilder, args, type); return; } else if (type.IsArray && type.GetArrayRank() == 1) { constructionOverloads = GetClrVectorFactories(); } else if (_structInfo != null) { constructionOverloads = new OverloadInfo[] { new ReflectionOverloadInfo(Methods.CreateStructInstance) }; } else if (_factories.Length != 0) { constructionOverloads = ArrayUtils.ConvertAll(_factories, (d) => new ReflectionOverloadInfo(d.GetMethod())); } else { // TODO: handle protected constructors constructionOverloads = GetConstructors(type == typeof(object) ? typeof(RubyObject) : type).ToArray(); if (type.IsValueType()) { if (constructionOverloads.Length == 0 || GetConstructor(type) == null) { constructionOverloads = ArrayUtils.Append(constructionOverloads, new ReflectionOverloadInfo(Methods.CreateDefaultInstance)); } } else if (constructionOverloads.Length == 0) { metaBuilder.SetError(Methods.MakeAllocatorUndefinedError.OpCall(Ast.Convert(args.TargetExpression, typeof(RubyClass)))); return; } callConvention = SelfCallConvention.NoSelf; implicitProtocolConversions = true; } RubyMethodGroupInfo.BuildCallNoFlow(metaBuilder, args, methodName, constructionOverloads, callConvention, implicitProtocolConversions); if (!metaBuilder.Error) { metaBuilder.Result = MarkNewException(metaBuilder.Result); // we need to handle break, which unwinds to a proc-converter that could be this method's frame: if (args.Signature.HasBlock) { metaBuilder.ControlFlowBuilder = RubyMethodGroupInfo.RuleControlFlowBuilder; } } } }
/// <summary> /// Resolves an library method overload and builds call expression. /// The resulting expression on meta-builder doesn't handle block control flow yet. /// </summary> internal static void BuildCallNoFlow(MetaObjectBuilder/*!*/ metaBuilder, CallArguments/*!*/ args, string/*!*/ name, IList<MethodBase>/*!*/ overloads, SelfCallConvention callConvention, bool implicitProtocolConversions) { RubyOverloadResolver resolver; var bindingTarget = ResolveOverload(metaBuilder, args, name, overloads, callConvention, implicitProtocolConversions, out resolver); if (bindingTarget.Success) { if (ReferenceEquals(bindingTarget.Method, Methods.CreateDefaultInstance)) { Debug.Assert(args.TargetClass.TypeTracker.Type.IsValueType); metaBuilder.Result = Ast.New(args.TargetClass.TypeTracker.Type); } else { metaBuilder.Result = bindingTarget.MakeExpression(); } } else { metaBuilder.SetError(resolver.MakeInvalidParametersError(bindingTarget).Expression); } }