Inheritance: FunctionInstance
Example #1
0
        //     METHODS
        //_________________________________________________________________________________________


        /// <summary>
        /// Populates the given object with properties, field and methods based on the given .NET
        /// type.
        /// </summary>
        /// <param name="target"> The object to populate. </param>
        /// <param name="type"> The .NET type to search for methods. </param>
        /// <param name="flags"> <c>BindingFlags.Static</c> to populate static methods;
        /// <c>BindingFlags.Instance</c> to populate instance methods. </param>
        internal static void PopulateMembers(ObjectInstance target, Type type, BindingFlags flags)
        {
            // Register static methods as functions.
            var methodGroups = new Dictionary <string, List <MethodBase> >();

            foreach (var member in type.GetMembers(BindingFlags.Public | BindingFlags.DeclaredOnly | flags))
            {
                switch (member.MemberType)
                {
                case MemberTypes.Method:
                    MethodInfo        method = (MethodInfo)member;
                    List <MethodBase> methodGroup;
                    if (methodGroups.TryGetValue(method.Name, out methodGroup) == true)
                    {
                        methodGroup.Add(method);
                    }
                    else
                    {
                        methodGroups.Add(method.Name, new List <MethodBase>()
                        {
                            method
                        });
                    }
                    break;

                case MemberTypes.Property:
                    PropertyInfo property  = (PropertyInfo)member;
                    var          getMethod = property.GetGetMethod();
                    ClrFunction  getter    = getMethod == null ? null : new ClrFunction(target.Engine.Function.InstancePrototype, new ClrBinder(getMethod));
                    var          setMethod = property.GetSetMethod();
                    ClrFunction  setter    = setMethod == null ? null : new ClrFunction(target.Engine.Function.InstancePrototype, new ClrBinder(setMethod));
                    target.DefineProperty(property.Name, new PropertyDescriptor(getter, setter, PropertyAttributes.NonEnumerable), false);

                    // Property getters and setters also show up as methods, so remove them here.
                    // NOTE: only works if properties are enumerated after methods.
                    if (getMethod != null)
                    {
                        methodGroups.Remove(getMethod.Name);
                    }
                    if (setMethod != null)
                    {
                        methodGroups.Remove(setMethod.Name);
                    }
                    break;

                case MemberTypes.Field:
                    FieldInfo   field       = (FieldInfo)member;
                    ClrFunction fieldGetter = new ClrFunction(target.Engine.Function.InstancePrototype, new FieldGetterBinder(field));
                    ClrFunction fieldSetter = new ClrFunction(target.Engine.Function.InstancePrototype, new FieldSetterBinder(field));
                    target.DefineProperty(field.Name, new PropertyDescriptor(fieldGetter, fieldSetter, PropertyAttributes.NonEnumerable), false);
                    break;

                case MemberTypes.Constructor:
                case MemberTypes.NestedType:
                case MemberTypes.Event:
                case MemberTypes.TypeInfo:
                    // Support not yet implemented.
                    break;
                }
            }
            foreach (var methodGroup in methodGroups.Values)
            {
                var binder   = new ClrBinder(methodGroup);
                var function = new ClrFunction(target.Engine.Function.InstancePrototype, binder);
                target.FastSetProperty(binder.Name, function, PropertyAttributes.NonEnumerable, overwriteAttributes: true);
            }
        }
