Skip to content

Commit

Permalink
Enable Intercept function call
Browse files Browse the repository at this point in the history
  • Loading branch information
libaowei committed Jul 1, 2023
1 parent dae3ec5 commit f177eb1
Show file tree
Hide file tree
Showing 3 changed files with 108 additions and 38 deletions.
10 changes: 10 additions & 0 deletions Jint/Options.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,9 @@
using Jint.Runtime.Interop;
using Jint.Runtime.Debugger;
using Jint.Runtime.Descriptors;
using Jint.Runtime.Interop.Function;
using Jint.Runtime.Modules;
using Esprima.Ast;

namespace Jint
{
Expand All @@ -18,6 +20,10 @@ namespace Jint

public delegate bool ExceptionHandlerDelegate(Exception exception);

public delegate void FunctionExecutingDelegate(FunctionExecutionContext context);

public delegate void FunctionExecutedDelegate(FunctionExecutionContext context);

public class Options
{
private ITimeSystem? _timeSystem;
Expand Down Expand Up @@ -333,6 +339,10 @@ public class InteropOptions
/// </summary>
public Func<object, string>? SerializeToJson { get; set; }

public FunctionExecutingDelegate? FunctionExecuting { get; set; }

public FunctionExecutedDelegate? FunctionExecuted { get; set; }

/// <summary>
/// What kind of date time should be produced when JavaScript date is converted to DateTime. If Local, uses <see cref="Options.TimeZone"/>.
/// Defaults to <see cref="System.DateTimeKind.Utc"/>.
Expand Down
14 changes: 14 additions & 0 deletions Jint/Runtime/Interop/Function/FunctionExecutionContext.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
using Esprima.Ast;
using Jint.Native;
#nullable disable

namespace Jint.Runtime.Interop.Function;

public class FunctionExecutionContext
{
public Guid UniqueId { get; set; }
public IFunction Function { get; set; }
public JsValue[] Arguments { get; set; }
public JsValue ReturnValue { get; set; }
public Exception Exception { get; set; }
}
122 changes: 84 additions & 38 deletions Jint/Runtime/Interpreter/JintFunctionDefinition.cs
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
using System.Runtime.CompilerServices;
using Esprima;
using Esprima.Ast;
using Jint.Native;
using Jint.Native.Argument;
using Jint.Native.Function;
using Jint.Native.Promise;
using Jint.Runtime.Environments;
using Jint.Runtime.Interop.Function;
using Jint.Runtime.Interpreter.Expressions;

namespace Jint.Runtime.Interpreter;
Expand All @@ -18,11 +20,13 @@ internal sealed class JintFunctionDefinition
private JintStatementList? _bodyStatementList;

public readonly string? Name;
public readonly Location Location;
public readonly IFunction Function;

public JintFunctionDefinition(IFunction function)
{
Function = function;
Location = ((SyntaxElement) Function).Location;
Name = !string.IsNullOrEmpty(function.Id?.Name) ? function.Id!.Name : null;
}

Expand All @@ -38,58 +42,100 @@ internal Completion EvaluateBody(EvaluationContext context, FunctionInstance fun
{
Completion result;
ArgumentsInstance? argumentsInstance = null;
if (Function.Expression)
//context.Engine.Options.Interop.
FunctionExecutionContext? executionContext = null;
context.Engine.Options.Interop.FunctionExecuting?.Invoke(executionContext = new FunctionExecutionContext
{
// https://tc39.es/ecma262/#sec-runtime-semantics-evaluateconcisebody
_bodyExpression ??= JintExpression.Build((Expression) Function.Body);
if (Function.Async)
Function = Function,
Arguments = argumentsList,
UniqueId = Guid.NewGuid()
});
try
{
if (Function.Expression)
{
var promiseCapability = PromiseConstructor.NewPromiseCapability(context.Engine, context.Engine.Realm.Intrinsics.Promise);
AsyncFunctionStart(context, promiseCapability, context =>
// https://tc39.es/ecma262/#sec-runtime-semantics-evaluateconcisebody
_bodyExpression ??= JintExpression.Build((Expression) Function.Body);
if (Function.Async)
{
context.Engine.FunctionDeclarationInstantiation(functionObject, argumentsList);
return new Completion(CompletionType.Return, _bodyExpression.GetValue(context), _bodyExpression._expression);
});
result = new Completion(CompletionType.Return, promiseCapability.PromiseInstance, Function.Body);
var promiseCapability =
PromiseConstructor.NewPromiseCapability(context.Engine,
context.Engine.Realm.Intrinsics.Promise);
AsyncFunctionStart(context, promiseCapability, context =>
{
context.Engine.FunctionDeclarationInstantiation(functionObject, argumentsList);
return new Completion(CompletionType.Return, _bodyExpression.GetValue(context),
_bodyExpression._expression);
});
result = new Completion(CompletionType.Return, promiseCapability.PromiseInstance, Function.Body);
}
else
{
argumentsInstance = context.Engine.FunctionDeclarationInstantiation(functionObject, argumentsList);
var jsValue = _bodyExpression.GetValue(context).Clone();
result = new Completion(CompletionType.Return, jsValue, Function.Body);
}
}
else
else if (Function.Generator)
{
// TODO generators
// result = EvaluateGeneratorBody(functionObject, argumentsList);
argumentsInstance = context.Engine.FunctionDeclarationInstantiation(functionObject, argumentsList);
var jsValue = _bodyExpression.GetValue(context).Clone();
result = new Completion(CompletionType.Return, jsValue, Function.Body);
_bodyStatementList ??= new JintStatementList(Function);
result = _bodyStatementList.Execute(context);
}
else
{
if (Function.Async)
{
var promiseCapability =
PromiseConstructor.NewPromiseCapability(context.Engine,
context.Engine.Realm.Intrinsics.Promise);
_bodyStatementList ??= new JintStatementList(Function);
AsyncFunctionStart(context, promiseCapability, context =>
{
context.Engine.FunctionDeclarationInstantiation(functionObject, argumentsList);
return _bodyStatementList.Execute(context);
});
result = new Completion(CompletionType.Return, promiseCapability.PromiseInstance, Function.Body);
}
else
{
// https://tc39.es/ecma262/#sec-runtime-semantics-evaluatefunctionbody
argumentsInstance = context.Engine.FunctionDeclarationInstantiation(functionObject, argumentsList);
_bodyStatementList ??= new JintStatementList(Function);
result = _bodyStatementList.Execute(context);
}
}

argumentsInstance?.FunctionWasCalled();
}
else if (Function.Generator)
{
// TODO generators
// result = EvaluateGeneratorBody(functionObject, argumentsList);
argumentsInstance = context.Engine.FunctionDeclarationInstantiation(functionObject, argumentsList);
_bodyStatementList ??= new JintStatementList(Function);
result = _bodyStatementList.Execute(context);
}
else
catch(Exception ex)
{
if (Function.Async)
if (context.Engine.Options.Interop.FunctionExecuted is not null)
{
var promiseCapability = PromiseConstructor.NewPromiseCapability(context.Engine, context.Engine.Realm.Intrinsics.Promise);
_bodyStatementList ??= new JintStatementList(Function);
AsyncFunctionStart(context, promiseCapability, context =>
executionContext ??= new FunctionExecutionContext()
{
context.Engine.FunctionDeclarationInstantiation(functionObject, argumentsList);
return _bodyStatementList.Execute(context);
});
result = new Completion(CompletionType.Return, promiseCapability.PromiseInstance, Function.Body);
Function = Function,
Arguments = argumentsList,
UniqueId = Guid.NewGuid()
};
executionContext.Exception = ex;
context.Engine.Options.Interop.FunctionExecuted(executionContext);
}
else
throw;
}
if (context.Engine.Options.Interop.FunctionExecuted is not null)
{
executionContext ??= new FunctionExecutionContext()
{
// https://tc39.es/ecma262/#sec-runtime-semantics-evaluatefunctionbody
argumentsInstance = context.Engine.FunctionDeclarationInstantiation(functionObject, argumentsList);
_bodyStatementList ??= new JintStatementList(Function);
result = _bodyStatementList.Execute(context);
}
Function = Function,
Arguments = argumentsList,
UniqueId = Guid.NewGuid()
};
executionContext.ReturnValue = result.Value;
context.Engine.Options.Interop.FunctionExecuted(executionContext);
}

argumentsInstance?.FunctionWasCalled();
return result;
}

Expand Down

0 comments on commit f177eb1

Please sign in to comment.