@nativescript/windows 0.1.0-alpha.8 → 0.1.0-alpha.81
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/build.ps1 +44 -2
- package/framework/__PROJECT_NAME__/App.xaml.cs +67 -22
- package/framework/__PROJECT_NAME__/Assets/SplashScreen.png +0 -0
- package/framework/__PROJECT_NAME__/Assets/Square150x150Logo.png +0 -0
- package/framework/__PROJECT_NAME__/Assets/Square44x44Logo.png +0 -0
- package/framework/__PROJECT_NAME__/Assets/StoreLogo.png +0 -0
- package/framework/__PROJECT_NAME__/Assets/Wide310x150Logo.png +0 -0
- package/framework/__PROJECT_NAME__/CrashDiagnostics.cs +262 -66
- package/framework/__PROJECT_NAME__/Directory.Build.props +15 -0
- package/framework/__PROJECT_NAME__/Directory.Build.targets +153 -0
- package/framework/__PROJECT_NAME__/Package.appxmanifest +2 -5
- package/framework/__PROJECT_NAME__/RuntimeHost.cs +97 -10
- package/framework/__PROJECT_NAME__/__PROJECT_NAME__.csproj +90 -17
- package/framework/dotnet-bridge/BinaryProtocol.cs +145 -0
- package/framework/dotnet-bridge/Bridge.BinaryDispatch.cs +216 -0
- package/framework/dotnet-bridge/Bridge.Dispatch.cs +468 -0
- package/framework/dotnet-bridge/Bridge.JsDelegate.cs +96 -0
- package/framework/dotnet-bridge/Bridge.TaskHelper.cs +207 -0
- package/framework/dotnet-bridge/Bridge.cs +402 -263
- package/framework/dotnet-bridge/DispatchTypes.cs +317 -0
- package/framework/dotnet-bridge/DotNetBridge.csproj +7 -1
- 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/framework/tools/dotnet-tool-arm64.exe +0 -0
- package/framework/tools/dotnet-tool-x64.exe +0 -0
- package/framework/tools/dotnet-tool.exe +0 -0
- package/package.json +19 -19
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
using System;
|
|
2
|
+
using System.Buffers;
|
|
3
|
+
using System.Linq;
|
|
4
|
+
using System.Linq.Expressions;
|
|
5
|
+
using System.Reflection;
|
|
6
|
+
using System.Runtime.InteropServices;
|
|
7
|
+
using System.Threading;
|
|
8
|
+
|
|
9
|
+
namespace NativeScriptBridge;
|
|
10
|
+
|
|
11
|
+
public static partial class Bridge
|
|
12
|
+
{
|
|
13
|
+
// opcode 0x09: given a delegate type name (or "" for System.Action) and a
|
|
14
|
+
// JS callback id, compile a .NET delegate that serialises its parameters as
|
|
15
|
+
// binary and calls back into V8 via the s_jsInvoker function pointer.
|
|
16
|
+
|
|
17
|
+
private static DispatchResult CreateJsDelegate(string typeName, int callbackId)
|
|
18
|
+
{
|
|
19
|
+
var delegateType = string.IsNullOrEmpty(typeName)
|
|
20
|
+
? typeof(Action)
|
|
21
|
+
: ResolveType(null, typeName)
|
|
22
|
+
?? throw new TypeLoadException($"Delegate type not found: {typeName}");
|
|
23
|
+
|
|
24
|
+
var invokeMethod = delegateType.GetMethod("Invoke")
|
|
25
|
+
?? throw new MissingMethodException($"No Invoke method on {delegateType}");
|
|
26
|
+
|
|
27
|
+
var parameters = invokeMethod.GetParameters();
|
|
28
|
+
var returnType = invokeMethod.ReturnType;
|
|
29
|
+
|
|
30
|
+
var paramExprs = parameters
|
|
31
|
+
.Select((p, i) => Expression.Parameter(p.ParameterType, p.Name ?? $"p{i}"))
|
|
32
|
+
.ToArray();
|
|
33
|
+
|
|
34
|
+
// object?[] args = new object?[] { (object?)p0, (object?)p1, … }
|
|
35
|
+
var objArgs = paramExprs.Select(p => (Expression)Expression.Convert(p, typeof(object)));
|
|
36
|
+
var argsArray = Expression.NewArrayInit(typeof(object), objArgs);
|
|
37
|
+
|
|
38
|
+
var callMethod = typeof(Bridge).GetMethod(
|
|
39
|
+
nameof(CallJsCallback),
|
|
40
|
+
BindingFlags.Static | BindingFlags.NonPublic)!;
|
|
41
|
+
|
|
42
|
+
var callExpr = Expression.Call(callMethod, Expression.Constant(callbackId), argsArray);
|
|
43
|
+
|
|
44
|
+
Expression body = returnType == typeof(void)
|
|
45
|
+
? callExpr
|
|
46
|
+
: (Expression)Expression.Block(returnType, callExpr, Expression.Default(returnType));
|
|
47
|
+
|
|
48
|
+
var del = Expression.Lambda(delegateType, body, paramExprs).Compile();
|
|
49
|
+
return Box(del);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
internal static unsafe void CallJsCallback(int id, object?[] args)
|
|
53
|
+
{
|
|
54
|
+
if (s_jsInvoker == null) return;
|
|
55
|
+
|
|
56
|
+
var buf = new ArrayBufferWriter<byte>(64);
|
|
57
|
+
var w = new BinWriter(buf);
|
|
58
|
+
w.WriteByte((byte)Math.Min(args.Length, 255));
|
|
59
|
+
foreach (var arg in args)
|
|
60
|
+
WriteCallbackArg(ref w, arg);
|
|
61
|
+
|
|
62
|
+
var bytes = buf.WrittenSpan;
|
|
63
|
+
byte* respPtr = null;
|
|
64
|
+
int respLen = 0;
|
|
65
|
+
fixed (byte* p = bytes)
|
|
66
|
+
s_jsInvoker(id, p, bytes.Length, &respPtr, &respLen);
|
|
67
|
+
|
|
68
|
+
// Response is only set for non-void delegates; free it when present.
|
|
69
|
+
if (respPtr != null && respLen > 0)
|
|
70
|
+
Marshal.FreeHGlobal((IntPtr)respPtr);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// Serialises a single delegate argument in response-binary tag format so
|
|
74
|
+
// the Rust side can reuse its existing bin_read_value parser.
|
|
75
|
+
private static void WriteCallbackArg(ref BinWriter w, object? arg)
|
|
76
|
+
{
|
|
77
|
+
if (arg is null) { w.WriteByte(0x00); return; }
|
|
78
|
+
if (arg is bool b) { w.WriteByte(b ? (byte)0x02 : (byte)0x01); return; }
|
|
79
|
+
if (arg is int i) { w.WriteByte(0x03); w.WriteI32(i); return; }
|
|
80
|
+
if (arg is uint u) { w.WriteByte(0x03); w.WriteI32((int)u); return; }
|
|
81
|
+
if (arg is long l) { w.WriteByte(0x04); w.WriteF64((double)l); return; }
|
|
82
|
+
if (arg is float f) { w.WriteByte(0x04); w.WriteF64((double)f); return; }
|
|
83
|
+
if (arg is double d){ w.WriteByte(0x04); w.WriteF64(d); return; }
|
|
84
|
+
if (arg is string s){ w.WriteByte(0x05); w.WriteString32(s); return; }
|
|
85
|
+
|
|
86
|
+
// Object: box in the handle map and send as a handle reference.
|
|
87
|
+
// The JS side receives {__handle, __type} which can be turned into a
|
|
88
|
+
// proxy via NSWinRT.dotnet.fromHandle(...).
|
|
89
|
+
var handleId = Interlocked.Increment(ref s_nextHandle);
|
|
90
|
+
s_handles[handleId] = arg;
|
|
91
|
+
var typeName = arg.GetType().FullName ?? arg.GetType().Name;
|
|
92
|
+
w.WriteByte(0x06);
|
|
93
|
+
w.WriteI32(handleId);
|
|
94
|
+
w.WriteString16(typeName); // u16 length prefix (matches bin_read_value tag 0x06)
|
|
95
|
+
}
|
|
96
|
+
}
|
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
using System;
|
|
2
|
+
using System.Collections.Concurrent;
|
|
3
|
+
using System.Collections.Generic;
|
|
4
|
+
using System.Linq;
|
|
5
|
+
using System.Linq.Expressions;
|
|
6
|
+
using System.Reflection;
|
|
7
|
+
using System.Threading.Tasks;
|
|
8
|
+
|
|
9
|
+
namespace NativeScriptBridge;
|
|
10
|
+
|
|
11
|
+
public static partial class Bridge
|
|
12
|
+
{
|
|
13
|
+
private static readonly ConcurrentDictionary<Type, Func<object, Task>?> s_awaitableCache = new();
|
|
14
|
+
|
|
15
|
+
internal static void ScheduleTaskContinuation(int handleId, int resolveId, int rejectId)
|
|
16
|
+
{
|
|
17
|
+
if (!s_handles.TryGetValue(handleId, out var obj) || obj is null)
|
|
18
|
+
throw new KeyNotFoundException($"Invalid handle {handleId}");
|
|
19
|
+
|
|
20
|
+
var task = GetTaskForAwaitable(obj)
|
|
21
|
+
?? throw new InvalidOperationException(
|
|
22
|
+
$"Object of type {obj.GetType().FullName} is not awaitable. " +
|
|
23
|
+
$"Expected Task<T>, ValueTask<T>, IAsyncOperation<T>, or any type implementing GetAwaiter().");
|
|
24
|
+
|
|
25
|
+
task.ContinueWith(completed =>
|
|
26
|
+
{
|
|
27
|
+
s_handles.TryRemove(handleId, out _);
|
|
28
|
+
try
|
|
29
|
+
{
|
|
30
|
+
if (completed.IsFaulted)
|
|
31
|
+
{
|
|
32
|
+
var ex = completed.Exception?.InnerException ?? completed.Exception;
|
|
33
|
+
CallJsCallback(rejectId, [ex?.Message ?? "Task faulted"]);
|
|
34
|
+
}
|
|
35
|
+
else if (completed.IsCanceled)
|
|
36
|
+
{
|
|
37
|
+
CallJsCallback(rejectId, ["Task cancelled"]);
|
|
38
|
+
}
|
|
39
|
+
else
|
|
40
|
+
{
|
|
41
|
+
CallJsCallback(resolveId, [TaskResultCache.GetResult(completed)]);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
catch (Exception ex)
|
|
45
|
+
{
|
|
46
|
+
CallJsCallback(rejectId, [ex.Message]);
|
|
47
|
+
}
|
|
48
|
+
}, TaskScheduler.Default);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
private static Task? GetTaskForAwaitable(object obj)
|
|
52
|
+
{
|
|
53
|
+
if (obj is Task t) return t;
|
|
54
|
+
if (obj is ValueTask vt) return vt.AsTask();
|
|
55
|
+
|
|
56
|
+
var type = obj.GetType();
|
|
57
|
+
if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(ValueTask<>))
|
|
58
|
+
return ValueTaskAsTaskCache.AsTask(type, obj);
|
|
59
|
+
|
|
60
|
+
var winrtTask = WinRtAsyncCache.TryGetTask(type, obj);
|
|
61
|
+
if (winrtTask != null) return winrtTask;
|
|
62
|
+
|
|
63
|
+
return s_awaitableCache.GetOrAdd(type, BuildAwaitableFactory)?.Invoke(obj);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
private static Func<object, Task>? BuildAwaitableFactory(Type type)
|
|
67
|
+
{
|
|
68
|
+
var getAwaiter = type.GetMethod("GetAwaiter",
|
|
69
|
+
BindingFlags.Instance | BindingFlags.Public, null, Type.EmptyTypes, null);
|
|
70
|
+
if (getAwaiter is null) return null;
|
|
71
|
+
|
|
72
|
+
var awaiterType = getAwaiter.ReturnType;
|
|
73
|
+
var isCompleted = awaiterType.GetProperty("IsCompleted");
|
|
74
|
+
var getResult = awaiterType.GetMethod("GetResult",
|
|
75
|
+
BindingFlags.Instance | BindingFlags.Public, null, Type.EmptyTypes, null);
|
|
76
|
+
var onCompleted = awaiterType.GetMethod("UnsafeOnCompleted",
|
|
77
|
+
BindingFlags.Instance | BindingFlags.Public)
|
|
78
|
+
?? awaiterType.GetMethod("OnCompleted",
|
|
79
|
+
BindingFlags.Instance | BindingFlags.Public);
|
|
80
|
+
|
|
81
|
+
if (isCompleted is null || onCompleted is null) return null;
|
|
82
|
+
|
|
83
|
+
return obj =>
|
|
84
|
+
{
|
|
85
|
+
var tcs = new TaskCompletionSource<object?>(
|
|
86
|
+
TaskCreationOptions.RunContinuationsAsynchronously);
|
|
87
|
+
try
|
|
88
|
+
{
|
|
89
|
+
var awaiter = getAwaiter.Invoke(obj, null);
|
|
90
|
+
if (awaiter is null) { tcs.SetResult(null); return tcs.Task; }
|
|
91
|
+
|
|
92
|
+
void Complete()
|
|
93
|
+
{
|
|
94
|
+
try
|
|
95
|
+
{
|
|
96
|
+
tcs.SetResult(getResult?.Invoke(awaiter, null));
|
|
97
|
+
}
|
|
98
|
+
catch (TargetInvocationException ex)
|
|
99
|
+
{
|
|
100
|
+
tcs.SetException(ex.InnerException ?? ex);
|
|
101
|
+
}
|
|
102
|
+
catch (Exception ex) { tcs.SetException(ex); }
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
if ((bool)(isCompleted.GetValue(awaiter) ?? false))
|
|
106
|
+
Complete();
|
|
107
|
+
else
|
|
108
|
+
onCompleted.Invoke(awaiter, [(Action)Complete]);
|
|
109
|
+
}
|
|
110
|
+
catch (Exception ex) { tcs.SetException(ex); }
|
|
111
|
+
|
|
112
|
+
return tcs.Task;
|
|
113
|
+
};
|
|
114
|
+
}
|
|
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
|
+
}
|