Ejemplo n.º 1
0
        public sealed override T BindDelegate <T>(CallSite <T> site, object[] args)
        {
            object        firstArg     = args[0];
            ArgumentArray argArray     = firstArg as ArgumentArray;
            Type          delegateType = typeof(T);
            T             result;

            if (argArray != null)
            {
                firstArg = argArray.GetArgument(0);
            }
            else
            {
                object precompiled = BindPrecompiled(delegateType, args);
                if (precompiled != null)
                {
                    result = (T)precompiled;
                    CacheTarget(result);
                    return(result);
                }
            }

            RubyContext context = _context ?? ((Signature.HasScope) ? ((RubyScope)firstArg).RubyContext : (RubyContext)firstArg);

            if (context.Options.NoAdaptiveCompilation)
            {
                return(null);
            }

#if DEBUG
            if (RubyOptions.ShowRules)
            {
                var sb = new StringBuilder();
                for (int i = 1; i < args.Length; i++)
                {
                    sb.Append(RubyUtils.ObjectToMutableString(context, args[i]));
                    sb.Append(", ");
                }

                Utils.Log(String.Format(
                              "{0}: {1}; {2} <: {3}",
                              this,
                              sb,
                              args.Length > 1 ? context.GetImmediateClassOf(args[1]).DebugName : null,
                              args.Length > 1 ? context.GetImmediateClassOf(args[1]).SuperClass.DebugName : null
                              ), "BIND");
            }
#endif
            result = this.LightBind <T>(args, context.Options.CompilationThreshold);
            CacheTarget(result);
            return(result);
        }
