Options.cs 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275
  1. #nullable enable
  2. using System;
  3. using System.Collections.Generic;
  4. using System.Dynamic;
  5. using System.Globalization;
  6. using System.Linq;
  7. using System.Reflection;
  8. using Jint.Native;
  9. using Jint.Native.Object;
  10. using Jint.Runtime;
  11. using Jint.Runtime.Interop;
  12. using Jint.Runtime.Debugger;
  13. using Jint.Runtime.Descriptors;
  14. namespace Jint
  15. {
  16. public delegate JsValue? MemberAccessorDelegate(Engine engine, object target, string member);
  17. public delegate ObjectInstance? WrapObjectDelegate(Engine engine, object target);
  18. public delegate bool ExceptionHandlerDelegate(Exception exception);
  19. public class Options
  20. {
  21. internal List<Action<Engine>> _configurations { get; } = new();
  22. /// <summary>
  23. /// Execution constraints for the engine.
  24. /// </summary>
  25. public ConstraintOptions Constraints { get; } = new();
  26. /// <summary>
  27. /// CLR interop related options.
  28. /// </summary>
  29. public InteropOptions Interop { get; } = new();
  30. /// <summary>
  31. /// Debugger configuration.
  32. /// </summary>
  33. public DebuggerOptions Debugger { get; } = new();
  34. /// <summary>
  35. /// Host options.
  36. /// </summary>
  37. internal HostOptions Host { get; } = new();
  38. /// <summary>
  39. /// Whether the code should be always considered to be in strict mode. Can improve performance.
  40. /// </summary>
  41. public bool Strict { get; set; }
  42. /// <summary>
  43. /// The culture the engine runs on, defaults to current culture.
  44. /// </summary>
  45. public CultureInfo Culture { get; set; } = CultureInfo.CurrentCulture;
  46. /// <summary>
  47. /// The time zone the engine runs on, defaults to local.
  48. /// </summary>
  49. public TimeZoneInfo TimeZone { get; set; } = TimeZoneInfo.Local;
  50. /// <summary>
  51. /// Reference resolver allows customizing behavior for reference resolving. This can be useful in cases where
  52. /// you want to ignore long chain of property accesses that might throw if anything is null or undefined.
  53. /// An example of such is <code>var a = obj.field.subField.value</code>. Custom resolver could accept chain to return
  54. /// null/undefined on first occurrence.
  55. /// </summary>
  56. public IReferenceResolver ReferenceResolver { get; set; } = DefaultReferenceResolver.Instance;
  57. /// <summary>
  58. /// Called by the <see cref="Engine"/> instance that loads this <see cref="Options" />
  59. /// once it is loaded.
  60. /// </summary>
  61. internal void Apply(Engine engine)
  62. {
  63. foreach (var configuration in _configurations)
  64. {
  65. configuration?.Invoke(engine);
  66. }
  67. // add missing bits if needed
  68. if (Interop.Enabled)
  69. {
  70. engine.Realm.GlobalObject.SetProperty("System",
  71. new PropertyDescriptor(new NamespaceReference(engine, "System"), PropertyFlag.AllForbidden));
  72. engine.Realm.GlobalObject.SetProperty("importNamespace", new PropertyDescriptor(new ClrFunctionInstance(
  73. engine,
  74. "importNamespace",
  75. (thisObj, arguments) =>
  76. new NamespaceReference(engine, TypeConverter.ToString(arguments.At(0)))),
  77. PropertyFlag.AllForbidden));
  78. }
  79. if (Interop.ExtensionMethodTypes.Count > 0)
  80. {
  81. AttachExtensionMethodsToPrototypes(engine);
  82. }
  83. // ensure defaults
  84. engine.ClrTypeConverter ??= new DefaultTypeConverter(engine);
  85. }
  86. private static void AttachExtensionMethodsToPrototypes(Engine engine)
  87. {
  88. AttachExtensionMethodsToPrototype(engine, engine.Realm.Intrinsics.Array.PrototypeObject, typeof(Array));
  89. AttachExtensionMethodsToPrototype(engine, engine.Realm.Intrinsics.Boolean.PrototypeObject, typeof(bool));
  90. AttachExtensionMethodsToPrototype(engine, engine.Realm.Intrinsics.Date.PrototypeObject, typeof(DateTime));
  91. AttachExtensionMethodsToPrototype(engine, engine.Realm.Intrinsics.Number.PrototypeObject, typeof(double));
  92. AttachExtensionMethodsToPrototype(engine, engine.Realm.Intrinsics.Object.PrototypeObject, typeof(ExpandoObject));
  93. AttachExtensionMethodsToPrototype(engine, engine.Realm.Intrinsics.RegExp.PrototypeObject, typeof(System.Text.RegularExpressions.Regex));
  94. AttachExtensionMethodsToPrototype(engine, engine.Realm.Intrinsics.String.PrototypeObject, typeof(string));
  95. }
  96. private static void AttachExtensionMethodsToPrototype(Engine engine, ObjectInstance prototype, Type objectType)
  97. {
  98. if (!engine._extensionMethods.TryGetExtensionMethods(objectType, out var methods))
  99. {
  100. return;
  101. }
  102. foreach (var overloads in methods.GroupBy(x => x.Name))
  103. {
  104. PropertyDescriptor CreateMethodInstancePropertyDescriptor(ClrFunctionInstance? function)
  105. {
  106. var instance = function is null
  107. ? new MethodInfoFunctionInstance(engine, MethodDescriptor.Build(overloads.ToList()))
  108. : new MethodInfoFunctionInstance(engine, MethodDescriptor.Build(overloads.ToList()), function);
  109. return new PropertyDescriptor(instance, PropertyFlag.AllForbidden);
  110. }
  111. JsValue key = overloads.Key;
  112. PropertyDescriptor? descriptorWithFallback = null;
  113. PropertyDescriptor? descriptorWithoutFallback = null;
  114. if (prototype.HasOwnProperty(key) &&
  115. prototype.GetOwnProperty(key).Value is ClrFunctionInstance clrFunctionInstance)
  116. {
  117. descriptorWithFallback = CreateMethodInstancePropertyDescriptor(clrFunctionInstance);
  118. prototype.SetOwnProperty(key, descriptorWithFallback);
  119. }
  120. else
  121. {
  122. descriptorWithoutFallback = CreateMethodInstancePropertyDescriptor(null);
  123. prototype.SetOwnProperty(key, descriptorWithoutFallback);
  124. }
  125. // make sure we register both lower case and upper case
  126. if (char.IsUpper(overloads.Key[0]))
  127. {
  128. key = char.ToLower(overloads.Key[0]) + overloads.Key.Substring(1);
  129. if (prototype.HasOwnProperty(key) &&
  130. prototype.GetOwnProperty(key).Value is ClrFunctionInstance lowerclrFunctionInstance)
  131. {
  132. descriptorWithFallback ??= CreateMethodInstancePropertyDescriptor(lowerclrFunctionInstance);
  133. prototype.SetOwnProperty(key, descriptorWithFallback);
  134. }
  135. else
  136. {
  137. descriptorWithoutFallback ??= CreateMethodInstancePropertyDescriptor(null);
  138. prototype.SetOwnProperty(key, descriptorWithoutFallback);
  139. }
  140. }
  141. }
  142. }
  143. }
  144. public class DebuggerOptions
  145. {
  146. /// <summary>
  147. /// Whether debugger functionality is enabled, defaults to false.
  148. /// </summary>
  149. public bool Enabled { get; set; }
  150. /// <summary>
  151. /// Configures the statement handling strategy, defaults to Ignore.
  152. /// </summary>
  153. public DebuggerStatementHandling StatementHandling { get; set; } = DebuggerStatementHandling.Ignore;
  154. }
  155. public class InteropOptions
  156. {
  157. /// <summary>
  158. /// Whether accessing CLR and it's types and methods is allowed from JS code, defaults to false.
  159. /// </summary>
  160. public bool Enabled { get; set; }
  161. /// <summary>
  162. /// Whether writing to CLR objects is allowed (set properties), defaults to true.
  163. /// </summary>
  164. public bool AllowWrite { get; set; } = true;
  165. /// <summary>
  166. /// Whether operator overloading resolution is allowed, defaults to false.
  167. /// </summary>
  168. public bool OperatorOverloadingAllowed { get; set; }
  169. /// <summary>
  170. /// Types holding extension methods that should be considered when resolving methods.
  171. /// </summary>
  172. public List<Type> ExtensionMethodTypes { get; } = new();
  173. /// <summary>
  174. /// Object converters to try when build-in conversions.
  175. /// </summary>
  176. public List<IObjectConverter> ObjectConverters { get; } = new();
  177. /// <summary>
  178. /// If no known type could be guessed, objects are by default wrapped as an
  179. /// ObjectInstance using class ObjectWrapper. This function can be used to
  180. /// change the behavior.
  181. /// </summary>
  182. public WrapObjectDelegate WrapObjectHandler { get; set; } = (engine, target) => new ObjectWrapper(engine, target);
  183. /// <summary>
  184. ///
  185. /// </summary>
  186. public MemberAccessorDelegate MemberAccessor { get; set; } = (engine, target, member) => null;
  187. /// <summary>
  188. /// Exceptions that thrown from CLR code are converted to JavaScript errors and
  189. /// can be used in at try/catch statement. By default these exceptions are bubbled
  190. /// to the CLR host and interrupt the script execution. If handler returns true these exceptions are converted
  191. /// to JS errors that can be caught by the script.
  192. /// </summary>
  193. public ExceptionHandlerDelegate ExceptionHandler { get; set; } = exception => false;
  194. /// <summary>
  195. /// Assemblies to allow scripts to call CLR types directly like <example>System.IO.File</example>.
  196. /// </summary>
  197. public List<Assembly> AllowedAssemblies { get; set; } = new();
  198. /// <summary>
  199. /// Type and member resolving strategy, which allows filtering allowed members and configuring member
  200. /// name matching comparison.
  201. /// </summary>
  202. /// <remarks>
  203. /// As this object holds caching state same instance should be shared between engines, if possible.
  204. /// </remarks>
  205. public TypeResolver TypeResolver { get; set; } = TypeResolver.Default;
  206. }
  207. public class ConstraintOptions
  208. {
  209. /// <summary>
  210. /// Registered constraints.
  211. /// </summary>
  212. public List<IConstraint> Constraints { get; } = new();
  213. /// <summary>
  214. /// Maximum recursion depth allowed, defaults to -1 (no checks).
  215. /// </summary>
  216. public int MaxRecursionDepth { get; set; } = -1;
  217. /// <summary>
  218. /// Maximum time a Regex is allowed to run, defaults to 10 seconds.
  219. /// </summary>
  220. public TimeSpan RegexTimeout { get; set; } = TimeSpan.FromSeconds(10);
  221. /// <summary>
  222. /// The maximum size for JavaScript array, defaults to <see cref="uint.MaxValue"/>.
  223. /// </summary>
  224. public uint MaxArraySize { get; set; } = uint.MaxValue;
  225. }
  226. /// <summary>
  227. /// Host related customization, still work in progress.
  228. /// </summary>
  229. public class HostOptions
  230. {
  231. internal Func<Engine, Host> Factory { get; set; } = _ => new Host();
  232. }
  233. }