diff --git a/Jint.Tests.PublicInterface/RavenApiUsageTests.cs b/Jint.Tests.PublicInterface/RavenApiUsageTests.cs index 2b82de326..69627337b 100644 --- a/Jint.Tests.PublicInterface/RavenApiUsageTests.cs +++ b/Jint.Tests.PublicInterface/RavenApiUsageTests.cs @@ -201,6 +201,36 @@ public void CanResetCallStack() var engine = new Engine(); engine.Advanced.ResetCallStack(); } + + [Fact] + public void CanUseCustomReferenceResolver() + { + var engine = new Engine(options => + { + options.ReferenceResolver = new MyReferenceResolver(); + }); + + engine + .Execute(""" + function output(doc) { + var rows123 = [{}]; + var test = null; + return { + Rows : [{}].map(row=>({row:row, myRows:test.filter(x=>x) + })).map(__rvn4=>({ + Custom:__rvn4.myRows[0].Custom, + Custom2:__rvn4.myRows + })) + }; + } + """); + + var result = engine.Evaluate("output()"); + + var rows = result.AsObject()["Rows"]; + var custom = rows.AsArray()[0].AsObject()["Custom"]; + Assert.Equal(JsValue.Null, custom); + } } file sealed class CustomString : JsString @@ -287,3 +317,35 @@ public override string ToString() return "null"; } } + +file sealed class MyReferenceResolver : IReferenceResolver +{ + public bool TryUnresolvableReference(Engine engine, Reference reference, out JsValue value) + { + JsValue referencedName = reference.ReferencedName; + + if (referencedName.IsString()) + { + value = reference.IsPropertyReference ? JsValue.Undefined : JsValue.Null; + return true; + } + + throw new InvalidOperationException(); + } + + public bool TryPropertyReference(Engine engine, Reference reference, ref JsValue value) + { + return value.IsNull() || value.IsUndefined(); + } + + public bool TryGetCallable(Engine engine, object callee, out JsValue value) + { + value = new ClrFunction(engine, "function", static (_, _) => JsValue.Undefined); + return true; + } + + public bool CheckCoercible(JsValue value) + { + return true; + } +} diff --git a/Jint/Engine.cs b/Jint/Engine.cs index d023d6471..d33d13043 100644 --- a/Jint/Engine.cs +++ b/Jint/Engine.cs @@ -1,5 +1,5 @@ using System.Diagnostics; -using System.Collections.Concurrent; +using System.Collections.Concurrent; using System.Diagnostics.CodeAnalysis; using System.Runtime.CompilerServices; using Jint.Native; @@ -50,7 +50,10 @@ public sealed partial class Engine : IDisposable internal readonly Constraint[] _constraints; internal readonly bool _isDebugMode; internal readonly bool _isStrict; + + private bool _customResolver; internal readonly IReferenceResolver _referenceResolver; + internal readonly ReferencePool _referencePool; internal readonly ArgumentsInstancePool _argumentsInstancePool; internal readonly JsValueArrayPool _jsValueArrayPool; @@ -135,6 +138,7 @@ private Engine(Options? options, Action? configure) _constraints = Options.Constraints.Constraints.ToArray(); _referenceResolver = Options.ReferenceResolver; + _customResolver = !ReferenceEquals(_referenceResolver, DefaultReferenceResolver.Instance); _referencePool = new ReferencePool(); _argumentsInstancePool = new ArgumentsInstancePool(this); @@ -564,18 +568,25 @@ internal JsValue GetValue(Reference reference, bool returnReferenceToPool) if (baseValue.IsUndefined()) { - if (_referenceResolver.TryUnresolvableReference(this, reference, out var val)) + if (_customResolver) { - return val; + reference.EvaluateAndCachePropertyKey(); + if (_referenceResolver.TryUnresolvableReference(this, reference, out var val)) + { + return val; + } } ExceptionHelper.ThrowReferenceError(Realm, reference); } - if ((baseValue._type & InternalTypes.ObjectEnvironmentRecord) == InternalTypes.Empty - && _referenceResolver.TryPropertyReference(this, reference, ref baseValue)) + if ((baseValue._type & InternalTypes.ObjectEnvironmentRecord) == InternalTypes.Empty && _customResolver) { - return baseValue; + reference.EvaluateAndCachePropertyKey(); + if (_referenceResolver.TryPropertyReference(this, reference, ref baseValue)) + { + return baseValue; + } } if (reference.IsPropertyReference)