Ejemplo n.º 2
0
        public static DynamicMetaObject TryBind(RubyContext/*!*/ context, GetMemberBinder/*!*/ binder, DynamicMetaObject/*!*/ target) {
            Assert.NotNull(context, target);
            var metaBuilder = new MetaObjectBuilder();
            var contextExpression = AstUtils.Constant(context);

            RubyClass targetClass = context.GetImmediateClassOf(target.Value);
            MethodResolutionResult method;
            RubyMemberInfo methodMissing = null;

            using (targetClass.Context.ClassHierarchyLocker()) {
                metaBuilder.AddTargetTypeTest(target.Value, targetClass, target.Expression, context, contextExpression);

                method = targetClass.ResolveMethodForSiteNoLock(binder.Name, RubyClass.IgnoreVisibility);
                if (method.Found) {
                    methodMissing = targetClass.ResolveMethodForSiteNoLock(Symbols.MethodMissing, RubyClass.IgnoreVisibility).Info;
                }
            }
            
            if (method.Found) {
                // we need to create a bound member:
                metaBuilder.Result = AstUtils.Constant(new RubyMethod(target.Value, method.Info, 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, 
                    new CallArguments(
                        new DynamicMetaObject(contextExpression, BindingRestrictions.Empty, context),
                        new[] { 
                            target,
                            new DynamicMetaObject(AstUtils.Constant(symbol), BindingRestrictions.Empty, symbol) 
                        },
                        RubyCallSignature.Simple(1)
                    ),
                    binder.Name,
                    methodMissing,
                    method.IncompatibleVisibility,
                    false
                );
            }

            // TODO: we should return null if we fail, we need to throw exception for now:
            return metaBuilder.CreateMetaObject(binder, DynamicMetaObject.EmptyMetaObjects);
        }
Ejemplo n.º 3
0
        // TODO: do not test runtime for runtime bound sites
        // TODO: ResolveMethod invalidates modules that were not initialized yet -> snapshot version after method resolution
        // TODO: thread safety: synchronize version snapshot and method resolution
        public void AddTargetTypeTest(object target, Expression /*!*/ targetParameter, RubyContext /*!*/ context, Expression /*!*/ contextExpression)
        {
            // singleton nil:
            if (target == null)
            {
                AddRestriction(Ast.Equal(targetParameter, Ast.Constant(null)));
                context.NilClass.AddFullVersionTest(this, contextExpression);
                return;
            }

            // singletons true, false:
            if (target is bool)
            {
                AddRestriction(Ast.AndAlso(
                                   Ast.TypeIs(targetParameter, typeof(bool)),
                                   Ast.Equal(Ast.Convert(targetParameter, typeof(bool)), Ast.Constant(target))
                                   ));

                if ((bool)target)
                {
                    context.TrueClass.AddFullVersionTest(this, contextExpression);
                }
                else
                {
                    context.FalseClass.AddFullVersionTest(this, contextExpression);
                }
                return;
            }

            RubyClass immediateClass = context.GetImmediateClassOf(target);

            // user defined instance singletons, modules, classes:
            if (immediateClass.IsSingletonClass)
            {
                AddRestriction(
                    Ast.Equal(
                        Ast.Convert(targetParameter, typeof(object)),
                        Ast.Convert(Ast.Constant(target), typeof(object))
                        )
                    );

                // we need to check for a runtime (e.g. "foo" .NET string instance could be shared accross runtimes):
                immediateClass.AddFullVersionTest(this, contextExpression);
                return;
            }

            Type type = target.GetType();

            AddTypeRestriction(type, targetParameter);

            if (typeof(IRubyObject).IsAssignableFrom(type))
            {
                // Ruby objects (get the method directly to prevent interface dispatch):
                MethodInfo classGetter = type.GetMethod("get_" + RubyObject.ClassPropertyName, BindingFlags.Public | BindingFlags.Instance);
                if (classGetter != null && classGetter.ReturnType == typeof(RubyClass))
                {
                    AddCondition(
                        // (#{type})target.Class.Version == #{immediateClass.Version}
                        Ast.Equal(
                            Ast.Call(Ast.Call(Ast.Convert(targetParameter, type), classGetter), RubyModule.VersionProperty.GetGetMethod()),
                            Ast.Constant(immediateClass.Version)
                            )
                        );
                    return;
                }

                // TODO: explicit iface-implementation
                throw new NotSupportedException("Type implementing IRubyObject should have RubyClass getter");
            }
            else
            {
                // CLR objects:
                immediateClass.AddFullVersionTest(this, contextExpression);
            }
        }
Ejemplo n.º 4
0
            private void WriteAnObject(object obj)
            {
                if (_recursionLimit == 0)
                {
                    throw RubyExceptions.CreateArgumentError("exceed depth limit");
                }
                if (_recursionLimit > 0)
                {
                    _recursionLimit--;
                }

                if (obj is int)
                {
                    int value = (int)obj;
                    if (value < -(1 << 30) || value >= (1 << 30))
                    {
                        obj = (BigInteger)value;
                    }
                }

                // TODO: use RubyUtils.IsRubyValueType?
                RubySymbol sym;

                if (obj == null)
                {
                    _writer.Write((byte)'0');
                }
                else if (obj is bool)
                {
                    _writer.Write((byte)((bool)obj ? 'T' : 'F'));
                }
                else if (obj is int)
                {
                    WriteFixnum((int)obj);
                }
                else if ((sym = obj as RubySymbol) != null)
                {
                    // TODO (encoding):
                    WriteSymbol(sym.ToString(), sym.Encoding);
                }
                else
                {
                    int objectRef;
                    if (_objects.TryGetValue(obj, out objectRef))
                    {
                        _writer.Write((byte)'@');
                        WriteInt32(objectRef);
                    }
                    else
                    {
                        objectRef     = _objects.Count;
                        _objects[obj] = objectRef;

                        // TODO: replace with a table-driven implementation
                        // TODO: visibility?
                        bool implementsDump        = _context.ResolveMethod(obj, "_dump", VisibilityContext.AllVisible).Found;
                        bool implementsMarshalDump = _context.ResolveMethod(obj, "marshal_dump", VisibilityContext.AllVisible).Found;

                        bool     writeInstanceData = false;
                        string[] instanceNames     = null;

                        if (!implementsDump && !implementsMarshalDump)
                        {
                            // Neither "_dump" nor "marshal_dump" writes instance vars separately
                            instanceNames = _context.GetInstanceVariableNames(obj);
                            if (instanceNames.Length > 0)
                            {
                                _writer.Write((byte)'I');
                                writeInstanceData = true;
                            }
                        }

                        if (!implementsDump || implementsMarshalDump)
                        {
                            // "_dump" doesn't write "extend" info but "marshal_dump" does
                            RubyClass theClass = _context.GetImmediateClassOf(obj);
                            if (theClass.IsSingletonClass)
                            {
                                foreach (var mixin in theClass.GetMixins())
                                {
                                    _writer.Write((byte)'e');
                                    WriteModuleName(mixin);
                                }
                            }
                        }

                        if (obj is double)
                        {
                            WriteFloat((double)obj);
                        }
                        else if (obj is float)
                        {
                            WriteFloat((double)(float)obj);
                        }
                        else if (obj is BigInteger)
                        {
                            WriteBignum((BigInteger)obj);
                        }
                        else if (implementsMarshalDump)
                        {
                            WriteUsingMarshalDump(obj);
                        }
                        else if (implementsDump)
                        {
                            WriteUsingDump(obj);
                        }
                        else if (obj is MutableString)
                        {
                            WriteString((MutableString)obj);
                        }
                        else if (obj is RubyArray)
                        {
                            WriteArray((RubyArray)obj);
                        }
                        else if (obj is Hash)
                        {
                            WriteHash((Hash)obj);
                        }
                        else if (obj is RubyRegex)
                        {
                            WriteRegex((RubyRegex)obj);
                        }
                        else if (obj is RubyClass)
                        {
                            WriteClass((RubyClass)obj);
                        }
                        else if (obj is RubyModule)
                        {
                            WriteModule((RubyModule)obj);
                        }
                        else if (obj is RubyStruct)
                        {
                            WriteStruct((RubyStruct)obj);
                        }
                        else if (obj is Range)
                        {
                            WriteRange((Range)obj);
                        }
                        else
                        {
                            if (writeInstanceData)
                            {
                                // Overwrite the "I"; we always have instance data
                                _writer.BaseStream.Seek(-1, SeekOrigin.Current);
                            }
                            else
                            {
                                writeInstanceData = true;
                            }
                            WriteObject(obj);
                        }

                        if (writeInstanceData)
                        {
                            WriteInt32(instanceNames.Length);
                            var encoding = _context.GetIdentifierEncoding();
                            foreach (string name in instanceNames)
                            {
                                object value;
                                if (!_context.TryGetInstanceVariable(obj, name, out value))
                                {
                                    value = null;
                                }
                                // TODO (encoding):
                                WriteSymbol(name, encoding);
                                WriteAnObject(value);
                            }
                        }
                    }
                }
                if (_recursionLimit >= 0)
                {
                    _recursionLimit++;
                }
            }
Ejemplo n.º 5
0
 private static RubyArray/*!*/ GetMethods(RubyContext/*!*/ context, object self, bool inherited, RubyMethodAttributes attributes) {
     RubyClass immediateClass = context.GetImmediateClassOf(self);
     return ModuleOps.GetMethods(immediateClass, inherited, attributes);
 }
Ejemplo n.º 6
0
 public static RubyArray/*!*/ GetSingletonMethods(RubyContext/*!*/ context, object self, [DefaultParameterValue(true)]bool inherited) {
     RubyClass immediateClass = context.GetImmediateClassOf(self);
     return ModuleOps.GetMethods(immediateClass, inherited, RubyMethodAttributes.Singleton | RubyMethodAttributes.Public | RubyMethodAttributes.Protected);
 }
Ejemplo n.º 7
0
        public static RubyArray/*!*/ GetMethods(RubyContext/*!*/ context, object self, [DefaultParameterValue(true)]bool inherited) {
            var foreignMembers = context.GetForeignDynamicMemberNames(self);

            RubyClass immediateClass = context.GetImmediateClassOf(self);
            if (!inherited && !immediateClass.IsSingletonClass) {
                var result = new RubyArray();
                if (foreignMembers.Count > 0) {
                    var symbolicNames = context.RubyOptions.Compatibility > RubyCompatibility.Ruby18;
                    foreach (var name in foreignMembers) {
                        result.Add(new ClrName(name));
                    }
                }
                return result;
            }

            return ModuleOps.GetMethods(immediateClass, inherited, RubyMethodAttributes.Public | RubyMethodAttributes.Protected, foreignMembers);
        }
Ejemplo n.º 8
0
        public static RubyArray GetMethods(RubyContext/*!*/ context, object self, [DefaultParameterValue(true)]bool inherited)
        {
            var foreignMembers = context.GetForeignDynamicMemberNames(self);

            RubyClass immediateClass = context.GetImmediateClassOf(self);
            if (!inherited && !immediateClass.IsSingletonClass) {
                var result = new RubyArray();
                if (foreignMembers.Count > 0) {
                    foreach (var name in foreignMembers) {
                        if (Tokenizer.IsMethodName(name) || Tokenizer.IsOperatorName(name)) {
                            result.Add(new ClrName(name));
                        }
                    }
                }
                return result;
            }

            return ModuleOps.GetMethods(immediateClass, inherited, RubyMethodAttributes.Public | RubyMethodAttributes.Protected, foreignMembers);
        }
Ejemplo n.º 9
0
        // TODO: do not test runtime for runtime bound sites
        // TODO: ResolveMethod invalidates modules that were not initialized yet -> snapshot version after method resolution
        // TODO: thread safety: synchronize version snapshot and method resolution
        public void AddTargetTypeTest(object target, Expression/*!*/ targetParameter, RubyContext/*!*/ context, Expression/*!*/ contextExpression) {

            // singleton nil:
            if (target == null) {
                AddRestriction(Ast.Equal(targetParameter, Ast.Constant(null)));
                AddFullVersionTest(context.NilClass, context, contextExpression);
                return;
            }

            // singletons true, false:
            if (target is bool) {
                AddRestriction(Ast.AndAlso(
                    Ast.TypeIs(targetParameter, typeof(bool)),
                    Ast.Equal(Ast.Convert(targetParameter, typeof(bool)), Ast.Constant(target))
                ));

                if ((bool)target) {
                    AddFullVersionTest(context.TrueClass, context, contextExpression);
                } else {
                    AddFullVersionTest(context.FalseClass, context, contextExpression);
                }
                return;

            }

            RubyClass immediateClass = context.GetImmediateClassOf(target);

            // user defined instance singletons, modules, classes:
            if (immediateClass.IsSingletonClass) {
                AddRestriction(
                    Ast.Equal(
                        Ast.Convert(targetParameter, typeof(object)),
                        Ast.Convert(Ast.Constant(target), typeof(object))
                    )
                );

                // we need to check for a runtime (e.g. "foo" .NET string instance could be shared accross runtimes):
                AddFullVersionTest(immediateClass, context, contextExpression);
                return;
            }

            Type type = target.GetType();
            AddTypeRestriction(type, targetParameter);
            
            if (typeof(IRubyObject).IsAssignableFrom(type)) {
                // Ruby objects (get the method directly to prevent interface dispatch):
                MethodInfo classGetter = type.GetMethod("get_" + RubyObject.ClassPropertyName, BindingFlags.Public | BindingFlags.Instance);
                if (classGetter != null && classGetter.ReturnType == typeof(RubyClass)) {
                    AddCondition(
                        // (#{type})target.Class.Version == #{immediateClass.Version}
                        Ast.Equal(
                            Ast.Call(Ast.Call(Ast.Convert(targetParameter, type), classGetter), RubyModule.VersionProperty.GetGetMethod()),
                            Ast.Constant(immediateClass.Version)
                        )
                    );
                    return;
                }

                // TODO: explicit iface-implementation
                throw new NotSupportedException("Type implementing IRubyObject should have RubyClass getter");
            } else {
                // CLR objects:
                AddFullVersionTest(immediateClass, context, contextExpression);
            }
        }
Ejemplo n.º 10
0
        static PropertyDescriptor[] GetPropertiesImpl(object self, Attribute[] attributes)
        {
            bool ok = true;

            foreach (var attr in attributes)
            {
                if (attr.GetType() != typeof(BrowsableAttribute))
                {
                    ok = false;
                    break;
                }
            }
            if (!ok)
            {
                return(new PropertyDescriptor[0]);
            }

            RubyContext context        = GetClass(self).Context;
            RubyClass   immediateClass = context.GetImmediateClassOf(self);

            const int readable = 0x01;
            const int writable = 0x02;

            var properties = new Dictionary <string, int>();

            immediateClass.ForEachMember(true, RubyMethodAttributes.DefaultVisibility, delegate(string /*!*/ name, RubyMemberInfo /*!*/ member) {
                int flag = 0;
                if (member is RubyAttributeReaderInfo)
                {
                    flag = readable;
                }
                else if (member is RubyAttributeWriterInfo)
                {
                    flag = writable;
                }
                else if (name == "initialize")
                {
                    // Special case; never a property
                }
                else if (member.Arity == 0)
                {
                    flag = readable;
                }
                else if (member.Arity == 1 && name.EndsWith("="))
                {
                    flag = writable;
                }
                if (flag != 0)
                {
                    if (flag == writable)
                    {
                        name = name.Substring(0, name.Length - 1);
                    }
                    int oldFlag;
                    properties.TryGetValue(name, out oldFlag);
                    properties[name] = oldFlag | flag;
                }
            });

            var result = new List <PropertyDescriptor>(properties.Count);

            foreach (var pair in properties)
            {
                if (pair.Value == (readable | writable))
                {
                    result.Add(new RubyPropertyDescriptor(pair.Key, self, immediateClass.GetUnderlyingSystemType()));
                }
            }
            return(result.ToArray());
        }