Represents either a named data property, or a named accessor property.
示例#1
0
        /// <summary>
        /// Defines or redefines the value and attributes of a property.  The prototype chain is
        /// not searched so if the property exists but only in the prototype chain a new property
        /// will be created.
        /// </summary>
        /// <param name="propertyName"> The name of the property to modify. </param>
        /// <param name="descriptor"> The property value and attributes. </param>
        /// <param name="throwOnError"> <c>true</c> to throw an exception if the property could not
        /// be set.  This can happen if the property is not configurable or the object is sealed. </param>
        /// <returns> <c>true</c> if the property was successfully modified; <c>false</c> otherwise. </returns>
        public virtual bool DefineProperty(string propertyName, PropertyDescriptor descriptor, bool throwOnError)
        {
            // Retrieve info on the property.
            var current = this.schema.GetPropertyIndexAndAttributes(propertyName);

            if (current.Exists == false)
            {
                // Create a new property.
                return AddProperty(propertyName, descriptor.Value, descriptor.Attributes, throwOnError);
            }

            // If the current property is not configurable, then the only change that is allowed is
            // a change from one simple value to another (i.e. accessors are not allowed) and only
            // if the writable attribute is set.
            if (current.IsConfigurable == false)
            {
                // Get the current value of the property.
                object currentValue = this.propertyValues[current.Index];
                object getter = null, setter = null;
                if (currentValue is PropertyAccessorValue)
                {
                    getter = ((PropertyAccessorValue)currentValue).Getter;
                    setter = ((PropertyAccessorValue)currentValue).Setter;
                }

                // Check if the modification is allowed.
                if (descriptor.Attributes != current.Attributes ||
                    (descriptor.IsAccessor == true && (getter != descriptor.Getter || setter != descriptor.Setter)) ||
                    (descriptor.IsAccessor == false && current.IsWritable == false && TypeComparer.SameValue(currentValue, descriptor.Value) == false))
                {
                    if (throwOnError == true)
                        throw new JavaScriptException(this.Engine, "TypeError", string.Format("The property '{0}' is non-configurable.", propertyName));
                    return false;
                }
            }

            // Set the property value and attributes.
            this.schema = this.schema.SetPropertyAttributes(propertyName, descriptor.Attributes);
            this.propertyValues[current.Index] = descriptor.Value;

            return true;
        }
示例#2
0
        /// <summary>
        /// Defines or redefines the value and attributes of a property.  The prototype chain is
        /// not searched so if the property exists but only in the prototype chain a new property
        /// will be created.
        /// </summary>
        /// <param name="propertyName"> The name of the property to modify. </param>
        /// <param name="descriptor"> The property value and attributes. </param>
        /// <param name="throwOnError"> <c>true</c> to throw an exception if the property could not
        /// be set.  This can happen if the property is not configurable or the object is sealed. </param>
        /// <returns> <c>true</c> if the property was successfully modified; <c>false</c> otherwise. </returns>
        public override bool DefineProperty(string propertyName, PropertyDescriptor descriptor, bool throwOnError)
        {
            // Make sure the property name isn't actually an array index.
            uint arrayIndex = ParseArrayIndex(propertyName);
            if (arrayIndex != uint.MaxValue)
            {
                // Spec violation: array elements are never accessor properties.
                if (descriptor.IsAccessor == true)
                {
                    if (throwOnError == true)
                        throw new JavaScriptException(this.Engine, "TypeError", string.Format("Accessors are not supported for array elements.", propertyName));
                    return false;
                }

                // Spec violation: array elements are always full access.
                if (descriptor.Attributes != PropertyAttributes.FullAccess)
                {
                    if (throwOnError == true)
                        throw new JavaScriptException(this.Engine, "TypeError", string.Format("Non-accessible array elements are not supported.", propertyName));
                    return false;
                }

                // The only thing that is supported is setting the value.
                object value = descriptor.Value ?? Undefined.Value;
                if (this.dense != null)
                    this.dense[arrayIndex] = value;
                else
                    this.sparse[arrayIndex] = value;
                return true;
            }

            // Delegate to the base class.
            return base.DefineProperty(propertyName, descriptor, throwOnError);
        }
示例#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);
            }
        }
 public PropertyNameAndValue(string name, PropertyDescriptor descriptor)
 {
     this.name = name;
     this.descriptor = descriptor;
 }
        /// <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);
            }
        }
