Пример #1
0
        /// <summary>
        /// Deletes a property from the schema.
        /// </summary>
        /// <param name="key"> The property key of the property to delete. </param>
        /// <returns> A new schema without the property. </returns>
        public HiddenClassSchema DeleteProperty(object key)
        {
            // Check if there is a transition to the schema already.
            HiddenClassSchema newSchema = null;

            if (this.deleteTransitions != null)
            {
                this.deleteTransitions.TryGetValue(key, out newSchema);
            }

            if (newSchema == null)
            {
                // Create a new schema based on this one.
                var properties = this.properties == null?CreatePropertiesDictionary() : new Dictionary <object, SchemaProperty>(this.properties);

                if (properties.Remove(key) == false)
                {
                    throw new InvalidOperationException(string.Format("The property '{0}' does not exist.", key));
                }
                newSchema = new HiddenClassSchema(properties, this.NextValueIndex);

                // Add a transition to the new schema.
                if (this.deleteTransitions == null)
                {
                    this.deleteTransitions = new Dictionary <object, HiddenClassSchema>(1);
                }
                this.deleteTransitions.Add(key, newSchema);
            }

            return(newSchema);
        }
Пример #2
0
 /// <summary>
 /// Called by derived classes to create a new object instance.
 /// </summary>
 /// <param name="prototype"> The next object in the prototype chain.  Cannot be <c>null</c>. </param>
 protected ObjectInstance(ObjectInstance prototype)
 {
     if (prototype == null)
         throw new ArgumentNullException("prototype");
     this.prototype = prototype;
     this.engine = prototype.Engine;
     this.schema = this.engine.EmptySchema;
 }
Пример #3
0
 /// <summary>
 /// Called by derived classes to create a new object instance.
 /// </summary>
 /// <param name="engine"> The script engine associated with this object. </param>
 /// <param name="prototype"> The next object in the prototype chain.  Can be <c>null</c>. </param>
 protected ObjectInstance(ScriptEngine engine, ObjectInstance prototype)
 {
     if (engine == null)
         throw new ArgumentNullException("engine");
     this.engine = engine;
     this.prototype = prototype;
     this.schema = engine.EmptySchema;
 }
Пример #4
0
 /// <summary>
 /// Enumerates the property names for this schema.
 /// </summary>
 /// <returns> An enumerable collection of property names. </returns>
 public IEnumerable <object> EnumeratePropertyNames()
 {
     if (this.properties == null)
     {
         this.properties = CreatePropertiesDictionary();
     }
     this.parent = null;     // Prevents the properties dictionary from being stolen while an enumeration is in progress.
     foreach (var pair in this.properties)
     {
         yield return(pair.Key);
     }
 }
Пример #5
0
 /// <summary>
 /// Enumerates the property names and values for this schema.
 /// </summary>
 /// <param name="values"> The array containing the property values. </param>
 /// <returns> An enumerable collection of property names and values. </returns>
 public IEnumerable <PropertyNameAndValue> EnumeratePropertyNamesAndValues(object[] values)
 {
     if (this.properties == null)
     {
         this.properties = CreatePropertiesDictionary();
     }
     this.parent = null;     // Prevents the properties dictionary from being stolen while an enumeration is in progress.
     foreach (var pair in this.properties)
     {
         yield return(new PropertyNameAndValue(pair.Key, values[pair.Value.Index], pair.Value.Attributes));
     }
 }
