/// <summary> /// Imports the delegate to the specified member. /// </summary> /// <param name="script">The script object to import into</param> /// <param name="member">The member.</param> /// <param name="function">The function delegate.</param> /// <exception cref="System.ArgumentNullException">if member or function are null</exception> public static void Import(this IScriptObject script, string member, Delegate function) { if (member == null) { throw new ArgumentNullException(nameof(member)); } if (function == null) { throw new ArgumentNullException(nameof(function)); } script.SetValue(null, new SourceSpan(), member, DynamicCustomFunction.Create(function.Target, function.GetMethodInfo()), true); }
public static void Import(this IScriptObject script, object obj, ScriptMemberImportFlags flags, MemberFilterDelegate filter = null, MemberRenamerDelegate renamer = null) { if (obj == null) { return; } if (!ScriptObject.IsImportable(obj)) { throw new ArgumentOutOfRangeException(nameof(obj), $"Unsupported object type `{obj.GetType()}`. Expecting plain class or struct"); } var typeInfo = (obj as Type ?? obj.GetType()); bool useStatic = false; bool useInstance = false; if (obj is Type) { useStatic = true; obj = null; } else { useInstance = true; } renamer = renamer ?? StandardMemberRenamer.Default; var typeToImports = new Stack <Type>(); while (typeInfo != null) { typeToImports.Push(typeInfo); if (typeInfo.BaseType == typeof(object)) { break; } typeInfo = typeInfo.BaseType; } var scriptObj = script as ScriptObject; while (typeToImports.Count > 0) { typeInfo = typeToImports.Pop(); if ((flags & ScriptMemberImportFlags.Field) != 0) { foreach (var field in typeInfo.GetFields(BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.DeclaredOnly)) { if (!field.IsPublic || field.IsLiteral) { continue; } if (filter != null && !filter(field)) { continue; } var keep = field.GetCustomAttribute <ScriptMemberIgnoreAttribute>() == null; if (keep && ((field.IsStatic && useStatic) || useInstance)) { var newFieldName = renamer(field); if (String.IsNullOrEmpty(newFieldName)) { newFieldName = field.Name; } // If field is init only or literal, it cannot be set back so we mark it as read-only if (scriptObj == null) { script.TrySetValue(null, new SourceSpan(), newFieldName, field.GetValue(obj), field.IsInitOnly || field.IsLiteral); } else { scriptObj.SetValue(newFieldName, field.GetValue(obj), field.IsInitOnly || field.IsLiteral); } } } } if ((flags & ScriptMemberImportFlags.Property) != 0) { foreach (var property in typeInfo.GetProperties(BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.DeclaredOnly)) { // Workaround with .NET Core, extension method is not working (retuning null despite doing property.GetMethod), so we need to inline it here var getMethod = property.GetMethod; if (!property.CanRead || !getMethod.IsPublic) { continue; } if (filter != null && !filter(property)) { continue; } var keep = property.GetCustomAttribute <ScriptMemberIgnoreAttribute>() == null; if (keep && (((getMethod.IsStatic && useStatic) || useInstance))) { var newPropertyName = renamer(property); if (String.IsNullOrEmpty(newPropertyName)) { newPropertyName = property.Name; } // Initially, we were setting readonly depending on the precense of a set method, but this is not compatible with liquid implems, so we remove readonly restriction //script.SetValue(null, new SourceSpan(), newPropertyName, property.GetValue(obj), property.GetSetMethod() == null || !property.GetSetMethod().IsPublic); if (scriptObj == null) { script.TrySetValue(null, new SourceSpan(), newPropertyName, property.GetValue(obj), false); } else { if (property.GetIndexParameters().Length == 0) { scriptObj.SetValue(newPropertyName, property.GetValue(obj), false); } } } } } if ((flags & ScriptMemberImportFlags.Method) != 0 && useStatic) { foreach (var method in typeInfo.GetMethods(BindingFlags.Static | BindingFlags.Public | BindingFlags.DeclaredOnly)) { if (filter != null && !filter(method)) { continue; } var keep = method.GetCustomAttribute <ScriptMemberIgnoreAttribute>() == null; if (keep && method.IsPublic && method.IsStatic && !method.IsSpecialName) { var newMethodName = renamer(method); if (String.IsNullOrEmpty(newMethodName)) { newMethodName = method.Name; } if (scriptObj == null) { script.TrySetValue(null, new SourceSpan(), newMethodName, DynamicCustomFunction.Create(obj, method), true); } else { scriptObj.SetValue(newMethodName, DynamicCustomFunction.Create(obj, method), true); } } } } } }
/// <summary> /// Imports the specified object. /// </summary> /// <param name="script">The script object to import into</param> /// <param name="obj">The object.</param> /// <param name="flags">The import flags.</param> /// <param name="filter">A filter applied on each member</param> /// <param name="renamer">The member renamer.</param> /// <exception cref="System.ArgumentOutOfRangeException"></exception> public static void Import(this IScriptObject script, object obj, ScriptMemberImportFlags flags, MemberFilterDelegate filter = null, MemberRenamerDelegate renamer = null) { if (obj == null) { return; } if (!ScriptObject.IsImportable(obj)) { throw new ArgumentOutOfRangeException(nameof(obj), $"Unsupported object type `{obj.GetType()}`. Expecting plain class or struct"); } var typeInfo = (obj as Type ?? obj.GetType()).GetTypeInfo(); bool useStatic = false; bool useInstance = false; bool useMethodInstance = false; if (obj is Type) { useStatic = true; obj = null; } else { useInstance = true; useMethodInstance = (flags & ScriptMemberImportFlags.MethodInstance) != 0; } renamer = renamer ?? StandardMemberRenamer.Default; if ((flags & ScriptMemberImportFlags.Field) != 0) { foreach (var field in typeInfo.GetDeclaredFields()) { if (!field.IsPublic) { continue; } if (filter != null && !filter(field)) { continue; } var keep = field.GetCustomAttribute <ScriptMemberIgnoreAttribute>() == null; if (keep && ((field.IsStatic && useStatic) || useInstance)) { var newFieldName = renamer(field); if (String.IsNullOrEmpty(newFieldName)) { newFieldName = field.Name; } // If field is init only or literal, it cannot be set back so we mark it as read-only script.SetValue(null, new SourceSpan(), newFieldName, field.GetValue(obj), field.IsInitOnly || field.IsLiteral); } } } if ((flags & ScriptMemberImportFlags.Property) != 0) { foreach (var property in typeInfo.GetDeclaredProperties()) { if (!property.CanRead || !property.GetGetMethod().IsPublic) { continue; } if (filter != null && !filter(property)) { continue; } var keep = property.GetCustomAttribute <ScriptMemberIgnoreAttribute>() == null; if (keep && (((property.GetGetMethod().IsStatic&& useStatic) || useInstance))) { var newPropertyName = renamer(property); if (String.IsNullOrEmpty(newPropertyName)) { newPropertyName = property.Name; } // Initially, we were setting readonly depending on the precense of a set method, but this is not compatible with liquid implems, so we remove readonly restriction //script.SetValue(null, new SourceSpan(), newPropertyName, property.GetValue(obj), property.GetSetMethod() == null || !property.GetSetMethod().IsPublic); script.SetValue(null, new SourceSpan(), newPropertyName, property.GetValue(obj), false); } } } if ((flags & ScriptMemberImportFlags.Method) != 0 && (useStatic || useMethodInstance)) { foreach (var method in typeInfo.GetDeclaredMethods()) { if (filter != null && !filter(method)) { continue; } var keep = method.GetCustomAttribute <ScriptMemberIgnoreAttribute>() == null; if (keep && method.IsPublic && ((useMethodInstance && !method.IsStatic) || (useStatic && method.IsStatic)) && !method.IsSpecialName) { var newMethodName = renamer(method); if (String.IsNullOrEmpty(newMethodName)) { newMethodName = method.Name; } script.SetValue(null, new SourceSpan(), newMethodName, DynamicCustomFunction.Create(obj, method), true); } } } }