Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
72 commits
Select commit Hold shift + click to select a range
2c03524
refactor(java): move buffer equality out of UnsafeOps
chaokunyang May 23, 2026
b9129ac
refactor(java): move direct buffer address access to MemoryBuffer
chaokunyang May 23, 2026
1fcdfea
remove unused api in UnsafeOps
chaokunyang May 23, 2026
83e1015
refactor(java): hide field offsets behind accessors
chaokunyang May 23, 2026
4179012
remove unused code
chaokunyang May 23, 2026
c3f76ef
remove UNSAFE.throwException usage
chaokunyang May 23, 2026
0add063
refactor(java): add Fory byte array streams
chaokunyang May 23, 2026
4603f53
refactor(java): route byte array stream wrapping through JDK access
chaokunyang May 23, 2026
8361bd4
remove unsafe fields sort
chaokunyang May 23, 2026
c563565
refactor(java): route private field access through accessors
chaokunyang May 23, 2026
1187a4a
refactor(java): move JDK access utilities to platform internal
chaokunyang May 23, 2026
26dec9d
fix(java): add JDK25 unsafe replacements
chaokunyang May 23, 2026
0947efd
feat(java): add JDK25 field accessor
chaokunyang May 23, 2026
1ed30dd
feat(java): add JDK25 zero-unsafe runtime path
chaokunyang May 24, 2026
b8ce0ab
chore(java): fix JDK25 memory buffer license header
chaokunyang May 24, 2026
287d5ef
fix(java): keep string coder lookup Java 8 safe
chaokunyang May 24, 2026
e947b5c
fix(ci): repair style and kotlin xlang peer jar
chaokunyang May 24, 2026
e9303f8
fix(ci): avoid android and graal field access failures
chaokunyang May 24, 2026
c8b8dda
style(ci): format java task helper
chaokunyang May 24, 2026
999fd92
fix(android): skip jdk string internals on android
chaokunyang May 24, 2026
e56a62d
fix(graalvm): use public string access in native images
chaokunyang May 24, 2026
607a180
fix(graalvm): avoid private string codegen in native images
chaokunyang May 24, 2026
047f712
perf(java): add JDK25 direct memory benchmark
chaokunyang May 24, 2026
139431b
perf(java): add JDK25 direct copy benchmark
chaokunyang May 24, 2026
79e7513
refactor(java): isolate string unsafe operations
chaokunyang May 24, 2026
d602cbf
refactor(java): remove versioned lambda serializer
chaokunyang May 24, 2026
a17ab3f
refactor(java): remove versioned serializers registry
chaokunyang May 24, 2026
1daa83e
docs(java): update final field mutation guidance
chaokunyang May 24, 2026
7272e5d
fix(java): restore final fields with method handles on JDK25
chaokunyang May 24, 2026
d5098bc
refactor(java): fold JDK25 codec builders into root
chaokunyang May 24, 2026
46f6324
feat(java): remove Unsafe for jdk25
chaokunyang May 24, 2026
f1f7b2e
fix(java): restore android and graal ci
chaokunyang May 24, 2026
0ff50fc
fix(java): open arrow memory on classpath
chaokunyang May 24, 2026
1517758
fix(java): restore graalvm array offset recompute
chaokunyang May 24, 2026
0152f45
chore: merge asf/main
chaokunyang Jun 1, 2026
63ad7e6
refactor(java): remove byte array stream private wrapping
chaokunyang Jun 1, 2026
844009e
feat(java): remove unsafe requirement on jdk25
chaokunyang Jun 1, 2026
050afdb
feat(java): remove unsafe requirement on jdk25
chaokunyang Jun 1, 2026
b3e2a8e
Merge remote-tracking branch 'asf/main' into remove_unsafe_for_jdk25
chaokunyang Jun 1, 2026
5692ad8
fix errors
chaokunyang Jun 2, 2026
7cee7e1
fix: stabilize jdk25 unsafe verification
chaokunyang Jun 2, 2026
e14b867
fix: preserve kotlin constructor containers
chaokunyang Jun 2, 2026
37dee62
refactor(java): isolate unsafe access for jdk25
chaokunyang Jun 2, 2026
0e3e9c5
refactor(java): clean jdk25 overlay branches
chaokunyang Jun 2, 2026
313035a
refactor(java): clean jdk25 hook lookups
chaokunyang Jun 2, 2026
4d5fe5c
refactor(java): clean constructor bypass allocation
chaokunyang Jun 2, 2026
7b2b42b
refactor(java): make constructor bypass allocator typed
chaokunyang Jun 2, 2026
b4c5947
refactor(java): close jdk25 unsafe class graph
chaokunyang Jun 2, 2026
a9c29fd
fix(java): repair allocator diagnostics and native image init
chaokunyang Jun 2, 2026
73fce7d
test(java): verify jdk25 multi-release class graph
chaokunyang Jun 2, 2026
6095e05
fix(java): preserve trusted lookup field writes
chaokunyang Jun 2, 2026
18a0890
refactor(java): collapse field accessor strategies
chaokunyang Jun 2, 2026
3232a2a
refactor(java): remove jdk25 hidden field accessors
chaokunyang Jun 2, 2026
9879a8b
fix(java): tighten jdk25 trusted lookup ownership
chaokunyang Jun 2, 2026
bb78ac7
refactor(java): rename field accessor owners
chaokunyang Jun 2, 2026
0e78b32
refactor(java): fold android field reflection into instance accessors
chaokunyang Jun 2, 2026
8e5a3bf
perf(java): specialize jdk25 generated field accessors
chaokunyang Jun 2, 2026
b47856d
refactor(java): root-own hidden nestmate definition
chaokunyang Jun 2, 2026
81767b4
feat(java): define JDK25 serializers as hidden nestmates
chaokunyang Jun 2, 2026
8e4ac84
fix(java): verify JDK25 module-path field access
chaokunyang Jun 2, 2026
16688ed
remove ForyConstructor
chaokunyang Jun 2, 2026
20ed6c8
refactor(java): clean object instantiator ownership
chaokunyang Jun 2, 2026
debe206
fix(java): support nonserializable reflection instantiation
chaokunyang Jun 2, 2026
f5105ce
refactor(java): simplify fory-core mrjar build
chaokunyang Jun 2, 2026
e83b31f
clean stale code/docs/tests
chaokunyang Jun 2, 2026
97ab18a
refactor(java): unify primitive object codec paths
chaokunyang Jun 2, 2026
6e84c4e
fix(java): align jdk25 access docs and jpms tests
chaokunyang Jun 2, 2026
199a47d
fix(java): support graalvm jdk25 object stream instantiation
chaokunyang Jun 2, 2026
d001d1f
fix(java): stabilize jdk25 ci paths
chaokunyang Jun 2, 2026
1afb098
chore: merge apache/main
chaokunyang Jun 2, 2026
8308f6e
cleanup code
chaokunyang Jun 3, 2026
3036e85
fix(java): stabilize jdk25 copy and graalvm hooks
chaokunyang Jun 3, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
203 changes: 202 additions & 1 deletion .agents/languages/java.md

