Skip to content

Commit

Permalink
Introduce EvaluationContext parameter to evaluation process
Browse files Browse the repository at this point in the history
  • Loading branch information
lahma committed Oct 20, 2021
1 parent c98cf5c commit 72d40e3
Show file tree
Hide file tree
Showing 88 changed files with 1,098 additions and 918 deletions.
4 changes: 3 additions & 1 deletion Jint.Tests/Runtime/Debugger/DebugHandlerTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
using Jint.Runtime.Debugger;
using Xunit;

#pragma warning disable 618

namespace Jint.Tests.Runtime.Debugger
{
public class DebugHandlerTests
Expand Down Expand Up @@ -33,7 +35,7 @@ public void AvoidsPauseRecursion()
var obj = info.CurrentScopeChain.Global.GetBindingValue("obj") as ObjectInstance;
var prop = obj.GetOwnProperty("name");
// This is where reentrance would occur:
var value = prop.Get.Invoke();
var value = prop.Get.Invoke(engine);
didPropertyAccess = true;
}
return StepMode.Into;
Expand Down
5 changes: 2 additions & 3 deletions Jint.Tests/Runtime/Debugger/ScopeTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -194,14 +194,13 @@ public void BlockScopedConstIsVisibleInsideBlock()
string script = @"
'dummy statement';
{
debugger; // const is initialized (as undefined) at beginning of block
const blockConst = 'block';
debugger; // const isn't initialized until declaration
}";

TestHelpers.TestAtBreak(script, info =>
{
var value = AssertOnlyScopeContains(info.CurrentScopeChain, "blockConst", DebugScopeType.Block);
Assert.Equal(JsUndefined.Undefined, value);
AssertOnlyScopeContains(info.CurrentScopeChain, "blockConst", DebugScopeType.Block);
});
}

Expand Down
26 changes: 9 additions & 17 deletions Jint.Tests/Runtime/EngineTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
using System.IO;
using System.Reflection;
using Esprima;
using Esprima.Ast;
using Jint.Native;
using Jint.Native.Array;
using Jint.Native.Object;
Expand All @@ -12,6 +11,8 @@
using Xunit;
using Xunit.Abstractions;

#pragma warning disable 618

namespace Jint.Tests.Runtime
{
public class EngineTests : IDisposable
Expand Down Expand Up @@ -816,7 +817,7 @@ public void ShouldInvokeAFunctionValue()

var add = _engine.GetValue("add");

Assert.Equal(3, add.Invoke(1, 2));
Assert.Equal(3, add.Invoke(_engine, 1, 2));
}

[Fact]
Expand All @@ -828,7 +829,7 @@ public void ShouldAllowInvokeAFunctionValueWithNullValueAsArgument()

var add = _engine.GetValue("get");
string str = null;
Assert.Equal(Native.JsValue.Null, add.Invoke(str));
Assert.Equal(Native.JsValue.Null, add.Invoke(_engine, str));
}


Expand All @@ -841,7 +842,8 @@ public void ShouldNotInvokeNonFunctionValue()

var x = _engine.GetValue("x");

Assert.Throws<TypeErrorException>(() => x.Invoke(1, 2));
var exception = Assert.Throws<JavaScriptException>(() => x.Invoke(_engine, 1, 2));
Assert.Equal("Can only invoke functions", exception.Message);
}

[Fact]
Expand Down Expand Up @@ -1022,16 +1024,6 @@ public void ShouldBeCultureInvariant()
Assert.Equal(3.3d, result);
}

[Fact]
public void ShouldGetTheLastSyntaxNode()
{
var engine = new Engine();
engine.Evaluate("1.2");

var result = engine.GetLastSyntaxNode();
Assert.Equal(Nodes.Literal, result.Type);
}

[Fact]
public void ShouldGetParseErrorLocation()
{
Expand Down Expand Up @@ -2509,7 +2501,7 @@ public void ShouldReturnCorrectConcatenatedStrings()
}");

var concat = _engine.GetValue("concat");
var result = concat.Invoke("concat", "well", "done").ToObject() as string;
var result = concat.Invoke(_engine, "concat", "well", "done").ToObject() as string;
Assert.Equal("concatwelldone", result);
}

Expand Down Expand Up @@ -2643,10 +2635,10 @@ public void ShouldSupportDefaultsInFunctionParameters()
");

var function = _engine.GetValue("f");
var result = function.Invoke(3).ToString();
var result = function.Invoke(_engine, 3).ToString();
Assert.Equal("15", result);

