Skip to content

Commit 70b0a69

Browse files
committed
Enable Intercept function call
1 parent db73cad commit 70b0a69

File tree

4 files changed

+114
-3
lines changed

4 files changed

+114
-3
lines changed

Jint.Tests/Runtime/InteropTests.cs

+58
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
using System.Globalization;
33
using System.Reflection;
44
using System.Runtime.CompilerServices;
5+
using Esprima.Ast;
56
using Jint.Native;
67
using Jint.Native.Object;
78
using Jint.Native.Symbol;
@@ -3251,5 +3252,62 @@ public void CanPassDateTimeMinAndMaxViaInterop()
32513252
engine.Execute("capture(maxDate);");
32523253
Assert.Equal(DateTime.MaxValue, dt);
32533254
}
3255+
3256+
[Fact]
3257+
public void CanInterceptFunctionCallViaInterop()
3258+
{
3259+
var records = new Dictionary<Guid, FunctionCallRecord>();
3260+
var engine = new Engine(cfg =>
3261+
{
3262+
cfg.Interop.FunctionExecuting = ctx =>
3263+
{
3264+
var location = ((Node) ctx.Function).Location;
3265+
var record = new FunctionCallRecord
3266+
{
3267+
Name = ctx.Function.Id?.Name,
3268+
StartLine = location.Start.Line,
3269+
EndLine = location.End.Line,
3270+
Args = ctx.Arguments.Select(x => x.ToObject()).ToArray()
3271+
};
3272+
records.Add(ctx.UniqueId, record);
3273+
};
3274+
cfg.Interop.FunctionExecuted = ctx =>
3275+
{
3276+
if (records.TryGetValue(ctx.UniqueId, out var record) && ctx.Result is { Type: CompletionType.Return })
3277+
{
3278+
record.Result = ctx.Result.Value.Value.ToObject();
3279+
}
3280+
};
3281+
});
3282+
const string Js = @"
3283+
function main() {
3284+
return add(1, 2);
3285+
}
3286+
function add(a, b) {
3287+
return a + b;
3288+
}";
3289+
var script = Engine.PrepareScript(Js.TrimStart());
3290+
engine.Execute(script);
3291+
var result = engine.Invoke("main").ToObject();
3292+
Assert.Equal(3, Convert.ToInt32(result));
3293+
//var traces = records.Values.ToList();
3294+
//Assert.Equal(2, traces.Count);
3295+
//Assert.Equal("main", traces[0].Name);
3296+
//Assert.Equal(3, Convert.ToInt32(traces[0].Result));
3297+
//Assert.Equal(2, traces[1].Args.Length);
3298+
//Assert.Equal(1, Convert.ToInt32(traces[1].Args[0]));
3299+
//Assert.Equal(2, Convert.ToInt32(traces[1].Args[1]));
3300+
//Assert.Equal(1, traces[0].StartLine);
3301+
//Assert.Equal(3, traces[0].EndLine);
3302+
}
3303+
3304+
private class FunctionCallRecord
3305+
{
3306+
public string Name { get; set; }
3307+
public int StartLine { get; set; }
3308+
public int EndLine { get; set; }
3309+
public object[] Args { get; set; }
3310+
public object Result { get; set; }
3311+
}
32543312
}
32553313
}

Jint/Options.cs

+11-2
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,12 @@
55
using Jint.Native;
66
using Jint.Native.Object;
77
using Jint.Runtime;
8-
using Jint.Runtime.Interop;
8+
using Jint.Runtime.CallStack;
99
using Jint.Runtime.Debugger;
1010
using Jint.Runtime.Descriptors;
11+
using Jint.Runtime.Interop;
12+
using Jint.Runtime.Interop.Function;
1113
using Jint.Runtime.Modules;
12-
using Jint.Runtime.CallStack;
1314

1415
namespace Jint
1516
{
@@ -19,6 +20,10 @@ namespace Jint
1920

2021
public delegate bool ExceptionHandlerDelegate(Exception exception);
2122

23+
public delegate void FunctionExecutingDelegate(FunctionExecutionContext context);
24+
25+
public delegate void FunctionExecutedDelegate(FunctionExecutionContext context);
26+
2227
public class Options
2328
{
2429
private ITimeSystem? _timeSystem;
@@ -334,6 +339,10 @@ public class InteropOptions
334339
/// </summary>
335340
public Func<object, string>? SerializeToJson { get; set; }
336341

342+
public FunctionExecutingDelegate? FunctionExecuting { get; set; }
343+
344+
public FunctionExecutedDelegate? FunctionExecuted { get; set; }
345+
337346
/// <summary>
338347
/// What kind of date time should be produced when JavaScript date is converted to DateTime. If Local, uses <see cref="Options.TimeZone"/>.
339348
/// Defaults to <see cref="System.DateTimeKind.Utc"/>.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
using Esprima.Ast;
2+
using Jint.Native;
3+
using Jint.Native.Function;
4+
5+
#nullable disable
6+
7+
namespace Jint.Runtime.Interop.Function;
8+
9+
public class FunctionExecutionContext
10+
{
11+
public Guid UniqueId { get; set; }
12+
public Engine Engine { get; set; }
13+
public IFunction Function { get; set; }
14+
public FunctionInstance FunctionInstance { get; set; }
15+
public JsValue[] Arguments { get; set; }
16+
public Completion? Result { get; set; }
17+
}

Jint/Runtime/Interpreter/JintFunctionDefinition.cs

+28-1
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,19 @@ internal Completion EvaluateBody(EvaluationContext context, FunctionInstance fun
3838
{
3939
Completion result;
4040
ArgumentsInstance? argumentsInstance = null;
41+
//FunctionExecutionContext? executionContext = null;
42+
//if (context.Engine.Options.Interop.FunctionExecuting != null)
43+
//{
44+
// executionContext = new FunctionExecutionContext
45+
// {
46+
// Engine = context.Engine,
47+
// FunctionInstance = functionObject,
48+
// Function = Function,
49+
// Arguments = argumentsList,
50+
// UniqueId = Guid.NewGuid()
51+
// };
52+
// context.Engine.Options.Interop.FunctionExecuting(executionContext);
53+
//}
4154
if (Function.Expression)
4255
{
4356
// https://tc39.es/ecma262/#sec-runtime-semantics-evaluateconcisebody
@@ -90,6 +103,20 @@ internal Completion EvaluateBody(EvaluationContext context, FunctionInstance fun
90103
}
91104

92105
argumentsInstance?.FunctionWasCalled();
106+
107+
//if (context.Engine.Options.Interop.FunctionExecuted != null)
108+
//{
109+
// executionContext ??= new FunctionExecutionContext
110+
// {
111+
// Engine = context.Engine,
112+
// Function = Function,
113+
// FunctionInstance = functionObject,
114+
// Arguments = argumentsList,
115+
// UniqueId = Guid.NewGuid()
116+
// };
117+
// executionContext.Result = result;
118+
// context.Engine.Options.Interop.FunctionExecuted(executionContext);
119+
//}
93120
return result;
94121
}
95122

@@ -415,7 +442,7 @@ private static void ProcessParameters(
415442
out bool hasArguments)
416443
{
417444
hasArguments = false;
418-
state.IsSimpleParameterList = true;
445+
state.IsSimpleParameterList = true;
419446

420447
var countParameters = true;
421448
ref readonly var functionDeclarationParams = ref function.Params;

0 commit comments

Comments
 (0)