private FastSetProperty ( object key, object value, PropertyAttributes attributes = PropertyAttributes.Sealed, bool overwriteAttributes = false ) : void | ||
key | object | The property key (either a string or a Symbol). |
value | object | The intended value of the property. |
attributes | PropertyAttributes | Attributes that indicate whether the property is writable, /// configurable and enumerable. |
overwriteAttributes | bool | Indicates whether to overwrite any existing attributes. |
return | void |
public static ObjectInstance Freeze(ObjectInstance obj) { var properties = new List <PropertyNameAndValue>(); foreach (var property in obj.Properties) { properties.Add(property); } foreach (var property in properties) { obj.FastSetProperty(property.Key, property.Value, property.Attributes & ~(PropertyAttributes.NonEnumerable), overwriteAttributes: true); } obj.IsExtensible = false; return(obj); }
public static ObjectInstance Seal([JSParameter(JSParameterFlags.DoNotConvert)] ObjectInstance obj) { var properties = new List <PropertyNameAndValue>(); foreach (var property in obj.Properties) { properties.Add(property); } foreach (var property in properties) { obj.FastSetProperty(property.Name, property.Value, property.Attributes & ~PropertyAttributes.Configurable, overwriteAttributes: true); } obj.IsExtensible = false; return(obj); }
// 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); } }
// 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); } }
// INITIALIZATION //_________________________________________________________________________________________ /// <summary> /// Creates a new instance of a built-in constructor function. /// </summary> /// <param name="prototype"> The next object in the prototype chain. </param> /// <param name="name"> The name of the function. </param> /// <param name="instancePrototype"> </param> protected ClrFunction(ObjectInstance prototype, string name, ObjectInstance instancePrototype) : base(prototype) { if (name == null) throw new ArgumentNullException("name"); if (instancePrototype == null) throw new ArgumentNullException("instancePrototype"); // This is a constructor so ignore the "this" parameter when the function is called. thisBinding = this; // Search through every method in this type looking for [JSFunction] attributes. var callBinderMethods = new List<JSBinderMethod>(1); var constructBinderMethods = new List<JSBinderMethod>(1); var methods = this.GetType().GetMethods(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static | BindingFlags.DeclaredOnly); foreach (var method in methods) { // Search for the [JSCallFunction] and [JSConstructorFunction] attributes. var callAttribute = (JSCallFunctionAttribute) Attribute.GetCustomAttribute(method, typeof(JSCallFunctionAttribute)); var constructorAttribute = (JSConstructorFunctionAttribute)Attribute.GetCustomAttribute(method, typeof(JSConstructorFunctionAttribute)); // Can't declare both attributes. if (callAttribute != null && constructorAttribute != null) throw new InvalidOperationException("Methods cannot be marked with both [JSCallFunction] and [JSConstructorFunction]."); if (callAttribute != null) { // Method is marked with [JSCallFunction] callBinderMethods.Add(new JSBinderMethod(method, callAttribute.Flags)); } else if (constructorAttribute != null) { var binderMethod = new JSBinderMethod(method, constructorAttribute.Flags); constructBinderMethods.Add(binderMethod); // Constructors must return ObjectInstance or a derived type. if (typeof(ObjectInstance).IsAssignableFrom(binderMethod.ReturnType) == false) throw new InvalidOperationException(string.Format("Constructors must return {0} (or a derived type).", typeof(ObjectInstance).Name)); } } // Initialize the Call function. if (callBinderMethods.Count > 0) this.callBinder = new JSBinder(callBinderMethods); else this.callBinder = new JSBinder(new JSBinderMethod(new Func<object>(() => Undefined.Value).Method)); // Initialize the Construct function. if (constructBinderMethods.Count > 0) this.constructBinder = new JSBinder(constructBinderMethods); else this.constructBinder = new JSBinder(new JSBinderMethod(new Func<ObjectInstance>(() => this.Engine.Object.Construct()).Method)); // Add function properties. this.FastSetProperty("name", name); this.FastSetProperty("length", this.callBinder.FunctionLength); this.FastSetProperty("prototype", instancePrototype); instancePrototype.FastSetProperty("constructor", this, PropertyAttributes.NonEnumerable); }
// INITIALIZATION //_________________________________________________________________________________________ /// <summary> /// Creates a new instance of a built-in constructor function. /// </summary> /// <param name="prototype"> The next object in the prototype chain. </param> /// <param name="name"> The name of the function. </param> /// <param name="instancePrototype"> </param> protected ClrFunction(ObjectInstance prototype, string name, ObjectInstance instancePrototype) : base(prototype) { if (name == null) { throw new ArgumentNullException("name"); } if (instancePrototype == null) { throw new ArgumentNullException("instancePrototype"); } // This is a constructor so ignore the "this" parameter when the function is called. thisBinding = this; // Search through every method in this type looking for [JSFunction] attributes. var callBinderMethods = new List <JSBinderMethod>(1); var constructBinderMethods = new List <JSBinderMethod>(1); var methods = this.GetType().GetMethods(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static | BindingFlags.DeclaredOnly); foreach (var method in methods) { // Search for the [JSCallFunction] and [JSConstructorFunction] attributes. var callAttribute = (JSCallFunctionAttribute)Attribute.GetCustomAttribute(method, typeof(JSCallFunctionAttribute)); var constructorAttribute = (JSConstructorFunctionAttribute)Attribute.GetCustomAttribute(method, typeof(JSConstructorFunctionAttribute)); // Can't declare both attributes. if (callAttribute != null && constructorAttribute != null) { throw new InvalidOperationException("Methods cannot be marked with both [JSCallFunction] and [JSConstructorFunction]."); } if (callAttribute != null) { // Method is marked with [JSCallFunction] callBinderMethods.Add(new JSBinderMethod(method, callAttribute.Flags)); } else if (constructorAttribute != null) { var binderMethod = new JSBinderMethod(method, constructorAttribute.Flags); constructBinderMethods.Add(binderMethod); // Constructors must return ObjectInstance or a derived type. if (typeof(ObjectInstance).IsAssignableFrom(binderMethod.ReturnType) == false) { throw new InvalidOperationException(string.Format("Constructors must return {0} (or a derived type).", typeof(ObjectInstance).Name)); } } } // Initialize the Call function. if (callBinderMethods.Count > 0) { this.callBinder = new JSBinder(callBinderMethods); } else { this.callBinder = new JSBinder(new JSBinderMethod(new Func <object>(() => Undefined.Value).Method)); } // Initialize the Construct function. if (constructBinderMethods.Count > 0) { this.constructBinder = new JSBinder(constructBinderMethods); } else { this.constructBinder = new JSBinder(new JSBinderMethod(new Func <ObjectInstance>(() => this.Engine.Object.Construct()).Method)); } // Add function properties. this.FastSetProperty("name", name); this.FastSetProperty("length", this.callBinder.FunctionLength); this.FastSetProperty("prototype", instancePrototype); instancePrototype.FastSetProperty("constructor", this, PropertyAttributes.NonEnumerable); }
public static ObjectInstance Seal(ObjectInstance obj) { var properties = new List<PropertyNameAndValue>(); foreach (var property in obj.Properties) properties.Add(property); foreach (var property in properties) { obj.FastSetProperty(property.Key, property.Value, property.Attributes & ~PropertyAttributes.Configurable, overwriteAttributes: true); } obj.IsExtensible = false; return obj; }