@nativescript/windows 0.1.0-alpha.60 → 0.1.0-alpha.62
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/framework/dotnet-bridge/Bridge.Dispatch.cs +3 -2
- package/framework/dotnet-bridge/Bridge.TaskHelper.cs +99 -13
- package/framework/dotnet-bridge/Bridge.cs +119 -0
- package/framework/libs/arm64/nativescript.dll +0 -0
- package/framework/libs/devtools/arm64/nativescript.dll +0 -0
- package/framework/libs/devtools/x64/nativescript.dll +0 -0
- package/framework/libs/x64/nativescript.dll +0 -0
- package/package.json +1 -1
|
@@ -256,8 +256,9 @@ public static partial class Bridge
|
|
|
256
256
|
{
|
|
257
257
|
if (value is Task || value is ValueTask) return true;
|
|
258
258
|
if (t.IsGenericType && t.GetGenericTypeDefinition() == typeof(ValueTask<>)) return true;
|
|
259
|
-
|
|
260
|
-
|
|
259
|
+
if ((t.FullName ?? "").StartsWith("Windows.Foundation.IAsync", StringComparison.Ordinal)) return true;
|
|
260
|
+
foreach (var iface in t.GetInterfaces())
|
|
261
|
+
if ((iface.FullName ?? "").StartsWith("Windows.Foundation.IAsync", StringComparison.Ordinal)) return true;
|
|
261
262
|
return t.GetMethod("GetAwaiter",
|
|
262
263
|
BindingFlags.Public | BindingFlags.Instance,
|
|
263
264
|
null, Type.EmptyTypes, null) is not null;
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
using System;
|
|
2
2
|
using System.Collections.Concurrent;
|
|
3
3
|
using System.Collections.Generic;
|
|
4
|
+
using System.Linq;
|
|
5
|
+
using System.Linq.Expressions;
|
|
4
6
|
using System.Reflection;
|
|
5
7
|
using System.Threading.Tasks;
|
|
6
8
|
|
|
@@ -10,10 +12,6 @@ public static partial class Bridge
|
|
|
10
12
|
{
|
|
11
13
|
private static readonly ConcurrentDictionary<Type, Func<object, Task>?> s_awaitableCache = new();
|
|
12
14
|
|
|
13
|
-
// Invoked when the binary instance call "__dotnet_await__(resolveId, rejectId)" arrives.
|
|
14
|
-
// Converts any awaitable handle (Task<T>, ValueTask<T>, IAsyncOperation<T>, …) into an
|
|
15
|
-
// async continuation that calls back into V8 via s_jsInvoker when it completes —
|
|
16
|
-
// without blocking the main thread.
|
|
17
15
|
internal static void ScheduleTaskContinuation(int handleId, int resolveId, int rejectId)
|
|
18
16
|
{
|
|
19
17
|
if (!s_handles.TryGetValue(handleId, out var obj) || obj is null)
|
|
@@ -22,7 +20,7 @@ public static partial class Bridge
|
|
|
22
20
|
var task = GetTaskForAwaitable(obj)
|
|
23
21
|
?? throw new InvalidOperationException(
|
|
24
22
|
$"Object of type {obj.GetType().FullName} is not awaitable. " +
|
|
25
|
-
$"Expected Task<T>, ValueTask<T>, or any type implementing GetAwaiter().");
|
|
23
|
+
$"Expected Task<T>, ValueTask<T>, IAsyncOperation<T>, or any type implementing GetAwaiter().");
|
|
26
24
|
|
|
27
25
|
task.ContinueWith(completed =>
|
|
28
26
|
{
|
|
@@ -50,10 +48,6 @@ public static partial class Bridge
|
|
|
50
48
|
}, TaskScheduler.Default);
|
|
51
49
|
}
|
|
52
50
|
|
|
53
|
-
// Converts any awaitable to a Task<object?> using the GetAwaiter() pattern via
|
|
54
|
-
// reflection. Handles Task<T>, ValueTask<T>, WinRT IAsyncOperation<T>, and any
|
|
55
|
-
// custom awaitable without requiring compile-time type knowledge. Results are
|
|
56
|
-
// cached per awaitable type so reflection only runs on the first encounter.
|
|
57
51
|
private static Task? GetTaskForAwaitable(object obj)
|
|
58
52
|
{
|
|
59
53
|
if (obj is Task t) return t;
|
|
@@ -63,12 +57,12 @@ public static partial class Bridge
|
|
|
63
57
|
if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(ValueTask<>))
|
|
64
58
|
return ValueTaskAsTaskCache.AsTask(type, obj);
|
|
65
59
|
|
|
60
|
+
var winrtTask = WinRtAsyncCache.TryGetTask(type, obj);
|
|
61
|
+
if (winrtTask != null) return winrtTask;
|
|
62
|
+
|
|
66
63
|
return s_awaitableCache.GetOrAdd(type, BuildAwaitableFactory)?.Invoke(obj);
|
|
67
64
|
}
|
|
68
65
|
|
|
69
|
-
// Builds a factory that wraps an arbitrary awaitable type in a
|
|
70
|
-
// TaskCompletionSource<object?>. Works for any type with GetAwaiter() returning
|
|
71
|
-
// an object that has IsCompleted, GetResult(), and OnCompleted/UnsafeOnCompleted.
|
|
72
66
|
private static Func<object, Task>? BuildAwaitableFactory(Type type)
|
|
73
67
|
{
|
|
74
68
|
var getAwaiter = type.GetMethod("GetAwaiter",
|
|
@@ -101,7 +95,7 @@ public static partial class Bridge
|
|
|
101
95
|
{
|
|
102
96
|
tcs.SetResult(getResult?.Invoke(awaiter, null));
|
|
103
97
|
}
|
|
104
|
-
catch (
|
|
98
|
+
catch (TargetInvocationException ex)
|
|
105
99
|
{
|
|
106
100
|
tcs.SetException(ex.InnerException ?? ex);
|
|
107
101
|
}
|
|
@@ -119,3 +113,95 @@ public static partial class Bridge
|
|
|
119
113
|
};
|
|
120
114
|
}
|
|
121
115
|
}
|
|
116
|
+
|
|
117
|
+
internal static class WinRtAsyncCache
|
|
118
|
+
{
|
|
119
|
+
private static readonly ConcurrentDictionary<Type, Func<object, Task>?> s_cache = new();
|
|
120
|
+
|
|
121
|
+
public static Task? TryGetTask(Type type, object obj)
|
|
122
|
+
=> s_cache.GetOrAdd(type, BuildFactory)?.Invoke(obj);
|
|
123
|
+
|
|
124
|
+
private static Func<object, Task>? BuildFactory(Type type)
|
|
125
|
+
{
|
|
126
|
+
foreach (var iface in type.GetInterfaces())
|
|
127
|
+
{
|
|
128
|
+
var ifn = iface.FullName ?? "";
|
|
129
|
+
if (!ifn.StartsWith("Windows.Foundation.IAsync", StringComparison.Ordinal)) continue;
|
|
130
|
+
|
|
131
|
+
var completedProp = iface.GetProperty("Completed");
|
|
132
|
+
if (completedProp?.GetSetMethod() is not { } setter) continue;
|
|
133
|
+
|
|
134
|
+
var delegateType = completedProp.PropertyType;
|
|
135
|
+
var delegateInvoke = delegateType.GetMethod("Invoke");
|
|
136
|
+
if (delegateInvoke == null) continue;
|
|
137
|
+
|
|
138
|
+
var dp = delegateInvoke.GetParameters()
|
|
139
|
+
.Select(p => Expression.Parameter(p.ParameterType))
|
|
140
|
+
.ToArray();
|
|
141
|
+
|
|
142
|
+
if (iface.IsGenericType)
|
|
143
|
+
{
|
|
144
|
+
var resultType = iface.GetGenericArguments()[0];
|
|
145
|
+
var getResults = iface.GetMethod("GetResults");
|
|
146
|
+
if (getResults == null) continue;
|
|
147
|
+
|
|
148
|
+
var tcsType = typeof(TaskCompletionSource<>).MakeGenericType(resultType);
|
|
149
|
+
var taskProp = tcsType.GetProperty("Task")!;
|
|
150
|
+
var completeM = typeof(WinRtAsyncCache)
|
|
151
|
+
.GetMethod(nameof(CompleteResult), BindingFlags.NonPublic | BindingFlags.Static)!
|
|
152
|
+
.MakeGenericMethod(resultType);
|
|
153
|
+
|
|
154
|
+
return obj =>
|
|
155
|
+
{
|
|
156
|
+
var tcs = Activator.CreateInstance(tcsType, TaskCreationOptions.RunContinuationsAsynchronously)!;
|
|
157
|
+
var body = Expression.Call(completeM,
|
|
158
|
+
Expression.Constant(tcs),
|
|
159
|
+
Expression.Convert(dp[0], typeof(object)),
|
|
160
|
+
Expression.Convert(dp[1], typeof(int)),
|
|
161
|
+
Expression.Constant(getResults));
|
|
162
|
+
setter.Invoke(obj, [Expression.Lambda(delegateType, body, dp).Compile()]);
|
|
163
|
+
return (Task)taskProp.GetValue(tcs)!;
|
|
164
|
+
};
|
|
165
|
+
}
|
|
166
|
+
else
|
|
167
|
+
{
|
|
168
|
+
var completeM = typeof(WinRtAsyncCache)
|
|
169
|
+
.GetMethod(nameof(CompleteAction), BindingFlags.NonPublic | BindingFlags.Static)!;
|
|
170
|
+
|
|
171
|
+
return obj =>
|
|
172
|
+
{
|
|
173
|
+
var tcs = new TaskCompletionSource<object?>(TaskCreationOptions.RunContinuationsAsynchronously);
|
|
174
|
+
var body = Expression.Call(completeM,
|
|
175
|
+
Expression.Constant(tcs),
|
|
176
|
+
Expression.Convert(dp[1], typeof(int)));
|
|
177
|
+
setter.Invoke(obj, [Expression.Lambda(delegateType, body, dp).Compile()]);
|
|
178
|
+
return tcs.Task;
|
|
179
|
+
};
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
return null;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
private static void CompleteResult<T>(object tcs, object sender, int status, MethodInfo getResults)
|
|
186
|
+
{
|
|
187
|
+
var typed = (TaskCompletionSource<T>)tcs;
|
|
188
|
+
try
|
|
189
|
+
{
|
|
190
|
+
if (status == 1)
|
|
191
|
+
typed.TrySetResult((T)getResults.Invoke(sender, null)!);
|
|
192
|
+
else if (status == 2)
|
|
193
|
+
typed.TrySetCanceled();
|
|
194
|
+
else
|
|
195
|
+
typed.TrySetException(new Exception($"WinRT async operation failed (status {status})"));
|
|
196
|
+
}
|
|
197
|
+
catch (TargetInvocationException ex) { typed.TrySetException(ex.InnerException ?? ex); }
|
|
198
|
+
catch (Exception ex) { typed.TrySetException(ex); }
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
private static void CompleteAction(TaskCompletionSource<object?> tcs, int status)
|
|
202
|
+
{
|
|
203
|
+
if (status == 1) tcs.TrySetResult(null);
|
|
204
|
+
else if (status == 2) tcs.TrySetCanceled();
|
|
205
|
+
else tcs.TrySetException(new Exception($"WinRT async action failed (status {status})"));
|
|
206
|
+
}
|
|
207
|
+
}
|
|
@@ -4,6 +4,8 @@ using System.Collections.Generic;
|
|
|
4
4
|
using System.IO;
|
|
5
5
|
using System.Linq;
|
|
6
6
|
using System.Reflection;
|
|
7
|
+
using System.Threading;
|
|
8
|
+
using System.Threading.Tasks;
|
|
7
9
|
using System.Runtime.InteropServices;
|
|
8
10
|
using System.Runtime.Loader;
|
|
9
11
|
using System.Text.Json;
|
|
@@ -159,6 +161,123 @@ public static partial class Bridge
|
|
|
159
161
|
}
|
|
160
162
|
}
|
|
161
163
|
|
|
164
|
+
// Helper invoked from JS runtime to schedule a previously-registered
|
|
165
|
+
// JS callback id to run on the application's UI thread. This method
|
|
166
|
+
// attempts several common dispatcher abstractions (SynchronizationContext,
|
|
167
|
+
// WPF Dispatcher, UWP/CoreDispatcher, WinUI DispatcherQueue) and falls
|
|
168
|
+
// back to running the callback on the current thread if none are found.
|
|
169
|
+
public static object? RunJsCallbackOnUIThread(int callbackId)
|
|
170
|
+
{
|
|
171
|
+
var action = new Action(() =>
|
|
172
|
+
{
|
|
173
|
+
unsafe
|
|
174
|
+
{
|
|
175
|
+
if (s_jsInvoker == null) return;
|
|
176
|
+
byte* respPtr = null;
|
|
177
|
+
int respLen = 0;
|
|
178
|
+
s_jsInvoker(callbackId, null, 0, &respPtr, &respLen);
|
|
179
|
+
if (respPtr != null && respLen > 0) Marshal.FreeHGlobal((IntPtr)respPtr);
|
|
180
|
+
}
|
|
181
|
+
});
|
|
182
|
+
|
|
183
|
+
// 1) SynchronizationContext (WinForms/WPF/UWP if present)
|
|
184
|
+
var sc = SynchronizationContext.Current;
|
|
185
|
+
if (sc != null)
|
|
186
|
+
{
|
|
187
|
+
var mre = new ManualResetEventSlim(false);
|
|
188
|
+
sc.Post(_ => { try { action(); } finally { mre.Set(); } }, null);
|
|
189
|
+
mre.Wait();
|
|
190
|
+
return null;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
// 2) WPF Dispatcher.CurrentDispatcher
|
|
194
|
+
try
|
|
195
|
+
{
|
|
196
|
+
var wpfDispatcherType = AppDomain.CurrentDomain.GetAssemblies()
|
|
197
|
+
.Select(a => a.GetType("System.Windows.Threading.Dispatcher"))
|
|
198
|
+
.FirstOrDefault(t => t != null);
|
|
199
|
+
if (wpfDispatcherType != null)
|
|
200
|
+
{
|
|
201
|
+
var currentDispatcherProp = wpfDispatcherType.GetProperty("CurrentDispatcher", BindingFlags.Public | BindingFlags.Static);
|
|
202
|
+
var dispatcher = currentDispatcherProp?.GetValue(null);
|
|
203
|
+
if (dispatcher != null)
|
|
204
|
+
{
|
|
205
|
+
var beginInvoke = dispatcher.GetType().GetMethod("BeginInvoke", new[] { typeof(Action) })
|
|
206
|
+
?? dispatcher.GetType().GetMethods().FirstOrDefault(m => m.Name == "BeginInvoke" && m.GetParameters().Length == 1);
|
|
207
|
+
if (beginInvoke != null)
|
|
208
|
+
{
|
|
209
|
+
var mre = new ManualResetEventSlim(false);
|
|
210
|
+
beginInvoke.Invoke(dispatcher, new object[] { new Action(() => { try { action(); } finally { mre.Set(); } }) });
|
|
211
|
+
mre.Wait();
|
|
212
|
+
return null;
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
catch { }
|
|
218
|
+
|
|
219
|
+
// 3) Try CoreDispatcher (UWP) via reflection
|
|
220
|
+
try
|
|
221
|
+
{
|
|
222
|
+
foreach (var asm in AppDomain.CurrentDomain.GetAssemblies())
|
|
223
|
+
{
|
|
224
|
+
var coreAppType = asm.GetType("Windows.ApplicationModel.Core.CoreApplication");
|
|
225
|
+
if (coreAppType == null) continue;
|
|
226
|
+
var mainViewProp = coreAppType.GetProperty("MainView", BindingFlags.Public | BindingFlags.Static);
|
|
227
|
+
var mainView = mainViewProp?.GetValue(null);
|
|
228
|
+
if (mainView == null) continue;
|
|
229
|
+
var dispatcherProp = mainView.GetType().GetProperty("Dispatcher", BindingFlags.Public | BindingFlags.Instance);
|
|
230
|
+
var dispatcher = dispatcherProp?.GetValue(mainView);
|
|
231
|
+
if (dispatcher == null) continue;
|
|
232
|
+
var runAsyncMethods = dispatcher.GetType().GetMethods().Where(m => m.Name == "RunAsync");
|
|
233
|
+
foreach (var m in runAsyncMethods)
|
|
234
|
+
{
|
|
235
|
+
var parameters = m.GetParameters();
|
|
236
|
+
if (parameters.Length != 2) continue;
|
|
237
|
+
var enumType = parameters[0].ParameterType;
|
|
238
|
+
object priority = enumType.IsEnum ? Enum.ToObject(enumType, 0) : Activator.CreateInstance(enumType)!;
|
|
239
|
+
var handlerType = parameters[1].ParameterType;
|
|
240
|
+
var d = Delegate.CreateDelegate(handlerType, action.Target, action.Method);
|
|
241
|
+
var result = m.Invoke(dispatcher, new object[] { priority, d });
|
|
242
|
+
var asTask = result?.GetType().GetMethod("AsTask");
|
|
243
|
+
if (asTask != null)
|
|
244
|
+
{
|
|
245
|
+
var t = (Task)asTask.Invoke(result, null)!;
|
|
246
|
+
t.GetAwaiter().GetResult();
|
|
247
|
+
}
|
|
248
|
+
return null;
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
catch { }
|
|
253
|
+
|
|
254
|
+
// 4) Try Microsoft.UI.Dispatching.DispatcherQueue
|
|
255
|
+
try
|
|
256
|
+
{
|
|
257
|
+
foreach (var asm in AppDomain.CurrentDomain.GetAssemblies())
|
|
258
|
+
{
|
|
259
|
+
var dqType = asm.GetType("Microsoft.UI.Dispatching.DispatcherQueue");
|
|
260
|
+
if (dqType == null) continue;
|
|
261
|
+
var getForCurrent = dqType.GetMethod("GetForCurrentThread", BindingFlags.Public | BindingFlags.Static);
|
|
262
|
+
var dq = getForCurrent?.Invoke(null, null);
|
|
263
|
+
if (dq == null) continue;
|
|
264
|
+
var tryEnqueueMethods = dq.GetType().GetMethods().Where(m => m.Name == "TryEnqueue" && m.GetParameters().Length == 1);
|
|
265
|
+
foreach (var m in tryEnqueueMethods)
|
|
266
|
+
{
|
|
267
|
+
var paramType = m.GetParameters()[0].ParameterType;
|
|
268
|
+
var d = Delegate.CreateDelegate(paramType, action.Target, action.Method);
|
|
269
|
+
var ok = m.Invoke(dq, new object[] { d });
|
|
270
|
+
return null;
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
catch { }
|
|
275
|
+
|
|
276
|
+
// Fallback: run directly on current thread.
|
|
277
|
+
action();
|
|
278
|
+
return null;
|
|
279
|
+
}
|
|
280
|
+
|
|
162
281
|
private static Assembly? OnAssemblyResolve(object? sender, ResolveEventArgs args)
|
|
163
282
|
{
|
|
164
283
|
try
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|