Пример #6
0
        /// <summary>
        /// Modifies the attributes for a property in the schema.
        /// </summary>
        /// <param name="key"> The property key of the property to modify. </param>
        /// <param name="attributes"> The new attributes. </param>
        /// <returns> A new schema with the modified property. </returns>
        public HiddenClassSchema SetPropertyAttributes(object key, PropertyAttributes attributes)
        {
            // Package the name and attributes into a struct.
            var transitionInfo = new TransitionInfo()
            {
                Key = key, Attributes = attributes
            };

            // Check if there is a transition to the schema already.
            HiddenClassSchema newSchema = null;

            if (this.modifyTransitions != null)
            {
                this.modifyTransitions.TryGetValue(transitionInfo, out newSchema);
            }

            if (newSchema == null)
            {
                // Create the properties dictionary if it hasn't already been created.
                if (this.properties == null)
                {
                    this.properties = CreatePropertiesDictionary();
                }

                // Check the attributes differ from the existing attributes.
                SchemaProperty propertyInfo;
                if (this.properties.TryGetValue(key, out propertyInfo) == false)
                {
                    throw new InvalidOperationException(string.Format("The property '{0}' does not exist.", key));
                }
                if (attributes == propertyInfo.Attributes)
                {
                    return(this);
                }

                // Create a new schema based on this one.
                var properties = new Dictionary <object, SchemaProperty>(this.properties);
                properties[key] = new SchemaProperty(propertyInfo.Index, attributes);
                newSchema       = new HiddenClassSchema(properties, this.NextValueIndex);

                // Add a transition to the new schema.
                if (this.modifyTransitions == null)
                {
                    this.modifyTransitions = new Dictionary <TransitionInfo, HiddenClassSchema>(1);
                }
                this.modifyTransitions.Add(transitionInfo, newSchema);
            }

            return(newSchema);
        }
Пример #7
0
        /// <summary>
        /// Adds a property to the schema.
        /// </summary>
        /// <param name="key"> The property key of the property to add. </param>
        /// <param name="attributes"> The property attributes. </param>
        /// <returns> A new schema with the extra property. </returns>
        public HiddenClassSchema AddProperty(object key, PropertyAttributes attributes = PropertyAttributes.FullAccess)
        {
            // Package the name and attributes into a struct.
            var transitionInfo = new TransitionInfo()
            {
                Key = key, Attributes = attributes
            };

            // Check if there is a transition to the schema already.
            HiddenClassSchema newSchema = null;

            if (this.addTransitions != null)
            {
                this.addTransitions.TryGetValue(transitionInfo, out newSchema);
            }

            if (newSchema == null)
            {
                if (this.parent == null)
                {
                    // Create a new schema based on this one.  A complete copy must be made of the properties hashtable.
                    var properties = new Dictionary <object, SchemaProperty>(this.properties);
                    properties.Add(key, new SchemaProperty(this.NextValueIndex, attributes));
                    newSchema = new HiddenClassSchema(properties, this.NextValueIndex + 1, this, transitionInfo);
                }
                else
                {
                    // Create a new schema based on this one.  The properties hashtable is "given
                    // away" so a copy does not have to be made.
                    if (this.properties == null)
                    {
                        this.properties = CreatePropertiesDictionary();
                    }
                    this.properties.Add(key, new SchemaProperty(this.NextValueIndex, attributes));
                    newSchema       = new HiddenClassSchema(this.properties, this.NextValueIndex + 1, this, transitionInfo);
                    this.properties = null;
                }


                // Add a transition to the new schema.
                if (this.addTransitions == null)
                {
                    this.addTransitions = new Dictionary <TransitionInfo, HiddenClassSchema>(1);
                }
                this.addTransitions.Add(transitionInfo, newSchema);
            }

            return(newSchema);
        }
Пример #8
0
 /// <summary>
 /// Adds multiple properties to the schema.
 /// </summary>
 /// <param name="properties"> The properties to add. </param>
 /// <returns> A new schema with the extra properties. </returns>
 public HiddenClassSchema AddProperties(IEnumerable <PropertyNameAndValue> properties)
 {
     if (this.properties == null)
     {
         var propertyDictionary = new Dictionary <object, SchemaProperty>(properties.Count());
         int nextValueIndex     = 0;
         foreach (var property in properties)
         {
             propertyDictionary.Add(property.Key, new SchemaProperty(nextValueIndex++, property.Attributes));
         }
         return(new HiddenClassSchema(propertyDictionary, nextValueIndex));
     }
     else
     {
         // There are already properties in the schema.  Just add them one by one.
         HiddenClassSchema newSchema = this;
         foreach (var property in properties)
         {
             newSchema = AddProperty(property.Key, property.Attributes);
         }
         return(newSchema);
     }
 }
Пример #9
0
 /// <summary>
 /// Creates a new HiddenClassSchema instance from an add operation.
 /// </summary>
 private HiddenClassSchema(Dictionary<string, SchemaProperty> properties, int nextValueIndex, HiddenClassSchema parent, TransitionInfo addPropertyTransitionInfo)
     : this(properties, nextValueIndex)
 {
     this.parent = parent;
     this.addPropertyTransitionInfo = addPropertyTransitionInfo;
 }
