Skip to content

Commit

Permalink
sebastienros#1573 - implemented convenience methods for shadowrealm a…
Browse files Browse the repository at this point in the history
…nd benchmarks
  • Loading branch information
gentledepp authored and lahma committed Oct 16, 2023
1 parent 89a1b61 commit 8b5600b
Show file tree
Hide file tree
Showing 3 changed files with 191 additions and 0 deletions.
61 changes: 61 additions & 0 deletions Jint.Benchmark/ShadowRealmBenchmark.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
using BenchmarkDotNet.Attributes;
using Esprima.Ast;

namespace Jint.Benchmark;

[MemoryDiagnoser]
[BenchmarkCategory("ShadowRealm")]
public class ShadowRealmBenchmark
{
private const string sourceCode = @"
(function (){return 'some string'})();
";

private Engine engine;
private Script parsedScript;

[GlobalSetup]
public void Setup()
{
engine = new Engine();
parsedScript = Engine.PrepareScript(sourceCode);
}

[Benchmark]
public void ReusingEngine()
{
engine.Evaluate(sourceCode);
}

[Benchmark]
public void NewEngineInstance()
{
new Engine().Evaluate(sourceCode);
}

[Benchmark]
public void ShadowRealm()
{
var shadowRealm = engine.Realm.Intrinsics.ShadowRealm.Construct();
shadowRealm.Evaluate(sourceCode);
}

[Benchmark]
public void ReusingEngine_ParsedScript()
{
engine.Evaluate(parsedScript);
}

[Benchmark]
public void NewEngineInstance_ParsedScript()
{
new Engine().Evaluate(parsedScript);
}

[Benchmark]
public void ShadowRealm_ParsedScript()
{
var shadowRealm = engine.Realm.Intrinsics.ShadowRealm.Construct();
shadowRealm.Evaluate(parsedScript);
}
}
65 changes: 65 additions & 0 deletions Jint.Tests.PublicInterface/ShadowRealmTests.cs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
using Jint.Native;
using Jint.Native.Object;

namespace Jint.Tests.PublicInterface;
Expand Down Expand Up @@ -28,6 +29,70 @@ public void CanUseViaEngineMethods()
Assert.Equal("John Doe", result);
}

[Fact]
public void MultipleShadowRealmsDoNotInterfere()
{
var engine = new Engine(options => options.EnableModules(GetBasePath()));
engine.SetValue("message", "world");
engine.Evaluate("function hello() {return message}");

Assert.Equal("world",engine.Evaluate("hello();"));

var shadowRealm = engine.Realm.Intrinsics.ShadowRealm.Construct();
shadowRealm.SetValue("message", "realm 1");
shadowRealm.Evaluate("function hello() {return message}");

var shadowRealm2 = engine.Realm.Intrinsics.ShadowRealm.Construct();
shadowRealm2.SetValue("message", "realm 2");
shadowRealm2.Evaluate("function hello() {return message}");

// Act & Assert
Assert.Equal("realm 1", shadowRealm.Evaluate("hello();"));
Assert.Equal("realm 2", shadowRealm2.Evaluate("hello();"));
}

[Fact]
public void MultipleShadowRealm_SettingGlobalVariable_DoNotInterfere()
{
var engine = new Engine(options => options.EnableModules(GetBasePath()));
engine.SetValue("message", "hello ");
engine.Evaluate("(function hello() {message += \"engine\"})();");

var shadowRealm = engine.Realm.Intrinsics.ShadowRealm.Construct();
shadowRealm.SetValue("message", "hello ");
shadowRealm.Evaluate("(function hello() {message += \"realm 1\"})();");

var shadowRealm2 = engine.Realm.Intrinsics.ShadowRealm.Construct();
shadowRealm2.SetValue("message", "hello ");
shadowRealm2.Evaluate("(function hello() {message += \"realm 2\"})();");

// Act & Assert
Assert.Equal("hello engine", engine.Evaluate("message"));
Assert.Equal("hello realm 1", shadowRealm.Evaluate("message"));
Assert.Equal("hello realm 2", shadowRealm2.Evaluate("message"));
}

