diff --git a/backend/src/Squidex.Domain.Apps.Core.Operations/Scripting/JintScriptEngine.cs b/backend/src/Squidex.Domain.Apps.Core.Operations/Scripting/JintScriptEngine.cs index 8ad87b9a6..428182cf7 100644 --- a/backend/src/Squidex.Domain.Apps.Core.Operations/Scripting/JintScriptEngine.cs +++ b/backend/src/Squidex.Domain.Apps.Core.Operations/Scripting/JintScriptEngine.cs @@ -146,7 +146,7 @@ public sealed class JintScriptEngine(IMemoryCache cache, IOptions new CustomClrConverter(engine)); - engineOptions.SetReferencesResolver(NullPropagation.Instance); + engineOptions.SetReferencesResolver(NullPropagation.Instance, NullPropagation.Interests); engineOptions.Strict(); if (!Debugger.IsAttached) diff --git a/backend/src/Squidex.Domain.Apps.Core.Operations/Scripting/NullPropagation.cs b/backend/src/Squidex.Domain.Apps.Core.Operations/Scripting/NullPropagation.cs index ee7684dc6..8e8d0c724 100644 --- a/backend/src/Squidex.Domain.Apps.Core.Operations/Scripting/NullPropagation.cs +++ b/backend/src/Squidex.Domain.Apps.Core.Operations/Scripting/NullPropagation.cs @@ -14,8 +14,38 @@ namespace Squidex.Domain.Apps.Core.Scripting; public sealed class NullPropagation : IReferenceResolver { + /// + /// The situations this resolver actually answers, declared so the engine keeps the fast paths for + /// everything else. + /// + /// + /// Deliberately omitted are and + /// , the pair that disables the + /// non-computed member-read inline caches, the dense-array indexed-read lane and the member-call callee + /// lane engine-wide. declines every base that is not null or + /// undefined, so those are situations where the engine consulting this resolver could never change the + /// result. Interests are a subscription filter and not a promise: a situation not subscribed to behaves + /// exactly as if no resolver were registered. + /// + public const ReferenceResolverInterests Interests = + ReferenceResolverInterests.NullishPropertyBase | + ReferenceResolverInterests.UnresolvableReference | + ReferenceResolverInterests.NonCallableCallee; + public static readonly NullPropagation Instance = new NullPropagation(); + /// + /// Answers a read of a name that resolves to no binding, so that an unknown name does not throw a + /// reference error. + /// + /// + /// Passing the reference base straight through hands script the engine's internal sentinel for the + /// unresolvable state - a reading [[Unresolvable]] - rather than + /// undefined, which is documented on and + /// on . That is what scripts have always seen here, so it is kept and pinned + /// by a test; assigning instead would be the tidier behaviour but a + /// breaking change for existing tenant scripts. + /// public bool TryUnresolvableReference(Engine engine, Reference reference, out JsValue value) { value = reference.Base;