Пример #10
0
        /// <summary>
        /// Modifies the attributes for a property in the schema.
        /// </summary>
        /// <param name="name"> The name of the property to modify. </param>
        /// <param name="attributes"> The new attributes. </param>
        /// <returns> A new schema with the modified property. </returns>
        public HiddenClassSchema SetPropertyAttributes(string name, PropertyAttributes attributes)
        {
            // Package the name and attributes into a struct.
            var transitionInfo = new TransitionInfo() { Name = name, Attributes = attributes };

            // Check if there is a transition to the schema already.
            HiddenClassSchema newSchema = null;
            if (this.modifyTransitions != null)
                this.modifyTransitions.TryGetValue(transitionInfo, out newSchema);

            if (newSchema == null)
            {
                // Create the properties dictionary if it hasn't already been created.
                if (this.properties == null)
                    this.properties = CreatePropertiesDictionary();

                // Check the attributes differ from the existing attributes.
                SchemaProperty propertyInfo;
                if (this.properties.TryGetValue(name, out propertyInfo) == false)
                    throw new InvalidOperationException(string.Format("The property '{0}' does not exist.", name));
                if (attributes == propertyInfo.Attributes)
                    return this;

                // Create a new schema based on this one.
                var properties = new Dictionary<string, SchemaProperty>(this.properties);
                properties[name] = new SchemaProperty(propertyInfo.Index, attributes);
                newSchema = new HiddenClassSchema(properties, this.NextValueIndex);

                // Add a transition to the new schema.
                if (this.modifyTransitions == null)
                    this.modifyTransitions = new Dictionary<TransitionInfo, HiddenClassSchema>(1);
                this.modifyTransitions.Add(transitionInfo, newSchema);
            }

            return newSchema;
        }
Пример #11
0
        /// <summary>
        /// Deletes a property from the schema.
        /// </summary>
        /// <param name="name"> The name of the property to delete. </param>
        /// <returns> A new schema without the property. </returns>
        public HiddenClassSchema DeleteProperty(string name)
        {
            // Check if there is a transition to the schema already.
            HiddenClassSchema newSchema = null;
            if (this.deleteTransitions != null)
                this.deleteTransitions.TryGetValue(name, out newSchema);

            if (newSchema == null)
            {
                // Create a new schema based on this one.
                var properties = this.properties == null ? CreatePropertiesDictionary() : new Dictionary<string, SchemaProperty>(this.properties);
                if (properties.Remove(name) == false)
                    throw new InvalidOperationException(string.Format("The property '{0}' does not exist.", name));
                newSchema = new HiddenClassSchema(properties, this.NextValueIndex);

                // Add a transition to the new schema.
                if (this.deleteTransitions == null)
                    this.deleteTransitions = new Dictionary<string, HiddenClassSchema>(1);
                this.deleteTransitions.Add(name, newSchema);
            }

            return newSchema;
        }
Пример #12
0
        /// <summary>
        /// Adds a property to the schema.
        /// </summary>
        /// <param name="name"> The name of the property to add. </param>
        /// <param name="attributes"> The property attributes. </param>
        /// <returns> A new schema with the extra property. </returns>
        public HiddenClassSchema AddProperty(string name, PropertyAttributes attributes = PropertyAttributes.FullAccess)
        {
            // Package the name and attributes into a struct.
            var transitionInfo = new TransitionInfo() { Name = name, Attributes = attributes };

            // Check if there is a transition to the schema already.
            HiddenClassSchema newSchema = null;
            if (this.addTransitions != null)
                this.addTransitions.TryGetValue(transitionInfo, out newSchema);

            if (newSchema == null)
            {
                if (this.parent == null)
                {
                    // Create a new schema based on this one.  A complete copy must be made of the properties hashtable.
                    var properties = new Dictionary<string, SchemaProperty>(this.properties);
                    properties.Add(name, new SchemaProperty(this.NextValueIndex, attributes));
                    newSchema = new HiddenClassSchema(properties, this.NextValueIndex + 1, this, transitionInfo);
                }
                else
                {
                    // Create a new schema based on this one.  The properties hashtable is "given
                    // away" so a copy does not have to be made.
                    if (this.properties == null)
                        this.properties = CreatePropertiesDictionary();
                    this.properties.Add(name, new SchemaProperty(this.NextValueIndex, attributes));
                    newSchema = new HiddenClassSchema(this.properties, this.NextValueIndex + 1, this, transitionInfo);
                    this.properties = null;
                }
                

                // Add a transition to the new schema.
                if (this.addTransitions == null)
                    this.addTransitions = new Dictionary<TransitionInfo, HiddenClassSchema>(1);
                this.addTransitions.Add(transitionInfo, newSchema);
            }

            return newSchema;
        }
