/// <summary> /// Sets the value of a class property to a getter. If the value already has a /// setter then it will be retained. /// </summary> /// <param name="obj"> The object to set the property on. </param> /// <param name="key"> The property key (can be a string or a symbol). </param> /// <param name="getter"> The getter function. </param> public static void SetClassGetter(ObjectInstance obj, object key, UserDefinedFunction getter) { var descriptor = obj.GetOwnPropertyDescriptor(key); if (descriptor.Exists == false || !descriptor.IsAccessor) { obj.DefineProperty(key, new PropertyDescriptor(getter, null, Library.PropertyAttributes.NonEnumerable), throwOnError: false); } else { obj.DefineProperty(key, new PropertyDescriptor(getter, descriptor.Setter, Library.PropertyAttributes.NonEnumerable), throwOnError: false); } }
/// <summary> /// Sets the value of a object literal property to a setter. If the value already has a /// getter then it will be retained. /// </summary> /// <param name="obj"> The object to set the property on. </param> /// <param name="key"> The property key (can be a string or a symbol).</param> /// <param name="setter"> The setter function. </param> public static void SetObjectLiteralSetter(ObjectInstance obj, object key, UserDefinedFunction setter) { var descriptor = obj.GetOwnPropertyDescriptor(key); if (descriptor.Exists == false || !descriptor.IsAccessor) { obj.DefineProperty(key, new PropertyDescriptor(null, setter, Library.PropertyAttributes.FullAccess), throwOnError: false); } else { obj.DefineProperty(key, new PropertyDescriptor(descriptor.Getter, setter, Library.PropertyAttributes.FullAccess), throwOnError: false); } }
public static ObjectInstance DefineProperty([JSParameter(JSParameterFlags.DoNotConvert)] ObjectInstance obj, string propertyName, [JSParameter(JSParameterFlags.DoNotConvert)] ObjectInstance attributes) { var defaults = obj.GetOwnPropertyDescriptor(propertyName); var descriptor = PropertyDescriptor.FromObject(attributes, defaults); obj.DefineProperty(propertyName, descriptor, true); return(obj); }
public static void DefineStaticProperties(this ObjectInstance jsObject, ClrTypeConverter ctx, Type type) { foreach (var staticField in type.GetFields(BindingFlags.Public | BindingFlags.Static) .Where(p => p.GetCustomAttribute <JsHiddenAttribute>() == null)) { var clrValue = staticField.GetValue(null); var prop = new PropertyDescriptor(ctx.ConvertToJs(clrValue), PropertyAttributes.Sealed); var name = staticField.GetCustomAttribute <JsNameAttribute>()?.Name ?? staticField.Name; jsObject.DefineProperty(name, prop, false); } }
// CODE GEN METHODS //_________________________________________________________________________________________ /// <summary> /// Sets the value of a object literal property to a value. /// </summary> /// <param name="obj"> The object to set the property on. </param> /// <param name="key"> The property key (can be a string or a symbol). </param> /// <param name="value"> The value to set. </param> public static void SetObjectLiteralValue(ObjectInstance obj, object key, object value) { obj.DefineProperty(key, new PropertyDescriptor(value, Library.PropertyAttributes.FullAccess), throwOnError: false); }
/// <summary> /// Sets the value of a class property to a value. /// </summary> /// <param name="obj"> The object to set the property on. </param> /// <param name="key"> The property key (can be a string or a symbol). </param> /// <param name="value"> The value to set. </param> public static void SetClassValue(ObjectInstance obj, object key, object value) { obj.DefineProperty(key, new PropertyDescriptor(value, Library.PropertyAttributes.NonEnumerable), throwOnError: false); }
/// <summary> /// Adds properties to the specified js object from the specified clr object. /// </summary> public static void DefineProperties(this ObjectInstance jsObject, ClrTypeConverter ctx, Type type, object clrOwner = null) { var bindingFlags = BindingFlags.Instance | BindingFlags.Public | BindingFlags.DeclaredOnly; //register properties foreach (var property in type.GetProperties(bindingFlags).Where(p => p.GetCustomAttribute <JsHiddenAttribute>() == null)) { var name = property.GetName(); //todo: compile delegate instead of reflection, research what is faster. var jsGetter = property.GetMethod != null && property.GetMethod.IsPublic ? new FuncInst(ctx, clrThis => property.GetValue(clrThis)) : null; var jsSetter = property.SetMethod != null && property.SetMethod.IsPublic ? new ActInst(ctx, (clrThis, objects) => property.SetValue(clrThis, ctx.ConvertToClr(objects[0], property.PropertyType, ctx.ConvertToJs(clrThis)))) : null; var jsProperty = new PropertyDescriptor(jsGetter, jsSetter, PropertyAttributes.Configurable); jsObject.DefineProperty(name, jsProperty, false); } //register methods foreach (var methods in type .GetMethods(bindingFlags) .Where(p => p.GetCustomAttribute <JsHiddenAttribute>() == null && p.GetCustomAttribute <JsCtorAttribute>() == null) .GroupBy(p => p.GetName())) { var methodsArray = methods.ToArray(); var firstMethod = methodsArray[0]; if (firstMethod.IsSpecialName) { continue; } if (firstMethod.DeclaringType == typeof(object)) { continue; } var name = methods.Key; var jsMethod = methodsArray.Length == 1 ? new ClrMethodInstance(ctx, clrOwner, firstMethod, name) : (FunctionInstance) new ClrOverloadMethodInstance(ctx, clrOwner, methodsArray, name); //todo: check the PropertyAttributes var jsProperty = new PropertyDescriptor(jsMethod, PropertyAttributes.Sealed); jsObject.DefineProperty(name, jsProperty, false); } //register events foreach (var evt in type.GetEvents(bindingFlags) .Where(p => p.GetCustomAttribute <JsHiddenAttribute>() == null)) { var name = evt.GetName().ToLowerInvariant(); var jsGetter = new FuncInst(ctx, clrThis => { var attachedEvents = ctx.GetAttachedEventsFor(clrThis); return(attachedEvents[evt]); }, false); var jsSetter = new ActInst(ctx, (clrThis, args) => { var jsThis = ctx.ConvertToJs(clrThis); var attachedEvents = ctx.GetAttachedEventsFor(clrThis); if (attachedEvents.TryGetValue(evt, out var existHandler)) { var clrHandler = ctx.ConvertToClr(existHandler, evt.EventHandlerType, jsThis); evt.RemoveMethod.Invoke(clrThis, new [] { clrHandler }); attachedEvents.Remove(evt); } if (args.Length != 0 && args[0] is FunctionInstance functionInstance) { var clrHandler = ctx.ConvertToClr(args[0], evt.EventHandlerType, jsThis); evt.AddMethod.Invoke(clrThis, new[] { clrHandler }); attachedEvents[evt] = functionInstance; } }); var jsProperty = new PropertyDescriptor(jsGetter, jsSetter, PropertyAttributes.Configurable); jsObject.DefineProperty(name, jsProperty, false); } //Register static fields DefineStaticProperties(jsObject, ctx, type); }
private static object ConvertValue(object src, ScriptEngine engine) { if (src == null) { return(null); } Type type = src.GetType(); object value = null; if (type.IsEnum) { // convert to string value = src.ToString(); } else if (type == typeof(string)) { value = src; } else if (type == typeof(int) || type == typeof(short) || type == typeof(byte)) { value = (int)src; } else if (type == typeof(double) || type == typeof(long) || type == typeof(float)) { value = (double)src; } else if (type == typeof(bool)) { value = (bool)src; } else if (type.IsSubclassOf(typeof(ValueType))) { value = value.ToString(); } else if (typeof(IDictionary <string, object>).IsAssignableFrom(type)) { IDictionary <string, object> dict = value as IDictionary <string, object>; ObjectInstance jsObj = engine.Object.Construct(); foreach (string key in dict.Keys) { jsObj.DefineProperty(key, new PropertyDescriptor(ConvertValue(dict[key], engine), Jurassic.Library.PropertyAttributes.Sealed | Jurassic.Library.PropertyAttributes.Enumerable), false); } value = jsObj; } else if (typeof(IDictionary).IsAssignableFrom(type)) { IDictionary dict = src as IDictionary; ObjectInstance jsObj = engine.Object.Construct(); foreach (object key in dict.Keys) { if (key is string) { jsObj.DefineProperty(key as string, new PropertyDescriptor(ConvertValue(dict[key], engine), Jurassic.Library.PropertyAttributes.Enumerable | Jurassic.Library.PropertyAttributes.Sealed), false); } else { throw new NotSupportedException("Cannot have an object key in a dictionary."); } } value = jsObj; } else if (type.IsArray || typeof(IEnumerable).IsAssignableFrom(type)) { IEnumerable collection = src as IEnumerable; ArrayInstance collectionResult = engine.Array.Construct(); foreach (object o in collection) { collectionResult.Push(ConvertValue(o, engine)); } value = collectionResult; } else { value = new JinxBotScriptObjectInstance(engine, src); } return(value); }
// 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)) { 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, true); } }