result = function.Invoke(3, JsValue.Undefined).ToString();
result = function.Invoke(_engine, 3, JsValue.Undefined).ToString();
Assert.Equal("15", result);
}

Expand Down
2 changes: 1 addition & 1 deletion Jint/Collections/ListDictionary.cs
Original file line number Diff line number Diff line change
Expand Up @@ -233,7 +233,7 @@ public void Dispose()
object IEnumerator.Current => _current;
}

internal class DictionaryNode
internal sealed class DictionaryNode
{
public Key Key;
public TValue Value;
Expand Down
92 changes: 66 additions & 26 deletions Jint/Engine.cs
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ public partial class Engine

private readonly ExecutionContextStack _executionContexts;
private JsValue _completionValue = JsValue.Undefined;
internal Node _lastSyntaxNode;
internal EvaluationContext _activeEvaluationContext;

private readonly EventLoop _eventLoop = new();

Expand Down Expand Up @@ -266,26 +266,34 @@ public Engine Execute(string source, ParserOptions parserOptions)

public Engine Execute(Script script)
{
var ownsContext = _activeEvaluationContext is null;
_activeEvaluationContext ??= new EvaluationContext(this);

ResetConstraints();
ResetLastStatement();

using (new StrictModeScope(_isStrict || script.Strict))
{
GlobalDeclarationInstantiation(
script,
Realm.GlobalEnv);

var list = new JintStatementList(this, null, script.Body);
var list = new JintStatementList(null, script.Body);

Completion result;
try
{
result = list.Execute();
result = list.Execute(_activeEvaluationContext);
}
catch
{
// unhandled exception
ResetCallStack();

if (ownsContext)
{
_activeEvaluationContext = null;
}

throw;
}

Expand All @@ -294,13 +302,25 @@ public Engine Execute(Script script)
var ex = new JavaScriptException(result.GetValueOrDefault())
.SetCallstack(this, result.Location);
ResetCallStack();

if (ownsContext)
{
_activeEvaluationContext = null;
}

throw ex;
}

// TODO what about callstack and thrown exceptions?
RunAvailableContinuations(_eventLoop);

_completionValue = result.GetValueOrDefault();

if (ownsContext)
{
_activeEvaluationContext = null;
}

}

return this;
Expand Down Expand Up @@ -359,11 +379,6 @@ private static void RunAvailableContinuations(EventLoop loop)
}
}

private void ResetLastStatement()
{
_lastSyntaxNode = null;
}

internal void RunBeforeExecuteStatementChecks(Statement statement)
{
// Avoid allocating the enumerator because we run this loop very often.
Expand Down Expand Up @@ -619,36 +634,61 @@ public JsValue Invoke(JsValue value, object thisObj, object[] arguments)
ExceptionHelper.ThrowTypeError(Realm, "Can only invoke functions");
}

var items = _jsValueArrayPool.RentArray(arguments.Length);
for (int i = 0; i < arguments.Length; ++i)
{
items[i] = JsValue.FromObject(this, arguments[i]);
}
var ownsContext = _activeEvaluationContext is null;
_activeEvaluationContext ??= new EvaluationContext(this);

var result = callable.Call(JsValue.FromObject(this, thisObj), items);
_jsValueArrayPool.ReturnArray(items);
try
{
var items = _jsValueArrayPool.RentArray(arguments.Length);
for (var i = 0; i < arguments.Length; ++i)
{
items[i] = JsValue.FromObject(this, arguments[i]);
}

return result;
var result = callable.Call(JsValue.FromObject(this, thisObj), items);
_jsValueArrayPool.ReturnArray(items);
return result;
}
finally
{
if (ownsContext)
{
_activeEvaluationContext = null;
}
}
}

/// <summary>
/// https://tc39.es/ecma262/#sec-invoke
/// </summary>
internal JsValue Invoke(JsValue v, JsValue p, JsValue[] arguments)
{
var func = GetV(v, p);
var callable = func as ICallable;
if (callable is null)
var ownsContext = _activeEvaluationContext is null;
_activeEvaluationContext ??= new EvaluationContext(this);
try
{
var func = GetV(v, p);
var callable = func as ICallable;
if (callable is null)
{
ExceptionHelper.ThrowTypeErrorNoEngine("Can only invoke functions");
}

return callable.Call(v, arguments);
}
finally
{
ExceptionHelper.ThrowTypeErrorNoEngine("Can only invoke functions");
if (ownsContext)
{
_activeEvaluationContext = null;
}
}
return callable.Call(v, arguments);
}