Large diffs are not rendered by default.

8 changes: 8 additions & 0 deletions .agents/languages/kotlin.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,14 @@ Load this file when changing `kotlin/`.

- Run Kotlin Maven commands from within `kotlin/`.
- Kotlin serializers build on the Java implementation. If Java changed and the updated Java artifacts are not installed yet, run `cd ../java && mvn -T16 install -DskipTests` first.
- KSP `@ForyStruct` serializers that use a primary constructor map constructor parameters to
same-named source properties at generation time and call the constructor directly. Do not restore
`@ForyConstructor`, runtime constructor registration, or Kotlin `javaParameters` dependencies;
mutable no-argument structs should use `var` properties with `@ForyField`.
- Preserve serializer-family selection for Kotlin standard-library types already registered by
Fory. Do not auto-install a new serializer for an existing type-registered Kotlin class unless the
wire format matches the previous serializer family and old-payload/new-runtime compatibility is
tested.

## Commands

Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -169,7 +169,7 @@ jobs:
MY_VAR: "PATH"
strategy:
matrix:
java-version: ${{ fromJSON(needs.changes.outputs.java_code == 'true' && '["8","11","17","21","25"]' || '["8"]') }}
java-version: ${{ fromJSON(needs.changes.outputs.java_code == 'true' && '["8","11","17","21","25","26"]' || '["8"]') }}
steps:
- uses: actions/checkout@v5
- name: Set up JDK ${{ matrix.java-version }}
Expand Down
6 changes: 3 additions & 3 deletions .github/workflows/release-java-snapshot.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -32,15 +32,15 @@ jobs:
- name: Set up Maven Central Repository
uses: actions/setup-java@v4
with:
java-version: "11"
distribution: "adopt"
java-version: "25"
distribution: "temurin"
architecture: x64
cache: maven
server-id: apache.snapshots.https
server-username: NEXUS_USERNAME
server-password: NEXUS_PASSWORD
- name: Publish Fory Java Snapshot
run: python ./ci/run_ci.py java --version 11 --release
run: python ./ci/run_ci.py java --version 25 --release
env:
NEXUS_USERNAME: ${{ secrets.NEXUS_USER }}
NEXUS_PASSWORD: ${{ secrets.NEXUS_PW }}
13 changes: 13 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,19 @@ Gradle:
implementation "org.apache.fory:fory-core:1.1.0"
```

On JDK25+, open `java.lang.invoke` to Fory. Use `ALL-UNNAMED` when Fory is on
the classpath:

```bash
--add-opens=java.base/java.lang.invoke=ALL-UNNAMED
```

Use the Fory core module name when Fory is on the module path:

```bash
--add-opens=java.base/java.lang.invoke=org.apache.fory.core
```

**Scala**

sbt:
Expand Down
154 changes: 147 additions & 7 deletions benchmarks/java/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -212,13 +212,6 @@
</plugin>
</plugins>
</build>
<dependencies>
<dependency>
<groupId>org.apache.fory</groupId>
<artifactId>fory-simd</artifactId>
<version>${project.version}</version>
</dependency>
</dependencies>
</profile>
<profile>
<id>jmh</id>
Expand All @@ -236,6 +229,136 @@
</dependency>
</dependencies>
</profile>
<profile>
<id>jdk25-benchmark-mrjar-check</id>
<activation>
<jdk>[25,)</jdk>
</activation>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-antrun-plugin</artifactId>
<executions>
<execution>
<id>verify-benchmark-mrjar</id>
<phase>package</phase>
<goals>
<goal>run</goal>
</goals>
<configuration>
<target>
<property
name="jdk25.benchmark.check.dir"
value="${project.build.directory}/jdk25-benchmark-check"/>
<delete dir="${jdk25.benchmark.check.dir}"/>
<mkdir dir="${jdk25.benchmark.check.dir}"/>
<unzip
src="${project.build.directory}/${uberjar.name}.jar"
dest="${jdk25.benchmark.check.dir}">
<patternset>
<include
name="org/apache/fory/platform/UnsafeOps.class"/>
<include
name="META-INF/versions/25/org/apache/fory/platform/UnsafeOps.class"/>
<include
name="META-INF/versions/25/org/apache/fory/memory/LittleEndian.class"/>
<include
name="META-INF/versions/25/org/apache/fory/memory/MemoryBuffer.class"/>
<include
name="META-INF/versions/25/org/apache/fory/platform/internal/_UnsafeUtils.class"/>
<include
name="META-INF/versions/25/org/apache/fory/reflect/InstanceFieldAccessors.class"/>
<include
name="META-INF/versions/25/org/apache/fory/serializer/PlatformStringUtils.class"/>
</patternset>
</unzip>
<available
file="${jdk25.benchmark.check.dir}/org/apache/fory/platform/UnsafeOps.class"
property="jdk25.benchmark.rootunsafeops.present"/>
<available
file="${jdk25.benchmark.check.dir}/META-INF/versions/25/org/apache/fory/platform/UnsafeOps.class"
property="jdk25.benchmark.unsafeops.present"/>
<available
file="${jdk25.benchmark.check.dir}/META-INF/versions/25/org/apache/fory/memory/LittleEndian.class"
property="jdk25.benchmark.littleendian.present"/>
<available
file="${jdk25.benchmark.check.dir}/META-INF/versions/25/org/apache/fory/memory/MemoryBuffer.class"
property="jdk25.benchmark.memorybuffer.present"/>
<available
file="${jdk25.benchmark.check.dir}/META-INF/versions/25/org/apache/fory/platform/internal/_UnsafeUtils.class"
property="jdk25.benchmark.unsafeutils.present"/>
<available
file="${jdk25.benchmark.check.dir}/META-INF/versions/25/org/apache/fory/reflect/InstanceFieldAccessors.class"
property="jdk25.benchmark.instancefieldaccessors.present"/>
<available
file="${jdk25.benchmark.check.dir}/META-INF/versions/25/org/apache/fory/serializer/PlatformStringUtils.class"
property="jdk25.benchmark.platformstring.present"/>
<fail
if="jdk25.benchmark.rootunsafeops.present"
message="JDK25 benchmark jar must not contain root UnsafeOps class."/>
<fail
if="jdk25.benchmark.unsafeops.present"
message="JDK25 benchmark jar must not contain versioned UnsafeOps class."/>
<fail
unless="jdk25.benchmark.littleendian.present"
message="JDK25 benchmark jar is missing the versioned LittleEndian class."/>
<fail
unless="jdk25.benchmark.memorybuffer.present"
message="JDK25 benchmark jar is missing the versioned MemoryBuffer class."/>
<fail
unless="jdk25.benchmark.unsafeutils.present"
message="JDK25 benchmark jar is missing the versioned _UnsafeUtils class."/>
<fail
unless="jdk25.benchmark.instancefieldaccessors.present"
message="JDK25 benchmark jar is missing the versioned InstanceFieldAccessors class."/>
<fail
unless="jdk25.benchmark.platformstring.present"
message="JDK25 benchmark jar is missing the versioned PlatformStringUtils class."/>
<exec
executable="${java.home}/bin/jdeps"
failonerror="true"
outputproperty="jdk25.benchmark.jdeps.output">
<arg value="--multi-release"/>
<arg value="25"/>
<arg value="--ignore-missing-deps"/>
<arg value="-include"/>
<arg value="org\.apache\.fory\..*"/>
<arg value="-verbose:class"/>
<arg value="${project.build.directory}/${uberjar.name}.jar"/>
</exec>
<condition property="jdk25.benchmark.jdeps.sunmisc.present">
<contains string="${jdk25.benchmark.jdeps.output}" substring="-&gt; sun.misc"/>
</condition>
<condition property="jdk25.benchmark.jdeps.jdkinternalreflect.present">
<contains
string="${jdk25.benchmark.jdeps.output}"
substring="-&gt; jdk.internal.reflect"/>
</condition>
<fail
if="jdk25.benchmark.jdeps.sunmisc.present"
message="JDK25 benchmark jar must not expose sun.misc dependencies in the resolved class graph."/>
<fail
if="jdk25.benchmark.jdeps.jdkinternalreflect.present"
message="JDK25 benchmark jar must not expose jdk.internal.reflect dependencies in the resolved class graph."/>
<java
classname="org.apache.fory.benchmark.Jdk25MrJarCheck"
fork="true"
failonerror="true">
<classpath>
<pathelement location="${project.build.directory}/${uberjar.name}.jar"/>
</classpath>
<jvmarg value="--add-opens=java.base/java.lang.invoke=ALL-UNNAMED"/>
<jvmarg value="--sun-misc-unsafe-memory-access=deny"/>
</java>
</target>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</profile>
</profiles>

<build>
Expand Down Expand Up @@ -266,6 +389,7 @@
<configuration>
<archive>
<manifestEntries>
<Multi-Release>true</Multi-Release>
<Automatic-Module-Name>org.apache.fory.benchmark</Automatic-Module-Name>
</manifestEntries>
</archive>
Expand All @@ -287,6 +411,10 @@
<transformer
implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
<mainClass>org.openjdk.jmh.Main</mainClass>
<manifestEntries>
<Multi-Release>true</Multi-Release>
<Automatic-Module-Name>org.apache.fory.benchmark</Automatic-Module-Name>
</manifestEntries>
</transformer>
</transformers>
<filters>
Expand All @@ -298,6 +426,18 @@
<exclude>META-INF/*.RSA</exclude>
</excludes>
</filter>
<filter>
<artifact>org.apache.logging.log4j:*</artifact>
<excludes>
<exclude>META-INF/versions/**</exclude>
</excludes>
</filter>
<filter>
<artifact>com.fasterxml.jackson.core:jackson-core</artifact>
<excludes>
<exclude>META-INF/versions/**</exclude>
</excludes>
</filter>
</filters>
</configuration>
</execution>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@

import java.nio.ByteBuffer;
import org.apache.fory.memory.MemoryBuffer;
import org.apache.fory.serializer.StringEncodingUtils;
import org.apache.fory.util.StringUtils;
import org.openjdk.jmh.Main;
import org.openjdk.jmh.annotations.Benchmark;
Expand Down Expand Up @@ -99,7 +100,7 @@ public Object latinScalarCheck() {

@Benchmark
public Object latinSuperWordCheck() {
return StringUtils.isLatin(latinStrChars);
return StringEncodingUtils.isLatin(latinStrChars);
}

public static void main(String[] args) throws Exception {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@

import java.util.ArrayList;
import java.util.List;
import org.apache.fory.platform.UnsafeOps;

// Derived from
// https://github.com/RuedigerMoeller/fast-serialization/blob/e8da5591daa09452791dcd992ea4f83b20937be7/src/main/java/org/nustaq/serialization/util/FSTIdentity2IdMap.java.
Expand Down Expand Up @@ -405,20 +404,10 @@ public static void clear(int[] arr, int len) {
int count = 0;
final int emptyArrayLength = EMPTY_INT_ARRAY.length;
while (len - count > emptyArrayLength) {
UnsafeOps.copyMemory(
EMPTY_INT_ARRAY,
UnsafeOps.INT_ARRAY_OFFSET,
arr,
UnsafeOps.INT_ARRAY_OFFSET + count,
emptyArrayLength);
System.arraycopy(EMPTY_INT_ARRAY, 0, arr, count, emptyArrayLength);
count += emptyArrayLength;
}
UnsafeOps.copyMemory(
EMPTY_INT_ARRAY,
UnsafeOps.INT_ARRAY_OFFSET,
arr,
UnsafeOps.INT_ARRAY_OFFSET + count,
len - count);
System.arraycopy(EMPTY_INT_ARRAY, 0, arr, count, len - count);
}

public static void clear(Object[] arr, int len) {
Expand Down
Loading
Loading