[Fact]
public void CanReuseScriptWithShadowRealm()
{
var engine = new Engine(options => options.EnableModules(GetBasePath()));
engine.SetValue("message", "engine");

var shadowRealm = engine.Realm.Intrinsics.ShadowRealm.Construct();
shadowRealm.SetValue("message", "realm 1");

var shadowRealm2 = engine.Realm.Intrinsics.ShadowRealm.Construct();
shadowRealm2.SetValue("message", "realm 2");

var parser = new Esprima.JavaScriptParser();
var script = parser.ParseScript("(function hello() {return \"hello \" + message})();");

// Act & Assert
Assert.Equal("hello engine", engine.Evaluate(script));
Assert.Equal("hello realm 1", shadowRealm.Evaluate(script));
Assert.Equal("hello realm 2", shadowRealm2.Evaluate(script));
}

private static string GetBasePath()
{
var assemblyDirectory = new DirectoryInfo(AppDomain.CurrentDomain.RelativeSearchPath ?? AppDomain.CurrentDomain.BaseDirectory);
Expand Down
65 changes: 65 additions & 0 deletions Jint/Native/ShadowRealm/ShadowRealm.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,9 @@
using Jint.Native.Object;
using Jint.Native.Promise;
using Jint.Runtime;
using Jint.Runtime.Descriptors;
using Jint.Runtime.Environments;
using Jint.Runtime.Interop;
using Jint.Runtime.Interpreter;
using Jint.Runtime.Interpreter.Statements;
using Jint.Runtime.Modules;
Expand Down Expand Up @@ -38,13 +40,60 @@ public JsValue Evaluate(string sourceText)
return PerformShadowRealmEval(sourceText, callerRealm);
}

public JsValue Evaluate(Script script)
{
var callerRealm = _engine.Realm;
return PerformShadowRealmEval(script, callerRealm);
}

public JsValue ImportValue(string specifier, string exportName)
{
var callerRealm = _engine.Realm;
var value = ShadowRealmImportValue(specifier, exportName, callerRealm);
_engine.RunAvailableContinuations();
return value;
}
public ShadowRealm SetValue(string name, Delegate value)
{
_shadowRealm.GlobalObject.FastSetProperty(name, new PropertyDescriptor(new DelegateWrapper(_engine, value), true, false, true));
return this;
}

public ShadowRealm SetValue(string name, string value)
{
return SetValue(name, JsString.Create(value));
}

public ShadowRealm SetValue(string name, double value)
{
return SetValue(name, JsNumber.Create(value));
}

public ShadowRealm SetValue(string name, int value)
{
return SetValue(name, JsNumber.Create(value));
}

public ShadowRealm SetValue(string name, bool value)
{
return SetValue(name, value ? JsBoolean.True : JsBoolean.False);
}

public ShadowRealm SetValue(string name, JsValue value)
{
_shadowRealm.GlobalObject.Set(name, value);
return this;
}

public ShadowRealm SetValue(string name, object obj)
{
var value = obj is Type t
? TypeReference.CreateTypeReference(_engine, t)
: JsValue.FromObject(_engine, obj);

return SetValue(name, value);
}


/// <summary>
/// https://tc39.es/proposal-shadowrealm/#sec-performshadowrealmeval
Expand Down Expand Up @@ -74,6 +123,22 @@ internal JsValue PerformShadowRealmEval(string sourceText, Realm callerRealm)
return default;
}

return PerformShadowRealmEvalInternal(script, callerRealm);
}

internal JsValue PerformShadowRealmEval(Script script, Realm callerRealm)
{
var evalRealm = _shadowRealm;

_engine._host.EnsureCanCompileStrings(callerRealm, evalRealm);

return PerformShadowRealmEvalInternal(script, callerRealm);
}

internal JsValue PerformShadowRealmEvalInternal(Script script, Realm callerRealm)
{
var evalRealm = _shadowRealm;

ref readonly var body = ref script.Body;
if (body.Count == 0)
{
Expand Down

0 comments on commit 8b5600b

Please sign in to comment.