/// <summary>
/// https://tc39.es/ecma262/#sec-getv
/// </summary>
internal JsValue GetV(JsValue v, JsValue p)
private JsValue GetV(JsValue v, JsValue p)
{
var o = TypeConverter.ToObject(Realm, v);
return o.Get(p);
Expand All @@ -668,7 +708,7 @@ public JsValue GetValue(string propertyName)
/// </summary>
internal Node GetLastSyntaxNode()
{
return _lastSyntaxNode;
return _activeEvaluationContext?.LastSyntaxNode;
}

/// <summary>
Expand All @@ -693,7 +733,7 @@ internal Reference ResolveBinding(string name, EnvironmentRecord env = null)
return GetIdentifierReference(env, name, StrictModeScope.IsStrictModeCode);
}

private Reference GetIdentifierReference(EnvironmentRecord env, string name, bool strict)
private static Reference GetIdentifierReference(EnvironmentRecord env, string name, bool strict)
{
if (env is null)
{
Expand Down Expand Up @@ -896,7 +936,7 @@ internal JsValue ResolveThisBinding()
if (!canInitializeParametersOnDeclaration)
{
// slower set
envRec.AddFunctionParameters(func.Function, argumentsList);
envRec.AddFunctionParameters(_activeEvaluationContext, func.Function, argumentsList);
}

// Let iteratorRecord be CreateListIteratorRecord(argumentsList).
Expand Down
7 changes: 4 additions & 3 deletions Jint/EsprimaExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,8 @@ or Nodes.AssignmentExpression
or Nodes.UnaryExpression
or Nodes.MemberExpression)
{
propertyKey = TypeConverter.ToPropertyKey(JintExpression.Build(engine, expression).GetValue());
var context = engine._activeEvaluationContext;
propertyKey = TypeConverter.ToPropertyKey(JintExpression.Build(engine, expression).GetValue(context));
return true;
}

Expand Down Expand Up @@ -217,7 +218,7 @@ internal static void GetBoundNames(this Node? parameter, List<string> target)

internal static void BindingInitialization(
this Expression? expression,
Engine engine,
EvaluationContext context,
JsValue value,
EnvironmentRecord env)
{
Expand All @@ -229,7 +230,7 @@ internal static void GetBoundNames(this Node? parameter, List<string> target)
else if (expression is BindingPattern bindingPattern)
{
BindingPatternAssignmentExpression.ProcessPatterns(
engine,
context,
bindingPattern,
value,
env);
Expand Down
13 changes: 8 additions & 5 deletions Jint/Native/Function/ClassDefinition.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,12 @@
using Jint.Runtime;
using Jint.Runtime.Descriptors;
using Jint.Runtime.Environments;
using Jint.Runtime.Interpreter;
using Jint.Runtime.Interpreter.Expressions;

namespace Jint.Native.Function
{
internal class ClassDefinition
internal sealed class ClassDefinition
{
private static readonly MethodDefinition _superConstructor;
private static readonly MethodDefinition _emptyConstructor;
Expand Down Expand Up @@ -47,11 +48,13 @@ static MethodDefinition CreateConstructorMethodDefinition(string source)
/// https://tc39.es/ecma262/#sec-runtime-semantics-classdefinitionevaluation
/// </summary>
public ScriptFunctionInstance BuildConstructor(
Engine engine,
EvaluationContext context,
EnvironmentRecord env)
{
// A class definition is always strict mode code.
using var _ = (new StrictModeScope(true, true));
using var _ = new StrictModeScope(true, true);

var engine = context.Engine;

var classScope = JintEnvironment.NewDeclarativeEnvironment(engine, env);

Expand All @@ -70,15 +73,15 @@ static MethodDefinition CreateConstructorMethodDefinition(string source)
else
{
engine.UpdateLexicalEnvironment(classScope);
var superclass = JintExpression.Build(engine, _superClass).GetValue();
var superclass = JintExpression.Build(engine, _superClass).GetValue(context);
engine.UpdateLexicalEnvironment(env);

if (superclass.IsNull())
{
protoParent = null;
constructorParent = engine.Realm.Intrinsics.Function.PrototypeObject;
}
else if (!superclass.IsConstructor)
else if (!superclass!.IsConstructor)
{
ExceptionHelper.ThrowTypeError(engine.Realm, "super class is not a constructor");
}
Expand Down

0 comments on commit 72d40e3

Please sign in to comment.