Пример #13
0
 /// <summary>
 /// Sets a property value and attributes, or adds a new property if it doesn't already
 /// exist.  Any existing attributes are ignored (and not modified).
 /// </summary>
 /// <param name="propertyName"> The name of the property. </param>
 /// <param name="value"> The intended value of the property. </param>
 /// <param name="attributes"> Attributes that indicate whether the property is writable,
 /// configurable and enumerable. </param>
 /// <param name="overwriteAttributes"> Indicates whether to overwrite any existing attributes. </param>
 internal void FastSetProperty(string propertyName, object value, PropertyAttributes attributes = PropertyAttributes.Sealed, bool overwriteAttributes = false)
 {
     int index = this.schema.GetPropertyIndex(propertyName);
     if (index < 0)
     {
         // The property is doesn't exist - add a new property.
         AddProperty(propertyName, value, attributes, false);
         return;
     }
     if (overwriteAttributes == true)
         this.schema = this.schema.SetPropertyAttributes(propertyName, attributes);
     this.propertyValues[index] = value;
 }
Пример #14
0
        /// <summary>
        /// Adds a property to this object.  The property must not already exist.
        /// </summary>
        /// <param name="propertyName"> The name of the property to add. </param>
        /// <param name="value"> The desired value of the property.  This can be a
        /// <see cref="PropertyAccessorValue"/>. </param>
        /// <param name="attributes"> Attributes describing how the property may be modified. </param>
        /// <param name="throwOnError"> <c>true</c> to throw an exception if the property could not
        /// be added (i.e. if the object is not extensible). </param>
        /// <returns> <c>true</c> if the property was successfully added; <c>false</c> otherwise. </returns>
        private bool AddProperty(string propertyName, object value, PropertyAttributes attributes, bool throwOnError)
        {
            // Make sure adding a property is allowed.
            if (this.IsExtensible == false)
            {
                if (throwOnError == true)
                    throw new JavaScriptException(this.Engine, "TypeError", string.Format("The property '{0}' cannot be created as the object is not extensible.", propertyName));
                return false;
            }

            // To avoid running out of memory, restrict the number of properties.
            if (this.schema.PropertyCount == 16384)
                throw new JavaScriptException(this.engine, "Error", "Maximum number of named properties reached.");

            // Do not store nulls - null represents a non-existant value.
            value = value ?? Undefined.Value;

            // Add a new property to the schema.
            this.schema = this.schema.AddProperty(propertyName, attributes);

            // Check if the value array needs to be resized.
            int propertyIndex = this.schema.NextValueIndex - 1;
            if (propertyIndex >= this.InlinePropertyValues.Length)
                Array.Resize(ref this.propertyValues, this.InlinePropertyValues.Length * 2);

            // Set the value of the property.
            this.propertyValues[propertyIndex] = value;

            // Success.
            return true;
        }
Пример #15
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;
        }
