-
-
Notifications
You must be signed in to change notification settings - Fork 462
/
Copy pathBinding.cs
152 lines (136 loc) · 5.39 KB
/
Binding.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
using System;
using System.Collections.Generic;
using System.Reflection;
using System.Threading.Tasks;
using Microsoft.Extensions.Logging;
using PuppeteerSharp.Cdp.Messaging;
namespace PuppeteerSharp
{
internal class Binding
{
public Binding(string name, Delegate fn)
{
Name = name;
Function = fn;
}
public string Name { get; }
public Delegate Function { get; }
internal async Task RunAsync(
ExecutionContext context,
int id,
object[] args,
bool isTrivial)
{
var garbage = new List<Task>();
try
{
if (!isTrivial)
{
// Getting non-trivial arguments.
var handles = await context.EvaluateFunctionHandleAsync(
@"(name, seq) => {
return globalThis[name].args.get(seq);
}",
Name,
id).ConfigureAwait(false);
try
{
var properties = await handles.GetPropertiesAsync().ConfigureAwait(false);
foreach (var kv in properties)
{
var handle = kv.Value;
// This is not straight-forward since some arguments can stringify, but
// aren't plain objects so add subtypes when the use-case arises.
if (int.TryParse(kv.Key, out var index) && args.Length > index)
{
switch (handle.RemoteObject.Subtype)
{
case RemoteObjectSubtype.Node:
args[index] = handle;
break;
default:
garbage.Add(handle.DisposeAsync().AsTask());
break;
}
}
else
{
garbage.Add(handle.DisposeAsync().AsTask());
}
}
}
finally
{
await handles.DisposeAsync().ConfigureAwait(false);
}
}
const string taskResultPropertyName = "Result";
var result = await BindingUtils.ExecuteBindingAsync(Function, args).ConfigureAwait(false);
switch (result)
{
case Task<object> taskObjectResult:
result = await taskObjectResult.ConfigureAwait(false);
break;
case Task taskResult:
{
await taskResult.ConfigureAwait(false);
if (taskResult.GetType().IsGenericType)
{
// the task is already awaited and therefore the call to property Result will not deadlock
result = taskResult.GetType().GetProperty(taskResultPropertyName)?.GetValue(taskResult);
}
break;
}
}
await context.EvaluateFunctionAsync(
@"(name, seq, result) => {
const callbacks = globalThis[name].callbacks;
callbacks.get(seq).resolve(result);
callbacks.delete(seq);
}",
Name,
id,
result).ConfigureAwait(false);
foreach (var arg in args)
{
if (arg is JSHandle handle)
{
garbage.Add(handle.DisposeAsync().AsTask());
}
}
}
catch (Exception ex)
{
// Get the exception thrown by the function
if (ex is TargetInvocationException && ex.InnerException != null)
{
ex = ex.InnerException;
}
try
{
await context.EvaluateFunctionAsync(
@"(name, seq, message, stack) => {
const error = new Error(message);
error.stack = stack;
const callbacks = globalThis[name].callbacks;
callbacks.get(seq).reject(error);
callbacks.delete(seq);
}",
Name,
id,
ex.Message,
ex.StackTrace).ConfigureAwait(false);
}
catch (Exception cleanupException)
{
var logger = context.Client.Connection.LoggerFactory.CreateLogger<Binding>();
logger.LogError(cleanupException.ToString());
}
}
finally
{
await Task.WhenAll(garbage.ToArray()).ConfigureAwait(false);
}
}
}
}