@nativescript/windows 0.1.0-alpha.5 → 0.1.0-alpha.51
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 +4 -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 +249 -66
- package/framework/__PROJECT_NAME__/Package.appxmanifest +2 -5
- package/framework/__PROJECT_NAME__/RuntimeHost.cs +132 -22
- package/framework/__PROJECT_NAME__/__PROJECT_NAME__.csproj +49 -12
- package/framework/dotnet-bridge/BinaryProtocol.cs +124 -0
- package/framework/dotnet-bridge/Bridge.BinaryDispatch.cs +177 -0
- package/framework/dotnet-bridge/Bridge.Dispatch.cs +404 -0
- package/framework/dotnet-bridge/Bridge.JsDelegate.cs +100 -0
- package/framework/dotnet-bridge/Bridge.cs +156 -298
- package/framework/dotnet-bridge/DispatchTypes.cs +281 -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 +19 -19
|
@@ -1,353 +1,211 @@
|
|
|
1
1
|
using System;
|
|
2
|
-
using System.Collections;
|
|
3
2
|
using System.Collections.Concurrent;
|
|
4
3
|
using System.Collections.Generic;
|
|
4
|
+
using System.IO;
|
|
5
5
|
using System.Linq;
|
|
6
6
|
using System.Reflection;
|
|
7
7
|
using System.Runtime.InteropServices;
|
|
8
|
+
using System.Runtime.Loader;
|
|
8
9
|
using System.Text.Json;
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
10
|
+
|
|
11
|
+
[assembly: System.Runtime.CompilerServices.InternalsVisibleTo("DotNetBridgeTests")]
|
|
12
|
+
[assembly: System.Runtime.CompilerServices.InternalsVisibleTo("DotNetBridgeBenchmarks")]
|
|
12
13
|
|
|
13
14
|
namespace NativeScriptBridge;
|
|
14
15
|
|
|
15
|
-
|
|
16
|
-
/// Reflection-based .NET dispatch bridge — UTF-8 ABI edition.
|
|
17
|
-
///
|
|
18
|
-
/// Invoke() receives a UTF-8 byte slice (no string allocation on the Rust side),
|
|
19
|
-
/// dispatches via reflection, and serialises the response directly to a UTF-8
|
|
20
|
-
/// byte array via JsonSerializer.SerializeToUtf8Bytes(). This removes the
|
|
21
|
-
/// UTF-16 encode/decode round-trip of the original char* ABI.
|
|
22
|
-
///
|
|
23
|
-
/// Request JSON schema
|
|
24
|
-
/// -------------------
|
|
25
|
-
/// Static call: { "assembly": "System", "typeName": "System.Diagnostics.Stopwatch", "method": "StartNew", "args": [] }
|
|
26
|
-
/// Constructor: { "assembly": "System", "typeName": "System.Text.StringBuilder", "method": ".ctor", "args": [128] }
|
|
27
|
-
/// Instance call: { "handle": 3, "method": "Stop", "args": [] }
|
|
28
|
-
/// Property get: { "assembly": "...", "typeName": "...", "method": "get_Now", "args": [] }
|
|
29
|
-
/// Property set: { "handle": 3, "method": "set_IsEnabled", "args": [true] }
|
|
30
|
-
/// Release: { "handle": 3, "method": "__release", "args": [] }
|
|
31
|
-
///
|
|
32
|
-
/// Response JSON schema
|
|
33
|
-
/// --------------------
|
|
34
|
-
/// Primitive / string: { "result": 42.5 }
|
|
35
|
-
/// Managed object: { "result": { "__handle": 7, "__type": "System.Diagnostics.Stopwatch" } }
|
|
36
|
-
/// Array / enumerable: { "result": [1, 2, 3] }
|
|
37
|
-
/// Error: { "error": "Method not found" }
|
|
38
|
-
/// </summary>
|
|
39
|
-
public static class Bridge
|
|
16
|
+
public static partial class Bridge
|
|
40
17
|
{
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
18
|
+
internal static readonly ConcurrentDictionary<int, object?> s_handles = new();
|
|
19
|
+
internal static int s_nextHandle;
|
|
20
|
+
|
|
21
|
+
// Function pointer registered by the Rust runtime so managed delegates can
|
|
22
|
+
// call back into V8 without a JSON round-trip.
|
|
23
|
+
internal static unsafe delegate* unmanaged[Cdecl]<int, byte*, int, byte**, int*, void>
|
|
24
|
+
s_jsInvoker;
|
|
25
|
+
|
|
26
|
+
private static readonly ConcurrentDictionary<string, Type?> s_typeCache
|
|
27
|
+
= new(StringComparer.Ordinal);
|
|
28
|
+
private static readonly ConcurrentDictionary<MethodKey, DispatchEntry> s_methodCache = new();
|
|
29
|
+
private static readonly ConcurrentDictionary<PropKey, PropertyInfo?> s_propCache = new();
|
|
30
|
+
private static readonly ConcurrentDictionary<CtorKey, CtorEntry> s_ctorCache = new();
|
|
31
|
+
// Cache of attempted assembly loads (simple name -> Assembly or null if not found).
|
|
32
|
+
private static readonly ConcurrentDictionary<string, Assembly?> s_assemblyLoadCache
|
|
33
|
+
= new(StringComparer.OrdinalIgnoreCase);
|
|
34
|
+
// Directories to search for assemblies (initialized once in static ctor).
|
|
35
|
+
private static readonly string[] s_assemblySearchDirs;
|
|
36
|
+
|
|
37
|
+
private static readonly JsonSerializerOptions s_coerceOpts = new()
|
|
57
38
|
{
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
{
|
|
61
|
-
var requestSpan = new ReadOnlySpan<byte>(requestPtr, requestLen);
|
|
62
|
-
var req = JsonSerializer.Deserialize<InvokeRequest>(requestSpan, JsonOptions.Default)!;
|
|
63
|
-
var result = Dispatch(req);
|
|
64
|
-
responseBytes = JsonSerializer.SerializeToUtf8Bytes(result, JsonOptions.Default);
|
|
65
|
-
}
|
|
66
|
-
catch (Exception ex)
|
|
67
|
-
{
|
|
68
|
-
var errResult = new InvokeResult(null, Unwrap(ex).Message);
|
|
69
|
-
responseBytes = JsonSerializer.SerializeToUtf8Bytes(errResult, JsonOptions.Default);
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
WriteResponse(responseBytes, responsePtr, responseLenPtr);
|
|
73
|
-
return 0;
|
|
74
|
-
}
|
|
39
|
+
PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
|
|
40
|
+
};
|
|
75
41
|
|
|
76
|
-
|
|
77
|
-
[UnmanagedCallersOnly(EntryPoint = "Free",
|
|
78
|
-
CallConvs = [typeof(System.Runtime.CompilerServices.CallConvCdecl)])]
|
|
79
|
-
public static unsafe void Free(byte* ptr)
|
|
42
|
+
internal static void ClearCaches()
|
|
80
43
|
{
|
|
81
|
-
|
|
82
|
-
|
|
44
|
+
s_typeCache.Clear();
|
|
45
|
+
s_methodCache.Clear();
|
|
46
|
+
s_propCache.Clear();
|
|
47
|
+
s_ctorCache.Clear();
|
|
48
|
+
s_handles.Clear();
|
|
49
|
+
s_nextHandle = 0;
|
|
83
50
|
}
|
|
84
51
|
|
|
85
|
-
//
|
|
86
|
-
|
|
87
|
-
|
|
52
|
+
// Returns a JSON object mapping top-level namespace roots (e.g. "NativeScript")
|
|
53
|
+
// to the assembly simple-name that most likely contains that namespace's types.
|
|
54
|
+
// This probes the application base directory and libs/ for assemblies and
|
|
55
|
+
// inspects exported types. The result is safe-to-call and tolerant of
|
|
56
|
+
// non-.NET DLLs.
|
|
57
|
+
public static string GetNamespaceAssemblyMapJson()
|
|
88
58
|
{
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
s_handles.TryRemove(req.Handle.Value, out _);
|
|
92
|
-
return new InvokeResult(null, null);
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
// Return reflection metadata so JS can distinguish methods from properties.
|
|
96
|
-
if (req.Method == "__members__")
|
|
59
|
+
var map = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
|
|
60
|
+
try
|
|
97
61
|
{
|
|
98
|
-
var
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
if (
|
|
102
|
-
var inst = BindingFlags.Public | BindingFlags.Instance;
|
|
103
|
-
var stat = BindingFlags.Public | BindingFlags.Static;
|
|
104
|
-
var members = new
|
|
62
|
+
var baseDir = AppContext.BaseDirectory ?? AppDomain.CurrentDomain.BaseDirectory;
|
|
63
|
+
var dirs = new List<string> { baseDir };
|
|
64
|
+
var libs = Path.Combine(baseDir, "libs");
|
|
65
|
+
if (Directory.Exists(libs))
|
|
105
66
|
{
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
staticProperties = t.GetProperties(stat).Select(p => p.Name).Distinct().ToArray(),
|
|
110
|
-
};
|
|
111
|
-
return new InvokeResult(JsonSerializer.SerializeToElement(members, JsonOptions.Default), null);
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
object? target = null;
|
|
115
|
-
Type? type = null;
|
|
116
|
-
|
|
117
|
-
if (req.Handle.HasValue)
|
|
118
|
-
{
|
|
119
|
-
if (!s_handles.TryGetValue(req.Handle.Value, out target))
|
|
120
|
-
return new InvokeResult(null, $"Invalid handle {req.Handle.Value}");
|
|
121
|
-
type = target?.GetType();
|
|
122
|
-
}
|
|
123
|
-
else
|
|
124
|
-
{
|
|
125
|
-
type = ResolveType(req.Assembly, req.TypeName);
|
|
126
|
-
if (type is null)
|
|
127
|
-
return new InvokeResult(null, $"Type not found: {req.TypeName}");
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
if (type is null)
|
|
131
|
-
return new InvokeResult(null, "Cannot determine target type");
|
|
132
|
-
|
|
133
|
-
var method = req.Method ?? throw new ArgumentException("Method is required");
|
|
134
|
-
|
|
135
|
-
// ── Constructor ──────────────────────────────────────────────────────
|
|
136
|
-
if (method == ".ctor")
|
|
137
|
-
{
|
|
138
|
-
var argElements = req.Args ?? [];
|
|
139
|
-
var ctors = type.GetConstructors(BindingFlags.Public | BindingFlags.Instance);
|
|
140
|
-
var ctor = ctors.FirstOrDefault(c => c.GetParameters().Length == argElements.Count)
|
|
141
|
-
?? ctors.FirstOrDefault();
|
|
142
|
-
if (ctor is null)
|
|
143
|
-
return new InvokeResult(null, $"No public constructor found on {type.FullName}");
|
|
144
|
-
var pars = ctor.GetParameters();
|
|
145
|
-
var ctorArgs = new object?[pars.Length];
|
|
146
|
-
for (int i = 0; i < pars.Length && i < argElements.Count; i++)
|
|
147
|
-
ctorArgs[i] = Coerce(argElements[i], pars[i].ParameterType);
|
|
148
|
-
return Box(ctor.Invoke(ctorArgs));
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
var isStatic = target is null;
|
|
152
|
-
var flags = (isStatic ? BindingFlags.Static : BindingFlags.Instance)
|
|
153
|
-
| BindingFlags.Public;
|
|
67
|
+
dirs.Add(libs);
|
|
68
|
+
try { dirs.AddRange(Directory.GetDirectories(libs, "*", SearchOption.AllDirectories)); } catch { }
|
|
69
|
+
}
|
|
154
70
|
|
|
155
|
-
|
|
156
|
-
if (method.StartsWith("get_", StringComparison.Ordinal))
|
|
157
|
-
{
|
|
158
|
-
var prop = GetCachedProperty(type, method[4..], flags);
|
|
159
|
-
if (prop is not null)
|
|
160
|
-
return Box(prop.GetValue(target));
|
|
161
|
-
}
|
|
162
|
-
if (method.StartsWith("set_", StringComparison.Ordinal) && req.Args?.Count == 1)
|
|
163
|
-
{
|
|
164
|
-
var prop = GetCachedProperty(type, method[4..], flags);
|
|
165
|
-
if (prop is not null)
|
|
71
|
+
foreach (var dir in dirs.Where(d => !string.IsNullOrEmpty(d) && Directory.Exists(d)))
|
|
166
72
|
{
|
|
167
|
-
|
|
168
|
-
|
|
73
|
+
try
|
|
74
|
+
{
|
|
75
|
+
foreach (var file in Directory.EnumerateFiles(dir, "*.dll", SearchOption.TopDirectoryOnly))
|
|
76
|
+
{
|
|
77
|
+
try
|
|
78
|
+
{
|
|
79
|
+
var simple = Path.GetFileNameWithoutExtension(file);
|
|
80
|
+
if (string.IsNullOrEmpty(simple)) continue;
|
|
81
|
+
|
|
82
|
+
Assembly asm = AppDomain.CurrentDomain.GetAssemblies()
|
|
83
|
+
.FirstOrDefault(a => string.Equals(a.GetName().Name, simple, StringComparison.OrdinalIgnoreCase));
|
|
84
|
+
if (asm is null)
|
|
85
|
+
{
|
|
86
|
+
try { asm = AssemblyLoadContext.Default.LoadFromAssemblyPath(file); }
|
|
87
|
+
catch { continue; }
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
Type[] types;
|
|
91
|
+
try { types = asm.GetExportedTypes(); }
|
|
92
|
+
catch { types = Array.Empty<Type>(); }
|
|
93
|
+
|
|
94
|
+
foreach (var t in types)
|
|
95
|
+
{
|
|
96
|
+
if (string.IsNullOrEmpty(t.Namespace)) continue;
|
|
97
|
+
var root = t.Namespace.Split('.')[0];
|
|
98
|
+
if (!map.ContainsKey(root)) map[root] = asm.GetName().Name;
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
catch { }
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
catch { }
|
|
169
105
|
}
|
|
170
106
|
}
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
var argElems = req.Args ?? [];
|
|
174
|
-
var mi = FindMethod(type, method, argElems.Count, flags);
|
|
175
|
-
if (mi is null)
|
|
176
|
-
return new InvokeResult(null,
|
|
177
|
-
$"Method '{method}' ({argElems.Count} args) not found on {type.FullName}");
|
|
178
|
-
|
|
179
|
-
var parameters = mi.GetParameters();
|
|
180
|
-
var callArgs = new object?[parameters.Length];
|
|
181
|
-
for (int i = 0; i < parameters.Length && i < argElems.Count; i++)
|
|
182
|
-
callArgs[i] = Coerce(argElems[i], parameters[i].ParameterType);
|
|
183
|
-
|
|
184
|
-
return Box(AwaitIfTask(mi.Invoke(target, callArgs)));
|
|
107
|
+
catch { }
|
|
108
|
+
try { return JsonSerializer.Serialize(map); } catch { return "{}"; }
|
|
185
109
|
}
|
|
186
110
|
|
|
187
|
-
//
|
|
188
|
-
|
|
189
|
-
private static object? AwaitIfTask(object? value)
|
|
111
|
+
// Static ctor: initialize assembly search directories and register resolver.
|
|
112
|
+
static Bridge()
|
|
190
113
|
{
|
|
191
|
-
|
|
192
|
-
var t = value.GetType();
|
|
193
|
-
|
|
194
|
-
if (value is Task task)
|
|
195
|
-
{
|
|
196
|
-
task.Wait();
|
|
197
|
-
if (task.IsFaulted)
|
|
198
|
-
throw task.Exception!.InnerException ?? task.Exception;
|
|
199
|
-
return t.GetProperty("Result")?.GetValue(value);
|
|
200
|
-
}
|
|
201
|
-
|
|
202
|
-
if (t.IsGenericType && t.GetGenericTypeDefinition() == typeof(ValueTask<>))
|
|
114
|
+
try
|
|
203
115
|
{
|
|
204
|
-
var
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
116
|
+
var baseDir = AppContext.BaseDirectory ?? AppDomain.CurrentDomain.BaseDirectory;
|
|
117
|
+
var dirs = new List<string> { baseDir };
|
|
118
|
+
var libs = Path.Combine(baseDir, "libs");
|
|
119
|
+
if (Directory.Exists(libs))
|
|
120
|
+
{
|
|
121
|
+
dirs.Add(libs);
|
|
122
|
+
try { dirs.AddRange(Directory.GetDirectories(libs, "*", SearchOption.AllDirectories)); } catch { }
|
|
123
|
+
}
|
|
124
|
+
s_assemblySearchDirs = dirs.Where(d => !string.IsNullOrEmpty(d)).Distinct(StringComparer.OrdinalIgnoreCase).ToArray();
|
|
125
|
+
AppDomain.CurrentDomain.AssemblyResolve += OnAssemblyResolve;
|
|
209
126
|
}
|
|
210
|
-
|
|
211
|
-
if (value is System.Threading.Tasks.ValueTask vt)
|
|
127
|
+
catch
|
|
212
128
|
{
|
|
213
|
-
|
|
214
|
-
return null;
|
|
129
|
+
s_assemblySearchDirs = Array.Empty<string>();
|
|
215
130
|
}
|
|
216
|
-
|
|
217
|
-
return value;
|
|
218
131
|
}
|
|
219
132
|
|
|
220
|
-
private static
|
|
133
|
+
private static Assembly? OnAssemblyResolve(object? sender, ResolveEventArgs args)
|
|
221
134
|
{
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
135
|
+
try
|
|
136
|
+
{
|
|
137
|
+
var requested = new AssemblyName(args.Name).Name;
|
|
138
|
+
if (string.IsNullOrEmpty(requested)) return null;
|
|
226
139
|
|
|
227
|
-
|
|
228
|
-
{
|
|
229
|
-
var fqn = string.IsNullOrEmpty(assemblyName) ? typeName! : $"{typeName}, {assemblyName}";
|
|
230
|
-
var t = Type.GetType(fqn);
|
|
231
|
-
if (t is not null) return t;
|
|
140
|
+
if (s_assemblyLoadCache.TryGetValue(requested, out var cached)) return cached;
|
|
232
141
|
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
if (
|
|
237
|
-
|
|
142
|
+
// Check already-loaded assemblies first (avoid recursive Assembly.Load).
|
|
143
|
+
var loadedAsm = AppDomain.CurrentDomain.GetAssemblies()
|
|
144
|
+
.FirstOrDefault(a => string.Equals(a.GetName().Name, requested, StringComparison.OrdinalIgnoreCase));
|
|
145
|
+
if (loadedAsm is not null)
|
|
146
|
+
{
|
|
147
|
+
s_assemblyLoadCache[requested] = loadedAsm;
|
|
148
|
+
return loadedAsm;
|
|
149
|
+
}
|
|
238
150
|
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
try
|
|
151
|
+
// Try to locate a matching dll in known search directories.
|
|
152
|
+
foreach (var dir in s_assemblySearchDirs)
|
|
242
153
|
{
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
154
|
+
try
|
|
155
|
+
{
|
|
156
|
+
var candidate = Path.Combine(dir, requested + ".dll");
|
|
157
|
+
if (File.Exists(candidate))
|
|
158
|
+
{
|
|
159
|
+
try
|
|
160
|
+
{
|
|
161
|
+
var asm = AssemblyLoadContext.Default.LoadFromAssemblyPath(candidate);
|
|
162
|
+
s_assemblyLoadCache[requested] = asm;
|
|
163
|
+
return asm;
|
|
164
|
+
}
|
|
165
|
+
catch { }
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
catch { }
|
|
246
169
|
}
|
|
247
|
-
catch { }
|
|
248
|
-
}
|
|
249
170
|
|
|
171
|
+
s_assemblyLoadCache[requested] = null;
|
|
172
|
+
}
|
|
173
|
+
catch { }
|
|
250
174
|
return null;
|
|
251
175
|
}
|
|
252
176
|
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
}
|
|
258
|
-
|
|
259
|
-
private static MethodInfo? FindMethodCore(Type type, string name, int argCount, BindingFlags flags)
|
|
260
|
-
{
|
|
261
|
-
var match = Array.Find(
|
|
262
|
-
type.GetMethods(flags),
|
|
263
|
-
m => m.Name == name && !m.IsGenericMethod && m.GetParameters().Length == argCount);
|
|
264
|
-
if (match is not null) return match;
|
|
265
|
-
return type.GetMethod(name, flags);
|
|
266
|
-
}
|
|
267
|
-
|
|
268
|
-
private static PropertyInfo? GetCachedProperty(Type type, string name, BindingFlags flags)
|
|
269
|
-
{
|
|
270
|
-
var key = $"{type.FullName}|{name}|{(int)flags}";
|
|
271
|
-
return s_propertyCache.GetOrAdd(key, _ => type.GetProperty(name, flags));
|
|
272
|
-
}
|
|
273
|
-
|
|
274
|
-
private static object? Coerce(JsonElement el, Type targetType)
|
|
177
|
+
[UnmanagedCallersOnly(EntryPoint = "RegisterJsCallback",
|
|
178
|
+
CallConvs = [typeof(System.Runtime.CompilerServices.CallConvCdecl)])]
|
|
179
|
+
public static unsafe int RegisterJsCallback(
|
|
180
|
+
delegate* unmanaged[Cdecl]<int, byte*, int, byte**, int*, void> callback)
|
|
275
181
|
{
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
{
|
|
279
|
-
s_handles.TryGetValue(h.GetInt32(), out var obj);
|
|
280
|
-
return obj;
|
|
281
|
-
}
|
|
282
|
-
return JsonSerializer.Deserialize(el.GetRawText(), targetType, JsonOptions.Default);
|
|
182
|
+
s_jsInvoker = callback;
|
|
183
|
+
return 0;
|
|
283
184
|
}
|
|
284
185
|
|
|
285
|
-
|
|
186
|
+
[UnmanagedCallersOnly(EntryPoint = "Invoke",
|
|
187
|
+
CallConvs = [typeof(System.Runtime.CompilerServices.CallConvCdecl)])]
|
|
188
|
+
public static unsafe int Invoke(
|
|
189
|
+
byte* requestPtr, int requestLen,
|
|
190
|
+
byte** responsePtr, int* responseLenPtr)
|
|
286
191
|
{
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
var t = value.GetType();
|
|
290
|
-
|
|
291
|
-
if (t.IsPrimitive || t == typeof(string) || t == typeof(decimal)
|
|
292
|
-
|| t == typeof(DateTime) || t == typeof(DateTimeOffset)
|
|
293
|
-
|| t == typeof(TimeSpan) || t == typeof(Guid))
|
|
192
|
+
try
|
|
294
193
|
{
|
|
295
|
-
|
|
296
|
-
|
|
194
|
+
var span = new ReadOnlySpan<byte>(requestPtr, requestLen);
|
|
195
|
+
var req = JsonSerializer.Deserialize(span, BridgeJsonContext.Default.InvokeRequest)!;
|
|
196
|
+
WriteResult(Dispatch(req), responsePtr, responseLenPtr);
|
|
297
197
|
}
|
|
298
|
-
|
|
299
|
-
// Arrays and IEnumerable — serialise as JSON arrays.
|
|
300
|
-
if (t.IsArray || (t != typeof(string) && value is IEnumerable enumerable))
|
|
198
|
+
catch (Exception ex)
|
|
301
199
|
{
|
|
302
|
-
|
|
303
|
-
{
|
|
304
|
-
var list = new List<object?>();
|
|
305
|
-
foreach (var item in (IEnumerable)value) list.Add(item);
|
|
306
|
-
return new InvokeResult(
|
|
307
|
-
JsonSerializer.SerializeToElement(list, typeof(List<object?>), JsonOptions.Default),
|
|
308
|
-
null);
|
|
309
|
-
}
|
|
310
|
-
catch { }
|
|
200
|
+
WriteError(Unwrap(ex).Message, responsePtr, responseLenPtr);
|
|
311
201
|
}
|
|
312
|
-
|
|
313
|
-
var id = Interlocked.Increment(ref s_nextHandle);
|
|
314
|
-
s_handles[id] = value;
|
|
315
|
-
return new InvokeResult(
|
|
316
|
-
JsonSerializer.SerializeToElement(
|
|
317
|
-
new { __handle = id, __type = t.FullName }, JsonOptions.Default),
|
|
318
|
-
null);
|
|
202
|
+
return 0;
|
|
319
203
|
}
|
|
320
204
|
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
private static unsafe void WriteResponse(byte[] json, byte** responsePtr, int* responseLenPtr)
|
|
205
|
+
[UnmanagedCallersOnly(EntryPoint = "Free",
|
|
206
|
+
CallConvs = [typeof(System.Runtime.CompilerServices.CallConvCdecl)])]
|
|
207
|
+
public static unsafe void Free(byte* ptr)
|
|
325
208
|
{
|
|
326
|
-
|
|
327
|
-
Marshal.Copy(json, 0, (IntPtr)ptr, json.Length);
|
|
328
|
-
ptr[json.Length] = 0;
|
|
329
|
-
*responsePtr = ptr;
|
|
330
|
-
*responseLenPtr = json.Length;
|
|
209
|
+
if (ptr != null) Marshal.FreeHGlobal((IntPtr)ptr);
|
|
331
210
|
}
|
|
332
211
|
}
|
|
333
|
-
|
|
334
|
-
// ── JSON types ────────────────────────────────────────────────────────────────
|
|
335
|
-
|
|
336
|
-
internal sealed record InvokeRequest(
|
|
337
|
-
string? Assembly,
|
|
338
|
-
string? TypeName,
|
|
339
|
-
string? Method,
|
|
340
|
-
int? Handle,
|
|
341
|
-
List<JsonElement>? Args
|
|
342
|
-
);
|
|
343
|
-
|
|
344
|
-
internal sealed record InvokeResult(JsonElement? Result, string? Error);
|
|
345
|
-
|
|
346
|
-
internal static class JsonOptions
|
|
347
|
-
{
|
|
348
|
-
internal static readonly JsonSerializerOptions Default = new()
|
|
349
|
-
{
|
|
350
|
-
PropertyNameCaseInsensitive = true,
|
|
351
|
-
PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
|
|
352
|
-
};
|
|
353
|
-
}
|