Пример #16
0
        /// <summary>
        /// Deletes the property with the given name.
        /// </summary>
        /// <param name="propertyName"> The name of the property to delete. </param>
        /// <param name="throwOnError"> <c>true</c> to throw an exception if the property could not
        /// be set because the property was marked as non-configurable.  </param>
        /// <returns> <c>true</c> if the property was successfully deleted, or if the property did
        /// not exist; <c>false</c> if the property was marked as non-configurable and
        /// <paramref name="throwOnError"/> was <c>false</c>. </returns>
        public bool Delete(string propertyName, bool throwOnError)
        {
            // Check if the property is an indexed property.
            uint arrayIndex = ArrayInstance.ParseArrayIndex(propertyName);
            if (arrayIndex != uint.MaxValue)
                return Delete(arrayIndex, throwOnError);

            // Retrieve the attributes for the property.
            var propertyInfo = this.schema.GetPropertyIndexAndAttributes(propertyName);
            if (propertyInfo.Exists == false)
                return true;    // Property doesn't exist - delete succeeded!

            // Check if the property can be deleted.
            if (propertyInfo.IsConfigurable == false)
            {
                if (throwOnError == true)
                    throw new JavaScriptException(this.Engine, "TypeError", string.Format("The property '{0}' cannot be deleted.", propertyName));
                return false;
            }

            // Delete the property.
            this.schema = this.schema.DeleteProperty(propertyName);
            return true;
        }
Пример #17
0
        /// <summary>
        /// Sets up multiple properties at once.
        /// </summary>
        /// <param name="properties"> The list of properties to set. </param>
        internal void FastSetProperties(IEnumerable<PropertyNameAndValue> properties)
        {
            if (this.schema.NextValueIndex != 0)
                throw new InvalidOperationException("This method can only be called on a virgin object (one with no properties).");

            if (this.propertyValues.Length < properties.Count())
                this.propertyValues = new object[properties.Count()];
            var propertyDictionary = new Dictionary<object, SchemaProperty>(properties.Count());
            int nextValueIndex = 0;
            foreach (var property in properties)
            {
                this.propertyValues[nextValueIndex] = property.Value;
                propertyDictionary.Add(property.Key, new SchemaProperty(nextValueIndex++, property.Attributes));
            }
            this.schema = new HiddenClassSchema(propertyDictionary, nextValueIndex);
        }
Пример #18
0
 /// <summary>
 /// Enumerates the property names and values for this schema.
 /// </summary>
 /// <param name="values"> The array containing the property values. </param>
 /// <returns> An enumerable collection of property names and values. </returns>
 public IEnumerable<PropertyNameAndValue> EnumeratePropertyNamesAndValues(object[] values)
 {
     if (this.properties == null)
         this.properties = CreatePropertiesDictionary();
     this.parent = null;     // Prevents the properties dictionary from being stolen while an enumeration is in progress.
     foreach (var pair in this.properties)
         yield return new PropertyNameAndValue(pair.Key, new PropertyDescriptor(values[pair.Value.Index], pair.Value.Attributes));
 }
Пример #19
0
 /// <summary>
 /// Creates a new HiddenClassSchema instance from an add operation.
 /// </summary>
 private HiddenClassSchema(Dictionary <object, SchemaProperty> properties, int nextValueIndex, HiddenClassSchema parent, TransitionInfo addPropertyTransitionInfo)
     : this(properties, nextValueIndex)
 {
     this.parent = parent;
     this.addPropertyTransitionInfo = addPropertyTransitionInfo;
 }
Пример #20
0
 /// <summary>
 /// Caches property details.
 /// </summary>
 /// <param name="schema"> A reference to a schema that defines how properties are stored. </param>
 /// <param name="index"> The index into the property array. </param>
 internal void CachePropertyDetails(HiddenClassSchema schema, int index)
 {
     this.CachedSchema = schema;
     this.CachedIndex  = index;
 }
Пример #21
0
        /// <summary>
        /// Deletes the property with the given array index.
        /// </summary>
        /// <param name="index"> The array index of the property to delete. </param>
        /// <param name="throwOnError"> <c>true</c> to throw an exception if the property could not
        /// be set because the property was marked as non-configurable.  </param>
        /// <returns> <c>true</c> if the property was successfully deleted, or if the property did
        /// not exist; <c>false</c> if the property was marked as non-configurable and
        /// <paramref name="throwOnError"/> was <c>false</c>. </returns>
        public virtual bool Delete(uint index, bool throwOnError)
        {
            string indexStr = index.ToString();

            // Retrieve the attributes for the property.
            var propertyInfo = this.schema.GetPropertyIndexAndAttributes(indexStr);
            if (propertyInfo.Exists == false)
                return true;    // Property doesn't exist - delete succeeded!

            // Delete the property.
            this.schema = this.schema.DeleteProperty(indexStr);
            return true;
        }