diff --git a/Jint/Options.cs b/Jint/Options.cs index 9d5a1a79b6..b2968ef76a 100644 --- a/Jint/Options.cs +++ b/Jint/Options.cs @@ -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 { @@ -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; @@ -333,6 +339,10 @@ public class InteropOptions /// public Func? SerializeToJson { get; set; } + public FunctionExecutingDelegate? FunctionExecuting { get; set; } + + public FunctionExecutedDelegate? FunctionExecuted { get; set; } + /// /// What kind of date time should be produced when JavaScript date is converted to DateTime. If Local, uses . /// Defaults to . diff --git a/Jint/Runtime/Interop/Function/FunctionExecutionContext.cs b/Jint/Runtime/Interop/Function/FunctionExecutionContext.cs new file mode 100644 index 0000000000..abc773c5ae --- /dev/null +++ b/Jint/Runtime/Interop/Function/FunctionExecutionContext.cs @@ -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; } +} diff --git a/Jint/Runtime/Interpreter/JintFunctionDefinition.cs b/Jint/Runtime/Interpreter/JintFunctionDefinition.cs index f68388eae6..b1224588e3 100644 --- a/Jint/Runtime/Interpreter/JintFunctionDefinition.cs +++ b/Jint/Runtime/Interpreter/JintFunctionDefinition.cs @@ -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; @@ -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; } @@ -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; }