Example #2
0
        //     METHODS
        //_________________________________________________________________________________________


        /// <summary>
        /// Populates the given object with properties, field and methods based on the given .NET
        /// type.
        /// </summary>
        /// <param name="target"> The object to populate. </param>
        /// <param name="type"> The .NET type to search for methods. </param>
        /// <param name="flags"> <c>BindingFlags.Static</c> to populate static methods;
        /// <c>BindingFlags.Instance</c> to populate instance methods. </param>
        internal static void PopulateMembers(ObjectInstance target, Type type, BindingFlags flags)
        {
            // Register static methods as functions.
            var methodGroups = new Dictionary<string, List<MethodBase>>();
            foreach (var member in type.GetMembers(BindingFlags.Public | BindingFlags.DeclaredOnly | flags))
            {
                switch (member.MemberType)
                {
                    case MemberTypes.Method:
                        MethodInfo method = (MethodInfo)member;
                        List<MethodBase> methodGroup;
                        if (methodGroups.TryGetValue(method.Name, out methodGroup) == true)
                            methodGroup.Add(method);
                        else
                            methodGroups.Add(method.Name, new List<MethodBase>() { method });
                        break;

                    case MemberTypes.Property:
                        PropertyInfo property = (PropertyInfo)member;
                        var getMethod = property.GetGetMethod();
                        ClrFunction getter = getMethod == null ? null : new ClrFunction(target.Engine.Function.InstancePrototype, new ClrBinder(getMethod));
                        var setMethod = property.GetSetMethod();
                        ClrFunction setter = setMethod == null ? null : new ClrFunction(target.Engine.Function.InstancePrototype, new ClrBinder(setMethod));
                        target.DefineProperty(property.Name, new PropertyDescriptor(getter, setter, PropertyAttributes.NonEnumerable), false);

                        // Property getters and setters also show up as methods, so remove them here.
                        // NOTE: only works if properties are enumerated after methods.
                        if (getMethod != null)
                            methodGroups.Remove(getMethod.Name);
                        if (setMethod != null)
                            methodGroups.Remove(setMethod.Name);
                        break;
 
                    case MemberTypes.Field:
                        FieldInfo field = (FieldInfo)member;
                        ClrFunction fieldGetter = new ClrFunction(target.Engine.Function.InstancePrototype, new FieldGetterBinder(field));
                        ClrFunction fieldSetter = new ClrFunction(target.Engine.Function.InstancePrototype, new FieldSetterBinder(field));
                        target.DefineProperty(field.Name, new PropertyDescriptor(fieldGetter, fieldSetter, PropertyAttributes.NonEnumerable), false);
                        break;

                    case MemberTypes.Constructor:
                    case MemberTypes.NestedType:
                    case MemberTypes.Event:
                    case MemberTypes.TypeInfo:
                        // Support not yet implemented.
                        break;
                }
                
            }
            foreach (var methodGroup in methodGroups.Values)
            {
                var binder = new ClrBinder(methodGroup);
                var function = new ClrFunction(target.Engine.Function.InstancePrototype, binder);
                target.FastSetProperty(binder.Name, function, PropertyAttributes.NonEnumerable, overwriteAttributes: true);
            }
        }
