/// <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; }
/// <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); }
/// <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); } }
/// <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); }
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); }