From 6f0d04bb407783ba37cb3f7d75f92339296f3fb5 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 10 Apr 2026 02:37:22 +0000 Subject: [PATCH 1/3] Bump MASES.JCOBridge from 2.6.7 to 2.6.8 --- updated-dependencies: - dependency-name: MASES.JCOBridge dependency-version: 2.6.8 dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: MASES.JCOBridge dependency-version: 2.6.8 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- src/net/JNet/JNet.csproj | 2 +- src/net/JNetReflector/JNetReflector.csproj | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/net/JNet/JNet.csproj b/src/net/JNet/JNet.csproj index 4c9e7ec075..80676444a0 100644 --- a/src/net/JNet/JNet.csproj +++ b/src/net/JNet/JNet.csproj @@ -50,7 +50,7 @@ - + All None diff --git a/src/net/JNetReflector/JNetReflector.csproj b/src/net/JNetReflector/JNetReflector.csproj index 36c745337f..7276cb151f 100644 --- a/src/net/JNetReflector/JNetReflector.csproj +++ b/src/net/JNetReflector/JNetReflector.csproj @@ -116,7 +116,7 @@ - + All None From 3779d92f6eb5da604d01b0e4c1330395c791821d Mon Sep 17 00:00:00 2001 From: MASES Public Developers Team <94312179+masesdevelopers@users.noreply.github.com> Date: Fri, 10 Apr 2026 09:14:01 +0200 Subject: [PATCH 2/3] Updates files not managed from dependabot --- .github/workflows/build.yaml | 2 +- .github/workflows/generateclasses.yaml | 2 +- src/documentation/articles/performance.md | 18 +++++++++--------- src/documentation/articles/performancetips.md | 8 ++++---- 4 files changed, 15 insertions(+), 15 deletions(-) diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index e1c400225f..6d2b31ccbe 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -161,4 +161,4 @@ jobs: secrets: actor: ${{ github.actor }} GITHUB_TOKEN_INHERITED: ${{ secrets.GITHUB_TOKEN }} - JCOBRIDGE_ENCODED: ${{ secrets.JCOBRIDGE_ENCODED_2_6_7 }} + JCOBRIDGE_ENCODED: ${{ secrets.JCOBRIDGE_ENCODED_2_6_8 }} diff --git a/.github/workflows/generateclasses.yaml b/.github/workflows/generateclasses.yaml index b10f1f1117..bf2911bfb4 100644 --- a/.github/workflows/generateclasses.yaml +++ b/.github/workflows/generateclasses.yaml @@ -47,7 +47,7 @@ jobs: DOTNET_CreateDumpDiagnostics: 1 DOTNET_CreateDumpVerboseDiagnostics: 1 DOTNET_EnableCrashReport: 1 - JCOBRIDGE_LicensePath: ${{ secrets.JCOBRIDGE_ENCODED_2_6_7 }} + JCOBRIDGE_LicensePath: ${{ secrets.JCOBRIDGE_ENCODED_2_6_8 }} # Steps represent a sequence of tasks that will be executed as part of the job steps: diff --git a/src/documentation/articles/performance.md b/src/documentation/articles/performance.md index 9e66513d87..503b72f178 100644 --- a/src/documentation/articles/performance.md +++ b/src/documentation/articles/performance.md @@ -8,7 +8,7 @@ _description: Benchmark results for JNet — JVM method invocation and callback This page reports benchmark results for the core JNet interop primitives: JVM method invocation from .NET and JVM→.NET callback latency. All benchmarks run on [GitHub Actions](https://github.com/masesgroup/JNet/actions) runners and are repeated automatically on each release across supported .NET and JDK versions. -Results are reported for two JCOBridge versions — 2.6.6 and 2.6.7 — and two runtime combinations. See [JCOBridge release notes](https://www.jcobridge.com/release-notes/) for details. +Results are reported for two JCOBridge versions — 2.6.6 and 2.6.7+ — and two runtime combinations. See [JCOBridge release notes](https://www.jcobridge.com/release-notes/) for details. > [!NOTE] > Benchmarks are run on shared GitHub-hosted runners. Absolute numbers reflect that environment and should be read comparatively rather than as absolute throughput figures for a dedicated host. @@ -50,15 +50,15 @@ A .NET-initiated test: .NET triggers a JVM call which immediately fires a callba A JVM-initiated test: .NET sends a single start command to JVM, which then fires 1 000 000 callback events toward the CLR autonomously without returning control to .NET. After all events are fired, the JVM returns and .NET measures the total elapsed time. Aside from the single startup call, this measures the pure cost of receiving a sustained stream of JVM-originated events — the scenario matching real-world usage (e.g. Kafka Streams functional interfaces, AWT event listeners). -Both callback tests share two configuration axes (2.6.7 only): +Both callback tests share two configuration axes (2.6.7+ only): **`byIndex` — event trigger identification:** - **`byIndex = false`** — the event is identified on the CLR side by a string key lookup. - **`byIndex = true`** — the event is identified on the CLR side by a numeric index, without any JVM call. In both cases, JVM object arguments are retrieved as JVM objects after the trigger is received. -**Two-level early-discard filter (`ShallManageEvent`, 2.6.7+):** +**Two-level early-discard filter (`ShallManageEvent`, 2.6.7++):** -JCOBridge 2.6.7 introduces two overloads of `ShallManageEvent` on the JNet callback base class, forming a two-gate filter applied before full event handling: +JCOBridge 2.6.7+ introduces two overloads of `ShallManageEvent` on the JNet callback base class, forming a two-gate filter applied before full event handling: **First gate — `bool ShallManageEvent(string eventName)`:** called before any argument data is read from the JVM. The return value: - **`false`** (`continueFirstCheck = false`) — discard immediately: no data is read, the handler is not invoked. @@ -109,9 +109,9 @@ Adding a `boolean` argument and return value (`feedback = true`) adds ~45–55% --- -## JCOBridge 2.6.7 +## JCOBridge 2.6.7+ -JCOBridge 2.6.7 introduces the two-level `ShallManageEvent` filter and the native `byIndex` trigger mechanism. General interop improvements reduce baseline overhead across all test types. +JCOBridge 2.6.7+ introduces the two-level `ShallManageEvent` filter and the native `byIndex` trigger mechanism. General interop improvements reduce baseline overhead across all test types. > [!NOTE] > `byIndex = true` is still simulated on the JVM side by invoking a dedicated class method rather than the interface `@Override`. The CLR-side numeric index resolution is fully active; a JVM dispatch difference (class method vs interface method) remains. The `byIndex = false` rows use the real interface override and are directly comparable between the two versions. @@ -180,7 +180,7 @@ The two-level filter reveals three distinct operating points: | Test | .NET 8 / T17 | | .NET 10 / T25 | | |---|---|---|---|---| -| | 2.6.6 | 2.6.7 | 2.6.6 | 2.6.7 | +| | 2.6.6 | 2.6.7+ | 2.6.6 | 2.6.7+ | | Static `Invoke` fb=false | 0.661 µs | 0.517 µs (−22%) | 0.602 µs | 0.480 µs (−20%) | | Static `IWS` fb=false | 0.494 µs | 0.356 µs (−28%) | 0.414 µs | 0.335 µs (−19%) | | Static `Invoke` fb=true | 0.901 µs | 0.609 µs (−32%) | 0.803 µs | 0.575 µs (−28%) | @@ -196,7 +196,7 @@ The two-level filter reveals three distinct operating points: | Sustained: second gate discard, `byIndex=false` | — | 0.625 µs | — | 0.493 µs | | Sustained: second gate discard, `byIndex=true` ¹ | — | **0.074 µs** | — | **0.067 µs** | -¹ `byIndex = true` simulated on the JVM side in 2.6.7 — see notes above. No 2.6.6 baseline available. +¹ `byIndex = true` simulated on the JVM side in 2.6.7+ — see notes above. No 2.6.6 baseline available. ### Comparison with raw JNI overhead @@ -209,7 +209,7 @@ JNet's first-gate discard path involves a JVM→CLR crossing, the numeric index ## Guidance - **Prefer `InvokeWithSignature`** (`IWS`) over `Invoke` in hot paths — it avoids .NET-side type matching on every call and consistently delivers 20–40% lower latency when arguments are involved. -- **The realistic JVM-originated callback reference** is `Sustained`, full processing, `byIndex = false`: ~5.1 µs (.NET 8 / T17) and ~4.7 µs (.NET 10 / T25) in 2.6.7. With `byIndex = true` this drops to ~4.5 µs and ~4.1 µs. +- **The realistic JVM-originated callback reference** is `Sustained`, full processing, `byIndex = false`: ~5.1 µs (.NET 8 / T17) and ~4.7 µs (.NET 10 / T25) in 2.6.7+. With `byIndex = true` this drops to ~4.5 µs and ~4.1 µs. - **Use the two-level `ShallManageEvent` filter** for high-event-rate sources where only a subset of events require full processing: - First gate (`ShallManageEventHandler`) — discard by event name alone, before any data read: ~45 ns (.NET 8) / ~41 ns (.NET 10) with `byIndex = true`. - Second gate (`ShallManageEventWithDataHandler`) — inspect raw data before deciding: ~74 ns (.NET 8) / ~67 ns (.NET 10) with `byIndex = true`. Use this when a lightweight field check on the raw payload is needed to decide whether to invoke the handler. diff --git a/src/documentation/articles/performancetips.md b/src/documentation/articles/performancetips.md index c0fbce3638..935012fdaf 100644 --- a/src/documentation/articles/performancetips.md +++ b/src/documentation/articles/performancetips.md @@ -93,7 +93,7 @@ Using the loop-based example as a baseline: When a JVM™ class fires events toward the CLR — for example an AWT component, a Kafka Streams functional interface, or any JNet callback wrapper — the standard flow reads argument data from the JVM™ before invoking the registered handler. For sources that produce many event types, most of them may have no handler registered in the application. Reading and converting argument data for events that will be immediately discarded is wasted work. -JCOBridge 2.6.7 introduces a two-level filter applied before full event handling, through two overloads of `ShallManageEvent` on the JNet callback base class. +JCOBridge 2.6.7+ introduces a two-level filter applied before full event handling, through two overloads of `ShallManageEvent` on the JNet callback base class. ### First gate — `bool ShallManageEvent(string eventName)` @@ -108,7 +108,7 @@ The `ShallManageEventHandler` (`Func`) delegate is the assignable > [!NOTE] > The combination "first gate returns `false`, second gate returns `true`" is never reached — if the first gate discards, the second gate is not called. -Default for both gates is `true` (full processing). Both overloads are available from JCOBridge 2.6.7. +Default for both gates is `true` (full processing). Both overloads are available from JCOBridge 2.6.7+. ### Usage @@ -150,7 +150,7 @@ listener.ActionPerformed += e => { /* handle */ }; ### Performance impact -The cost per event at each gate, measured in a sustained JVM-originated stream on a GitHub Actions runner (2.6.7, 1 000 000 iterations): +The cost per event at each gate, measured in a sustained JVM-originated stream on a GitHub Actions runner (2.6.7+, 1 000 000 iterations): | Gate | `byIndex` | .NET 8 / T17 | .NET 10 / T25 | Events/sec (.NET 10) | |---|---|---|---|---| @@ -172,7 +172,7 @@ See [performance](performance.md) for the complete benchmark data. > Apply these filters whenever a JVM™ source fires multiple event types and only a subset have registered handlers. Typical candidates: AWT/Swing components with many listener methods, Kafka Streams topologies with mixed functional interfaces, and any JVM™ observable that emits high-frequency events of heterogeneous types. > [!NOTE] -> Both `ShallManageEvent` overloads are available from JCOBridge 2.6.7. On earlier versions all events follow the full data-read path regardless of whether a handler is registered. +> Both `ShallManageEvent` overloads are available from JCOBridge 2.6.7+. On earlier versions all events follow the full data-read path regardless of whether a handler is registered. ## Memory transfer at CLR-JVM™ boundary From 992cedc3642c2b8f170bb5b9ebe8af6533a99ac8 Mon Sep 17 00:00:00 2001 From: masesdevelopers <94312179+masesdevelopers@users.noreply.github.com> Date: Fri, 10 Apr 2026 09:42:59 +0200 Subject: [PATCH 3/3] Replace JVMBridgeBaseCore with new class name JVMBridgeCore --- src/documentation/articles/usageReflector.md | 2 +- src/net/JNetReflector/InternalExtensions.cs | 6 +++--- .../Templates/AllPackageClassesStubClass.template | 2 +- .../AllPackageClassesStubClassInterfaceOrAbstract.template | 2 +- .../Templates/AllPackageClassesStubClassListener.template | 2 +- src/net/JNetReflector/Templates/Templates.cs | 2 +- 6 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/documentation/articles/usageReflector.md b/src/documentation/articles/usageReflector.md index d816fca400..d8adcd7e0a 100644 --- a/src/documentation/articles/usageReflector.md +++ b/src/documentation/articles/usageReflector.md @@ -76,7 +76,7 @@ _jnetreflector_ accepts the following command-line switch: * **DoNotCamel**: Does not use camelized names in methods, class and so on * **TraceLevel**: The level of traces to be reported: 0 - Critical ... 5 - Verbose * **TraceTo**: The file name where traces will be write, default write to console -* **AlwaysUseIDisposablePattern**: The generator do not use the JCOBridge base class without IDisposable (JVMBridgeBaseCore) and fallback to previous class JVMBridgeBase +* **AlwaysUseIDisposablePattern**: The generator do not use the JCOBridge base class without IDisposable (JVMBridgeCore) and fallback to previous class JVMBridgeBase ## JNet reflected classes diff --git a/src/net/JNetReflector/InternalExtensions.cs b/src/net/JNetReflector/InternalExtensions.cs index 9c8c2ff75d..0823ddbe9c 100644 --- a/src/net/JNetReflector/InternalExtensions.cs +++ b/src/net/JNetReflector/InternalExtensions.cs @@ -1438,7 +1438,7 @@ public static string JVMBaseClassName(this Class entry, bool isClassCloseable, b string className = entry.JVMClassName(null, usedInGenerics, false); return JNetReflectorCore.AlwaysUseIDisposablePattern || isClassCloseable ? $"MASES.JCOBridge.C2JBridge.JVMBridgeBase<{className}>" - : $"MASES.JCOBridge.C2JBridge.JVMBridgeBaseCore<{className}>"; + : $"MASES.JCOBridge.C2JBridge.JVMBridgeCore<{className}>"; } try { @@ -1517,7 +1517,7 @@ public static string JVMBaseClassName(this Class entry, bool isClassCloseable, b string innerName = entry.JVMClassName(null, usedInGenerics, false); return JNetReflectorCore.AlwaysUseIDisposablePattern || isClassCloseable ? $"MASES.JCOBridge.C2JBridge.JVMBridgeBase<{innerName}>" - : $"MASES.JCOBridge.C2JBridge.JVMBridgeBaseCore<{innerName}>"; + : $"MASES.JCOBridge.C2JBridge.JVMBridgeCore<{innerName}>"; } } else if ((usedInGenerics || !entry.IsJVMGenericClass()) && superCls.IsJVMGenericClass()) @@ -1532,7 +1532,7 @@ public static string JVMBaseClassName(this Class entry, bool isClassCloseable, b string className = entry.JVMClassName(null, usedInGenerics, false); return JNetReflectorCore.AlwaysUseIDisposablePattern || isClassCloseable ? $"MASES.JCOBridge.C2JBridge.JVMBridgeBase<{className}>" - : $"MASES.JCOBridge.C2JBridge.JVMBridgeBaseCore<{className}>"; + : $"MASES.JCOBridge.C2JBridge.JVMBridgeCore<{className}>"; } } diff --git a/src/net/JNetReflector/Templates/AllPackageClassesStubClass.template b/src/net/JNetReflector/Templates/AllPackageClassesStubClass.template index d1881f3f20..8a295aadf6 100644 --- a/src/net/JNetReflector/Templates/AllPackageClassesStubClass.template +++ b/src/net/JNetReflector/Templates/AllPackageClassesStubClass.template @@ -14,7 +14,7 @@ public partial class ALLPACKAGE_CLASSES_STUB_CLASS_PLACEHOLDER : ALLPACKAGE_CLAS public ALLPACKAGE_CLASSES_STUB_SIMPLECLASS_PLACEHOLDER(params object[] args) : base(args) { } private static readonly global::System.Exception _LocalBridgeClazzException = null; - private static readonly MASES.JCOBridge.C2JBridge.JVMInterop.IJavaType _LocalBridgeClazz = JVMBridgeBaseCore.ClazzOf(_bridgeClassName, out _LocalBridgeClazzException, false); + private static readonly MASES.JCOBridge.C2JBridge.JVMInterop.IJavaType _LocalBridgeClazz = JVMBridgeCore.ClazzOf(_bridgeClassName, out _LocalBridgeClazzException, false); private static MASES.JCOBridge.C2JBridge.JVMInterop.IJavaType LocalBridgeClazz => _LocalBridgeClazz ?? throw _LocalBridgeClazzException ?? new global::System.InvalidOperationException($"Class {_bridgeClassName} was not found."); /// diff --git a/src/net/JNetReflector/Templates/AllPackageClassesStubClassInterfaceOrAbstract.template b/src/net/JNetReflector/Templates/AllPackageClassesStubClassInterfaceOrAbstract.template index 10a13ca363..447e0691cd 100644 --- a/src/net/JNetReflector/Templates/AllPackageClassesStubClassInterfaceOrAbstract.template +++ b/src/net/JNetReflector/Templates/AllPackageClassesStubClassInterfaceOrAbstract.template @@ -15,7 +15,7 @@ public partial class ALLPACKAGE_CLASSES_STUB_CLASS_PLACEHOLDER : ALLPACKAGE_CLAS public ALLPACKAGE_CLASSES_STUB_SIMPLECLASS_PLACEHOLDER(params object[] args) : base(args) { } private static readonly global::System.Exception _LocalBridgeClazzException = null; - private static readonly MASES.JCOBridge.C2JBridge.JVMInterop.IJavaType _LocalBridgeClazz = JVMBridgeBaseCore.ClazzOf(_bridgeClassName, out _LocalBridgeClazzException, false); + private static readonly MASES.JCOBridge.C2JBridge.JVMInterop.IJavaType _LocalBridgeClazz = JVMBridgeCore.ClazzOf(_bridgeClassName, out _LocalBridgeClazzException, false); private static MASES.JCOBridge.C2JBridge.JVMInterop.IJavaType LocalBridgeClazz => _LocalBridgeClazz ?? throw _LocalBridgeClazzException ?? new global::System.InvalidOperationException($"Class {_bridgeClassName} was not found."); /// diff --git a/src/net/JNetReflector/Templates/AllPackageClassesStubClassListener.template b/src/net/JNetReflector/Templates/AllPackageClassesStubClassListener.template index d8a85ef033..8643565849 100644 --- a/src/net/JNetReflector/Templates/AllPackageClassesStubClassListener.template +++ b/src/net/JNetReflector/Templates/AllPackageClassesStubClassListener.template @@ -42,7 +42,7 @@ public partial class ALLPACKAGE_CLASSES_STUB_CLASS_DIRECT_PLACEHOLDER : ALLPACKA const string _bridgeClassName = "ALLPACKAGE_CLASSES_STUB_JAVACLASS_DIRECT_PLACEHOLDER"; private static readonly global::System.Exception _LocalBridgeClazzException = null; - private static readonly MASES.JCOBridge.C2JBridge.JVMInterop.IJavaType _LocalBridgeClazz = JVMBridgeBaseCore.ClazzOf(_bridgeClassName, out _LocalBridgeClazzException, false); + private static readonly MASES.JCOBridge.C2JBridge.JVMInterop.IJavaType _LocalBridgeClazz = JVMBridgeCore.ClazzOf(_bridgeClassName, out _LocalBridgeClazzException, false); private static MASES.JCOBridge.C2JBridge.JVMInterop.IJavaType LocalBridgeClazz => _LocalBridgeClazz ?? throw _LocalBridgeClazzException ?? new global::System.InvalidOperationException($"Class {_bridgeClassName} was not found."); /// diff --git a/src/net/JNetReflector/Templates/Templates.cs b/src/net/JNetReflector/Templates/Templates.cs index 56ab6cdd75..b9c64c8e4b 100644 --- a/src/net/JNetReflector/Templates/Templates.cs +++ b/src/net/JNetReflector/Templates/Templates.cs @@ -160,7 +160,7 @@ public class ClassStub public static string LISTENER_CLASS_BLOCK = " const string _bridgeClassName = \"ALLPACKAGE_CLASSES_STUB_JAVACLASS_PLACEHOLDER\";" + Environment.NewLine + " private static readonly global::System.Exception _LocalBridgeClazzException = null;" + Environment.NewLine - + " private static readonly MASES.JCOBridge.C2JBridge.JVMInterop.IJavaType _LocalBridgeClazz = JVMBridgeBaseCore.ClazzOf(_bridgeClassName, out _LocalBridgeClazzException, false);" + Environment.NewLine + + " private static readonly MASES.JCOBridge.C2JBridge.JVMInterop.IJavaType _LocalBridgeClazz = JVMBridgeCore.ClazzOf(_bridgeClassName, out _LocalBridgeClazzException, false);" + Environment.NewLine + " private static MASES.JCOBridge.C2JBridge.JVMInterop.IJavaType LocalBridgeClazz => _LocalBridgeClazz ?? throw _LocalBridgeClazzException ?? new global::System.InvalidOperationException($\"Class {_bridgeClassName} was not found.\");" + Environment.NewLine + " " + Environment.NewLine + " /// " + Environment.NewLine