示例#6
0
        /// <summary>
        /// The idea here is to cache descriptors so that we have lightning fast performance.
        /// </summary>
        /// <param name="parent">The ScriptEngine that owns this ColorInstance.</param>
        private static void PopulateDescriptors(ScriptEngine parent)
        {
            if (_descriptors != null) return;

            _descriptors = new PropertyDescriptor[5];
            for (int i = 0; i < 4; ++i)
            {
                var getter = new ColorGetter(parent, i);
                var setter = new ColorSetter(parent, i);
                _descriptors[i] = new PropertyDescriptor(getter, setter, PropertyAttributes.Sealed);
            }
            _descriptors[4] = new PropertyDescriptor(new ToStringFunc(parent, "color"), PropertyAttributes.Sealed);
        }
        //     OBJECT SERIALIZATION AND DESERIALIZATION
        //_________________________________________________________________________________________

        /// <summary>
        /// Creates a property descriptor from an object containing any of the following
        /// properties: configurable, writable, enumerable, value, get, set.
        /// </summary>
        /// <param name="obj"> The object to get the property values from. </param>
        /// <param name="defaults"> The values to use if the relevant value is not specified. </param>
        /// <returns> A PropertyDescriptor that corresponds to the object. </returns>
        public static PropertyDescriptor FromObject(ObjectInstance obj, PropertyDescriptor defaults)
        {
            if (obj == null)
                return PropertyDescriptor.Undefined;

            // Read configurable attribute.
            bool configurable = defaults.IsConfigurable;
            if (obj.HasProperty("configurable"))
                configurable = TypeConverter.ToBoolean(obj["configurable"]);

            // Read writable attribute.
            bool writable = defaults.IsWritable;
            if (obj.HasProperty("writable"))
                writable = TypeConverter.ToBoolean(obj["writable"]);

            // Read enumerable attribute.
            bool enumerable = defaults.IsEnumerable;
            if (obj.HasProperty("enumerable"))
                enumerable = TypeConverter.ToBoolean(obj["enumerable"]);

            // Read property value.
            object value = defaults.Value;
            if (obj.HasProperty("value"))
                value = obj["value"];

            // The descriptor is an accessor if get or set is present.
            bool isAccessor = false;

            // Read get accessor.
            FunctionInstance getter = defaults.Getter;
            if (obj.HasProperty("get"))
            {
                if (obj.HasProperty("value"))
                    throw new JavaScriptException(obj.Engine, "TypeError", "Property descriptors cannot have both 'get' and 'value' set");
                if (obj.HasProperty("writable"))
                    throw new JavaScriptException(obj.Engine, "TypeError", "Property descriptors with 'get' or 'set' defined must not have 'writable' set");
                if (obj["get"] is FunctionInstance)
                    getter = (FunctionInstance)obj["get"];
                else if (TypeUtilities.IsUndefined(obj["get"]) == true)
                    getter = null;
                else
                    throw new JavaScriptException(obj.Engine, "TypeError", "Property descriptor 'get' must be a function");
                isAccessor = true;
            }

            // Read set accessor.
            FunctionInstance setter = defaults.Setter;
            if (obj.HasProperty("set"))
            {
                if (obj.HasProperty("value"))
                    throw new JavaScriptException(obj.Engine, "TypeError", "Property descriptors cannot have both 'set' and 'value' set");
                if (obj.HasProperty("writable"))
                    throw new JavaScriptException(obj.Engine, "TypeError", "Property descriptors with 'get' or 'set' defined must not have 'writable' set");
                if (obj["set"] is FunctionInstance)
                    setter = (FunctionInstance)obj["set"];
                else if (TypeUtilities.IsUndefined(obj["set"]) == true)
                    setter = null;
                else
                    throw new JavaScriptException(obj.Engine, "TypeError", "Property descriptor 'set' must be a function");
                isAccessor = true;
            }

            // Build up the attributes enum.
            PropertyAttributes attributes = PropertyAttributes.Sealed;
            if (configurable == true)
                attributes |= PropertyAttributes.Configurable;
            if (writable == true)
                attributes |= PropertyAttributes.Writable;
            if (enumerable == true)
                attributes |= PropertyAttributes.Enumerable;

            // Either a value or an accessor is possible.
            object descriptorValue = value;
            if (isAccessor == true)
                descriptorValue = new PropertyAccessorValue(getter, setter);

            // Create the new property descriptor.
            return new PropertyDescriptor(descriptorValue, attributes);
        }
示例#8
-1
 private static void PopulateDescriptors(ScriptEngine parent)
 {
     if (_descriptors != null) return;
     _descriptors = new PropertyDescriptor[5];
     _descriptors[0] = new PropertyDescriptor(new GetSizeFunc(parent), PropertyAttributes.Sealed);
     _descriptors[1] = new PropertyDescriptor(new ConcatFunc(parent), PropertyAttributes.Sealed);
     _descriptors[2] = new PropertyDescriptor(new SliceFunc(parent), PropertyAttributes.Sealed);
     _descriptors[3] = new PropertyDescriptor(new GetSizeFunc(parent), null, PropertyAttributes.Sealed);
     _descriptors[4] = new PropertyDescriptor(new ToStringFunc(parent, "bytearray"), PropertyAttributes.Sealed);
 }