internal static void SetMethodAttributes(RubyScope/*!*/ scope, RubyModule/*!*/ module, object[]/*!*/ methodNames, RubyMethodAttributes attributes) { ContractUtils.RequiresNotNull(scope, "scope"); ContractUtils.RequiresNotNull(methodNames, "methodNames"); if (methodNames.Length == 0) { scope.GetMethodAttributesDefinitionScope().MethodAttributes = attributes; } else { foreach (string methodName in Protocols.CastToSymbols(scope.RubyContext, methodNames)) { RubyMemberInfo method = module.ResolveMethodFallbackToObject(methodName, true); if (method == null) { throw RubyExceptions.CreateNameError(RubyExceptions.FormatMethodMissingMessage(scope.RubyContext, module, methodName)); } if ((attributes & RubyMethodAttributes.ModuleFunction) == RubyMethodAttributes.ModuleFunction) { module.AddModuleFunction(scope.RubyContext, methodName, method); } else { module.SetMethodVisibility(scope.RubyContext, methodName, method, (RubyMethodVisibility)(attributes & RubyMethodAttributes.VisibilityMask)); } } } }
private static RubyMethodVisibility GetDefinedMethodVisibility(RubyScope/*!*/ scope, RubyModule/*!*/ module, string/*!*/ methodName) { // MRI: Special names are private. // MRI: Doesn't create a singleton method if module_function is used in the scope, however the private visibility is applied (bug?) // MRI 1.8: uses the current scope's visibility only if the target module is the same as the scope's module (bug?) // MFI 1.9: always uses public visibility (bug?) RubyMethodVisibility visibility; if (scope.RubyContext.RubyOptions.Compatibility == RubyCompatibility.Ruby18) { var attributesScope = scope.GetMethodAttributesDefinitionScope(); if (attributesScope.GetInnerMostModuleForMethodLookup() == module) { bool isModuleFunction = (attributesScope.MethodAttributes & RubyMethodAttributes.ModuleFunction) == RubyMethodAttributes.ModuleFunction; visibility = (isModuleFunction) ? RubyMethodVisibility.Private : attributesScope.Visibility; } else { visibility = RubyMethodVisibility.Public; } } else { visibility = RubyMethodVisibility.Public; } return RubyUtils.GetSpecialMethodVisibility(visibility, methodName); }
private static void DefineAccessor(RubyScope/*!*/ scope, RubyModule/*!*/ self, string/*!*/ name, bool readable, bool writable) { // MRI: ignores ModuleFunction scope flag (doesn't create singleton methods): if (!Tokenizer.IsVariableName(name, true)) { throw RubyExceptions.CreateNameError("invalid attribute name `{0}'", name); } var varName = "@" + name; var attributesScope = scope.GetMethodAttributesDefinitionScope(); if (readable) { var flags = (RubyMemberFlags)RubyUtils.GetSpecialMethodVisibility(attributesScope.Visibility, name); self.AddMethod(scope.RubyContext, name, new RubyAttributeReaderInfo(flags, self, varName)); } if (writable) { self.AddMethod(scope.RubyContext, name + "=", new RubyAttributeWriterInfo((RubyMemberFlags)attributesScope.Visibility, self, varName)); } }
internal static void SetMethodAttributes(RubyScope/*!*/ scope, RubyModule/*!*/ module, string/*!*/[]/*!*/ methodNames, RubyMethodAttributes attributes) { if (methodNames.Length == 0) { scope.GetMethodAttributesDefinitionScope().MethodAttributes = attributes; } else { SetMethodAttributes(module, methodNames, attributes); } }
[Emitted] // MethodDeclaration: public static object DefineMethod(object targetOrSelf, RubyScope/*!*/ scope, RubyMethodBody/*!*/ body) { Assert.NotNull(body, scope); RubyModule instanceOwner, singletonOwner; RubyMemberFlags instanceFlags, singletonFlags; bool moduleFunction = false; if (body.HasTarget) { if (!RubyUtils.CanCreateSingleton(targetOrSelf)) { throw RubyExceptions.CreateTypeError("can't define singleton method for literals"); } instanceOwner = null; instanceFlags = RubyMemberFlags.Invalid; singletonOwner = scope.RubyContext.CreateSingletonClass(targetOrSelf); singletonFlags = RubyMemberFlags.Public; } else { var attributesScope = scope.GetMethodAttributesDefinitionScope(); if ((attributesScope.MethodAttributes & RubyMethodAttributes.ModuleFunction) == RubyMethodAttributes.ModuleFunction) { // Singleton module-function's scope points to the instance method's RubyMemberInfo. // This affects: // 1) super call // Super call is looking for Method.DeclaringModule while searching MRO, which would fail if the singleton module-function // was in MRO. Since module-function can only be used on module the singleton method could only be on module's singleton. // Module's singleton is never part of MRO so we are safe. // 2) trace // Method call trace reports non-singleton module. // MRI 1.8: instance method owner is self -> it is possible (via define_method) to define m.f. on a class (bug) // MRI 1.9: instance method owner GetMethodDefinitionOwner // MRI allows to define m.f. on classes but then doesn't work correctly with it. instanceOwner = scope.GetMethodDefinitionOwner(); if (instanceOwner.IsClass) { throw RubyExceptions.CreateTypeError("A module function cannot be defined on a class."); } instanceFlags = RubyMemberFlags.Private; singletonOwner = instanceOwner.SingletonClass; singletonFlags = RubyMemberFlags.Public; moduleFunction = true; } else { instanceOwner = scope.GetMethodDefinitionOwner(); instanceFlags = (RubyMemberFlags)RubyUtils.GetSpecialMethodVisibility(attributesScope.Visibility, body.Name); singletonOwner = null; singletonFlags = RubyMemberFlags.Invalid; } } RubyMethodInfo instanceMethod = null, singletonMethod = null; if (instanceOwner != null) { SetMethod(scope.RubyContext, instanceMethod = new RubyMethodInfo(body, scope, instanceOwner, instanceFlags) ); } if (singletonOwner != null) { SetMethod(scope.RubyContext, singletonMethod = new RubyMethodInfo(body, scope, singletonOwner, singletonFlags) ); } // the method's scope saves the result => singleton module-function uses instance-method var method = instanceMethod ?? singletonMethod; method.DeclaringModule.MethodAdded(body.Name); if (moduleFunction) { Debug.Assert(!method.DeclaringModule.IsClass); method.DeclaringModule.SingletonClass.MethodAdded(body.Name); } return null; }
private static void DefineAccessor(RubyScope/*!*/ scope, RubyModule/*!*/ self, string/*!*/ name, bool readable, bool writable) { // MRI: ignores ModuleFunction scope flag (doesn't create singleton methods): var varName = "@" + name; var attributesScope = scope.GetMethodAttributesDefinitionScope(); if (readable) { var flags = (RubyMemberFlags)RubyUtils.GetSpecialMethodVisibility(attributesScope.Visibility, name); self.SetLibraryMethod(name, new RubyAttributeReaderInfo(flags, self, varName), false); } if (writable) { self.SetLibraryMethod(name + "=", new RubyAttributeWriterInfo((RubyMemberFlags)attributesScope.Visibility, self, varName), false); } }
private static void DefineMethod(RubyScope/*!*/ scope, RubyModule/*!*/ self, string/*!*/ methodName, RubyMemberInfo/*!*/ info, RubyModule/*!*/ targetConstraint) { // MRI: doesn't create a singleton method if module_function is used in the scope, however the the private visibility is applied var attributesScope = scope.GetMethodAttributesDefinitionScope(); bool isModuleFunction = (attributesScope.MethodAttributes & RubyMethodAttributes.ModuleFunction) == RubyMethodAttributes.ModuleFunction; var visibility = isModuleFunction ? RubyMethodVisibility.Private : attributesScope.Visibility; using (self.Context.ClassHierarchyLocker()) { // MRI 1.8 does the check when the method is called, 1.9 checks it upfront as we do: if (!self.HasAncestorNoLock(targetConstraint)) { throw RubyExceptions.CreateTypeError( String.Format("bind argument must be a subclass of {0}", targetConstraint.GetName(scope.RubyContext)) ); } self.SetDefinedMethodNoEventNoLock(self.Context, methodName, info, visibility); } self.Context.MethodAdded(self, methodName); }
[Emitted] // MethodDeclaration: public static RubyMethodInfo/*!*/ DefineMethod(object targetOrSelf, object/*!*/ ast, RubyScope/*!*/ scope, bool hasTarget, string/*!*/ name, Delegate/*!*/ clrMethod, int mandatory, int optional, bool hasUnsplatParameter) { Assert.NotNull(ast, scope, clrMethod, name); RubyModule instanceOwner, singletonOwner; RubyMemberFlags instanceFlags, singletonFlags; if (hasTarget) { if (!RubyUtils.CanCreateSingleton(targetOrSelf)) { throw RubyExceptions.CreateTypeError("can't define singleton method for literals"); } instanceOwner = null; instanceFlags = RubyMemberFlags.Invalid; singletonOwner = scope.RubyContext.CreateSingletonClass(targetOrSelf); singletonFlags = RubyMemberFlags.Public; } else { // TODO: ??? var attributesScope = scope.GetMethodAttributesDefinitionScope(); //var attributesScope = scope; if ((attributesScope.MethodAttributes & RubyMethodAttributes.ModuleFunction) == RubyMethodAttributes.ModuleFunction) { // Singleton module-function's scope points to the instance method's RubyMemberInfo. // This affects: // 1) super call // Super call is looking for Method.DeclaringModule while searching MRO, which would fail if the singleton module-function // was in MRO. Since module-function can only be used on module the singleton method could only be on module's singleton. // Module's singleton is never part of MRO so we are safe. // 2) trace // Method call trace reports non-singleton module. // MRI 1.8: instance method owner is self -> it is possible (via define_method) to define m.f. on a class (bug) // MRI 1.9: instance method owner GetMethodDefinitionOwner // MRI allows to define m.f. on classes but then doesn't work correctly with it. instanceOwner = scope.GetMethodDefinitionOwner(); if (instanceOwner.IsClass) { throw RubyExceptions.CreateTypeError("A module function cannot be defined on a class."); } instanceFlags = RubyMemberFlags.ModuleFunction | RubyMemberFlags.Private; singletonOwner = instanceOwner.SingletonClass; singletonFlags = RubyMemberFlags.ModuleFunction | RubyMemberFlags.Public; } else { instanceOwner = scope.GetMethodDefinitionOwner(); instanceFlags = (RubyMemberFlags)RubyUtils.GetSpecialMethodVisibility(attributesScope.Visibility, name); singletonOwner = null; singletonFlags = RubyMemberFlags.Invalid; } } RubyMethodInfo instanceMethod = null, singletonMethod = null; if (instanceOwner != null) { SetMethod(scope.RubyContext, instanceMethod = new RubyMethodInfo(ast, clrMethod, instanceOwner, name, mandatory, optional, hasUnsplatParameter, instanceFlags) ); } if (singletonOwner != null) { SetMethod(scope.RubyContext, singletonMethod = new RubyMethodInfo(ast, clrMethod, singletonOwner, name, mandatory, optional, hasUnsplatParameter, singletonFlags) ); } // the method's scope saves the result => singleton module-function uses instance-method return instanceMethod ?? singletonMethod; }