Example #3
0
        /// <summary>
        /// Populates the object with functions by searching a .NET type for methods marked with
        /// the [JSFunction] attribute.  Should be called only once at startup.
        /// </summary>
        /// <param name="type"> The type to search for methods. </param>
        internal protected void PopulateFunctions(Type type)
        {
            if (type == null)
                type = this.GetType();
            
            // Group the methods on the given type by name.
            var functions = new Dictionary<string, MethodGroup>(20);
            var methods = type.GetMethods(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static | BindingFlags.DeclaredOnly);
            foreach (var method in methods)
            {
                // Make sure the method has the [JSFunction] attribute.
                var attribute = (JSFunctionAttribute)Attribute.GetCustomAttribute(method, typeof(JSFunctionAttribute));
                if (attribute == null)
                    continue;

                // Determine the name of the method.
                string name;
                if (attribute.Name != null)
                {
                    name = Compiler.Lexer.ResolveIdentifier(this.Engine, attribute.Name);
                    if (name == null)
                        throw new InvalidOperationException(string.Format("The name provided to [JSFunction] on {0} is not a valid identifier.", method));
                }
                else
                    name = method.Name;

                // Get a reference to the method group.
                MethodGroup methodGroup;
                if (functions.ContainsKey(name) == false)
                {
                    methodGroup = new MethodGroup { Methods = new List<JSBinderMethod>(1), Length = -1 };
                    functions.Add(name, methodGroup);
                }
                else
                    methodGroup = functions[name];

                // Add the method to the list.
                methodGroup.Methods.Add(new JSBinderMethod(method, attribute.Flags));

                // If the length doesn't equal -1, that indicates an explicit length has been set.
                // Make sure it is consistant with the other methods.
                if (attribute.Length >= 0)
                {
                    if (methodGroup.Length != -1 && methodGroup.Length != attribute.Length)
                        throw new InvalidOperationException(string.Format("Inconsistant Length property detected on {0}.", method));
                    methodGroup.Length = attribute.Length;
                }
            }

            // Now set the relevant properties on the object.
            foreach (KeyValuePair<string, MethodGroup> pair in functions)
            {
                string name = pair.Key;
                MethodGroup methodGroup = pair.Value;

                // Add the function as a property of the object.
                this.FastSetProperty(name, new ClrFunction(this.Engine.Function.InstancePrototype, methodGroup.Methods, name, methodGroup.Length), PropertyAttributes.NonEnumerable);
            }

            PropertyInfo[] properties = type.GetProperties(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static | BindingFlags.DeclaredOnly);
            foreach (PropertyInfo prop in properties)
            {
                var attribute = Attribute.GetCustomAttribute(prop, typeof(JSPropertyAttribute), false) as JSPropertyAttribute;
                if (attribute == null)
                    continue;

                string name;
                if (!string.IsNullOrEmpty(attribute.Name))
                {
                    name = Compiler.Lexer.ResolveIdentifier(this.Engine, attribute.Name);
                    if (name == null)
                        throw new InvalidOperationException(string.Format("The name provided to [JSProperty] on {0} is not a valid identifier.", prop));
                }
                else
                {
                    name = prop.Name;
                }

                ClrFunction getter = null, setter = null;
                PropertyAttributes descriptorAttributes = PropertyAttributes.Sealed;
                if (prop.CanRead)
                {
                    var getMethod = prop.GetGetMethod(true);
                    getter = new ClrFunction(engine.Function.InstancePrototype, new ClrBinder(getMethod));
                    descriptorAttributes |= PropertyAttributes.IsAccessorProperty;
                }

                if (prop.CanWrite)
                {
                    var setMethod = prop.GetSetMethod();
                    setter = new ClrFunction(engine.Function.InstancePrototype, new ClrBinder(setMethod));
                    descriptorAttributes |= PropertyAttributes.Writable;
                }

                if (attribute.IsEnumerable)
                    descriptorAttributes |= PropertyAttributes.Enumerable;
                if (attribute.IsConfigurable)
                    descriptorAttributes |= PropertyAttributes.Configurable;

                PropertyDescriptor descriptor = new PropertyDescriptor(getter, setter, descriptorAttributes);
                this.DefineProperty(name, descriptor, true);
            }
        }
        /// <summary>
        /// Populates the object with functions by searching a .NET type for methods marked with
        /// the [JSFunction] attribute.  Should be called only once at startup.  Also automatically
        /// populates properties marked with the [JSProperty] attribute.
        /// </summary>
        /// <param name="type"> The type to search for methods. </param>
        /// <param name="bindingFlags"> The binding flags to use to search for properties and methods. </param>
        internal protected void PopulateFunctions(Type type, BindingFlags bindingFlags)
        {
            if (type == null)
                type = this.GetType();
            
            // Group the methods on the given type by name.
            var functions = new Dictionary<string, MethodGroup>(20);
            var methods = type.GetMethods(bindingFlags);
            foreach (var method in methods)
            {
                // Make sure the method has the [JSInternalFunction] attribute.
                var attribute = (JSFunctionAttribute)Attribute.GetCustomAttribute(method, typeof(JSFunctionAttribute));
                if (attribute == null)
                    continue;

                // Determine the name of the method.
                string name;
                if (attribute.Name != null)
                    name = attribute.Name;
                else
                    name = method.Name;

                // Get a reference to the method group.
                MethodGroup methodGroup;
                if (functions.ContainsKey(name) == false)
                {
                    methodGroup = new MethodGroup { Methods = new List<JSBinderMethod>(1), Length = -1 };
                    functions.Add(name, methodGroup);
                }
                else
                    methodGroup = functions[name];

                // Internal functions return nulls as undefined.
                if (attribute is JSInternalFunctionAttribute)
                    attribute.Flags |= JSFunctionFlags.ConvertNullReturnValueToUndefined;

                // Add the method to the list.
                methodGroup.Methods.Add(new JSBinderMethod(method, attribute.Flags));

                // If the length doesn't equal -1, that indicates an explicit length has been set.
                // Make sure it is consistant with the other methods.
                if (attribute.Length >= 0)
                {
                    if (methodGroup.Length != -1 && methodGroup.Length != attribute.Length)
                        throw new InvalidOperationException(string.Format("Inconsistant Length property detected on {0}.", method));
                    methodGroup.Length = attribute.Length;
                }

                // Check property attributes.
                var descriptorAttributes = PropertyAttributes.Sealed;
                if (attribute.IsEnumerable)
                    descriptorAttributes |= PropertyAttributes.Enumerable;
                if (attribute.IsConfigurable)
                    descriptorAttributes |= PropertyAttributes.Configurable;
                if (attribute.IsWritable)
                    descriptorAttributes |= PropertyAttributes.Writable;
                if (methodGroup.Methods.Count > 1 && methodGroup.PropertyAttributes != descriptorAttributes)
                    throw new InvalidOperationException(string.Format("Inconsistant property attributes detected on {0}.", method));
                methodGroup.PropertyAttributes = descriptorAttributes;
            }

            // Now set the relevant properties on the object.
            foreach (KeyValuePair<string, MethodGroup> pair in functions)
            {
                string name = pair.Key;
                MethodGroup methodGroup = pair.Value;

                // Add the function as a property of the object.
                this.FastSetProperty(name, new ClrFunction(this.Engine.Function.InstancePrototype, methodGroup.Methods, name, methodGroup.Length), methodGroup.PropertyAttributes);
            }

            PropertyInfo[] properties = type.GetProperties(bindingFlags);
            foreach (PropertyInfo prop in properties)
            {
                var attribute = Attribute.GetCustomAttribute(prop, typeof(JSPropertyAttribute), false) as JSPropertyAttribute;
                if (attribute == null)
                    continue;

                // The property name.
                string name;
                if (attribute.Name != null)
                    name = attribute.Name;
                else
                    name = prop.Name;

                // The property getter.
                ClrFunction getter = null;
                if (prop.CanRead)
                {
                    var getMethod = prop.GetGetMethod(true);
                    getter = new ClrFunction(engine.Function.InstancePrototype, new JSBinderMethod[] { new JSBinderMethod(getMethod) }, name, 0);
                }

                // The property setter.
                ClrFunction setter = null;
                if (prop.CanWrite)
                {
                    var setMethod = prop.GetSetMethod((bindingFlags & BindingFlags.NonPublic) != 0);
                    if (setMethod != null)
                        setter = new ClrFunction(engine.Function.InstancePrototype, new JSBinderMethod[] { new JSBinderMethod(setMethod) }, name, 1);
                }

                // The property attributes.
                var descriptorAttributes = PropertyAttributes.Sealed;
                if (attribute.IsEnumerable)
                    descriptorAttributes |= PropertyAttributes.Enumerable;
                if (attribute.IsConfigurable)
                    descriptorAttributes |= PropertyAttributes.Configurable;

                // Define the property.
                var descriptor = new PropertyDescriptor(getter, setter, descriptorAttributes);
                this.DefineProperty(name, descriptor, true);
            }
        }