Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions AGENTS.md

Large diffs are not rendered by default.

63 changes: 63 additions & 0 deletions Jint.Tests.PublicInterface/HostInterpreterCacheDiscardTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
#nullable enable

using System;
using System.Runtime.CompilerServices;

namespace Jint.Tests.PublicInterface;

/// <summary>
/// <c>engine.Advanced.DiscardInterpreterCaches()</c> from the embedder's side: the engine-pool shape — capture
/// once, evaluate a cached prepared script with per-request host globals, restore — with the discard added on
/// return, which is the only way a pooled engine stops keeping the last request's services alive through a
/// warmed call site.
/// </summary>
[CollectionDefinition(nameof(HostInterpreterCacheDiscardTests), DisableParallelization = true)]
[Collection(nameof(HostInterpreterCacheDiscardTests))]
public class HostInterpreterCacheDiscardTests
{
private sealed class RequestServices
{
public string Name => "services";
}

[Fact]
public void APooledEngineReleasesTheLastRequestsServicesAfterADiscard()
{
var engine = new Engine();
var snapshot = engine.Advanced.CaptureGlobalSnapshot();
var script = Engine.PrepareScript("describe(ctx.Name)");

RunRequest(engine, snapshot, script);
var services = RunRequest(engine, snapshot, script);

engine.Advanced.DiscardInterpreterCaches();
GC.Collect(2, GCCollectionMode.Forced, blocking: true);
GC.WaitForPendingFinalizers();
GC.Collect(2, GCCollectionMode.Forced, blocking: true);

services.IsAlive.Should().BeFalse("nothing in a restored, discarded engine may reach a finished request's services");

// the engine stays fully usable and computes the same thing
RunRequest(engine, snapshot, script);
}

[MethodImpl(MethodImplOptions.NoInlining)]
private static WeakReference RunRequest(Engine engine, GlobalSnapshot snapshot, Prepared<Script> script)
{
var services = new RequestServices();
engine.SetValue("ctx", services);
engine.SetValue("describe", new Func<string, string>(value => value + ":" + services.Name));
engine.Evaluate(script).AsString().Should().Be("services:services");
engine.Advanced.RestoreGlobalSnapshot(snapshot);
return new WeakReference(services);
}

[Fact]
public void DiscardingWhileAnEvaluationIsInProgressIsRefused()
{
var engine = new Engine();
engine.SetValue("discard", new Action(() => engine.Advanced.DiscardInterpreterCaches()));

Assert.Throws<InvalidOperationException>(() => engine.Execute("discard()"));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,7 @@ namespace Jint
public Jint.GlobalSnapshot CaptureGlobalSnapshot() { }
public Jint.Native.Object.ObjectInstance CreateProxy(Jint.Native.Object.ObjectInstance target, Jint.Runtime.Interop.ProxyHandler handler) { }
public Jint.Runtime.Interop.RevocableProxy CreateRevocableProxy(Jint.Native.Object.ObjectInstance target, Jint.Runtime.Interop.ProxyHandler handler) { }
public void DiscardInterpreterCaches() { }
public Jint.InteropConversionDiagnostics GetInteropConversionDiagnostics() { }
public Jint.ObjectRepresentation GetObjectRepresentation(Jint.Native.Object.ObjectInstance value) { }
public Jint.Native.Object.PropertyAccessSemantics GetPropertyAccessSemantics(Jint.Native.Object.ObjectInstance value) { }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,7 @@ namespace Jint
public Jint.GlobalSnapshot CaptureGlobalSnapshot() { }
public Jint.Native.Object.ObjectInstance CreateProxy(Jint.Native.Object.ObjectInstance target, Jint.Runtime.Interop.ProxyHandler handler) { }
public Jint.Runtime.Interop.RevocableProxy CreateRevocableProxy(Jint.Native.Object.ObjectInstance target, Jint.Runtime.Interop.ProxyHandler handler) { }
public void DiscardInterpreterCaches() { }
public Jint.InteropConversionDiagnostics GetInteropConversionDiagnostics() { }
public Jint.ObjectRepresentation GetObjectRepresentation(Jint.Native.Object.ObjectInstance value) { }
public Jint.Native.Object.PropertyAccessSemantics GetPropertyAccessSemantics(Jint.Native.Object.ObjectInstance value) { }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,7 @@ namespace Jint
public Jint.GlobalSnapshot CaptureGlobalSnapshot() { }
public Jint.Native.Object.ObjectInstance CreateProxy(Jint.Native.Object.ObjectInstance target, Jint.Runtime.Interop.ProxyHandler handler) { }
public Jint.Runtime.Interop.RevocableProxy CreateRevocableProxy(Jint.Native.Object.ObjectInstance target, Jint.Runtime.Interop.ProxyHandler handler) { }
public void DiscardInterpreterCaches() { }
public Jint.InteropConversionDiagnostics GetInteropConversionDiagnostics() { }
public Jint.ObjectRepresentation GetObjectRepresentation(Jint.Native.Object.ObjectInstance value) { }
public Jint.Native.Object.PropertyAccessSemantics GetPropertyAccessSemantics(Jint.Native.Object.ObjectInstance value) { }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,7 @@ namespace Jint
public Jint.GlobalSnapshot CaptureGlobalSnapshot() { }
public Jint.Native.Object.ObjectInstance CreateProxy(Jint.Native.Object.ObjectInstance target, Jint.Runtime.Interop.ProxyHandler handler) { }
public Jint.Runtime.Interop.RevocableProxy CreateRevocableProxy(Jint.Native.Object.ObjectInstance target, Jint.Runtime.Interop.ProxyHandler handler) { }
public void DiscardInterpreterCaches() { }
public Jint.InteropConversionDiagnostics GetInteropConversionDiagnostics() { }
public Jint.ObjectRepresentation GetObjectRepresentation(Jint.Native.Object.ObjectInstance value) { }
public Jint.Native.Object.PropertyAccessSemantics GetPropertyAccessSemantics(Jint.Native.Object.ObjectInstance value) { }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,7 @@ namespace Jint
public Jint.GlobalSnapshot CaptureGlobalSnapshot() { }
public Jint.Native.Object.ObjectInstance CreateProxy(Jint.Native.Object.ObjectInstance target, Jint.Runtime.Interop.ProxyHandler handler) { }
public Jint.Runtime.Interop.RevocableProxy CreateRevocableProxy(Jint.Native.Object.ObjectInstance target, Jint.Runtime.Interop.ProxyHandler handler) { }
public void DiscardInterpreterCaches() { }
public Jint.InteropConversionDiagnostics GetInteropConversionDiagnostics() { }
public Jint.ObjectRepresentation GetObjectRepresentation(Jint.Native.Object.ObjectInstance value) { }
public Jint.Native.Object.PropertyAccessSemantics GetPropertyAccessSemantics(Jint.Native.Object.ObjectInstance value) { }
Expand Down
Loading
Loading