Browse Source

Declare the object converter's types and convert enums natively

An IObjectConverter registered without declaring the CLR types it handles
can be handed anything, so the engine has to assume every wrapped CLR
member read might reach it and disables the compiled interop member-read
lane engine-wide. JintObjectConverter handles a closed set, so declare it:
matching is by assignability, which keeps IUser covering every
implementation. The converter is still offered every value - the
declaration only lets the engine keep the fast lane for members whose
declared type could never produce a handled value, and it errs towards
claiming (a member typed `object` is always claimed).

The Enum branch is dropped in favour of
Options.Interop.EnumConversion = EnumConversionMode.String, which Jint
documents as the member name "as produced by object.ToString()", including
the comma-separated combination for a [Flags] value and the numeric value
rendered as a string for a value with no name - verbatim what the branch
did. The write direction keeps accepting both the name and the number.
Handling enums natively rather than through the converter also keeps one
more declared type off the list, so more members stay on the fast lane.

Pinned by Should_convert_enum_to_name, Should_convert_flags_enum_to_names
and Should_convert_enum_member_of_wrapped_object_to_name, which pass before
and after.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016uV6H9cTntzsoKiaJRBn4f
pull/1326/head
Marko Lahma 1 month ago
parent
commit
3a2460aed9
  1. 29
      backend/src/Squidex.Domain.Apps.Core.Operations/Scripting/Internal/JintObjectConverter.cs
  2. 3
      backend/src/Squidex.Domain.Apps.Core.Operations/Scripting/JintScriptEngine.cs

29
backend/src/Squidex.Domain.Apps.Core.Operations/Scripting/Internal/JintObjectConverter.cs

@ -21,6 +21,29 @@ namespace Squidex.Domain.Apps.Core.Scripting.Internal;
public sealed class JintObjectConverter : IObjectConverter public sealed class JintObjectConverter : IObjectConverter
{ {
/// <summary>
/// The CLR types this converter answers for, declared at registration so the engine can keep its
/// compiled interop member-read lane for members whose declared type can never reach this converter.
/// </summary>
/// <remarks>
/// Matching is by assignability, so <see cref="IUser"/> covers every implementation. Registering the
/// converter without this set makes every wrapped CLR member read in the engine take the slow lane.
/// Enums are not listed: they are handled natively through
/// <see cref="Options.InteropOptions.EnumConversion"/>.
/// </remarks>
public static readonly Type[] HandledTypes =
[
typeof(IUser),
typeof(ClaimsPrincipal),
typeof(ScriptVars),
typeof(JsonValue),
typeof(DomainId),
typeof(Guid),
typeof(Instant),
typeof(Status),
typeof(ContentData),
];
public static readonly JintObjectConverter Instance = new JintObjectConverter(); public static readonly JintObjectConverter Instance = new JintObjectConverter();
private JintObjectConverter() private JintObjectConverter()
@ -31,12 +54,6 @@ public sealed class JintObjectConverter : IObjectConverter
{ {
result = null!; result = null!;
if (value is Enum)
{
result = value.ToString();
return true;
}
switch (value) switch (value)
{ {
case IUser user: case IUser user:

3
backend/src/Squidex.Domain.Apps.Core.Operations/Scripting/JintScriptEngine.cs

@ -143,8 +143,9 @@ public sealed class JintScriptEngine(IMemoryCache cache, IOptions<JintScriptOpti
var engine = new Engine(engineOptions => var engine = new Engine(engineOptions =>
{ {
engineOptions.AddObjectConverter(JintObjectConverter.Instance); engineOptions.AddObjectConverter(JintObjectConverter.Instance, JintObjectConverter.HandledTypes);
engineOptions.AllowClrWrite(!options.Readonly); engineOptions.AllowClrWrite(!options.Readonly);
engineOptions.Interop.EnumConversion = EnumConversionMode.String;
engineOptions.SetTypeConverter(engine => new CustomClrConverter(engine)); engineOptions.SetTypeConverter(engine => new CustomClrConverter(engine));
engineOptions.SetReferencesResolver(NullPropagation.Instance, NullPropagation.Interests); engineOptions.SetReferencesResolver(NullPropagation.Instance, NullPropagation.Interests);
engineOptions.Strict(); engineOptions.Strict();

Loading…
Cancel
Save