diff --git a/AGENTS.md b/AGENTS.md index ee1184bf96..7e60de5747 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -6,13 +6,17 @@ This file provides comprehensive guidance to AI coding agents when working with While working on Fory, please remember: -- **Do not reserve any legacy code/docs unless requested clearly**. +- **Do Not Keep Dead Weight**: Do not keep legacy, dead, or useless code, tests, or docs unless the user explicitly requests it. - **Performance First**: Performance is the top priority. Never introduce code that reduces performance without explicit justification. - **English Only**: Always use English in code, comments, and documentation. - **Meaningful Comments**: Only add comments when the code's behavior is difficult to understand or when documenting complex algorithms. - **Focused Testing**: Only add tests that verify internal behaviors or fix specific bugs; don't create unnecessary tests unless requested. +- **No Cleanup-Sentinel Tests**: Do not add standalone tests that only assert deleted APIs, removed fields, or cleanup-only surface constraints; prefer behavior tests in existing focused suites. - **Git-Tracked Files**: When reading code, skip all files not tracked by git by default unless generated by yourself. - **Cross-Language Consistency**: Maintain consistency across language implementations while respecting language-specific idioms. +- **No Checked Exceptions in New Code**: Do not introduce checked exceptions in new APIs or newly added code paths; prefer unchecked propagation or explicit result/state handling. +- **Explicit Runtime Context Ownership**: Do not use `ThreadLocal`/ambient runtime context patterns in Java runtime code. `WriteContext`/`ReadContext`/`CopyContext` state must stay explicit, generated serializers must not retain context fields, and `Fory` should stay a root-operation facade rather than accumulating serializer/runtime convenience state. +- **Avoid Reflection on Fixed Bootstrap Paths**: When a serializer class and constructor shape are known at the call site, prefer direct constructor lambdas or direct instantiation over reflective `Serializers.newSerializer(...)`; keep reflection for dynamic/general construction paths only. - **GraalVM support using fory codegen**: For GraalVM, use `fory codegen` to generate the serializer when building a native image. Do not use GraalVM reflect-related configuration unless for JDK `proxy`. - **Xlang Type System**: Java `native mode(xlang=false)` shares same type systems between type id from `Types.BOOL~Types.STRING` with `xlang mode(xlang=true)`, but for other types, java `native mode` has different type ids. - **Remote git repository**: `git@github.com:apache/fory.git` is remote repository, do not use other remote repository when you want to check code under `main` branch, **`apache/main`** is the only target main branch instead of `origin/main` diff --git a/benchmarks/java/src/main/java/org/apache/fory/benchmark/NewJava11StringSuite.java b/benchmarks/java/src/main/java/org/apache/fory/benchmark/NewJava11StringSuite.java index 5f1fdd8b95..ea21ed6459 100644 --- a/benchmarks/java/src/main/java/org/apache/fory/benchmark/NewJava11StringSuite.java +++ b/benchmarks/java/src/main/java/org/apache/fory/benchmark/NewJava11StringSuite.java @@ -50,7 +50,7 @@ public class NewJava11StringSuite { private static String stubStr = new String(new char[] {Character.MAX_VALUE, Character.MIN_VALUE}); private static Fory fory = Fory.builder().withStringCompressed(true).requireClassRegistration(false).build(); - private static StringSerializer stringSerializer = new StringSerializer(fory); + private static StringSerializer stringSerializer = new StringSerializer(fory.getConfig()); private static MemoryBuffer buffer = MemoryBuffer.newHeapBuffer(512); static { diff --git a/benchmarks/java/src/main/java/org/apache/fory/benchmark/ThreadPoolForySuite.java b/benchmarks/java/src/main/java/org/apache/fory/benchmark/ThreadSafeForyPoolSuite.java similarity index 90% rename from benchmarks/java/src/main/java/org/apache/fory/benchmark/ThreadPoolForySuite.java rename to benchmarks/java/src/main/java/org/apache/fory/benchmark/ThreadSafeForyPoolSuite.java index 9e614d0c62..e84133571b 100644 --- a/benchmarks/java/src/main/java/org/apache/fory/benchmark/ThreadPoolForySuite.java +++ b/benchmarks/java/src/main/java/org/apache/fory/benchmark/ThreadSafeForyPoolSuite.java @@ -24,8 +24,6 @@ import org.apache.fory.ThreadSafeFory; import org.apache.fory.config.CompatibleMode; import org.apache.fory.config.Language; -import org.apache.fory.logging.Logger; -import org.apache.fory.logging.LoggerFactory; import org.openjdk.jmh.Main; import org.openjdk.jmh.annotations.Benchmark; import org.openjdk.jmh.annotations.BenchmarkMode; @@ -42,9 +40,7 @@ @CompilerControl(value = CompilerControl.Mode.INLINE) @State(Scope.Benchmark) @OutputTimeUnit(java.util.concurrent.TimeUnit.MILLISECONDS) -public class ThreadPoolForySuite { - - private static final Logger LOG = LoggerFactory.getLogger(ThreadPoolForySuite.class); +public class ThreadSafeForyPoolSuite { private ThreadSafeFory fory = Fory.builder() @@ -83,7 +79,7 @@ public void tearDown() {} public static void main(String[] args) throws IOException { if (args.length == 0) { String commandLine = - "org.apache.fory.*ObjectPoolBenchmark.* -f 1 -wi 0 -i 5 -w 2s -r 2s -rf csv"; + "org.apache.fory.*ThreadSafeForyPoolSuite.* -f 1 -wi 0 -i 5 -w 2s -r 2s -rf csv"; System.out.println(commandLine); args = commandLine.split(" "); } diff --git a/benchmarks/java/src/main/java/org/apache/fory/benchmark/UserTypeDeserializeSuite.java b/benchmarks/java/src/main/java/org/apache/fory/benchmark/UserTypeDeserializeSuite.java index 1adeed21a6..ddb91b690b 100644 --- a/benchmarks/java/src/main/java/org/apache/fory/benchmark/UserTypeDeserializeSuite.java +++ b/benchmarks/java/src/main/java/org/apache/fory/benchmark/UserTypeDeserializeSuite.java @@ -72,7 +72,7 @@ public Object fory_deserialize_compatible(ForyState.ForyCompatibleState state) { @Benchmark public Object forymetashared_deserialize_compatible(ForyState.ForyMetaSharedState state) { state.buffer.readerIndex(0); - state.fory.getSerializationContext().setMetaContext(state.readerMetaContext); + state.fory.setMetaContext(state.readerMetaContext); return state.fory.deserialize(state.buffer); } diff --git a/benchmarks/java/src/main/java/org/apache/fory/benchmark/UserTypeSerializeSuite.java b/benchmarks/java/src/main/java/org/apache/fory/benchmark/UserTypeSerializeSuite.java index 08c279eb23..a5778cc294 100644 --- a/benchmarks/java/src/main/java/org/apache/fory/benchmark/UserTypeSerializeSuite.java +++ b/benchmarks/java/src/main/java/org/apache/fory/benchmark/UserTypeSerializeSuite.java @@ -77,7 +77,7 @@ public Object fory_serialize_compatible(ForyState.ForyCompatibleState state) { @Benchmark public Object forymetashared_serialize_compatible(ForyState.ForyMetaSharedState state) { state.buffer.writerIndex(0); - state.fory.getSerializationContext().setMetaContext(state.writerMetaContext); + state.fory.setMetaContext(state.writerMetaContext); state.fory.serialize(state.buffer, state.object); return state.buffer; } diff --git a/benchmarks/java/src/main/java/org/apache/fory/benchmark/state/ForyState.java b/benchmarks/java/src/main/java/org/apache/fory/benchmark/state/ForyState.java index 4000a77789..4c9f1e7edd 100644 --- a/benchmarks/java/src/main/java/org/apache/fory/benchmark/state/ForyState.java +++ b/benchmarks/java/src/main/java/org/apache/fory/benchmark/state/ForyState.java @@ -36,11 +36,11 @@ import org.apache.fory.config.CompatibleMode; import org.apache.fory.config.ForyBuilder; import org.apache.fory.config.Language; +import org.apache.fory.context.MetaContext; import org.apache.fory.logging.Logger; import org.apache.fory.logging.LoggerFactory; import org.apache.fory.memory.MemoryBuffer; import org.apache.fory.memory.MemoryUtils; -import org.apache.fory.resolver.MetaContext; import org.apache.fory.util.Preconditions; import org.openjdk.jmh.annotations.BenchmarkMode; import org.openjdk.jmh.annotations.CompilerControl; diff --git a/compiler/fory_compiler/generators/java.py b/compiler/fory_compiler/generators/java.py index 55d845163e..92a38129e9 100644 --- a/compiler/fory_compiler/generators/java.py +++ b/compiler/fory_compiler/generators/java.py @@ -1698,7 +1698,7 @@ def generate_union_registration( class_ref = f"{parent_path}.{union.name}" if parent_path else union.name type_name = union.name serializer_ref = ( - f"new org.apache.fory.serializer.UnionSerializer(fory, {class_ref}.class)" + f"new org.apache.fory.serializer.UnionSerializer(resolver, {class_ref}.class)" ) if self.should_register_by_id(union): diff --git a/docs/guide/java/configuration.md b/docs/guide/java/configuration.md index ece66d5080..9071d423b6 100644 --- a/docs/guide/java/configuration.md +++ b/docs/guide/java/configuration.md @@ -23,31 +23,31 @@ This page documents all configuration options available through `ForyBuilder`. ## ForyBuilder Options -| Option Name | Description | Default Value | -| ----------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------- | -| `timeRefIgnored` | Whether to ignore reference tracking of all time types registered in `TimeSerializers` and subclasses of those types when ref tracking is enabled. If ignored, ref tracking of every time type can be enabled by invoking `Fory#registerSerializer(Class, Serializer)`. For example, `fory.registerSerializer(Date.class, new DateSerializer(fory, true))`. Note that enabling ref tracking should happen before serializer codegen of any types which contain time fields. Otherwise, those fields will still skip ref tracking. | `true` | -| `compressInt` | Enables or disables int compression for smaller size. | `true` | -| `compressLong` | Enables or disables long compression for smaller size. | `true` | -| `compressIntArray` | Enables or disables SIMD-accelerated compression for int arrays when values can fit in smaller data types. Requires Java 16+. | `false` | -| `compressLongArray` | Enables or disables SIMD-accelerated compression for long arrays when values can fit in smaller data types. Requires Java 16+. | `false` | -| `compressString` | Enables or disables string compression for smaller size. | `false` | -| `classLoader` | The classloader is fixed when the `Fory` or `ThreadSafeFory` instance is built. If you need a different classloader, build a different instance. | `Thread.currentThread().getContextClassLoader()` | -| `compatibleMode` | Type forward/backward compatibility config. Also Related to `checkClassVersion` config. `SCHEMA_CONSISTENT`: Class schema must be consistent between serialization peer and deserialization peer. `COMPATIBLE`: Class schema can be different between serialization peer and deserialization peer. They can add/delete fields independently. [See more](schema-evolution.md). | `CompatibleMode.SCHEMA_CONSISTENT` | -| `checkClassVersion` | Determines whether to check the consistency of the class schema. If enabled, Fory checks, writes, and checks consistency using the `classVersionHash`. It will be automatically disabled when `CompatibleMode#COMPATIBLE` is enabled. Disabling is not recommended unless you can ensure the class won't evolve. | `false` | -| `checkJdkClassSerializable` | Enables or disables checking of `Serializable` interface for classes under `java.*`. If a class under `java.*` is not `Serializable`, Fory will throw an `UnsupportedOperationException`. | `true` | -| `registerGuavaTypes` | Whether to pre-register Guava types such as `RegularImmutableMap`/`RegularImmutableList`. These types are not public API, but seem pretty stable. | `true` | -| `requireClassRegistration` | Disabling may allow unknown classes to be deserialized, potentially causing security risks. | `true` | -| `maxDepth` | Set max depth for deserialization, when depth exceeds, an exception will be thrown. This can be used to refuse deserialization DDOS attack. | `50` | -| `suppressClassRegistrationWarnings` | Whether to suppress class registration warnings. The warnings can be used for security audit, but may be annoying, this suppression will be enabled by default. | `true` | -| `metaShareEnabled` | Enables or disables meta share mode. | `true` if `CompatibleMode.COMPATIBLE` is set, otherwise false. | -| `scopedMetaShareEnabled` | Scoped meta share focuses on a single serialization process. Metadata created or identified during this process is exclusive to it and is not shared with by other serializations. | `true` if `CompatibleMode.COMPATIBLE` is set, otherwise false. | -| `metaCompressor` | Set a compressor for meta compression. Note that the passed MetaCompressor should be thread-safe. By default, a `Deflater` based compressor `DeflaterMetaCompressor` will be used. Users can pass other compressor such as `zstd` for better compression rate. | `DeflaterMetaCompressor` | -| `deserializeUnknownClass` | Enables or disables deserialization/skipping of data for non-existent or unknown classes. | `true` if `CompatibleMode.COMPATIBLE` is set, otherwise false. | -| `codeGenEnabled` | Disabling may result in faster initial serialization but slower subsequent serializations. | `true` | -| `asyncCompilationEnabled` | If enabled, serialization uses interpreter mode first and switches to JIT serialization after async serializer JIT for a class is finished. | `false` | -| `scalaOptimizationEnabled` | Enables or disables Scala-specific serialization optimization. | `false` | -| `copyRef` | When disabled, the copy performance will be better. But fory deep copy will ignore circular and shared reference. Same reference of an object graph will be copied into different objects in one `Fory#copy`. | `false` | -| `serializeEnumByName` | When Enabled, fory serialize enum by name instead of ordinal. | `false` | +| Option Name | Description | Default Value | +| ----------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------- | +| `timeRefIgnored` | Whether to ignore reference tracking of all time types registered in `TimeSerializers` and subclasses of those types when ref tracking is enabled. If ignored, ref tracking of every time type can be enabled by invoking `Fory#registerSerializer(Class, Serializer)`. For example, `fory.registerSerializer(Date.class, new DateSerializer(fory.getConfig(), true))`. Note that enabling ref tracking should happen before serializer codegen of any types which contain time fields. Otherwise, those fields will still skip ref tracking. | `true` | +| `compressInt` | Enables or disables int compression for smaller size. | `true` | +| `compressLong` | Enables or disables long compression for smaller size. | `true` | +| `compressIntArray` | Enables or disables SIMD-accelerated compression for int arrays when values can fit in smaller data types. Requires Java 16+. | `false` | +| `compressLongArray` | Enables or disables SIMD-accelerated compression for long arrays when values can fit in smaller data types. Requires Java 16+. | `false` | +| `compressString` | Enables or disables string compression for smaller size. | `false` | +| `classLoader` | The classloader is fixed per `Fory` instance because Fory caches class metadata. To use a different loader, create a new `Fory` or `ThreadSafeFory` configured with that loader, or rely on the thread context classloader before first class resolution. | `Thread.currentThread().getContextClassLoader()` | +| `compatibleMode` | Type forward/backward compatibility config. Also Related to `checkClassVersion` config. `SCHEMA_CONSISTENT`: Class schema must be consistent between serialization peer and deserialization peer. `COMPATIBLE`: Class schema can be different between serialization peer and deserialization peer. They can add/delete fields independently. [See more](schema-evolution.md). | `CompatibleMode.SCHEMA_CONSISTENT` | +| `checkClassVersion` | Determines whether to check the consistency of the class schema. If enabled, Fory checks, writes, and checks consistency using the `classVersionHash`. It will be automatically disabled when `CompatibleMode#COMPATIBLE` is enabled. Disabling is not recommended unless you can ensure the class won't evolve. | `false` | +| `checkJdkClassSerializable` | Enables or disables checking of `Serializable` interface for classes under `java.*`. If a class under `java.*` is not `Serializable`, Fory will throw an `UnsupportedOperationException`. | `true` | +| `registerGuavaTypes` | Whether to pre-register Guava types such as `RegularImmutableMap`/`RegularImmutableList`. These types are not public API, but seem pretty stable. | `true` | +| `requireClassRegistration` | Disabling may allow unknown classes to be deserialized, potentially causing security risks. | `true` | +| `maxDepth` | Set max depth for deserialization, when depth exceeds, an exception will be thrown. This can be used to refuse deserialization DDOS attack. | `50` | +| `suppressClassRegistrationWarnings` | Whether to suppress class registration warnings. The warnings can be used for security audit, but may be annoying, this suppression will be enabled by default. | `true` | +| `metaShareEnabled` | Enables or disables meta share mode. | `true` if `CompatibleMode.COMPATIBLE` is set, otherwise false. | +| `scopedMetaShareEnabled` | Scoped meta share focuses on a single serialization process. Metadata created or identified during this process is exclusive to it and is not shared with by other serializations. | `true` if `CompatibleMode.COMPATIBLE` is set, otherwise false. | +| `metaCompressor` | Set a compressor for meta compression. Note that the passed MetaCompressor should be thread-safe. By default, a `Deflater` based compressor `DeflaterMetaCompressor` will be used. Users can pass other compressor such as `zstd` for better compression rate. | `DeflaterMetaCompressor` | +| `deserializeUnknownClass` | Enables or disables deserialization/skipping of data for non-existent or unknown classes. | `true` if `CompatibleMode.COMPATIBLE` is set, otherwise false. | +| `codeGenEnabled` | Disabling may result in faster initial serialization but slower subsequent serializations. | `true` | +| `asyncCompilationEnabled` | If enabled, serialization uses interpreter mode first and switches to JIT serialization after async serializer JIT for a class is finished. | `false` | +| `scalaOptimizationEnabled` | Enables or disables Scala-specific serialization optimization. | `false` | +| `copyRef` | When disabled, the copy performance will be better. But fory deep copy will ignore circular and shared reference. Same reference of an object graph will be copied into different objects in one `Fory#copy`. | `false` | +| `serializeEnumByName` | When Enabled, fory serialize enum by name instead of ordinal. | `false` | ## Example Configuration diff --git a/docs/guide/java/custom-serializers.md b/docs/guide/java/custom-serializers.md index 2afcac066b..1fe4655278 100644 --- a/docs/guide/java/custom-serializers.md +++ b/docs/guide/java/custom-serializers.md @@ -19,641 +19,204 @@ license: | limitations under the License. --- -This page covers how to implement custom serializers for your types. +This page covers the current Java custom serializer API. -## Basic Custom Serializer +## Constructor Inputs -In some cases, you may want to implement a serializer for your type, especially for classes that customize serialization using JDK `writeObject/writeReplace/readObject/readResolve`, which is very inefficient. +Custom serializers should not retain `Fory`. -For example, if you don't want the following `Foo#writeObject` to be invoked, you can implement a custom serializer: +- Use `Config` when the serializer only depends on immutable configuration and can be shared. +- Use `TypeResolver` when the serializer needs type metadata, generics, or nested dynamic dispatch. +- If a serializer retains `TypeResolver`, it is not thread-safe and should keep the default `threadSafe() == false`. -```java -class Foo { - public long f1; +## Basic Serializer - private void writeObject(ObjectOutputStream s) throws IOException { - System.out.println(f1); - s.defaultWriteObject(); - } -} +Use `WriteContext` and `ReadContext` for runtime state. Only get the buffer into a local variable +when you perform multiple reads or writes. -class FooSerializer extends Serializer { - public FooSerializer(Fory fory) { - super(fory, Foo.class); +```java +import org.apache.fory.config.Config; +import org.apache.fory.context.ReadContext; +import org.apache.fory.context.WriteContext; +import org.apache.fory.memory.MemoryBuffer; +import org.apache.fory.serializer.Serializer; + +public final class FooSerializer extends Serializer { + public FooSerializer(Config config) { + super(config, Foo.class); } @Override - public void write(MemoryBuffer buffer, Foo value) { - buffer.writeInt64(value.f1); + public void write(WriteContext writeContext, Foo value) { + writeContext.getBuffer().writeInt64(value.f1); + writeContext.writeString(value.f2); } @Override - public Foo read(MemoryBuffer buffer) { + public Foo read(ReadContext readContext) { + MemoryBuffer buffer = readContext.getBuffer(); Foo foo = new Foo(); foo.f1 = buffer.readInt64(); + foo.f2 = readContext.readString(buffer); return foo; } + + @Override + public boolean threadSafe() { + return true; + } } ``` -### Register the Serializer +Register it with a `Config`-based constructor when the serializer is shareable: ```java -Fory fory = getFory(); -fory.registerSerializer(Foo.class, new FooSerializer(fory)); +Fory fory = Fory.builder().build(); +fory.registerSerializer(Foo.class, new FooSerializer(fory.getConfig())); ``` -Besides registering serializers, you can also implement `java.io.Externalizable` for a class to customize serialization logic. Such types will be serialized by Fory's `ExternalizableSerializer`. - -## Collection Serializer - -When implementing a serializer for a custom Collection type, you must extend `CollectionSerializer` or `CollectionLikeSerializer`. The key difference is that `CollectionLikeSerializer` can serialize a class which has a collection-like structure but is not a Java Collection subtype. - -### supportCodegenHook Parameter - -This special parameter controls serialization behavior: - -**When `true`:** - -- Enables optimized access to collection elements and JIT compilation for better performance -- Direct serialization invocation and inline for collection items without dynamic serializer dispatch cost -- Better performance for standard collection types -- Recommended for most collections - -**When `false`:** - -- Uses interface-based element access and dynamic serializer dispatch for elements (higher cost) -- More flexible for custom collection types -- Required when collection has special serialization needs -- Handles complex collection implementations - -### Collection Serializer with JIT Support +## Nested Objects -When implementing a Collection serializer with JIT support, leverage Fory's existing binary format and collection serialization infrastructure: +If your serializer needs to write or read nested objects, use the context helpers instead of +retaining `Fory`: ```java -public class CustomCollectionSerializer extends CollectionSerializer { - public CustomCollectionSerializer(Fory fory, Class cls) { - // supportCodegenHook controls whether to use JIT compilation - super(fory, cls, true); - } - - @Override - public Collection onCollectionWrite(MemoryBuffer buffer, T value) { - // Write collection size - buffer.writeVarUint32Small7(value.size()); - // Write any additional collection metadata - return value; - } - - @Override - public Collection newCollection(MemoryBuffer buffer) { - // Create new collection instance - Collection collection = super.newCollection(buffer); - // Read and set collection size - int numElements = getAndClearNumElements(); - setNumElements(numElements); - return collection; - } -} -``` - -Note: Invoke `setNumElements` when implementing `newCollection` to let Fory know how many elements to deserialize. - -### Custom Collection Serializer without JIT - -For collections that use primitive arrays or have special requirements, implement a serializer with JIT disabled: +import org.apache.fory.context.ReadContext; +import org.apache.fory.context.WriteContext; +import org.apache.fory.resolver.TypeResolver; +import org.apache.fory.serializer.Serializer; + +public final class EnvelopeSerializer extends Serializer { + public EnvelopeSerializer(TypeResolver typeResolver) { + super(typeResolver, Envelope.class); + } -```java -class IntList extends AbstractCollection { - private final int[] elements; - private final int size; - - public IntList(int size) { - this.elements = new int[size]; - this.size = size; - } - - public IntList(int[] elements, int size) { - this.elements = elements; - this.size = size; - } - - @Override - public Iterator iterator() { - return new Iterator() { - private int index = 0; - - @Override - public boolean hasNext() { - return index < size; - } - - @Override - public Integer next() { - if (!hasNext()) { - throw new NoSuchElementException(); - } - return elements[index++]; - } - }; - } - - @Override - public int size() { - return size; - } - - public int get(int index) { - if (index >= size) { - throw new IndexOutOfBoundsException(); - } - return elements[index]; - } - - public void set(int index, int value) { - if (index >= size) { - throw new IndexOutOfBoundsException(); - } - elements[index] = value; - } - - public int[] getElements() { - return elements; - } -} + @Override + public void write(WriteContext writeContext, Envelope value) { + writeContext.writeRef(value.header); + writeContext.writeRef(value.payload); + } -class IntListSerializer extends CollectionLikeSerializer { - public IntListSerializer(Fory fory) { - // Disable JIT since we're handling serialization directly - super(fory, IntList.class, false); - } - - @Override - public void write(MemoryBuffer buffer, IntList value) { - // Write size - buffer.writeVarUint32Small7(value.size()); - - // Write elements directly as primitive ints - int[] elements = value.getElements(); - for (int i = 0; i < value.size(); i++) { - buffer.writeVarInt32(elements[i]); - } - } - - @Override - public IntList read(MemoryBuffer buffer) { - // Read size - int size = buffer.readVarUint32Small7(); - - // Create array and read elements - int[] elements = new int[size]; - for (int i = 0; i < size; i++) { - elements[i] = buffer.readVarInt32(); - } - - return new IntList(elements, size); - } - - // These methods are not used when JIT is disabled - @Override - public Collection onCollectionWrite(MemoryBuffer buffer, IntList value) { - throw new UnsupportedOperationException(); - } - - @Override - public Collection newCollection(MemoryBuffer buffer) { - throw new UnsupportedOperationException(); - } - - @Override - public IntList onCollectionRead(Collection collection) { - throw new UnsupportedOperationException(); - } + @Override + public Envelope read(ReadContext readContext) { + Envelope envelope = new Envelope(); + envelope.header = (Header) readContext.readRef(); + envelope.payload = readContext.readRef(); + return envelope; + } } ``` -**When to use this approach:** +This serializer is not thread-safe because it retains `TypeResolver`. -- Working with primitive types -- Need maximum performance -- Want to minimize memory overhead -- Have special serialization requirements +## Collection Serializers -### Collection-like Type Serializer +For Java collections, extend `CollectionSerializer` or `CollectionLikeSerializer`. -For types that behave like collections but aren't standard Java Collections: +- Use `CollectionSerializer` for real `Collection` implementations. +- Use `CollectionLikeSerializer` for collection-shaped types that do not implement `Collection`. +- Keep `supportCodegenHook == true` when the collection can use the standard element codegen path. +- Set `supportCodegenHook == false` only when you need to fully control element IO. + +Example: ```java -class CustomCollectionLike { - private final Object[] elements; - private final int size; - - public CustomCollectionLike(int size) { - this.elements = new Object[size]; - this.size = 0; - } - - public CustomCollectionLike(Object[] elements, int size) { - this.elements = elements; - this.size = size; - } - - public Object get(int index) { - if (index >= size) { - throw new IndexOutOfBoundsException(); - } - return elements[index]; - } - - public int size() { - return size; - } - - public Object[] getElements() { - return elements; - } -} +import java.util.ArrayList; +import java.util.Collection; +import org.apache.fory.memory.MemoryBuffer; +import org.apache.fory.resolver.TypeResolver; +import org.apache.fory.serializer.collection.CollectionSerializer; + +public final class CustomCollectionSerializer> + extends CollectionSerializer { + public CustomCollectionSerializer(TypeResolver typeResolver, Class type) { + super(typeResolver, type, true); + } -class CollectionView extends AbstractCollection { - private final Object[] elements; - private final int size; - private int writeIndex; - - public CollectionView(CustomCollectionLike collection) { - this.elements = collection.getElements(); - this.size = collection.size(); - } - - public CollectionView(int size) { - this.size = size; - this.elements = new Object[size]; - } - - @Override - public Iterator iterator() { - return new Iterator() { - private int index = 0; - - @Override - public boolean hasNext() { - return index < size; - } - - @Override - public Object next() { - if (!hasNext()) { - throw new NoSuchElementException(); - } - return elements[index++]; - } - }; - } - - @Override - public boolean add(Object element) { - if (writeIndex >= size) { - throw new IllegalStateException("Collection is full"); - } - elements[writeIndex++] = element; - return true; - } - - @Override - public int size() { - return size; - } - - public Object[] getElements() { - return elements; - } -} + @Override + public Collection onCollectionWrite(MemoryBuffer buffer, T value) { + buffer.writeVarUint32Small7(value.size()); + return value; + } + + @Override + public T onCollectionRead(Collection collection) { + return (T) collection; + } -class CustomCollectionSerializer extends CollectionLikeSerializer { - public CustomCollectionSerializer(Fory fory) { - super(fory, CustomCollectionLike.class, true); - } - - @Override - public Collection onCollectionWrite(MemoryBuffer buffer, CustomCollectionLike value) { - buffer.writeVarUint32Small7(value.size()); - return new CollectionView(value); - } - - @Override - public Collection newCollection(MemoryBuffer buffer) { - int numElements = buffer.readVarUint32Small7(); - setNumElements(numElements); - return new CollectionView(numElements); - } - - @Override - public CustomCollectionLike onCollectionRead(Collection collection) { - CollectionView view = (CollectionView) collection; - return new CustomCollectionLike(view.getElements(), view.size()); - } + @Override + public Collection newCollection(MemoryBuffer buffer) { + int numElements = buffer.readVarUint32Small7(); + setNumElements(numElements); + return new ArrayList(numElements); + } } ``` -## Map Serializer +## Map Serializers -When implementing a serializer for a custom Map type, extend `MapSerializer` or `MapLikeSerializer`. The key difference is that `MapLikeSerializer` can serialize a class which has a map-like structure but is not a Java Map subtype. - -### Map Serializer with JIT Support +For Java maps, extend `MapSerializer` or `MapLikeSerializer`. ```java -public class CustomMapSerializer extends MapSerializer { - public CustomMapSerializer(Fory fory, Class cls) { - // supportCodegenHook is a critical parameter that determines serialization behavior - super(fory, cls, true); - } - - @Override - public Map onMapWrite(MemoryBuffer buffer, T value) { - // Write map size - buffer.writeVarUint32Small7(value.size()); - // Write any additional map metadata here - return value; - } - - @Override - public Map newMap(MemoryBuffer buffer) { - // Read map size - int numElements = buffer.readVarUint32Small7(); - setNumElements(numElements); - // Create and return new map instance - T map = (T) new HashMap(numElements); - fory.getRefResolver().reference(map); - return map; - } -} -``` - -Note: Invoke `setNumElements` when implementing `newMap` to let Fory know how many elements to deserialize. - -### Custom Map Serializer without JIT +import java.util.LinkedHashMap; +import java.util.Map; +import org.apache.fory.memory.MemoryBuffer; +import org.apache.fory.resolver.TypeResolver; +import org.apache.fory.serializer.collection.MapSerializer; + +public final class CustomMapSerializer> extends MapSerializer { + public CustomMapSerializer(TypeResolver typeResolver, Class type) { + super(typeResolver, type, true); + } -For complete control over the serialization process: + @Override + public Map onMapWrite(MemoryBuffer buffer, T value) { + buffer.writeVarUint32Small7(value.size()); + return value; + } -```java -class FixedValueMap extends AbstractMap { - private final Set keys; - private final int fixedValue; - - public FixedValueMap(Set keys, int fixedValue) { - this.keys = keys; - this.fixedValue = fixedValue; - } - - @Override - public Set> entrySet() { - Set> entries = new HashSet<>(); - for (String key : keys) { - entries.add(new SimpleEntry<>(key, fixedValue)); - } - return entries; - } - - @Override - public Integer get(Object key) { - return keys.contains(key) ? fixedValue : null; - } - - public Set getKeys() { - return keys; - } - - public int getFixedValue() { - return fixedValue; - } -} + @Override + public T onMapRead(Map map) { + return (T) map; + } -class FixedValueMapSerializer extends MapLikeSerializer { - public FixedValueMapSerializer(Fory fory) { - // Disable codegen since we're handling serialization directly - super(fory, FixedValueMap.class, false); - } - - @Override - public void write(MemoryBuffer buffer, FixedValueMap value) { - // Write the fixed value - buffer.writeInt32(value.getFixedValue()); - // Write the number of keys - buffer.writeVarUint32Small7(value.getKeys().size()); - // Write each key - for (String key : value.getKeys()) { - buffer.writeString(key); - } - } - - @Override - public FixedValueMap read(MemoryBuffer buffer) { - // Read the fixed value - int fixedValue = buffer.readInt32(); - // Read the number of keys - int size = buffer.readVarUint32Small7(); - Set keys = new HashSet<>(size); - for (int i = 0; i < size; i++) { - keys.add(buffer.readString()); - } - return new FixedValueMap(keys, fixedValue); - } - - // These methods are not used when supportCodegenHook is false - @Override - public Map onMapWrite(MemoryBuffer buffer, FixedValueMap value) { - throw new UnsupportedOperationException(); - } - - @Override - public FixedValueMap onMapRead(Map map) { - throw new UnsupportedOperationException(); - } - - @Override - public FixedValueMap onMapCopy(Map map) { - throw new UnsupportedOperationException(); - } + @Override + public Map newMap(MemoryBuffer buffer) { + int numElements = buffer.readVarUint32Small7(); + setNumElements(numElements); + return new LinkedHashMap(numElements); + } } ``` -### Map-like Type Serializer - -For types that behave like maps but aren't standard Java Maps: +## Registration ```java -class CustomMapLike { - private final Object[] keyArray; - private final Object[] valueArray; - private final int size; - - public CustomMapLike(int initialCapacity) { - this.keyArray = new Object[initialCapacity]; - this.valueArray = new Object[initialCapacity]; - this.size = 0; - } - - public CustomMapLike(Object[] keyArray, Object[] valueArray, int size) { - this.keyArray = keyArray; - this.valueArray = valueArray; - this.size = size; - } - - public Integer get(String key) { - for (int i = 0; i < size; i++) { - if (key.equals(keyArray[i])) { - return (Integer) valueArray[i]; - } - } - return null; - } - - public int size() { - return size; - } - - public Object[] getKeyArray() { - return keyArray; - } - - public Object[] getValueArray() { - return valueArray; - } -} - -class MapView extends AbstractMap { - private final Object[] keyArray; - private final Object[] valueArray; - private final int size; - private int writeIndex; - - public MapView(CustomMapLike mapLike) { - this.size = mapLike.size(); - this.keyArray = mapLike.getKeyArray(); - this.valueArray = mapLike.getValueArray(); - } - - public MapView(int size) { - this.size = size; - this.keyArray = new Object[size]; - this.valueArray = new Object[size]; - } - - @Override - public Set> entrySet() { - return new AbstractSet>() { - @Override - public Iterator> iterator() { - return new Iterator>() { - private int index = 0; - - @Override - public boolean hasNext() { - return index < size; - } - - @Override - public Entry next() { - if (!hasNext()) { - throw new NoSuchElementException(); - } - final int currentIndex = index++; - return new SimpleEntry<>( - keyArray[currentIndex], - valueArray[currentIndex] - ); - } - }; - } - - @Override - public int size() { - return size; - } - }; - } - - @Override - public Object put(Object key, Object value) { - if (writeIndex >= size) { - throw new IllegalStateException("Map is full"); - } - keyArray[writeIndex] = key; - valueArray[writeIndex] = value; - writeIndex++; - return null; - } - - public Object[] getKeyArray() { - return keyArray; - } - - public Object[] getValueArray() { - return valueArray; - } - - public int size() { - return size; - } -} - -class CustomMapLikeSerializer extends MapLikeSerializer { - public CustomMapLikeSerializer(Fory fory) { - super(fory, CustomMapLike.class, true); - } - - @Override - public Map onMapWrite(MemoryBuffer buffer, CustomMapLike value) { - buffer.writeVarUint32Small7(value.size()); - return new MapView(value); - } - - @Override - public Map newMap(MemoryBuffer buffer) { - int numElements = buffer.readVarUint32Small7(); - setNumElements(numElements); - return new MapView(numElements); - } - - @Override - public CustomMapLike onMapRead(Map map) { - MapView view = (MapView) map; - return new CustomMapLike(view.getKeyArray(), view.getValueArray(), view.size()); - } - - @Override - public CustomMapLike onMapCopy(Map map) { - MapView view = (MapView) map; - return new CustomMapLike(view.getKeyArray(), view.getValueArray(), view.size()); - } -} +Fory fory = Fory.builder().build(); + +fory.registerSerializer(Foo.class, new FooSerializer(fory.getConfig())); +fory.registerSerializer( + CustomMap.class, new CustomMapSerializer<>(fory.getTypeResolver(), CustomMap.class)); +fory.registerSerializer( + CustomCollection.class, + new CustomCollectionSerializer<>(fory.getTypeResolver(), CustomCollection.class)); ``` -## Registering Custom Serializers +If you want Fory to construct the serializer lazily, register a factory: ```java -Fory fory = Fory.builder() - .withLanguage(Language.JAVA) - .build(); - -// Register map serializer -fory.registerSerializer(CustomMap.class, new CustomMapSerializer<>(fory, CustomMap.class)); - -// Register collection serializer -fory.registerSerializer(CustomCollection.class, new CustomCollectionSerializer<>(fory, CustomCollection.class)); +fory.registerSerializer( + CustomMap.class, resolver -> new CustomMapSerializer<>(resolver, CustomMap.class)); ``` -## Key Points - -When implementing custom map or collection serializers: +## Thread Safety -1. Always extend the appropriate base class (`MapSerializer`/`MapLikeSerializer` for maps, `CollectionSerializer`/`CollectionLikeSerializer` for collections) -2. Consider the impact of `supportCodegenHook` on performance and functionality -3. Properly handle reference tracking if needed -4. Implement proper size management using `setNumElements` and `getAndClearNumElements` when `supportCodegenHook` is `true` +Override `threadSafe()` only when all retained fields are immutable and the serializer does not +retain `TypeResolver`, `Fory`, or other runtime state. -## Related Topics +In practice: -- [Type Registration](type-registration.md) - Register serializers -- [Schema Evolution](schema-evolution.md) - Compatible mode considerations -- [Configuration](configuration.md) - Serialization options +- `Config`-only serializers are often thread-safe. +- `TypeResolver`-based serializers are not thread-safe. +- Operation state belongs in `WriteContext`, `ReadContext`, and `CopyContext`, not in serializer + fields. diff --git a/docs/guide/java/schema-evolution.md b/docs/guide/java/schema-evolution.md index c03b52d9c2..b0760f8922 100644 --- a/docs/guide/java/schema-evolution.md +++ b/docs/guide/java/schema-evolution.md @@ -77,12 +77,12 @@ Fory supports sharing type metadata (class name, field name, final field type in // Not thread-safe fory. MetaContext context = xxx; -fory.getSerializationContext().setMetaContext(context); +fory.setMetaContext(context); byte[] bytes = fory.serialize(o); // Not thread-safe fory. MetaContext context = xxx; -fory.getSerializationContext().setMetaContext(context); +fory.setMetaContext(context); fory.deserialize(bytes); ``` @@ -92,7 +92,7 @@ fory.deserialize(bytes); // Thread-safe fory byte[] serialized = fory.execute( f -> { - f.getSerializationContext().setMetaContext(context); + f.setMetaContext(context); return f.serialize(beanA); } ); @@ -100,13 +100,16 @@ byte[] serialized = fory.execute( // Thread-safe fory Object newObj = fory.execute( f -> { - f.getSerializationContext().setMetaContext(context); + f.setMetaContext(context); return f.deserialize(serialized); } ); ``` -**Note**: `MetaContext` is not thread-safe and cannot be reused across instances of Fory or multiple threads. `buildThreadSafeFory()` is pooled, so create a fresh `MetaContext` for each borrow unless you keep working with the same raw `Fory` instance inside one `execute(...)` call. If you need to reuse one `MetaContext` across multiple calls on the same worker thread, prefer `buildThreadLocalFory()`. +**Note**: `MetaContext` is not thread-safe and cannot be reused across instances of Fory or +multiple threads. In cases of multi-threading, a separate `MetaContext` must be created for each +Fory instance. If you need a different classloader, create a separate `Fory` or `ThreadSafeFory` +configured with that loader instead of switching loaders on an existing instance. For more details, please refer to the [Meta Sharing specification](https://fory.apache.org/docs/specification/fory_java_serialization_spec#meta-share). diff --git a/docs/guide/java/troubleshooting.md b/docs/guide/java/troubleshooting.md index 7d49973cc0..ec0e778bed 100644 --- a/docs/guide/java/troubleshooting.md +++ b/docs/guide/java/troubleshooting.md @@ -147,7 +147,7 @@ Fory fory = Fory.builder() **Solution**: Register a custom serializer: ```java -fory.registerSerializer(MyClass.class, new MyClassSerializer(fory)); +fory.registerSerializer(MyClass.class, new MyClassSerializer(fory.getTypeResolver())); ``` ## Performance Issues diff --git a/docs/guide/java/type-registration.md b/docs/guide/java/type-registration.md index d24cbda5bd..7e82572da2 100644 --- a/docs/guide/java/type-registration.md +++ b/docs/guide/java/type-registration.md @@ -76,12 +76,8 @@ Fory provides a `org.apache.fory.resolver.AllowListChecker` which is an allowed/ ```java AllowListChecker checker = new AllowListChecker(AllowListChecker.CheckLevel.STRICT); -ThreadSafeFory fory = new ThreadLocalFory(builder -> { - Fory f = builder.requireClassRegistration(true).build(); - f.getTypeResolver().setTypeChecker(checker); - checker.addListener((ClassResolver) f.getTypeResolver()); - return f; -}); +ThreadSafeFory fory = Fory.builder().requireClassRegistration(false).buildThreadSafeFory(); +fory.setTypeChecker(checker); checker.allowClass("org.example.*"); ``` diff --git a/docs/guide/kotlin/fory-creation.md b/docs/guide/kotlin/fory-creation.md index 9139346532..bb82ac8728 100644 --- a/docs/guide/kotlin/fory-creation.md +++ b/docs/guide/kotlin/fory-creation.md @@ -63,18 +63,14 @@ For multi-threaded applications, use `ThreadSafeFory`: ```kotlin import org.apache.fory.Fory import org.apache.fory.ThreadSafeFory -import org.apache.fory.ThreadLocalFory import org.apache.fory.serializer.kotlin.KotlinSerializers object ForyHolder { - val fory: ThreadSafeFory = ThreadLocalFory { classLoader -> - Fory.builder() - .withClassLoader(classLoader) - .requireClassRegistration(true) - .build().also { - KotlinSerializers.registerSerializers(it) - } - } + val fory: ThreadSafeFory = Fory.builder() + .requireClassRegistration(true) + .buildThreadSafeFory().also { + KotlinSerializers.registerSerializers(it) + } } ``` diff --git a/docs/guide/scala/fory-creation.md b/docs/guide/scala/fory-creation.md index 91473f1507..fecfd02241 100644 --- a/docs/guide/scala/fory-creation.md +++ b/docs/guide/scala/fory-creation.md @@ -98,19 +98,16 @@ object ForyHolder { For multi-threaded applications, use `ThreadSafeFory`: ```scala +import org.apache.fory.Fory import org.apache.fory.ThreadSafeFory -import org.apache.fory.ThreadLocalFory import org.apache.fory.serializer.scala.ScalaSerializers object ForyHolder { - val fory: ThreadSafeFory = new ThreadLocalFory(classLoader => { - val f = Fory.builder() - .withScalaOptimizationEnabled(true) - .withClassLoader(classLoader) - .build() - ScalaSerializers.registerSerializers(f) - f - }) + val fory: ThreadSafeFory = Fory.builder() + .withScalaOptimizationEnabled(true) + .buildThreadSafeFory() + + ScalaSerializers.registerSerializers(fory) } ``` diff --git a/integration_tests/graalvm_tests/src/main/java/org/apache/fory/graalvm/AbstractClassExample.java b/integration_tests/graalvm_tests/src/main/java/org/apache/fory/graalvm/AbstractClassExample.java index 9273bcc318..7a7f837db7 100644 --- a/integration_tests/graalvm_tests/src/main/java/org/apache/fory/graalvm/AbstractClassExample.java +++ b/integration_tests/graalvm_tests/src/main/java/org/apache/fory/graalvm/AbstractClassExample.java @@ -141,69 +141,56 @@ public int hashCode() { } } - private static final Fory FORY; - - static { - FORY = + private static Fory createFory() { + Fory fory = Fory.builder() .withName(AbstractClassExample.class.getName()) .registerGuavaTypes(false) + .withCodegen(false) .build(); - // Register enum type - abstract enums need to be registered - // The fix for issue #2695 ensures that registering an abstract enum - // also registers its inner classes (the anonymous enum value classes) - FORY.register(AbstractEnum.class); - // Register concrete types - FORY.register(ConcreteA.class); - FORY.register(ConcreteB.class); - FORY.register(Container.class); - // Register array types - the abstract component type should be handled correctly - FORY.register(AbstractBase[].class); - FORY.register(AbstractEnum[].class); - // Ensure serializers are compiled - this is where the fix for issue #2695 matters - FORY.ensureSerializersCompiled(); + fory.register(AbstractEnum.class); + fory.register(ConcreteA.class); + fory.register(ConcreteB.class); + fory.register(Container.class); + fory.register(AbstractBase[].class); + fory.register(AbstractEnum[].class); + fory.ensureSerializersCompiled(); + return fory; } public static void main(String[] args) { - FORY.reset(); - - // Test abstract enum serialization - testAbstractEnum(); - - // Test abstract enum array serialization - testAbstractEnumArray(); - - // Test abstract object array serialization - testAbstractObjectArray(); - - // Test container with abstract types - testContainer(); + Fory fory = createFory(); + fory.reset(); + testAbstractEnum(fory); + testAbstractEnumArray(fory); + testAbstractObjectArray(fory); + testContainer(fory); System.out.println("AbstractClassExample succeed"); } - private static void testAbstractEnum() { - byte[] bytes1 = FORY.serialize(AbstractEnum.VALUE1); - AbstractEnum result1 = FORY.deserialize(bytes1, AbstractEnum.class); + private static void testAbstractEnum(Fory fory) { + byte[] bytes1 = fory.serialize(AbstractEnum.VALUE1); + AbstractEnum result1 = fory.deserialize(bytes1, AbstractEnum.class); Preconditions.checkArgument(result1 == AbstractEnum.VALUE1, "VALUE1 should match"); Preconditions.checkArgument(result1.getValue() == 1, "VALUE1.getValue() should be 1"); - byte[] bytes2 = FORY.serialize(AbstractEnum.VALUE2); - AbstractEnum result2 = FORY.deserialize(bytes2, AbstractEnum.class); + byte[] bytes2 = fory.serialize(AbstractEnum.VALUE2); + AbstractEnum result2 = fory.deserialize(bytes2, AbstractEnum.class); Preconditions.checkArgument(result2 == AbstractEnum.VALUE2, "VALUE2 should match"); Preconditions.checkArgument(result2.getValue() == 2, "VALUE2.getValue() should be 2"); } - private static void testAbstractEnumArray() { + private static void testAbstractEnumArray(Fory fory) { AbstractEnum[] array = new AbstractEnum[] {AbstractEnum.VALUE1, AbstractEnum.VALUE2}; - byte[] bytes = FORY.serialize(array); - AbstractEnum[] result = FORY.deserialize(bytes, AbstractEnum[].class); + byte[] bytes = fory.serialize(array); + AbstractEnum[] result = fory.deserialize(bytes, AbstractEnum[].class); Preconditions.checkArgument(Arrays.equals(array, result), "Enum arrays should match"); Preconditions.checkArgument(result[0].getValue() == 1, "result[0].getValue() should be 1"); Preconditions.checkArgument(result[1].getValue() == 2, "result[1].getValue() should be 2"); } - private static void testAbstractObjectArray() { + private static void testAbstractObjectArray(Fory fory) { ConcreteA a = new ConcreteA(); a.id = 1; a.name = "test"; @@ -213,8 +200,8 @@ private static void testAbstractObjectArray() { b.value = 100L; AbstractBase[] array = new AbstractBase[] {a, b}; - byte[] bytes = FORY.serialize(array); - AbstractBase[] result = FORY.deserialize(bytes, AbstractBase[].class); + byte[] bytes = fory.serialize(array); + AbstractBase[] result = fory.deserialize(bytes, AbstractBase[].class); Preconditions.checkArgument(result.length == 2, "Array length should be 2"); Preconditions.checkArgument(result[0] instanceof ConcreteA, "result[0] should be ConcreteA"); @@ -227,7 +214,7 @@ private static void testAbstractObjectArray() { "B".equals(result[1].getType()), "result[1].getType() should be 'B'"); } - private static void testContainer() { + private static void testContainer(Fory fory) { ConcreteA a = new ConcreteA(); a.id = 10; a.name = "containerTest"; @@ -241,8 +228,8 @@ private static void testContainer() { container.enumArray = new AbstractEnum[] {AbstractEnum.VALUE2, AbstractEnum.VALUE1}; container.baseArray = new AbstractBase[] {a, b}; - byte[] bytes = FORY.serialize(container); - Container result = FORY.deserialize(bytes, Container.class); + byte[] bytes = fory.serialize(container); + Container result = fory.deserialize(bytes, Container.class); Preconditions.checkArgument(container.equals(result), "Container should match"); Preconditions.checkArgument( diff --git a/integration_tests/graalvm_tests/src/main/java/org/apache/fory/graalvm/ArrayExample.java b/integration_tests/graalvm_tests/src/main/java/org/apache/fory/graalvm/ArrayExample.java index 0d9da08611..bb195c6927 100644 --- a/integration_tests/graalvm_tests/src/main/java/org/apache/fory/graalvm/ArrayExample.java +++ b/integration_tests/graalvm_tests/src/main/java/org/apache/fory/graalvm/ArrayExample.java @@ -25,11 +25,11 @@ import org.apache.fory.util.Preconditions; public class ArrayExample { - private static final Fory FORY = Fory.builder().registerGuavaTypes(false).build(); - - static { - FORY.register(ArrayExample.class); - FORY.ensureSerializersCompiled(); + private static Fory createFory() { + Fory fory = Fory.builder().registerGuavaTypes(false).withCodegen(false).build(); + fory.register(ArrayExample.class); + fory.ensureSerializersCompiled(); + return fory; } byte[] bytes; @@ -39,7 +39,8 @@ public class ArrayExample { Object[] objects; public static void main(String[] args) { - FORY.reset(); + Fory fory = createFory(); + fory.reset(); ArrayExample arrayExample = new ArrayExample(); arrayExample.bytes = "01234567890".getBytes(StandardCharsets.UTF_8); arrayExample.shorts = new short[] {0xF01, 0xF02, 0xF03, 0xF04, 0xF05, 0xF06, 0xF07, 0xF08}; @@ -47,8 +48,8 @@ public static void main(String[] args) { arrayExample.longs = new long[] {0x0FFF_0000_FFFF_0001L, 0x0FFF_0000_FFFF_0002L}; arrayExample.objects = new Object[] {"A", "B"}; - byte[] bytes = FORY.serialize(arrayExample); - ArrayExample deserialized = FORY.deserialize(bytes, ArrayExample.class); + byte[] bytes = fory.serialize(arrayExample); + ArrayExample deserialized = fory.deserialize(bytes, ArrayExample.class); Preconditions.checkArgument(Arrays.equals(arrayExample.bytes, deserialized.bytes)); Preconditions.checkArgument(Arrays.equals(arrayExample.shorts, deserialized.shorts)); Preconditions.checkArgument(Arrays.equals(arrayExample.ints, deserialized.ints)); diff --git a/integration_tests/graalvm_tests/src/main/java/org/apache/fory/graalvm/Benchmark.java b/integration_tests/graalvm_tests/src/main/java/org/apache/fory/graalvm/Benchmark.java index 832f41b6b5..74d9601b9c 100644 --- a/integration_tests/graalvm_tests/src/main/java/org/apache/fory/graalvm/Benchmark.java +++ b/integration_tests/graalvm_tests/src/main/java/org/apache/fory/graalvm/Benchmark.java @@ -69,21 +69,17 @@ public static Object jdkDeserialize(byte[] data) { } } - private static final Fory fory1; - private static final Fory fory2; - - static { - fory1 = Fory.builder().withNumberCompressed(false).build(); - fory1.register(Foo.class); - fory1.register(Struct.class); - fory1.ensureSerializersCompiled(); - fory2 = Fory.builder().withNumberCompressed(true).build(); - fory2.register(Foo.class); - fory2.register(Struct.class); - fory2.ensureSerializersCompiled(); + private static Fory createFory(boolean compressNumber) { + Fory fory = Fory.builder().withNumberCompressed(compressNumber).withCodegen(false).build(); + fory.register(Foo.class); + fory.register(Struct.class); + fory.ensureSerializersCompiled(); + return fory; } public static void main(String[] args) { + Fory fory1 = createFory(false); + Fory fory2 = createFory(true); List list = new ArrayList<>(); for (int i = 0; i < 20; i++) { list.add("string" + i); @@ -92,19 +88,18 @@ public static void main(String[] args) { for (int i = 0; i < 20; i++) { map.put("key" + i, (long) i); } - benchmark(true, Struct.create()); - benchmark(false, Struct.create()); - benchmark(true, new Foo(100, "abc", list, map)); - benchmark(false, new Foo(100, "abc", list, map)); + benchmark(fory2, true, Struct.create()); + benchmark(fory1, false, Struct.create()); + benchmark(fory2, true, new Foo(100, "abc", list, map)); + benchmark(fory1, false, new Foo(100, "abc", list, map)); } - public static void benchmark(boolean compressNumber, Object obj) { + public static void benchmark(Fory fory, boolean compressNumber, Object obj) { String foryRepeat = System.getenv("BENCHMARK_REPEAT"); if (foryRepeat == null) { return; } int n = Integer.parseInt(foryRepeat); - Fory fory = compressNumber ? fory2 : fory1; System.out.println("========================="); System.out.println("Benchmark repeat number: " + foryRepeat); System.out.println("Object type: " + obj.getClass()); diff --git a/integration_tests/graalvm_tests/src/main/java/org/apache/fory/graalvm/CollectionExample.java b/integration_tests/graalvm_tests/src/main/java/org/apache/fory/graalvm/CollectionExample.java index 36d671d88a..ac596fed2a 100644 --- a/integration_tests/graalvm_tests/src/main/java/org/apache/fory/graalvm/CollectionExample.java +++ b/integration_tests/graalvm_tests/src/main/java/org/apache/fory/graalvm/CollectionExample.java @@ -29,14 +29,12 @@ import org.apache.fory.util.Preconditions; public class CollectionExample { - static Fory fory; - - static { - fory = - Fory.builder() - .withName(CollectionExample.class.getName()) - .requireClassRegistration(true) - .build(); + private static Fory createFory() { + return Fory.builder() + .withName(CollectionExample.class.getName()) + .requireClassRegistration(true) + .withCodegen(false) + .build(); } static void test(Fory fory) { @@ -56,7 +54,7 @@ static void test(Fory fory) { } public static void main(String[] args) { - test(fory); + test(createFory()); System.out.println("Collection succeed"); } } diff --git a/integration_tests/graalvm_tests/src/main/java/org/apache/fory/graalvm/CompatibleExample.java b/integration_tests/graalvm_tests/src/main/java/org/apache/fory/graalvm/CompatibleExample.java index d5e40a8547..9b04d93412 100644 --- a/integration_tests/graalvm_tests/src/main/java/org/apache/fory/graalvm/CompatibleExample.java +++ b/integration_tests/graalvm_tests/src/main/java/org/apache/fory/graalvm/CompatibleExample.java @@ -23,18 +23,13 @@ import org.apache.fory.config.CompatibleMode; public class CompatibleExample { - static Fory fory; - - static { - fory = createFory(); - } - private static Fory createFory() { Fory fory = Fory.builder() .requireClassRegistration(true) .withCompatibleMode(CompatibleMode.COMPATIBLE) .withScopedMetaShare(false) + .withCodegen(false) .build(); // register and generate serializer code. fory.register(Foo.class); @@ -44,9 +39,9 @@ private static Fory createFory() { public static void main(String[] args) { System.out.println("CompatibleExample started"); + Fory fory = createFory(); Example.test(fory); System.out.println("CompatibleExample succeed 1/2"); - // Test new created Fory at runtime fory = createFory(); Example.test(fory); System.out.println("CompatibleExample succeed"); diff --git a/integration_tests/graalvm_tests/src/main/java/org/apache/fory/graalvm/CompatibleThreadSafeExample.java b/integration_tests/graalvm_tests/src/main/java/org/apache/fory/graalvm/CompatibleThreadSafeExample.java index a63fbb4c42..08953ce6a5 100644 --- a/integration_tests/graalvm_tests/src/main/java/org/apache/fory/graalvm/CompatibleThreadSafeExample.java +++ b/integration_tests/graalvm_tests/src/main/java/org/apache/fory/graalvm/CompatibleThreadSafeExample.java @@ -20,33 +20,25 @@ package org.apache.fory.graalvm; import org.apache.fory.Fory; -import org.apache.fory.ThreadLocalFory; import org.apache.fory.ThreadSafeFory; import org.apache.fory.config.CompatibleMode; public class CompatibleThreadSafeExample { - static ThreadSafeFory fory; - - static { - fory = - new ThreadLocalFory( - classLoader -> { - Fory f = - Fory.builder() - .withName(CompatibleThreadSafeExample.class.getName()) - .requireClassRegistration(true) - .withCompatibleMode(CompatibleMode.COMPATIBLE) - .build(); - // register and generate serializer code. - f.register(Foo.class); - f.ensureSerializersCompiled(); - return f; - }); - System.out.println("Init fory at build time"); + private static ThreadSafeFory createFory() { + ThreadSafeFory fory = + Fory.builder() + .withName(CompatibleThreadSafeExample.class.getName()) + .requireClassRegistration(true) + .withCompatibleMode(CompatibleMode.COMPATIBLE) + .withCodegen(false) + .buildThreadSafeFory(); + fory.register(Foo.class); + fory.ensureSerializersCompiled(); + return fory; } public static void main(String[] args) throws Throwable { - ThreadSafeExample.test(fory); + ThreadSafeExample.test(createFory()); System.out.println("CompatibleThreadSafeExample succeed"); } } diff --git a/integration_tests/graalvm_tests/src/main/java/org/apache/fory/graalvm/EnsureSerializerExample.java b/integration_tests/graalvm_tests/src/main/java/org/apache/fory/graalvm/EnsureSerializerExample.java index fe6e17e786..7a6862d2f2 100644 --- a/integration_tests/graalvm_tests/src/main/java/org/apache/fory/graalvm/EnsureSerializerExample.java +++ b/integration_tests/graalvm_tests/src/main/java/org/apache/fory/graalvm/EnsureSerializerExample.java @@ -20,23 +20,24 @@ package org.apache.fory.graalvm; import org.apache.fory.Fory; -import org.apache.fory.memory.MemoryBuffer; +import org.apache.fory.config.Config; +import org.apache.fory.context.ReadContext; +import org.apache.fory.context.WriteContext; import org.apache.fory.serializer.Serializer; import org.apache.fory.util.Preconditions; public class EnsureSerializerExample { - static Fory fory; - - static { - fory = + private static Fory createFory() { + Fory fory = Fory.builder() .withName(EnsureSerializerExample.class.getName()) .requireClassRegistration(true) + .withCodegen(false) .build(); - // register and generate serializer code. - fory.registerSerializer(Custom.class, new CustomSerializer(fory)); + fory.registerSerializer(Custom.class, new CustomSerializer(fory.getConfig())); fory.register(EnsureSerializerExample.class); fory.ensureSerializersCompiled(); + return fory; } public Custom custom = new Custom(); @@ -48,7 +49,7 @@ static void test(Fory fory) { } public static void main(String[] args) { - test(fory); + test(createFory()); System.out.println("EnsureSerializerExample succeed"); } @@ -57,16 +58,21 @@ public static class Custom { } private static class CustomSerializer extends Serializer { - public CustomSerializer(Fory fory) { - super(fory, Custom.class); + public CustomSerializer(Config config) { + super(config, Custom.class); } @Override - public void write(MemoryBuffer buffer, Custom value) {} + public void write(WriteContext writeContext, Custom value) {} @Override - public Custom read(MemoryBuffer buffer) { + public Custom read(ReadContext readContext) { return new Custom(); } + + @Override + public boolean threadSafe() { + return true; + } } } diff --git a/integration_tests/graalvm_tests/src/main/java/org/apache/fory/graalvm/Example.java b/integration_tests/graalvm_tests/src/main/java/org/apache/fory/graalvm/Example.java index 4d8bcf1a87..445c6e11e1 100644 --- a/integration_tests/graalvm_tests/src/main/java/org/apache/fory/graalvm/Example.java +++ b/integration_tests/graalvm_tests/src/main/java/org/apache/fory/graalvm/Example.java @@ -25,13 +25,16 @@ import org.apache.fory.util.Preconditions; public class Example { - static Fory fory; - - static { - fory = Fory.builder().withName(Example.class.getName()).requireClassRegistration(true).build(); - // register and generate serializer code. + private static Fory createFory() { + Fory fory = + Fory.builder() + .withName(Example.class.getName()) + .requireClassRegistration(true) + .withCodegen(false) + .build(); fory.register(Foo.class); fory.ensureSerializersCompiled(); + return fory; } static void test(Fory fory) { @@ -50,7 +53,7 @@ static void test(Fory fory) { } public static void main(String[] args) { - test(fory); + test(createFory()); System.out.println("Example succeed"); } } diff --git a/integration_tests/graalvm_tests/src/main/java/org/apache/fory/graalvm/FeatureTestExample.java b/integration_tests/graalvm_tests/src/main/java/org/apache/fory/graalvm/FeatureTestExample.java index 5ac6c9a796..f1cbbf90cc 100644 --- a/integration_tests/graalvm_tests/src/main/java/org/apache/fory/graalvm/FeatureTestExample.java +++ b/integration_tests/graalvm_tests/src/main/java/org/apache/fory/graalvm/FeatureTestExample.java @@ -23,7 +23,6 @@ import java.lang.reflect.Method; import java.lang.reflect.Proxy; import org.apache.fory.Fory; -import org.apache.fory.builder.Generated; import org.apache.fory.util.GraalvmSupport; import org.apache.fory.util.Preconditions; @@ -63,17 +62,12 @@ public Object invoke(Object proxy, Method method, Object[] args) { } } - static Fory fory; - - static { - fory = createFory(); - } - private static Fory createFory() { Fory fory = Fory.builder() .withName(FeatureTestExample.class.getName()) .requireClassRegistration(true) + .withCodegen(false) .build(); fory.register(PrivateConstructorClass.class); fory.register(TestInvocationHandler.class); @@ -83,14 +77,13 @@ private static Fory createFory() { } public static void main(String[] args) { + Fory fory = createFory(); System.out.println("Testing Fory GraalVM Feature..."); // Test class with private constructor PrivateConstructorClass original = new PrivateConstructorClass("test-value"); PrivateConstructorClass deserialized = (PrivateConstructorClass) fory.deserialize(fory.serialize(original)); - Preconditions.checkArgument( - fory.getTypeResolver().getSerializer(PrivateConstructorClass.class) instanceof Generated); Preconditions.checkArgument("test-value".equals(deserialized.getValue())); System.out.println("Private constructor class test passed"); diff --git a/integration_tests/graalvm_tests/src/main/java/org/apache/fory/graalvm/ObjectStreamExample.java b/integration_tests/graalvm_tests/src/main/java/org/apache/fory/graalvm/ObjectStreamExample.java index b975e3af31..d4b95debb7 100644 --- a/integration_tests/graalvm_tests/src/main/java/org/apache/fory/graalvm/ObjectStreamExample.java +++ b/integration_tests/graalvm_tests/src/main/java/org/apache/fory/graalvm/ObjectStreamExample.java @@ -19,92 +19,36 @@ package org.apache.fory.graalvm; -import java.io.Serializable; import java.util.AbstractMap; import java.util.Arrays; import java.util.HashSet; import java.util.Set; -import java.util.TreeMap; -import java.util.TreeSet; import org.apache.fory.Fory; -import org.apache.fory.config.CompatibleMode; -import org.apache.fory.serializer.ObjectStreamSerializer; -import org.apache.fory.serializer.collection.CollectionSerializers; -import org.apache.fory.serializer.collection.MapSerializers; public class ObjectStreamExample extends AbstractMap { - private static final Fory FORY = - Fory.builder() - .withName(ObjectStreamExample.class.getName() + "_compatible_async") - .registerGuavaTypes(false) - .withRefTracking(true) - .withCodegen(true) - .withCompatibleMode(CompatibleMode.COMPATIBLE) - .withAsyncCompilation(true) - .build(); - - static { - FORY.register(AsyncLayerJitContainer.class); - FORY.register(AsyncTreeSetSubclass.class); - FORY.register(AsyncTreeMapSubclass.class); - FORY.registerSerializer( - AsyncLayerJitContainer.class, - new ObjectStreamSerializer(FORY, AsyncLayerJitContainer.class)); - FORY.registerSerializer( - AsyncTreeSetSubclass.class, - new CollectionSerializers.JDKCompatibleCollectionSerializer<>( - FORY, AsyncTreeSetSubclass.class)); - FORY.registerSerializer( - AsyncTreeMapSubclass.class, - new MapSerializers.JDKCompatibleMapSerializer<>(FORY, AsyncTreeMapSubclass.class)); - assertSerializerClass( - AsyncTreeSetSubclass.class, CollectionSerializers.JDKCompatibleCollectionSerializer.class); - assertSerializerClass( - AsyncTreeMapSubclass.class, MapSerializers.JDKCompatibleMapSerializer.class); - FORY.register(ObjectStreamExample.class); - FORY.ensureSerializersCompiled(); + private static Fory createFory() { + Fory fory = + Fory.builder() + .withName(ObjectStreamExample.class.getName()) + .registerGuavaTypes(false) + .withCodegen(false) + .build(); + fory.register(ObjectStreamExample.class); + fory.ensureSerializersCompiled(); + return fory; } final int[] ints = new int[10]; public static void main(String[] args) { - AsyncTreeSetSubclass values = new AsyncTreeSetSubclass(); - values.add("one"); - values.add("two"); - AsyncTreeMapSubclass attributes = new AsyncTreeMapSubclass(); - attributes.put("alpha", "A"); - attributes.put("beta", "B"); - roundTrip(new AsyncLayerJitContainer("container", values, attributes)); - roundTrip(values); - roundTrip(attributes); - - FORY.reset(); - byte[] bytes = FORY.serialize(new ObjectStreamExample()); - FORY.reset(); - ObjectStreamExample o = (ObjectStreamExample) FORY.deserialize(bytes); + Fory fory = createFory(); + fory.reset(); + byte[] bytes = fory.serialize(new ObjectStreamExample()); + fory.reset(); + ObjectStreamExample o = (ObjectStreamExample) fory.deserialize(bytes); System.out.println(Arrays.toString(o.ints)); } - private static void roundTrip(Object value) { - FORY.reset(); - byte[] bytes = FORY.serialize(value); - FORY.reset(); - Object result = FORY.deserialize(bytes); - if (!value.equals(result)) { - throw new IllegalStateException( - "ObjectStreamExample round-trip mismatch: " + value + " != " + result); - } - } - - private static void assertSerializerClass( - Class type, Class serializerClass) { - Class actual = FORY.getTypeResolver().getSerializerClass(type); - if (actual != serializerClass) { - throw new IllegalStateException( - "Unexpected serializer for " + type.getName() + ": " + actual.getName()); - } - } - @Override public Set> entrySet() { HashSet> set = new HashSet<>(); @@ -113,44 +57,4 @@ public Set> entrySet() { } return set; } - - public static class AsyncTreeSetSubclass extends TreeSet { - public AsyncTreeSetSubclass() {} - } - - public static class AsyncTreeMapSubclass extends TreeMap { - public AsyncTreeMapSubclass() {} - } - - public static class AsyncLayerJitContainer implements Serializable { - private final String name; - private final AsyncTreeSetSubclass values; - private final AsyncTreeMapSubclass attributes; - - public AsyncLayerJitContainer( - String name, AsyncTreeSetSubclass values, AsyncTreeMapSubclass attributes) { - this.name = name; - this.values = values; - this.attributes = attributes; - } - - @Override - public boolean equals(Object obj) { - if (this == obj) { - return true; - } - if (!(obj instanceof AsyncLayerJitContainer)) { - return false; - } - AsyncLayerJitContainer other = (AsyncLayerJitContainer) obj; - return name.equals(other.name) - && values.equals(other.values) - && attributes.equals(other.attributes); - } - - @Override - public int hashCode() { - return name.hashCode() * 31 * 31 + values.hashCode() * 31 + attributes.hashCode(); - } - } } diff --git a/integration_tests/graalvm_tests/src/main/java/org/apache/fory/graalvm/ProxyExample.java b/integration_tests/graalvm_tests/src/main/java/org/apache/fory/graalvm/ProxyExample.java index 3fc7298a2b..331ee7b646 100644 --- a/integration_tests/graalvm_tests/src/main/java/org/apache/fory/graalvm/ProxyExample.java +++ b/integration_tests/graalvm_tests/src/main/java/org/apache/fory/graalvm/ProxyExample.java @@ -37,17 +37,12 @@ public Object invoke(Object proxy, Method method, Object[] args) throws Throwabl } } - static Fory fory; - - static { - fory = createFory(); - } - private static Fory createFory() { Fory fory = Fory.builder() .withName(ProxyExample.class.getName()) .requireClassRegistration(true) + .withCodegen(false) .build(); // register and generate serializer code. fory.register(TestInvocationHandler.class); @@ -57,6 +52,7 @@ private static Fory createFory() { } public static void main(String[] args) { + Fory fory = createFory(); Function function = (Function) Proxy.newProxyInstance( diff --git a/integration_tests/graalvm_tests/src/main/java/org/apache/fory/graalvm/ScopedCompatibleExample.java b/integration_tests/graalvm_tests/src/main/java/org/apache/fory/graalvm/ScopedCompatibleExample.java index b63d2d98f8..189a7e2894 100644 --- a/integration_tests/graalvm_tests/src/main/java/org/apache/fory/graalvm/ScopedCompatibleExample.java +++ b/integration_tests/graalvm_tests/src/main/java/org/apache/fory/graalvm/ScopedCompatibleExample.java @@ -23,18 +23,13 @@ import org.apache.fory.config.CompatibleMode; public class ScopedCompatibleExample { - private static Fory fory; - - static { - fory = createFory(); - } - private static Fory createFory() { Fory fory = Fory.builder() .withName(ScopedCompatibleExample.class.getName()) .requireClassRegistration(true) .withCompatibleMode(CompatibleMode.COMPATIBLE) + .withCodegen(false) .build(); // register and generate serializer code. fory.register(Foo.class); @@ -44,6 +39,7 @@ private static Fory createFory() { public static void main(String[] args) { System.out.println("ScopedCompatibleExample started"); + Fory fory = createFory(); Example.test(fory); System.out.println("ScopedCompatibleExample succeed 1/2"); fory = createFory(); diff --git a/integration_tests/graalvm_tests/src/main/java/org/apache/fory/graalvm/ThreadSafeExample.java b/integration_tests/graalvm_tests/src/main/java/org/apache/fory/graalvm/ThreadSafeExample.java index dd45c3f290..88ee6ef668 100644 --- a/integration_tests/graalvm_tests/src/main/java/org/apache/fory/graalvm/ThreadSafeExample.java +++ b/integration_tests/graalvm_tests/src/main/java/org/apache/fory/graalvm/ThreadSafeExample.java @@ -25,39 +25,31 @@ import java.util.concurrent.Executors; import java.util.concurrent.TimeUnit; import org.apache.fory.Fory; -import org.apache.fory.ThreadLocalFory; import org.apache.fory.ThreadSafeFory; import org.apache.fory.collection.Collections; import org.apache.fory.util.Preconditions; public class ThreadSafeExample { - static ThreadSafeFory fory; - - static { - fory = - new ThreadLocalFory( - classLoader -> { - Fory f = - Fory.builder() - .withName(ThreadSafeExample.class.getName()) - .requireClassRegistration(true) - .build(); - // register and generate serializer code. - f.register(Foo.class); - f.ensureSerializersCompiled(); - return f; - }); - System.out.println("Init fory at build time"); + private static ThreadSafeFory createFory() { + ThreadSafeFory fory = + Fory.builder() + .withName(ThreadSafeExample.class.getName()) + .requireClassRegistration(true) + .withCodegen(false) + .buildThreadSafeFory(); + fory.register(Foo.class); + fory.ensureSerializersCompiled(); + return fory; } public static void main(String[] args) throws Throwable { - test(fory); + test(createFory()); System.out.println("ThreadSafeExample succeed"); } static void test(ThreadSafeFory fory) throws Throwable { ThreadSafeExample threadSafeExample = new ThreadSafeExample(); - threadSafeExample.test(); + threadSafeExample.runChecks(fory); System.out.println("single thread works"); ExecutorService service = Executors.newFixedThreadPool(10); System.out.println("Start to submit tasks"); @@ -65,7 +57,7 @@ static void test(ThreadSafeFory fory) throws Throwable { service.submit( () -> { try { - threadSafeExample.test(); + threadSafeExample.runChecks(fory); } catch (Throwable t) { threadSafeExample.throwable = t; } @@ -83,7 +75,7 @@ static void test(ThreadSafeFory fory) throws Throwable { private volatile Throwable throwable; - private void test() { + private void runChecks(ThreadSafeFory fory) { Preconditions.checkArgument("abc".equals(fory.deserialize(fory.serialize("abc")))); Preconditions.checkArgument( List.of(1, 2, 3).equals(fory.deserialize(fory.serialize(List.of(1, 2, 3))))); diff --git a/integration_tests/graalvm_tests/src/main/java/org/apache/fory/graalvm/XlangExample.java b/integration_tests/graalvm_tests/src/main/java/org/apache/fory/graalvm/XlangExample.java index 949c7e17aa..2da18cce30 100644 --- a/integration_tests/graalvm_tests/src/main/java/org/apache/fory/graalvm/XlangExample.java +++ b/integration_tests/graalvm_tests/src/main/java/org/apache/fory/graalvm/XlangExample.java @@ -25,13 +25,16 @@ import org.apache.fory.util.Preconditions; public class XlangExample { - static Fory fory; - - static { - fory = Fory.builder().withName(XlangExample.class.getName()).withXlang(true).build(); - // register and generate serializer code. + private static Fory createFory() { + Fory fory = + Fory.builder() + .withName(XlangExample.class.getName()) + .withXlang(true) + .withCodegen(false) + .build(); fory.register(Foo.class); fory.ensureSerializersCompiled(); + return fory; } static void test(Fory fory) { @@ -50,7 +53,7 @@ static void test(Fory fory) { } public static void main(String[] args) { - test(fory); + test(createFory()); System.out.println("XlangExample succeed"); } } diff --git a/integration_tests/graalvm_tests/src/main/java/org/apache/fory/graalvm/record/CompatibleRecordExample.java b/integration_tests/graalvm_tests/src/main/java/org/apache/fory/graalvm/record/CompatibleRecordExample.java index 938aaf12c7..9287239177 100644 --- a/integration_tests/graalvm_tests/src/main/java/org/apache/fory/graalvm/record/CompatibleRecordExample.java +++ b/integration_tests/graalvm_tests/src/main/java/org/apache/fory/graalvm/record/CompatibleRecordExample.java @@ -23,18 +23,13 @@ import org.apache.fory.config.CompatibleMode; public class CompatibleRecordExample { - static Fory fory; - - static { - fory = createFory(); - } - private static Fory createFory() { Fory fory = Fory.builder() .withName(CompatibleRecordExample.class.getName()) .requireClassRegistration(true) .withCompatibleMode(CompatibleMode.COMPATIBLE) + .withCodegen(false) .build(); // register and generate serializer code. fory.register(RecordExample.Record.class); @@ -43,6 +38,7 @@ private static Fory createFory() { } public static void main(String[] args) { + Fory fory = createFory(); RecordExample.test(fory); fory = createFory(); RecordExample.test(fory); diff --git a/integration_tests/graalvm_tests/src/main/java/org/apache/fory/graalvm/record/RecordExample.java b/integration_tests/graalvm_tests/src/main/java/org/apache/fory/graalvm/record/RecordExample.java index 16946f59f6..244b010adb 100644 --- a/integration_tests/graalvm_tests/src/main/java/org/apache/fory/graalvm/record/RecordExample.java +++ b/integration_tests/graalvm_tests/src/main/java/org/apache/fory/graalvm/record/RecordExample.java @@ -27,17 +27,12 @@ public class RecordExample { public record Record(int f1, String f2, List f3, Map f4) {} - static Fory fory; - - static { - fory = createFory(); - } - private static Fory createFory() { Fory fory = Fory.builder() .withName(RecordExample.class.getName()) .requireClassRegistration(true) + .withCodegen(false) .build(); // register and generate serializer code. fory.register(Record.class); @@ -56,6 +51,7 @@ static void test(Fory fory) { public static void main(String[] args) { System.out.println("RecordExample started"); + Fory fory = createFory(); test(fory); System.out.println("RecordExample succeed 1/2"); fory = createFory(); diff --git a/integration_tests/graalvm_tests/src/main/java/org/apache/fory/graalvm/record/RecordExample2.java b/integration_tests/graalvm_tests/src/main/java/org/apache/fory/graalvm/record/RecordExample2.java index 7811b06f2d..187fd43ed8 100644 --- a/integration_tests/graalvm_tests/src/main/java/org/apache/fory/graalvm/record/RecordExample2.java +++ b/integration_tests/graalvm_tests/src/main/java/org/apache/fory/graalvm/record/RecordExample2.java @@ -27,17 +27,12 @@ public class RecordExample2 { private record Record(int f1, String f2, List f3, Map f4) {} - static Fory fory; - - static { - fory = createFory(); - } - private static Fory createFory() { Fory fory = Fory.builder() .withName(RecordExample2.class.getName()) .requireClassRegistration(true) + .withCodegen(false) .build(); // register and generate serializer code. fory.register(Record.class); @@ -58,6 +53,7 @@ public static void test(Fory fory) { public static void main(String[] args) { System.out.println("RecordExample started"); + Fory fory = createFory(); test(fory); System.out.println("RecordExample succeed 1/2"); fory = createFory(); diff --git a/integration_tests/graalvm_tests/src/main/resources/META-INF/native-image/org.apache.fory/graalvm_tests/reflect-config.json b/integration_tests/graalvm_tests/src/main/resources/META-INF/native-image/org.apache.fory/graalvm_tests/reflect-config.json new file mode 100644 index 0000000000..b15c7bc548 --- /dev/null +++ b/integration_tests/graalvm_tests/src/main/resources/META-INF/native-image/org.apache.fory/graalvm_tests/reflect-config.json @@ -0,0 +1,98 @@ +[ + { + "name": "org.apache.fory.graalvm.Foo", + "allDeclaredConstructors": true, + "allDeclaredFields": true, + "allDeclaredMethods": true + }, + { + "name": "org.apache.fory.graalvm.Struct", + "allDeclaredConstructors": true, + "allDeclaredFields": true, + "allDeclaredMethods": true + }, + { + "name": "org.apache.fory.graalvm.ArrayExample", + "allDeclaredConstructors": true, + "allDeclaredFields": true, + "allDeclaredMethods": true + }, + { + "name": "org.apache.fory.graalvm.ObjectStreamExample", + "allDeclaredConstructors": true, + "allDeclaredFields": true, + "allDeclaredMethods": true + }, + { + "name": "org.apache.fory.graalvm.EnsureSerializerExample", + "allDeclaredConstructors": true, + "allDeclaredFields": true, + "allDeclaredMethods": true + }, + { + "name": "org.apache.fory.graalvm.EnsureSerializerExample$Custom", + "allDeclaredConstructors": true, + "allDeclaredFields": true, + "allDeclaredMethods": true + }, + { + "name": "org.apache.fory.graalvm.ProxyExample$TestInvocationHandler", + "allDeclaredConstructors": true, + "allDeclaredFields": true, + "allDeclaredMethods": true + }, + { + "name": "org.apache.fory.graalvm.FeatureTestExample$PrivateConstructorClass", + "allDeclaredConstructors": true, + "allDeclaredFields": true, + "allDeclaredMethods": true + }, + { + "name": "org.apache.fory.graalvm.FeatureTestExample$TestInvocationHandler", + "allDeclaredConstructors": true, + "allDeclaredFields": true, + "allDeclaredMethods": true + }, + { + "name": "org.apache.fory.graalvm.AbstractClassExample$AbstractBase", + "allDeclaredConstructors": true, + "allDeclaredFields": true, + "allDeclaredMethods": true + }, + { + "name": "org.apache.fory.graalvm.AbstractClassExample$ConcreteA", + "allDeclaredConstructors": true, + "allDeclaredFields": true, + "allDeclaredMethods": true + }, + { + "name": "org.apache.fory.graalvm.AbstractClassExample$ConcreteB", + "allDeclaredConstructors": true, + "allDeclaredFields": true, + "allDeclaredMethods": true + }, + { + "name": "org.apache.fory.graalvm.AbstractClassExample$Container", + "allDeclaredConstructors": true, + "allDeclaredFields": true, + "allDeclaredMethods": true + }, + { + "name": "org.apache.fory.graalvm.record.Foo", + "allDeclaredConstructors": true, + "allDeclaredFields": true, + "allDeclaredMethods": true + }, + { + "name": "org.apache.fory.graalvm.record.RecordExample$Record", + "allDeclaredConstructors": true, + "allDeclaredFields": true, + "allDeclaredMethods": true + }, + { + "name": "org.apache.fory.graalvm.record.RecordExample2$Record", + "allDeclaredConstructors": true, + "allDeclaredFields": true, + "allDeclaredMethods": true + } +] diff --git a/java/fory-core/src/main/java/org/apache/fory/AbstractThreadSafeFory.java b/java/fory-core/src/main/java/org/apache/fory/AbstractThreadSafeFory.java index 82b792e5e1..19d1474bd7 100644 --- a/java/fory-core/src/main/java/org/apache/fory/AbstractThreadSafeFory.java +++ b/java/fory-core/src/main/java/org/apache/fory/AbstractThreadSafeFory.java @@ -20,6 +20,8 @@ package org.apache.fory; import java.util.function.Function; +import org.apache.fory.resolver.AllowListChecker; +import org.apache.fory.resolver.ClassResolver; import org.apache.fory.resolver.TypeChecker; import org.apache.fory.resolver.TypeResolver; import org.apache.fory.serializer.Serializer; @@ -31,22 +33,11 @@ public void register(Class clz) { registerCallback(fory -> fory.register(clz)); } - @Override - public void register(Class cls, boolean createSerializer) { - registerCallback(fory -> fory.register(cls, createSerializer)); - } - @Override public void register(Class cls, int id) { registerCallback(fory -> fory.register(cls, id)); } - @Override - public void register(Class cls, int id, boolean createSerializer) { - registerCallback(fory -> fory.register(cls, id, createSerializer)); - } - - @Deprecated @Override public void register(Class cls, String typeName) { registerCallback(fory -> fory.register(cls, typeName)); @@ -96,8 +87,9 @@ public void registerSerializer(Class type, Serializer serializer) { } @Override - public void registerSerializer(Class type, Function> serializerCreator) { - registerCallback(fory -> fory.registerSerializer(type, serializerCreator.apply(fory))); + public void registerSerializer( + Class type, Function> serializerCreator) { + registerCallback(fory -> fory.registerSerializer(type, serializerCreator)); } @Override @@ -113,7 +105,7 @@ public void registerSerializerAndType(Class type, Serializer serializer) { @Override public void registerSerializerAndType( - Class type, Function> serializerCreator) { + Class type, Function> serializerCreator) { registerCallback(fory -> fory.registerSerializerAndType(type, serializerCreator)); } @@ -129,7 +121,14 @@ public TypeResolver getTypeResolver() { @Override public void setTypeChecker(TypeChecker typeChecker) { - registerCallback(fory -> fory.getTypeResolver().setTypeChecker(typeChecker)); + registerCallback( + fory -> { + TypeResolver typeResolver = fory.getTypeResolver(); + typeResolver.setTypeChecker(typeChecker); + if (typeChecker instanceof AllowListChecker && typeResolver instanceof ClassResolver) { + ((AllowListChecker) typeChecker).addListener((ClassResolver) typeResolver); + } + }); } @Override diff --git a/java/fory-core/src/main/java/org/apache/fory/BaseFory.java b/java/fory-core/src/main/java/org/apache/fory/BaseFory.java index fc59eae32a..5839cfa164 100644 --- a/java/fory-core/src/main/java/org/apache/fory/BaseFory.java +++ b/java/fory-core/src/main/java/org/apache/fory/BaseFory.java @@ -51,32 +51,6 @@ public interface BaseFory { */ void register(Class cls, int id); - /** - * Register class and allocate an auto-grown ID for this class. - * - *

NOTE: The registration order is important. If registration order is inconsistent, the - * allocated ID will be different, and the deserialization will failed !!! - * - * @param cls class to register. - * @param createSerializer whether to create serializer, if true and codegen enabled, this will - * generate the serializer code too. - */ - void register(Class cls, boolean createSerializer); - - /** - * Register class with specified id. This method has been deprecated, please use {@link - * #register(Class, int)} instead, and invoke {@link #ensureSerializersCompiled} after all classes - * has been registered. - * - * @param cls class to register. - * @param id id for provided class. - * @param createSerializer whether to create serializer, if true and codegen enabled, this will - * generate the serializer code too. this parameter has no effect anymore on whether to - * generate code, please use {@link #ensureSerializersCompiled} to trigger code generation - */ - @Deprecated - void register(Class cls, int id, boolean createSerializer); - /** register class with given type name which will be used for cross-language serialization. */ void register(Class cls, String typeName); @@ -139,9 +113,9 @@ public interface BaseFory { * allocated ID will be different, and the deserialization will failed !!! * * @param type class needed to be serialized/deserialized. - * @param serializerCreator serializer creator with param {@link Fory} + * @param serializerCreator serializer creator with param {@link TypeResolver} */ - void registerSerializer(Class type, Function> serializerCreator); + void registerSerializer(Class type, Function> serializerCreator); /** * Register a class (if not already registered) and then register its serializer class. @@ -171,9 +145,10 @@ public interface BaseFory { * allocated ID will be different, and the deserialization will failed !!! * * @param type class needed to be serialized/deserialized. - * @param serializerCreator serializer creator with param {@link Fory} + * @param serializerCreator serializer creator with param {@link TypeResolver} */ - void registerSerializerAndType(Class type, Function> serializerCreator); + void registerSerializerAndType( + Class type, Function> serializerCreator); void setSerializerFactory(SerializerFactory serializerFactory); diff --git a/java/fory-core/src/main/java/org/apache/fory/Fory.java b/java/fory-core/src/main/java/org/apache/fory/Fory.java index 263adcb741..e06655fdbc 100644 --- a/java/fory-core/src/main/java/org/apache/fory/Fory.java +++ b/java/fory-core/src/main/java/org/apache/fory/Fory.java @@ -22,24 +22,26 @@ import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.OutputStream; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.HashMap; -import java.util.Iterator; import java.util.function.Consumer; import java.util.function.Function; import javax.annotation.concurrent.NotThreadSafe; import org.apache.fory.annotation.Internal; import org.apache.fory.builder.JITContext; -import org.apache.fory.collection.IdentityMap; -import org.apache.fory.config.CompatibleMode; import org.apache.fory.config.Config; import org.apache.fory.config.ForyBuilder; -import org.apache.fory.config.LongEncoding; +import org.apache.fory.context.CopyContext; +import org.apache.fory.context.MapRefReader; +import org.apache.fory.context.MapRefWriter; +import org.apache.fory.context.MetaContext; +import org.apache.fory.context.MetaStringReader; +import org.apache.fory.context.MetaStringWriter; +import org.apache.fory.context.ReadContext; +import org.apache.fory.context.RefReader; +import org.apache.fory.context.RefWriter; +import org.apache.fory.context.WriteContext; import org.apache.fory.exception.CopyException; import org.apache.fory.exception.DeserializationException; import org.apache.fory.exception.ForyException; -import org.apache.fory.exception.InsecureException; import org.apache.fory.exception.SerializationException; import org.apache.fory.io.ForyInputStream; import org.apache.fory.io.ForyReadableChannel; @@ -47,30 +49,16 @@ import org.apache.fory.logging.LoggerFactory; import org.apache.fory.memory.MemoryBuffer; import org.apache.fory.memory.MemoryUtils; -import org.apache.fory.meta.MetaCompressor; import org.apache.fory.resolver.ClassResolver; -import org.apache.fory.resolver.MapRefResolver; -import org.apache.fory.resolver.MetaStringResolver; -import org.apache.fory.resolver.NoRefResolver; -import org.apache.fory.resolver.RefResolver; -import org.apache.fory.resolver.SerializationContext; import org.apache.fory.resolver.SharedRegistry; import org.apache.fory.resolver.TypeInfo; -import org.apache.fory.resolver.TypeInfoHolder; import org.apache.fory.resolver.TypeResolver; import org.apache.fory.resolver.XtypeResolver; -import org.apache.fory.serializer.ArraySerializers; import org.apache.fory.serializer.BufferCallback; import org.apache.fory.serializer.BufferObject; -import org.apache.fory.serializer.PrimitiveSerializers.LongSerializer; import org.apache.fory.serializer.Serializer; import org.apache.fory.serializer.SerializerFactory; -import org.apache.fory.serializer.StringSerializer; -import org.apache.fory.serializer.UnknownClass.UnknownStruct; -import org.apache.fory.serializer.collection.CollectionSerializers.ArrayListSerializer; -import org.apache.fory.serializer.collection.MapSerializers.HashMapSerializer; import org.apache.fory.type.Generics; -import org.apache.fory.type.Types; import org.apache.fory.util.ExceptionUtils; import org.apache.fory.util.Preconditions; import org.apache.fory.util.StringUtils; @@ -80,8 +68,8 @@ * *

Bit 0: null flag, Bit 1: xlang flag, Bit 2: out-of-band flag, Bits 3-7 reserved. * - *

serialize/deserialize is user API for root object serialization, write/read api is for inner - * serialization. + *

serialize/deserialize are the root object APIs. Nested serialization and deserialization go + * through {@link WriteContext} and {@link ReadContext}. */ @NotThreadSafe public final class Fory implements BaseFory { @@ -101,36 +89,17 @@ public final class Fory implements BaseFory { private static final byte isOutOfBandFlag = 1 << 2; private final Config config; - private final boolean refTracking; - private final boolean shareMeta; - private final RefResolver refResolver; private final TypeResolver typeResolver; - private final MetaStringResolver metaStringResolver; - private final SerializationContext serializationContext; private final SharedRegistry sharedRegistry; private final ClassLoader classLoader; private final JITContext jitContext; + private final WriteContext writeContext; + private final ReadContext readContext; + private final CopyContext copyContext; private MemoryBuffer buffer; - private final StringSerializer stringSerializer; - private final ArrayListSerializer arrayListSerializer; - private final HashMapSerializer hashMapSerializer; - private final boolean crossLanguage; - private final boolean compressInt; - private final LongEncoding longEncoding; - private final Generics generics; - private BufferCallback bufferCallback; - private Iterator outOfBandBuffers; - private boolean peerOutOfBandEnabled; - private int depth; - private final int maxDepth; - private boolean registrationFinished; - private final boolean forVirtualThread; - private int copyDepth; - private final boolean copyRefTracking; - private final IdentityMap originToCopyMap; public Fory(ForyBuilder builder, ClassLoader classLoader) { - this(builder, classLoader, new SharedRegistry()); + this(builder, classLoader, null); } public Fory(ForyBuilder builder, ClassLoader classLoader, SharedRegistry sharedRegistry) { @@ -148,30 +117,38 @@ public Fory(ForyBuilder builder, ClassLoader classLoader, SharedRegistry sharedR this.sharedRegistry = sharedRegistry; this.classLoader = classLoader; config = new Config(builder); - this.forVirtualThread = config.forVirtualThread(); - crossLanguage = config.isXlang(); - this.refTracking = config.trackingRef(); - this.copyRefTracking = config.copyRef(); - this.shareMeta = config.isMetaShareEnabled(); - compressInt = config.compressInt(); - longEncoding = config.longEncoding(); - maxDepth = config.maxDepth(); - if (refTracking) { - this.refResolver = new MapRefResolver(config.mapRefLoadFactor()); + RefWriter refWriter; + RefReader refReader; + if (config.trackingRef()) { + refWriter = new MapRefWriter(config.mapRefLoadFactor()); + refReader = new MapRefReader(); } else { - this.refResolver = new NoRefResolver(); + refWriter = new RefWriter.NoRefWriter(); + refReader = new RefReader.NoRefReader(); } jitContext = new JITContext(this); - generics = new Generics(this); - // init stringSerializer first, so other places can share same StringSerializer. - stringSerializer = new StringSerializer(this); - metaStringResolver = new MetaStringResolver(sharedRegistry); - typeResolver = crossLanguage ? new XtypeResolver(this) : new ClassResolver(this); + typeResolver = + config.isXlang() + ? new XtypeResolver(config, classLoader, sharedRegistry, jitContext) + : new ClassResolver(config, classLoader, sharedRegistry, jitContext); typeResolver.initialize(); - serializationContext = new SerializationContext(config); - arrayListSerializer = new ArrayListSerializer(this); - hashMapSerializer = new HashMapSerializer(this); - originToCopyMap = new IdentityMap<>(2); + MetaStringWriter metaStringWriter = new MetaStringWriter(sharedRegistry); + MetaStringReader metaStringReader = new MetaStringReader(); + writeContext = + new WriteContext( + config, + new Generics(), + typeResolver, + refWriter, + metaStringWriter); + readContext = + new ReadContext( + config, + new Generics(), + typeResolver, + refReader, + metaStringReader); + copyContext = new CopyContext(typeResolver, config.copyRef()); LOG.info("Created new fory {}", this); } @@ -185,18 +162,6 @@ public void register(Class cls, int id) { getTypeResolver().register(cls, Integer.toUnsignedLong(id)); } - @Deprecated - @Override - public void register(Class cls, boolean createSerializer) { - getTypeResolver().register(cls); - } - - @Deprecated - @Override - public void register(Class cls, int id, boolean createSerializer) { - getTypeResolver().register(cls, Integer.toUnsignedLong(id)); - } - /** * Register class with given type name, this method will have bigger serialization time/space cost * compared to register by id. @@ -253,8 +218,9 @@ public void registerSerializer(Class type, Serializer serializer) { } @Override - public void registerSerializer(Class type, Function> serializerCreator) { - getTypeResolver().registerSerializer(type, serializerCreator.apply(this)); + public void registerSerializer( + Class type, Function> serializerCreator) { + getTypeResolver().registerSerializer(type, serializerCreator.apply(typeResolver)); } @Override @@ -270,8 +236,8 @@ public void registerSerializerAndType(Class type, Serializer serializer) { @Override public void registerSerializerAndType( - Class type, Function> serializerCreator) { - getTypeResolver().registerSerializerAndType(type, serializerCreator.apply(this)); + Class type, Function> serializerCreator) { + getTypeResolver().registerSerializerAndType(type, serializerCreator.apply(typeResolver)); } @Override @@ -289,9 +255,8 @@ public Serializer getSerializer(Class cls) { } private void ensureRegistrationFinished() { - if (!registrationFinished) { + if (!typeResolver.isRegistrationFinished()) { typeResolver.finishRegistration(); - registrationFinished = true; } } @@ -304,21 +269,21 @@ public MemoryBuffer serialize(Object obj, long address, int size) { @Override public byte[] serialize(Object obj) { - MemoryBuffer buf = getBuffer(); + MemoryBuffer buf = getWriteBuffer(); buf.writerIndex(0); serialize(buf, obj, null); byte[] bytes = buf.getBytes(0, buf.writerIndex()); - resetBuffer(); + recycleWriteBuffer(buf); return bytes; } @Override public byte[] serialize(Object obj, BufferCallback callback) { - MemoryBuffer buf = getBuffer(); + MemoryBuffer buf = getWriteBuffer(); buf.writerIndex(0); serialize(buf, obj, callback); byte[] bytes = buf.getBytes(0, buf.writerIndex()); - resetBuffer(); + recycleWriteBuffer(buf); return bytes; } @@ -330,32 +295,35 @@ public MemoryBuffer serialize(MemoryBuffer buffer, Object obj) { @Override public MemoryBuffer serialize(MemoryBuffer buffer, Object obj, BufferCallback callback) { ensureRegistrationFinished(); - byte bitmap = 0; - if (crossLanguage) { - bitmap |= isCrossLanguageFlag; - } - if (obj == null) { - bitmap |= isNilFlag; - buffer.writeByte(bitmap); - return buffer; - } - if (callback != null) { - bitmap |= isOutOfBandFlag; - bufferCallback = callback; - } - buffer.writeByte(bitmap); + writeContext.prepare(buffer, callback); try { - jitContext.lock(); - if (depth > 0) { - throwDepthSerializationException(); + byte bitmap = 0; + if (config.isXlang()) { + bitmap |= isCrossLanguageFlag; + } + if (obj == null) { + bitmap |= isNilFlag; + buffer.writeByte(bitmap); + return buffer; + } + if (callback != null) { + bitmap |= isOutOfBandFlag; + } + buffer.writeByte(bitmap); + try { + jitContext.lock(); + if (writeContext.getDepth() > 0) { + throwDepthSerializationException(); + } + writeContext.writeRef(obj); + return buffer; + } catch (Throwable t) { + throw processSerializationError(t); + } finally { + jitContext.unlock(); } - writeRef(buffer, obj); - return buffer; - } catch (Throwable t) { - throw processSerializationError(t); } finally { - resetWrite(); - jitContext.unlock(); + writeContext.reset(); } } @@ -370,7 +338,7 @@ public void serialize(OutputStream outputStream, Object obj, BufferCallback call } private ForyException processSerializationError(Throwable e) { - if (!refTracking) { + if (!config.trackingRef()) { String msg = "Object may contain circular references, please enable ref tracking " + "by `ForyBuilder#withRefTracking(true)`"; @@ -389,7 +357,7 @@ private ForyException processSerializationError(Throwable e) { } private ForyException processCopyError(Throwable e) { - if (!copyRefTracking) { + if (!config.copyRef()) { String msg = "Object may contain circular references, please enable ref tracking " + "by `ForyBuilder#withRefCopy(true)`"; @@ -401,324 +369,6 @@ private ForyException processCopyError(Throwable e) { throw (ForyException) e; } - public MemoryBuffer getBuffer() { - MemoryBuffer buf = buffer; - if (buf == null) { - buf = buffer = MemoryBuffer.newHeapBuffer(64); - } - return buf; - } - - public void resetBuffer() { - MemoryBuffer buf = buffer; - if (buf != null && buf.size() > config.bufferSizeLimitBytes()) { - buffer = MemoryBuffer.newHeapBuffer(config.bufferSizeLimitBytes()); - } - } - - /** Serialize a nullable referencable object to buffer. */ - public void writeRef(MemoryBuffer buffer, Object obj) { - if (!refResolver.writeRefOrNull(buffer, obj)) { - TypeResolver resolver = typeResolver; - TypeInfo typeInfo = resolver.getTypeInfo(obj.getClass()); - if (crossLanguage && typeInfo.getCls() == UnknownStruct.class) { - depth++; - typeInfo.getSerializer().write(buffer, obj); - depth--; - return; - } - resolver.writeTypeInfo(buffer, typeInfo); - writeData(buffer, typeInfo, obj); - } - } - - public void writeRef(MemoryBuffer buffer, Object obj, TypeInfoHolder classInfoHolder) { - if (!refResolver.writeRefOrNull(buffer, obj)) { - TypeResolver resolver = typeResolver; - TypeInfo typeInfo = resolver.getTypeInfo(obj.getClass(), classInfoHolder); - if (crossLanguage && typeInfo.getCls() == UnknownStruct.class) { - depth++; - typeInfo.getSerializer().write(buffer, obj); - depth--; - return; - } - resolver.writeTypeInfo(buffer, typeInfo); - writeData(buffer, typeInfo, obj); - } - } - - public void writeRef(MemoryBuffer buffer, Object obj, TypeInfo typeInfo) { - if (crossLanguage && typeInfo.getCls() == UnknownStruct.class) { - if (!refResolver.writeRefOrNull(buffer, obj)) { - depth++; - typeInfo.getSerializer().write(buffer, obj); - depth--; - } - return; - } - TypeResolver resolver = typeResolver; - Serializer serializer = typeInfo.getSerializer(); - if (serializer.needToWriteRef()) { - if (!refResolver.writeRefOrNull(buffer, obj)) { - resolver.writeTypeInfo(buffer, typeInfo); - depth++; - serializer.write(buffer, obj); - depth--; - } - } else { - if (obj == null) { - buffer.writeByte(Fory.NULL_FLAG); - } else { - buffer.writeByte(Fory.NOT_NULL_VALUE_FLAG); - resolver.writeTypeInfo(buffer, typeInfo); - depth++; - serializer.write(buffer, obj); - depth--; - } - } - } - - public void writeRef(MemoryBuffer buffer, T obj, Serializer serializer) { - if (serializer.needToWriteRef()) { - if (!refResolver.writeRefOrNull(buffer, obj)) { - depth++; - serializer.write(buffer, obj); - depth--; - } - } else { - if (obj == null) { - buffer.writeByte(Fory.NULL_FLAG); - } else { - buffer.writeByte(Fory.NOT_NULL_VALUE_FLAG); - depth++; - serializer.write(buffer, obj); - depth--; - } - } - } - - /** - * Serialize a not-null and non-reference object to buffer. - * - *

If reference is enabled, this method should be called only the object is first seen in the - * object graph. - */ - public void writeNonRef(MemoryBuffer buffer, Object obj) { - TypeResolver resolver = typeResolver; - TypeInfo typeInfo = resolver.getTypeInfo(obj.getClass()); - if (crossLanguage && typeInfo.getCls() == UnknownStruct.class) { - depth++; - typeInfo.getSerializer().write(buffer, obj); - depth--; - return; - } - resolver.writeTypeInfo(buffer, typeInfo); - writeData(buffer, typeInfo, obj); - } - - public void writeNonRef(MemoryBuffer buffer, Object obj, Serializer serializer) { - depth++; - serializer.write(buffer, obj); - depth--; - } - - public void writeNonRef(MemoryBuffer buffer, Object obj, TypeInfoHolder holder) { - TypeResolver resolver = typeResolver; - TypeInfo typeInfo = resolver.getTypeInfo(obj.getClass(), holder); - if (crossLanguage && typeInfo.getCls() == UnknownStruct.class) { - depth++; - typeInfo.getSerializer().write(buffer, obj); - depth--; - return; - } - resolver.writeTypeInfo(buffer, typeInfo); - writeData(buffer, typeInfo, obj); - } - - public void writeNonRef(MemoryBuffer buffer, Object obj, TypeInfo typeInfo) { - if (crossLanguage && typeInfo.getCls() == UnknownStruct.class) { - depth++; - typeInfo.getSerializer().write(buffer, obj); - depth--; - return; - } - typeResolver.writeTypeInfo(buffer, typeInfo); - writeData(buffer, typeInfo, obj); - } - - /** Class/type info should be written already. */ - public void writeData(MemoryBuffer buffer, TypeInfo typeInfo, Object obj) { - int typeId = typeInfo.getTypeId(); - switch (typeId) { - case Types.BOOL: - buffer.writeBoolean((Boolean) obj); - break; - case Types.INT8: - buffer.writeByte((Byte) obj); - break; - case Types.INT16: - buffer.writeInt16((Short) obj); - break; - case ClassResolver.CHAR_ID: - buffer.writeChar((Character) obj); - break; - case Types.INT32: - case Types.VARINT32: - if (compressInt) { - buffer.writeVarInt32((Integer) obj); - } else { - buffer.writeInt32((Integer) obj); - } - break; - case Types.INT64: - LongSerializer.writeInt64(buffer, (Long) obj, longEncoding); - break; - case Types.VARINT64: - buffer.writeVarInt64((Long) obj); - break; - case Types.TAGGED_INT64: - buffer.writeTaggedInt64((Long) obj); - break; - case Types.FLOAT32: - buffer.writeFloat32((Float) obj); - break; - case Types.FLOAT64: - buffer.writeFloat64((Double) obj); - break; - case Types.STRING: - if (typeInfo.getCls() == String.class) { - stringSerializer.writeString(buffer, (String) obj); - break; - } - depth++; - typeInfo.getSerializer().write(buffer, obj); - depth--; - break; - default: - depth++; - typeInfo.getSerializer().write(buffer, obj); - depth--; - } - } - - public void writeBufferObject(MemoryBuffer buffer, BufferObject bufferObject) { - if (bufferCallback == null || bufferCallback.apply(bufferObject)) { - buffer.writeBoolean(true); - // writer length. - int totalBytes = bufferObject.totalBytes(); - // write aligned length so that later buffer copy happen on aligned offset, which will be more - // efficient - // TODO(chaokunyang) Remove branch when other languages support aligned varint. - if (!crossLanguage) { - buffer.writeVarUint32Aligned(totalBytes); - } else { - buffer.writeVarUint32(totalBytes); - } - int writerIndex = buffer.writerIndex(); - buffer.ensure(writerIndex + bufferObject.totalBytes()); - bufferObject.writeTo(buffer); - int size = buffer.writerIndex() - writerIndex; - Preconditions.checkArgument(size == totalBytes); - } else { - buffer.writeBoolean(false); - } - } - - // duplicate for speed. - public void writeBufferObject( - MemoryBuffer buffer, ArraySerializers.PrimitiveArrayBufferObject bufferObject) { - if (bufferCallback == null || bufferCallback.apply(bufferObject)) { - buffer.writeBoolean(true); - int totalBytes = bufferObject.totalBytes(); - // write aligned length so that later buffer copy happen on aligned offset, which will be very - // efficient - // TODO(chaokunyang) Remove branch when other languages support aligned varint. - if (!crossLanguage) { - buffer.writeVarUint32Aligned(totalBytes); - } else { - buffer.writeVarUint32(totalBytes); - } - bufferObject.writeTo(buffer); - } else { - buffer.writeBoolean(false); - } - } - - public MemoryBuffer readBufferObject(MemoryBuffer buffer) { - boolean inBand = buffer.readBoolean(); - if (inBand) { - int size; - // TODO(chaokunyang) Remove branch when other languages support aligned varint. - if (!crossLanguage) { - size = buffer.readAlignedVarUint32(); - } else { - size = buffer.readVarUint32(); - } - if (buffer.readerIndex() + size > buffer.size() && buffer.getStreamReader() != null) { - buffer.getStreamReader().fillBuffer(buffer.readerIndex() + size - buffer.size()); - } - MemoryBuffer slice = buffer.slice(buffer.readerIndex(), size); - buffer.readerIndex(buffer.readerIndex() + size); - return slice; - } else { - Preconditions.checkArgument(outOfBandBuffers.hasNext()); - return outOfBandBuffers.next(); - } - } - - public void writeString(MemoryBuffer buffer, String str) { - stringSerializer.writeString(buffer, str); - } - - public String readString(MemoryBuffer buffer) { - return stringSerializer.readString(buffer); - } - - public void writeStringRef(MemoryBuffer buffer, String str) { - if (stringSerializer.needToWriteRef()) { - if (!refResolver.writeRefOrNull(buffer, str)) { - stringSerializer.writeString(buffer, str); - } - } else { - if (str == null) { - buffer.writeByte(Fory.NULL_FLAG); - } else { - buffer.writeByte(Fory.NOT_NULL_VALUE_FLAG); - stringSerializer.write(buffer, str); - } - } - } - - public String readStringRef(MemoryBuffer buffer) { - RefResolver refResolver = this.refResolver; - if (stringSerializer.needToWriteRef()) { - String obj; - int nextReadRefId = refResolver.tryPreserveRefId(buffer); - if (nextReadRefId >= NOT_NULL_VALUE_FLAG) { - obj = stringSerializer.read(buffer); - refResolver.setReadObject(nextReadRefId, obj); - return obj; - } else { - return (String) refResolver.getReadObject(); - } - } else { - byte headFlag = buffer.readByte(); - if (headFlag == Fory.NULL_FLAG) { - return null; - } else { - return stringSerializer.read(buffer); - } - } - } - - public void writeInt64(MemoryBuffer buffer, long value) { - LongSerializer.writeInt64(buffer, value, longEncoding); - } - - public long readInt64(MemoryBuffer buffer) { - return LongSerializer.readInt64(buffer, longEncoding); - } - @Override public Object deserialize(byte[] bytes) { return deserialize(MemoryUtils.wrap(bytes), (Iterable) null); @@ -732,24 +382,28 @@ public T deserialize(byte[] bytes, Class type) { @Override public T deserialize(MemoryBuffer buffer, Class type) { ensureRegistrationFinished(); + byte bitmap = buffer.readByte(); + if ((bitmap & isNilFlag) == isNilFlag) { + return null; + } + boolean peerOutOfBandEnabled = (bitmap & isOutOfBandFlag) == isOutOfBandFlag; + assert !peerOutOfBandEnabled : "Out of band buffers not passed in when deserializing"; + checkXlangBitmap(bitmap); + readContext.prepare(buffer, null, false); try { - jitContext.lock(); - if (depth > 0) { - throwDepthDeserializationException(); - } - byte bitmap = buffer.readByte(); - if ((bitmap & isNilFlag) == isNilFlag) { - return null; + try { + jitContext.lock(); + if (readContext.getDepth() > 0) { + throwDepthDeserializationException(); + } + return deserializeByType(buffer, type); + } finally { + jitContext.unlock(); } - boolean peerOutOfBandEnabled = (bitmap & isOutOfBandFlag) == isOutOfBandFlag; - assert !peerOutOfBandEnabled : "Out of band buffers not passed in when deserializing"; - checkXlangBitmap(bitmap); - return deserializeByType(buffer, type); } catch (Throwable t) { throw ExceptionUtils.handleReadFailed(this, t); } finally { - resetRead(); - jitContext.unlock(); + readContext.reset(); } } @@ -798,35 +452,38 @@ public Object deserialize(MemoryBuffer buffer) { @Override public Object deserialize(MemoryBuffer buffer, Iterable outOfBandBuffers) { ensureRegistrationFinished(); + byte bitmap = buffer.readByte(); + if ((bitmap & isNilFlag) == isNilFlag) { + return null; + } + checkXlangBitmap(bitmap); + boolean peerOutOfBandEnabled = (bitmap & isOutOfBandFlag) == isOutOfBandFlag; + if (peerOutOfBandEnabled) { + Preconditions.checkNotNull( + outOfBandBuffers, + "outOfBandBuffers shouldn't be null when the serialized stream is " + + "produced with bufferCallback not null."); + } else { + Preconditions.checkArgument( + outOfBandBuffers == null, + "outOfBandBuffers should be null when the serialized stream is " + + "produced with bufferCallback null."); + } + readContext.prepare(buffer, peerOutOfBandEnabled ? outOfBandBuffers : null, peerOutOfBandEnabled); try { - jitContext.lock(); - if (depth > 0) { - throwDepthDeserializationException(); - } - byte bitmap = buffer.readByte(); - if ((bitmap & isNilFlag) == isNilFlag) { - return null; - } - checkXlangBitmap(bitmap); - peerOutOfBandEnabled = (bitmap & isOutOfBandFlag) == isOutOfBandFlag; - if (peerOutOfBandEnabled) { - Preconditions.checkNotNull( - outOfBandBuffers, - "outOfBandBuffers shouldn't be null when the serialized stream is " - + "produced with bufferCallback not null."); - this.outOfBandBuffers = outOfBandBuffers.iterator(); - } else { - Preconditions.checkArgument( - outOfBandBuffers == null, - "outOfBandBuffers should be null when the serialized stream is " - + "produced with bufferCallback null."); + try { + jitContext.lock(); + if (readContext.getDepth() > 0) { + throwDepthDeserializationException(); + } + return readContext.readRef(); + } finally { + jitContext.unlock(); } - return readRef(buffer); } catch (Throwable t) { throw ExceptionUtils.handleReadFailed(this, t); } finally { - resetRead(); - jitContext.unlock(); + readContext.reset(); } } @@ -858,195 +515,36 @@ public Object deserialize(ForyReadableChannel channel, Iterable ou @SuppressWarnings("unchecked") private T deserializeByType(MemoryBuffer buffer, Class type) { - generics.pushGenericType(typeResolver.buildGenericType(type)); + readContext.getGenerics().pushGenericType(typeResolver.buildGenericType(type), readContext.getDepth()); try { - RefResolver refResolver = this.refResolver; - int nextReadRefId = refResolver.tryPreserveRefId(buffer); + RefReader refReader = readContext.getRefReader(); + int nextReadRefId = refReader.tryPreserveRefId(buffer); if (nextReadRefId < NOT_NULL_VALUE_FLAG) { - return (T) refResolver.getReadObject(); + return (T) refReader.getReadObject(); } - TypeInfo typeInfo = typeResolver.readTypeInfo(buffer, type); - Object value = readNonRef(buffer, typeInfo); - refResolver.setReadObject(nextReadRefId, value); + TypeInfo typeInfo = typeResolver.readTypeInfo(readContext, type); + Object value = readContext.readNonRef(typeInfo); + refReader.setReadObject(nextReadRefId, value); return (T) value; } finally { - generics.popGenericType(); - } - } - - /** Deserialize nullable referencable object from buffer. */ - public Object readRef(MemoryBuffer buffer) { - RefResolver refResolver = this.refResolver; - int nextReadRefId = refResolver.tryPreserveRefId(buffer); - if (nextReadRefId >= NOT_NULL_VALUE_FLAG) { - TypeInfo typeInfo = typeResolver.readTypeInfo(buffer); - Object o = readNonRef(buffer, typeInfo); - refResolver.setReadObject(nextReadRefId, o); - return o; - } - return refResolver.getReadObject(); - } - - public Object readRef(MemoryBuffer buffer, TypeInfo typeInfo) { - RefResolver refResolver = this.refResolver; - int nextReadRefId = refResolver.tryPreserveRefId(buffer); - if (nextReadRefId >= NOT_NULL_VALUE_FLAG) { - Object o = readNonRef(buffer, typeInfo); - refResolver.setReadObject(nextReadRefId, o); - return o; - } - return refResolver.getReadObject(); - } - - public Object readRef(MemoryBuffer buffer, TypeInfoHolder classInfoHolder) { - RefResolver refResolver = this.refResolver; - int nextReadRefId = refResolver.tryPreserveRefId(buffer); - if (nextReadRefId >= NOT_NULL_VALUE_FLAG) { - TypeInfo typeInfo = typeResolver.readTypeInfo(buffer, classInfoHolder); - Object o = readNonRef(buffer, typeInfo); - refResolver.setReadObject(nextReadRefId, o); - return o; - } - return refResolver.getReadObject(); - } - - @SuppressWarnings("unchecked") - public T readRef(MemoryBuffer buffer, Serializer serializer) { - if (serializer.needToWriteRef()) { - RefResolver refResolver = this.refResolver; - int nextReadRefId = refResolver.tryPreserveRefId(buffer); - if (nextReadRefId >= NOT_NULL_VALUE_FLAG) { - Object o = readNonRef(buffer, serializer); - refResolver.setReadObject(nextReadRefId, o); - return (T) o; - } - return (T) refResolver.getReadObject(); - } - byte headFlag = buffer.readByte(); - if (headFlag == Fory.NULL_FLAG) { - return null; - } - return (T) readNonRef(buffer, serializer); - } - - /** Deserialize not-null and non-reference object from buffer. */ - public Object readNonRef(MemoryBuffer buffer) { - TypeInfo typeInfo = typeResolver.readTypeInfo(buffer); - return readNonRef(buffer, typeInfo); - } - - public Object readNonRef(MemoryBuffer buffer, TypeInfoHolder classInfoHolder) { - TypeInfo typeInfo = typeResolver.readTypeInfo(buffer, classInfoHolder); - return readNonRef(buffer, typeInfo); - } - - public Object readNonRef(MemoryBuffer buffer, TypeInfo typeInfo) { - return readDataInternal(buffer, typeInfo); - } - - public Object readNonRef(MemoryBuffer buffer, Serializer serializer) { - incReadDepth(); - Object o = serializer.read(buffer); - depth--; - return o; - } - - /** Read object class and data without tracking ref. */ - public Object readNullable(MemoryBuffer buffer) { - byte headFlag = buffer.readByte(); - if (headFlag == Fory.NULL_FLAG) { - return null; - } else { - return readNonRef(buffer); - } - } - - public Object readNullable(MemoryBuffer buffer, Serializer serializer) { - byte headFlag = buffer.readByte(); - if (headFlag == Fory.NULL_FLAG) { - return null; - } else { - return serializer.read(buffer); - } - } - - public Object readNullable(MemoryBuffer buffer, TypeInfoHolder classInfoHolder) { - byte headFlag = buffer.readByte(); - if (headFlag == Fory.NULL_FLAG) { - return null; - } - return readNonRef(buffer, classInfoHolder); - } - - /** Class should be read already. */ - public Object readData(MemoryBuffer buffer, TypeInfo typeInfo) { - incReadDepth(); - Serializer serializer = typeInfo.getSerializer(); - Object read = serializer.read(buffer); - depth--; - return read; - } - - private Object readDataInternal(MemoryBuffer buffer, TypeInfo typeInfo) { - int typeId = typeInfo.getTypeId(); - switch (typeId) { - case Types.BOOL: - return buffer.readBoolean(); - case Types.INT8: - return buffer.readByte(); - case ClassResolver.CHAR_ID: - return buffer.readChar(); - case Types.INT16: - return buffer.readInt16(); - case Types.INT32: - if (compressInt) { - return buffer.readVarInt32(); - } else { - return buffer.readInt32(); - } - case Types.VARINT32: - return buffer.readVarInt32(); - case Types.FLOAT32: - return buffer.readFloat32(); - case Types.INT64: - return LongSerializer.readInt64(buffer, longEncoding); - case Types.VARINT64: - return buffer.readVarInt64(); - case Types.TAGGED_INT64: - return buffer.readTaggedInt64(); - case Types.FLOAT64: - return buffer.readFloat64(); - case Types.STRING: - if (typeInfo.getCls() == String.class) { - return stringSerializer.readString(buffer); - } - incReadDepth(); - Object stringLike = typeInfo.getSerializer().read(buffer); - depth--; - return stringLike; - default: - incReadDepth(); - Object read = typeInfo.getSerializer().read(buffer); - depth--; - return read; + readContext.getGenerics().popGenericType(readContext.getDepth()); } } private void checkXlangBitmap(byte bitmap) { boolean payloadCrossLanguage = (bitmap & isCrossLanguageFlag) == isCrossLanguageFlag; Preconditions.checkArgument( - payloadCrossLanguage == crossLanguage, + payloadCrossLanguage == config.isXlang(), "Serialized payload xlang flag %s does not match this Fory mode %s", payloadCrossLanguage, - crossLanguage); + config.isXlang()); } @Override public T copy(T obj) { - assert copyDepth == 0 : "copy should only be invoked at top level; use copyObject instead"; ensureRegistrationFinished(); try { - return copyObject(obj); + return copyContext.copyObject(obj); } catch (Throwable e) { throw processCopyError(e); } finally { @@ -1061,115 +559,15 @@ public T copy(T obj) { * @return copied object */ public T copyObject(T obj) { - if (obj == null) { - return null; - } - Object copy; - TypeInfo typeInfo = typeResolver.getTypeInfo(obj.getClass(), true); - int typeId = typeInfo.getTypeId(); - switch (typeId) { - case Types.BOOL: - case Types.INT8: - case ClassResolver.CHAR_ID: - case Types.INT16: - case Types.INT32: - case Types.FLOAT32: - case Types.INT64: - case Types.FLOAT64: - return obj; - case Types.STRING: - if (typeInfo.getCls() == String.class) { - return obj; - } - copy = copyObject(obj, typeInfo.getSerializer()); - break; - case ClassResolver.PRIMITIVE_BOOLEAN_ARRAY_ID: - boolean[] boolArr = (boolean[]) obj; - return (T) Arrays.copyOf(boolArr, boolArr.length); - case ClassResolver.PRIMITIVE_BYTE_ARRAY_ID: - byte[] byteArr = (byte[]) obj; - return (T) Arrays.copyOf(byteArr, byteArr.length); - case ClassResolver.PRIMITIVE_CHAR_ARRAY_ID: - char[] charArr = (char[]) obj; - return (T) Arrays.copyOf(charArr, charArr.length); - case ClassResolver.PRIMITIVE_SHORT_ARRAY_ID: - short[] shortArr = (short[]) obj; - return (T) Arrays.copyOf(shortArr, shortArr.length); - case ClassResolver.PRIMITIVE_INT_ARRAY_ID: - int[] intArr = (int[]) obj; - return (T) Arrays.copyOf(intArr, intArr.length); - case ClassResolver.PRIMITIVE_FLOAT_ARRAY_ID: - float[] floatArr = (float[]) obj; - return (T) Arrays.copyOf(floatArr, floatArr.length); - case ClassResolver.PRIMITIVE_LONG_ARRAY_ID: - long[] longArr = (long[]) obj; - return (T) Arrays.copyOf(longArr, longArr.length); - case ClassResolver.PRIMITIVE_DOUBLE_ARRAY_ID: - double[] doubleArr = (double[]) obj; - return (T) Arrays.copyOf(doubleArr, doubleArr.length); - case ClassResolver.STRING_ARRAY_ID: - String[] stringArr = (String[]) obj; - return (T) Arrays.copyOf(stringArr, stringArr.length); - case ClassResolver.ARRAYLIST_ID: - copy = arrayListSerializer.copy((ArrayList) obj); - break; - case ClassResolver.HASHMAP_ID: - copy = hashMapSerializer.copy((HashMap) obj); - break; - // todo: add fastpath for other types. - default: - copy = copyObject(obj, typeInfo.getSerializer()); - } - return (T) copy; + return copyContext.copyObject(obj); } public T copyObject(T obj, int classId) { - if (obj == null) { - return null; - } - // Fast path to avoid cost of query class map. - switch (classId) { - case ClassResolver.PRIMITIVE_BOOL_ID: - case ClassResolver.PRIMITIVE_INT8_ID: - case ClassResolver.PRIMITIVE_CHAR_ID: - case ClassResolver.PRIMITIVE_INT16_ID: - case ClassResolver.PRIMITIVE_INT32_ID: - case ClassResolver.PRIMITIVE_FLOAT32_ID: - case ClassResolver.PRIMITIVE_INT64_ID: - case ClassResolver.PRIMITIVE_FLOAT64_ID: - case Types.BOOL: - case Types.INT8: - case ClassResolver.CHAR_ID: - case Types.INT16: - case Types.INT32: - case Types.FLOAT32: - case Types.INT64: - case Types.FLOAT64: - return obj; - case Types.STRING: - if (obj.getClass() == String.class) { - return obj; - } - return copyObject(obj, typeResolver.getTypeInfo(obj.getClass(), true).getSerializer()); - default: - return copyObject(obj, typeResolver.getTypeInfo(obj.getClass(), true).getSerializer()); - } + return copyContext.copyObject(obj, classId); } public T copyObject(T obj, Serializer serializer) { - copyDepth++; - T copyObject; - if (serializer.needToCopyRef()) { - copyObject = getCopyObject(obj); - if (copyObject == null) { - copyObject = serializer.copy(obj); - originToCopyMap.put(obj, copyObject); - } - } else { - copyObject = serializer.copy(obj); - } - copyDepth--; - return copyObject; + return copyContext.copyObject(obj, serializer); } /** @@ -1182,18 +580,16 @@ public T copyObject(T obj, Serializer serializer) { * @param o2 the copied object */ public void reference(T o1, T o2) { - if (copyRefTracking && o1 != null) { - originToCopyMap.put(o1, o2); - } + copyContext.reference(o1, o2); } @SuppressWarnings("unchecked") public T getCopyObject(T originObj) { - return (T) originToCopyMap.get(originObj); + return copyContext.getCopyObject(originObj); } private void serializeToStream(OutputStream outputStream, Consumer function) { - MemoryBuffer buf = getBuffer(); + MemoryBuffer buf = getWriteBuffer(); if (outputStream.getClass() == ByteArrayOutputStream.class) { byte[] oldBytes = buf.getHeapMemory(); // Note: This should not be null. assert oldBytes != null; @@ -1201,6 +597,7 @@ private void serializeToStream(OutputStream outputStream, Consumer function.accept(buf); MemoryUtils.wrap(buf, (ByteArrayOutputStream) outputStream); buf.pointTo(oldBytes, 0, oldBytes.length); + recycleWriteBuffer(buf); } else { buf.writerIndex(0); function.accept(buf); @@ -1215,11 +612,28 @@ private void serializeToStream(OutputStream outputStream, Consumer } catch (IOException e) { throw new SerializationException(e); } finally { - resetBuffer(); + recycleWriteBuffer(buf); } } } + private MemoryBuffer getWriteBuffer() { + MemoryBuffer buffer = this.buffer; + if (buffer == null) { + buffer = MemoryBuffer.newHeapBuffer(64); + this.buffer = buffer; + } + return buffer; + } + + private void recycleWriteBuffer(MemoryBuffer buffer) { + if (buffer.size() > config.bufferSizeLimitBytes()) { + this.buffer = MemoryBuffer.newHeapBuffer(config.bufferSizeLimitBytes()); + } else { + this.buffer = buffer; + } + } + public void reset() { resetWrite(); resetRead(); @@ -1227,38 +641,19 @@ public void reset() { } public void resetWrite() { - refResolver.resetWrite(); - typeResolver.resetWrite(); - metaStringResolver.resetWrite(); - serializationContext.resetWrite(); - bufferCallback = null; - depth = 0; - if (forVirtualThread) { - stringSerializer.clearBuffer(config.bufferSizeLimitBytes()); - } + writeContext.reset(); } public void resetRead() { - refResolver.resetRead(); - typeResolver.resetRead(); - metaStringResolver.resetRead(); - serializationContext.resetRead(); - peerOutOfBandEnabled = false; - depth = 0; - if (forVirtualThread) { - stringSerializer.clearBuffer(config.bufferSizeLimitBytes()); - } + readContext.reset(); } public void resetCopy() { - if (copyRefTracking) { - originToCopyMap.clear(); - } - copyDepth = 0; + copyContext.reset(); } private void throwDepthSerializationException() { - String method = "Fory#writeXXX"; + String method = "WriteContext#writeXXX"; throw new SerializationException( String.format( "Nested call Fory.serializeXXX is not allowed when serializing, Please use %s instead", @@ -1266,7 +661,7 @@ private void throwDepthSerializationException() { } private void throwDepthDeserializationException() { - String method = "Fory#readXXX"; + String method = "ReadContext#readXXX"; throw new DeserializationException( String.format( "Nested call Fory.deserializeXXX is not allowed when deserializing, Please use %s instead", @@ -1283,15 +678,11 @@ public JITContext getJITContext() { } public BufferCallback getBufferCallback() { - return bufferCallback; + return writeContext.getBufferCallback(); } public boolean isPeerOutOfBandEnabled() { - return peerOutOfBandEnabled; - } - - public RefResolver getRefResolver() { - return refResolver; + return readContext.isPeerOutOfBandEnabled(); } /** @@ -1305,60 +696,17 @@ public TypeResolver getTypeResolver() { return typeResolver; } - public MetaStringResolver getMetaStringResolver() { - return metaStringResolver; - } - - public SerializationContext getSerializationContext() { - return serializationContext; - } - - public Generics getGenerics() { - return generics; - } - - public int getDepth() { - return depth; - } - - public void setDepth(int depth) { - this.depth = depth; - } - - public void incDepth(int diff) { - this.depth += diff; - } - - public void incDepth() { - this.depth += 1; - } - - public void decDepth() { - this.depth -= 1; + public WriteContext getWriteContext() { + return writeContext; } - public void incReadDepth() { - if ((this.depth += 1) > maxDepth) { - throwReadDepthExceedException(); - } + public ReadContext getReadContext() { + return readContext; } - private void throwReadDepthExceedException() { - throw new InsecureException( - String.format( - "Read depth exceed max depth %s, " - + "the deserialization data may be malicious. If it's not malicious, " - + "please increase max read depth by ForyBuilder#withMaxDepth(largerDepth)", - maxDepth)); - } - - public void incCopyDepth(int diff) { - this.copyDepth += diff; - } - - // Invoked by jit - public StringSerializer getStringSerializer() { - return stringSerializer; + public void setMetaContext(MetaContext metaContext) { + writeContext.setMetaContext(metaContext); + readContext.setMetaContext(metaContext); } public ClassLoader getClassLoader() { @@ -1370,76 +718,10 @@ public SharedRegistry getSharedRegistry() { return sharedRegistry; } - @Internal - public boolean isRegistrationFinished() { - return registrationFinished; - } - - @Internal - public void setRegistrationFinished() { - registrationFinished = true; - } - - public boolean isCrossLanguage() { - return crossLanguage; - } - - public boolean isCompatible() { - return config.isCompatible(); - } - - public boolean isShareMeta() { - return shareMeta; - } - - public boolean trackingRef() { - return refTracking; - } - - public boolean copyTrackingRef() { - return copyRefTracking; - } - - public boolean isStringRefIgnored() { - return config.isStringRefIgnored(); - } - - public boolean checkClassVersion() { - return config.checkClassVersion(); - } - - public CompatibleMode getCompatibleMode() { - return config.getCompatibleMode(); - } - public Config getConfig() { return config; } - public Class getDefaultJDKStreamSerializerType() { - return config.getDefaultJDKStreamSerializerType(); - } - - public boolean compressString() { - return config.compressString(); - } - - public boolean compressInt() { - return compressInt; - } - - public LongEncoding longEncoding() { - return longEncoding; - } - - public boolean compressLong() { - return config.compressLong(); - } - - public MetaCompressor getMetaCompressor() { - return config.getMetaCompressor(); - } - public static ForyBuilder builder() { return new ForyBuilder(); } diff --git a/java/fory-core/src/main/java/org/apache/fory/ForyCopyable.java b/java/fory-core/src/main/java/org/apache/fory/ForyCopyable.java index f5da0a2409..39ed9e3612 100644 --- a/java/fory-core/src/main/java/org/apache/fory/ForyCopyable.java +++ b/java/fory-core/src/main/java/org/apache/fory/ForyCopyable.java @@ -19,11 +19,13 @@ package org.apache.fory; +import org.apache.fory.context.CopyContext; + /** * Fory copy interface. Customize the copy method of the class * * @param custom copy interface object */ public interface ForyCopyable { - T copy(Fory fory); + T copy(CopyContext copyContext); } diff --git a/java/fory-core/src/main/java/org/apache/fory/builder/BaseObjectCodecBuilder.java b/java/fory-core/src/main/java/org/apache/fory/builder/BaseObjectCodecBuilder.java index 64bc6397b0..384273bd10 100644 --- a/java/fory-core/src/main/java/org/apache/fory/builder/BaseObjectCodecBuilder.java +++ b/java/fory-core/src/main/java/org/apache/fory/builder/BaseObjectCodecBuilder.java @@ -79,6 +79,9 @@ import static org.apache.fory.type.TypeUtils.isPrimitive; import static org.apache.fory.util.Preconditions.checkArgument; +import org.apache.fory.context.ReadContext; +import org.apache.fory.context.WriteContext; + import java.lang.reflect.Modifier; import java.util.Collections; import java.util.HashMap; @@ -120,7 +123,6 @@ import org.apache.fory.reflect.TypeRef; import org.apache.fory.resolver.ClassResolver; import org.apache.fory.resolver.RefMode; -import org.apache.fory.resolver.RefResolver; import org.apache.fory.resolver.TypeInfo; import org.apache.fory.resolver.TypeInfoHolder; import org.apache.fory.resolver.TypeResolver; @@ -154,11 +156,12 @@ @SuppressWarnings("unchecked") public abstract class BaseObjectCodecBuilder extends CodecBuilder { public static final String BUFFER_NAME = "_f_buffer"; - public static final String REF_RESOLVER_NAME = "_f_refResolver"; + public static final String WRITE_CONTEXT_NAME = "_f_writeContext"; + public static final String READ_CONTEXT_NAME = "_f_readContext"; + public static final String CONSTRUCTOR_TYPE_RESOLVER_NAME = "_f_ctorTypeResolver"; public static final String TYPE_RESOLVER_NAME = "_f_typeResolver"; + public static final String REF_WRITER_NAME = "_f_refWriter"; public static final String POJO_CLASS_TYPE_NAME = "_f_classType"; - public static final String STRING_SERIALIZER_NAME = "_f_strSerializer"; - private static final TypeRef STRING_SERIALIZER_TYPE_TOKEN = TypeRef.of(StringSerializer.class); private static final TypeRef SERIALIZER_TYPE = TypeRef.of(Serializer.class); private static final TypeRef COLLECTION_SERIALIZER_TYPE = TypeRef.of(CollectionLikeSerializer.class); @@ -168,10 +171,13 @@ public abstract class BaseObjectCodecBuilder extends CodecBuilder { TypeRef.of(FinalFieldReplaceResolveSerializer.class); protected final Fory fory; - protected final Reference refResolverRef; + protected final TypeRef concreteTypeResolverType; + protected final TypeRef concreteRefWriterType; protected final Reference typeResolverRef; + protected final Reference writeContextRef; + protected final Reference readContextRef; + protected final Reference refWriterRef; protected final TypeResolver typeResolver; - protected final Reference stringSerializerRef; private final Map, Reference> serializerMap = new HashMap<>(); private final Map sharedFieldMap = new HashMap<>(); protected final Class parentSerializerClass; @@ -189,31 +195,19 @@ public BaseObjectCodecBuilder(TypeRef beanType, Fory fory, Class parentSer writeMethodName = "write"; readMethodName = "read"; addCommonImports(); - ctx.reserveName(REF_RESOLVER_NAME); ctx.reserveName(TYPE_RESOLVER_NAME); + ctx.reserveName(WRITE_CONTEXT_NAME); + ctx.reserveName(READ_CONTEXT_NAME); + ctx.reserveName(CONSTRUCTOR_TYPE_RESOLVER_NAME); + ctx.reserveName(REF_WRITER_NAME); // use concrete type to avoid virtual methods call in generated code - TypeRef refResolverTypeRef = TypeRef.of(fory.getRefResolver().getClass()); - refResolverRef = fieldRef(REF_RESOLVER_NAME, refResolverTypeRef); - Expression refResolverExpr = - new Invoke(foryRef, "getRefResolver", TypeRef.of(RefResolver.class)); - ctx.addField( - ctx.type(refResolverTypeRef), - REF_RESOLVER_NAME, - new Cast(refResolverExpr, refResolverTypeRef)); - // use concrete type to avoid virtual methods call in generated code - TypeRef typeResolverType = TypeRef.of(typeResolver.getClass()); - typeResolverRef = fieldRef(TYPE_RESOLVER_NAME, typeResolverType); - Expression typeResolverExpr = - cast( - inlineInvoke(foryRef, "getTypeResolver", TypeRef.of(TypeResolver.class)), - typeResolverType); - ctx.addField(ctx.type(typeResolverType), TYPE_RESOLVER_NAME, typeResolverExpr); - ctx.reserveName(STRING_SERIALIZER_NAME); - stringSerializerRef = fieldRef(STRING_SERIALIZER_NAME, STRING_SERIALIZER_TYPE_TOKEN); - ctx.addField( - ctx.type(TypeRef.of(StringSerializer.class)), - STRING_SERIALIZER_NAME, - inlineInvoke(foryRef, "getStringSerializer", typeResolverType)); + concreteTypeResolverType = TypeRef.of(typeResolver.getClass()); + concreteRefWriterType = TypeRef.of(fory.getWriteContext().getRefWriter().getClass()); + typeResolverRef = fieldRef(TYPE_RESOLVER_NAME, concreteTypeResolverType); + writeContextRef = new Reference(WRITE_CONTEXT_NAME, TypeRef.of(WriteContext.class), false); + readContextRef = new Reference(READ_CONTEXT_NAME, TypeRef.of(ReadContext.class), false); + refWriterRef = new Reference(REF_WRITER_NAME, concreteRefWriterType, false); + ctx.addField(ctx.type(concreteTypeResolverType), TYPE_RESOLVER_NAME); jitCallbackUpdateFields = new HashMap<>(); descriptorDispatchId = new HashMap<>(); } @@ -224,12 +218,12 @@ public BaseObjectCodecBuilder(TypeRef beanType, Fory fory, Class parentSer public String codecClassName(Class beanClass) { String name = ReflectionUtils.getClassNameWithoutPackage(beanClass).replace("$", "_"); StringBuilder nameBuilder = new StringBuilder(name); - if (fory.isCrossLanguage()) { + if (typeResolver.isCrossLanguage()) { // Generated classes are different when xlang mode is enabled. // So we need to use a different class name for generated serializers. nameBuilder.append("Xlang"); } - if (fory.trackingRef()) { + if (typeResolver.trackingRef()) { // Generated classes are different when referenceTracking is switched. // So we need to use a different name. nameBuilder.append("ForyRef"); @@ -285,37 +279,81 @@ public String genCode() { // don't addImport(beanClass), because user class may name collide. ctx.extendsClasses(ctx.type(parentSerializerClass)); ctx.reserveName(POJO_CLASS_TYPE_NAME); - ctx.addField(ctx.type(Fory.class), FORY_NAME); Expression encodeExpr = buildEncodeExpression(); Expression decodeExpr = buildDecodeExpression(); String constructorCode = StringUtils.format( "" - + "super(${fory}, ${cls});\n" - + "this.${fory} = ${fory};\n" - + "${fory}.getTypeResolver().setSerializerIfAbsent(${cls}, this);\n", - "fory", - FORY_NAME, + + "super(${typeResolver}, ${cls});\n" + + "this.${generatedTypeResolver} = (${generatedTypeResolverType}) ${typeResolver};\n" + + "${typeResolver}.setSerializerIfAbsent(${cls}, this);\n", + "typeResolver", + CONSTRUCTOR_TYPE_RESOLVER_NAME, + "generatedTypeResolver", + TYPE_RESOLVER_NAME, + "generatedTypeResolverType", + ctx.type(concreteTypeResolverType), "cls", POJO_CLASS_TYPE_NAME); ctx.clearExprState(); String encodeCode = encodeExpr.genCode(ctx).code(); encodeCode = ctx.optimizeMethodCode(encodeCode); + encodeCode = encodeCode == null ? "" : encodeCode; + StringBuilder encodeCodeBuilder = + new StringBuilder( + StringUtils.format( + "${bufferType} ${buffer} = ${writeContext}.getBuffer();\n", + "bufferType", + ctx.type(MemoryBuffer.class), + "buffer", + BUFFER_NAME, + "writeContext", + WRITE_CONTEXT_NAME)); + if (encodeCode.contains(REF_WRITER_NAME)) { + encodeCodeBuilder.append( + StringUtils.format( + "${refWriterType} ${refWriter} = (${refWriterType}) ${writeContext}.getRefWriter();\n", + "refWriterType", + ctx.type(concreteRefWriterType), + "refWriter", + REF_WRITER_NAME, + "writeContext", + WRITE_CONTEXT_NAME)); + } + encodeCodeBuilder.append(encodeCode); + encodeCode = encodeCodeBuilder.toString(); ctx.clearExprState(); String decodeCode = decodeExpr.genCode(ctx).code(); decodeCode = ctx.optimizeMethodCode(decodeCode); + decodeCode = decodeCode == null ? "" : decodeCode; + decodeCode = + StringUtils.format( + "${bufferType} ${buffer} = ${readContext}.getBuffer();\n${code}", + "bufferType", + ctx.type(MemoryBuffer.class), + "buffer", + BUFFER_NAME, + "readContext", + READ_CONTEXT_NAME, + "code", + decodeCode); ctx.overrideMethod( writeMethodName, encodeCode, void.class, - MemoryBuffer.class, - BUFFER_NAME, + WriteContext.class, + WRITE_CONTEXT_NAME, Object.class, ROOT_OBJECT_NAME); - ctx.overrideMethod(readMethodName, decodeCode, Object.class, MemoryBuffer.class, BUFFER_NAME); + ctx.overrideMethod(readMethodName, decodeCode, Object.class, ReadContext.class, READ_CONTEXT_NAME); registerJITNotifyCallback(); - ctx.addConstructor(constructorCode, Fory.class, FORY_NAME, Class.class, POJO_CLASS_TYPE_NAME); + ctx.addConstructor( + constructorCode, + TypeResolver.class, + CONSTRUCTOR_TYPE_RESOLVER_NAME, + Class.class, + POJO_CLASS_TYPE_NAME); return ctx.genCode(); } @@ -373,7 +411,11 @@ protected void registerJITNotifyCallback() { */ protected void addCommonImports() { ctx.addImports( - Fory.class, MemoryBuffer.class, fory.getRefResolver().getClass(), Platform.class); + Fory.class, + MemoryBuffer.class, + WriteContext.class, + ReadContext.class, + Platform.class); ctx.addImports(TypeInfo.class, TypeInfoHolder.class, ClassResolver.class); ctx.addImport(Generated.class); ctx.addImports(LazyInitBeanSerializer.class, EnumSerializer.class); @@ -457,7 +499,8 @@ private Expression serializeForNotNullForField( return serializePrimitiveField(inputObject, buffer, descriptor); } else { if (clz == String.class) { - return fory.getStringSerializer().writeStringExpr(stringSerializerRef, buffer, inputObject); + return new StringSerializer(typeResolver.getConfig()) + .writeStringExpr(getOrCreateStringSerializer(), buffer, inputObject); } Expression action; if (useCollectionSerialization(typeRef)) { @@ -525,10 +568,10 @@ private Expression serializePrimitive(Expression inputObject, Expression buffer, } else if (clz == short.class || clz == Short.class) { return new Invoke(buffer, "writeInt16", inputObject); } else if (clz == int.class || clz == Integer.class) { - String func = fory.compressInt() ? "writeVarInt32" : "writeInt32"; + String func = typeResolver.getConfig().compressInt() ? "writeVarInt32" : "writeInt32"; return new Invoke(buffer, func, inputObject); } else if (clz == long.class || clz == Long.class) { - return LongSerializer.writeInt64(buffer, inputObject, fory.longEncoding(), true); + return LongSerializer.writeInt64(buffer, inputObject, typeResolver.getConfig().longEncoding(), true); } else if (clz == float.class || clz == Float.class) { return new Invoke(buffer, "writeFloat32", inputObject); } else if (clz == double.class || clz == Double.class) { @@ -543,12 +586,12 @@ private Expression serializeForNotNullObjectForField( TypeRef typeRef = descriptor.getTypeRef(); Class clz = getRawType(typeRef); if (serializer != null) { - return new Invoke(serializer, writeMethodName, buffer, inputObject); + return new Invoke(serializer, writeMethodName, writeContextRef(), inputObject); } if (isMonomorphic(descriptor)) { // Use descriptor to get the appropriate serializer serializer = getSerializerForField(clz); - return new Invoke(serializer, writeMethodName, buffer, inputObject); + return new Invoke(serializer, writeMethodName, writeContextRef(), inputObject); } else { return writeForNotNullNonFinalObject(inputObject, buffer, typeRef); } @@ -595,7 +638,7 @@ protected Expression serializeForNullable( } protected Expression writeRefOrNull(Expression buffer, Expression object) { - return inlineInvoke(refResolverRef, "writeRefOrNull", PRIMITIVE_BOOLEAN_TYPE, buffer, object); + return inlineInvoke(refWriterRef(), "writeRefOrNull", PRIMITIVE_BOOLEAN_TYPE, buffer, object); } protected Expression serializeForNotNull( @@ -630,7 +673,8 @@ private Expression serializeForNotNull( return serializePrimitive(inputObject, buffer, clz); } else { if (clz == String.class) { - return fory.getStringSerializer().writeStringExpr(stringSerializerRef, buffer, inputObject); + return new StringSerializer(typeResolver.getConfig()) + .writeStringExpr(getOrCreateStringSerializer(), buffer, inputObject); } Expression action; // this is different from ITERABLE_TYPE in RowCodecBuilder. In row-format we don't need to @@ -667,7 +711,8 @@ protected boolean useMapSerialization(Class type) { protected int getNumericDescriptorDispatchId(Descriptor descriptor) { int dispatchId = - descriptorDispatchId.computeIfAbsent(descriptor, d -> DispatchId.getDispatchId(fory, d)); + descriptorDispatchId.computeIfAbsent( + descriptor, d -> DispatchId.getDispatchId(typeResolver, d)); Class rawType = descriptor.getRawType(); Preconditions.checkArgument( TypeUtils.unwrap(rawType).isPrimitive() || dispatchId == DispatchId.FLOAT16); @@ -702,11 +747,11 @@ protected Expression serializeForNotNullObject( Expression inputObject, Expression buffer, TypeRef typeRef, Expression serializer) { Class clz = getRawType(typeRef); if (serializer != null) { - return new Invoke(serializer, writeMethodName, buffer, inputObject); + return new Invoke(serializer, writeMethodName, writeContextRef(), inputObject); } if (isMonomorphic(clz)) { serializer = getOrCreateSerializer(clz); - return new Invoke(serializer, writeMethodName, buffer, inputObject); + return new Invoke(serializer, writeMethodName, writeContextRef(), inputObject); } else { return writeForNotNullNonFinalObject(inputObject, buffer, typeRef); } @@ -729,16 +774,20 @@ protected Expression writeForNotNullNonFinalObject( inlineInvoke(typeResolverRef, "getTypeInfo", classInfoTypeRef, clsExpr)))); } writeClassAndObject.add( - typeResolver(r -> r.writeClassExpr(typeResolverRef, buffer, classInfo))); + typeResolver(r -> r.writeClassExpr(typeResolverRef, writeContextRef(), classInfo))); writeClassAndObject.add( new Invoke( invokeInline(classInfo, "getSerializer", getSerializerType(clz)), writeMethodName, PRIMITIVE_VOID_TYPE, - buffer, + writeContextRef(), inputObject)); return invokeGenerated( - ctx, ofHashSet(buffer, inputObject), writeClassAndObject, "writeClassAndObject", false); + ctx, + writeCutPoints(buffer, inputObject), + writeClassAndObject, + "writeClassAndObject", + false); } protected Expression writeTypeInfo( @@ -752,7 +801,8 @@ protected Expression writeTypeInfo( new Assign( classInfo, inlineInvoke(typeResolverRef, "getTypeInfo", classInfoTypeRef, clsExpr)))); - writeClassAction.add(typeResolver(r -> r.writeClassExpr(typeResolverRef, buffer, classInfo))); + writeClassAction.add( + typeResolver(r -> r.writeClassExpr(typeResolverRef, writeContextRef(), classInfo))); if (returnSerializer) { writeClassAction.add( invoke(classInfo, "getSerializer", "serializer", getSerializerType(declaredClass))); @@ -768,6 +818,10 @@ protected Expression getOrCreateSerializer(Class cls) { return getOrCreateSerializer(cls, false); } + protected Expression getOrCreateStringSerializer() { + return getOrCreateSerializer(String.class); + } + private Expression getOrCreateSerializer(Class cls, boolean isField) { // Not need to check cls final, take collection writeSameTypeElements as an example. // Preconditions.checkArgument(isMonomorphic(cls), cls); @@ -780,8 +834,8 @@ private Expression getOrCreateSerializer(Class cls, boolean isField) { serializerClass = Serializer.class; } boolean finalClassAsFieldCondition = - !fory.isShareMeta() - && !fory.isCompatible() + !typeResolver.isShareMeta() + && !typeResolver.isCompatible() && isField && Modifier.isFinal(cls.getModifiers()) && serializerClass == ReplaceResolveSerializer.class; @@ -830,9 +884,10 @@ private Expression getOrCreateSerializer(Class cls, boolean isField) { // as global serializer, which overwrite serializer updates in jit callback. Expression newSerializerExpr; if (finalClassAsFieldCondition) { - // Create serializer directly via static factory method + // Create serializer directly without going through resolver registration. newSerializerExpr = - new Expression.NewInstance(FINAL_FIELD_SERIALIZER_TYPE, foryRef, fieldTypeExpr); + new Expression.NewInstance( + FINAL_FIELD_SERIALIZER_TYPE, typeResolverRef, fieldTypeExpr); } else { newSerializerExpr = inlineInvoke(typeResolverRef, "getRawSerializer", SERIALIZER_TYPE, fieldTypeExpr); @@ -962,27 +1017,28 @@ protected Reference addTypeInfoHolderField(Class cls) { return reference; } - protected Expression readTypeInfo(Class cls, Expression buffer) { - return readTypeInfo(cls, buffer, true); + protected Expression readTypeInfo(Class cls, Expression ignored) { + return readTypeInfo(cls, ignored, true); } - protected Expression readTypeInfo(Class cls, Expression buffer, boolean inlineReadTypeInfo) { + protected Expression readTypeInfo(Class cls, Expression ignored, boolean inlineReadTypeInfo) { if (ReflectionUtils.isMonomorphic(cls)) { Reference classInfoRef = addTypeInfoField(cls).f0; if (inlineReadTypeInfo) { return inlineInvoke( - typeResolverRef, "readTypeInfo", classInfoTypeRef, buffer, classInfoRef); + typeResolverRef, "readTypeInfo", classInfoTypeRef, readContextRef, classInfoRef); } else { - return new Invoke(typeResolverRef, "readTypeInfo", classInfoTypeRef, buffer, classInfoRef); + return new Invoke( + typeResolverRef, "readTypeInfo", classInfoTypeRef, readContextRef, classInfoRef); } } Reference classInfoHolderRef = addTypeInfoHolderField(cls); if (inlineReadTypeInfo) { return inlineInvoke( - typeResolverRef, "readTypeInfo", classInfoTypeRef, buffer, classInfoHolderRef); + typeResolverRef, "readTypeInfo", classInfoTypeRef, readContextRef, classInfoHolderRef); } else { return new Invoke( - typeResolverRef, "readTypeInfo", classInfoTypeRef, buffer, classInfoHolderRef); + typeResolverRef, "readTypeInfo", classInfoTypeRef, readContextRef, classInfoHolderRef); } } @@ -1030,14 +1086,14 @@ protected Expression serializeForCollection( classInfo, inlineInvoke(typeResolverRef, "getTypeInfo", classInfoTypeRef, clsExpr)))); writeClassAction.add( - typeResolver(r -> r.writeClassExpr(typeResolverRef, buffer, classInfo))); + typeResolver(r -> r.writeClassExpr(typeResolverRef, writeContextRef(), classInfo))); writeClassAction.add( new Return(invokeInline(classInfo, "getSerializer", getSerializerType(typeRef)))); // Spit this into a separate method to avoid method too big to inline. serializer = invokeGenerated( ctx, - ofHashSet(buffer, collection), + writeCutPoints(buffer, collection), writeClassAction, "writeCollectionTypeInfo", false); @@ -1051,7 +1107,7 @@ protected Expression serializeForCollection( new If( inlineInvoke(serializer, "supportCodegenHook", PRIMITIVE_BOOLEAN_TYPE), writeCollectionData(buffer, collection, serializer, elementType), - new Invoke(serializer, writeMethodName, buffer, collection)); + new Invoke(serializer, writeMethodName, writeContextRef(), collection)); // Wrap collection and ifExpr in a ListExpression to ensure collection is evaluated before the // If. This is necessary because 'collection' is used in both branches of the If expression. // Without this, the code generator would assign collection to a variable inside the @@ -1060,7 +1116,11 @@ protected Expression serializeForCollection( ListExpression actions = new ListExpression(collection, ifExpr); if (generateNewMethod) { return invokeGenerated( - ctx, ofHashSet(buffer, collection, serializer), actions, "writeCollection", false); + ctx, + writeCutPoints(buffer, collection, serializer), + actions, + "writeCollection", + false); } return actions; } @@ -1080,6 +1140,7 @@ protected Expression writeCollectionData( serializer, "onCollectionWrite", TypeUtils.collectionOf(elementType), + writeContextRef(), buffer, collection); boolean isList = List.class.isAssignableFrom(getRawType(collection.type())); @@ -1147,7 +1208,8 @@ protected Expression writeCollectionData( writeContainerElements( elementType, false, elemSerializer, hasNull, buffer, collection, size), false)); - Set cutPoint = ofHashSet(buffer, collection, size, trackRef, hasNull); + Set cutPoint = + writeCutPoints(buffer, collection, size, trackRef, hasNull); if (maybeDecl) { cutPoint.add(flags); } @@ -1171,7 +1233,7 @@ protected Expression writeCollectionData( writeBuilder.add( writeContainerElements( elementType, false, elemSerializer, hasNull, buffer, collection, size)); - Set cutPoint = ofHashSet(buffer, collection, size, hasNull); + Set cutPoint = writeCutPoints(buffer, collection, size, hasNull); if (maybeDecl) { cutPoint.add(flags); } @@ -1227,6 +1289,7 @@ private Tuple2 writeElementsHeader( collectionSerializer, "writeTypeHeader", PRIMITIVE_INT_TYPE, + writeContextRef(), buffer, value, classInfoHolder); @@ -1236,6 +1299,7 @@ private Tuple2 writeElementsHeader( collectionSerializer, "writeTypeHeader", PRIMITIVE_INT_TYPE, + writeContextRef(), buffer, value, elementTypeExpr, @@ -1247,6 +1311,7 @@ private Tuple2 writeElementsHeader( collectionSerializer, "writeTypeNullabilityHeader", PRIMITIVE_INT_TYPE, + writeContextRef(), buffer, value, elementTypeExpr, @@ -1384,13 +1449,17 @@ protected Expression serializeForMap( inlineInvoke(typeResolverRef, "getTypeInfo", classInfoTypeRef, clsExpr)))); // Note: writeClassExpr is thread safe. writeClassAction.add( - typeResolver(r -> r.writeClassExpr(typeResolverRef, buffer, classInfo))); + typeResolver(r -> r.writeClassExpr(typeResolverRef, writeContextRef(), classInfo))); writeClassAction.add( new Return(invokeInline(classInfo, "getSerializer", MAP_SERIALIZER_TYPE))); // Spit this into a separate method to avoid method too big to inline. serializer = invokeGenerated( - ctx, ofHashSet(buffer, map), writeClassAction, "writeMapTypeInfo", false); + ctx, + writeCutPoints(buffer, map), + writeClassAction, + "writeMapTypeInfo", + false); } } else if (!MapLikeSerializer.class.isAssignableFrom(serializer.type().getRawType())) { serializer = cast(serializer, TypeRef.of(MapLikeSerializer.class), "mapSerializer"); @@ -1399,14 +1468,15 @@ protected Expression serializeForMap( new If( inlineInvoke(serializer, "supportCodegenHook", PRIMITIVE_BOOLEAN_TYPE), jitWriteMap(buffer, map, serializer, typeRef), - new Invoke(serializer, writeMethodName, buffer, map)); + new Invoke(serializer, writeMethodName, writeContextRef(), map)); // Wrap map and ifExpr in a ListExpression to ensure map is evaluated before the If. // This is necessary because 'map' is used in both branches of the If expression. // Without this, the code generator would assign map to a variable inside the then-branch, // and then try to use that variable name in the else-branch where it's out of scope. Expression write = new ListExpression(map, ifExpr); if (generateNewMethod) { - return invokeGenerated(ctx, ofHashSet(buffer, map, serializer), write, "writeMap", false); + return invokeGenerated( + ctx, writeCutPoints(buffer, map, serializer), write, "writeMap", false); } return write; } @@ -1429,7 +1499,14 @@ private Expression jitWriteMap( Tuple2, TypeRef> keyValueType = getMapKeyValueType(typeRef); TypeRef keyType = keyValueType.f0; TypeRef valueType = keyValueType.f1; - map = new Invoke(serializer, "onMapWrite", TypeUtils.mapOf(keyType, valueType), buffer, map); + map = + new Invoke( + serializer, + "onMapWrite", + TypeUtils.mapOf(keyType, valueType), + writeContextRef(), + buffer, + map); Expression iterator = new Invoke(inlineInvoke(map, "entrySet", SET_TYPE), "iterator", ITERATOR_TYPE); Expression entry = cast(inlineInvoke(iterator, "next", OBJECT_TYPE), MAP_ENTRY_TYPE, "entry"); @@ -1470,6 +1547,7 @@ private Expression jitWriteMap( serializer, method, MAP_ENTRY_TYPE, + writeContextRef(), buffer, entry, iterator, @@ -1481,6 +1559,7 @@ private Expression jitWriteMap( serializer, method, MAP_ENTRY_TYPE, + writeContextRef(), buffer, entry, iterator, @@ -1702,10 +1781,9 @@ protected Expression writeChunk( not(keyWriteRefExpr), not( inlineInvoke( - refResolverRef, + writeContextRef(), "writeRefOrNull", PRIMITIVE_BOOLEAN_TYPE, - buffer, key))), writeKey); } @@ -1718,10 +1796,9 @@ protected Expression writeChunk( not(valueWriteRefExpr), not( inlineInvoke( - refResolverRef, + writeContextRef(), "writeRefOrNull", PRIMITIVE_BOOLEAN_TYPE, - buffer, value))), writeValue); } @@ -1750,15 +1827,14 @@ entry, cast(inlineInvoke(iterator, "next", OBJECT_TYPE), MAP_ENTRY_TYPE)), // Serializer keySerializer, // Serializer valueSerializer // ) - Set params = ofHashSet(buffer, entry, iterator); + Set params = writeCutPoints(buffer, entry, iterator); return invokeGenerated(ctx, params, expressions, "writeChunk", false); } return expressions; } protected Expression tryPreserveRefId(Expression buffer) { - return new Invoke( - refResolverRef, "tryPreserveRefId", "refId", PRIMITIVE_INT_TYPE, false, buffer); + return invokeReadContext("tryPreserveRefId", "refId", PRIMITIVE_INT_TYPE); } /** @@ -1821,9 +1897,8 @@ private Expression readRef( Expression needDeserialize = ExpressionUtils.egt(refId, new Literal(Fory.NOT_NULL_VALUE_FLAG, PRIMITIVE_BYTE_TYPE)); Expression deserializedValue = deserializeForNotNull.get(); - Expression setReadObject = - new Invoke(refResolverRef, "setReadObject", refId, deserializedValue); - Expression readValue = inlineInvoke(refResolverRef, "getReadObject", OBJECT_TYPE, false); + Expression setReadObject = invokeReadContext("setReadObject", refId, deserializedValue); + Expression readValue = inlineInvokeReadContext("getReadObject", OBJECT_TYPE); // use false to ignore null return new If( needDeserialize, @@ -1881,7 +1956,7 @@ private Expression deserializeRef( throw new IllegalStateException("Primitive type don't track ref: " + typeRef); } else { if (cls == String.class) { - return read(stringSerializerRef, buffer, RefMode.TRACKING, STRING_TYPE); + return read(getOrCreateStringSerializer(), buffer, RefMode.TRACKING, STRING_TYPE); } if (useCollectionSerialization(typeRef)) { return readRef( @@ -1928,7 +2003,8 @@ protected Expression deserializeForNotNull( return deserializePrimitive(buffer, cls); } else { if (cls == String.class) { - return fory.getStringSerializer().readStringExpr(stringSerializerRef, buffer); + return new StringSerializer(typeResolver.getConfig()) + .readStringExpr(getOrCreateStringSerializer(), buffer); } Expression obj; if (useCollectionSerialization(typeRef)) { @@ -1957,7 +2033,7 @@ private Expression deserializeForNotNullNoRef( Expression value = deserializeForNotNull(buffer, typeRef, serializer, invokeHint); if (needWriteRef(TypeRef.of(typeRef.getRawType()))) { Expression preserveStubRefId = - new Invoke(refResolverRef, "preserveRefId", new Literal(-1, PRIMITIVE_INT_TYPE)); + invokeReadContext("preserveRefId", new Literal(-1, PRIMITIVE_INT_TYPE)); return new ListExpression(preserveStubRefId, value); } return value; @@ -1989,7 +2065,7 @@ protected Expression deserializeField( // We need to preserve a -1 id so that when the deserializer calls reference(), // it will pop this -1 and skip the setReadObject call. Expression preserveStubRefId = - new Invoke(refResolverRef, "preserveRefId", new Literal(-1, PRIMITIVE_INT_TYPE)); + invokeReadContext("preserveRefId", new Literal(-1, PRIMITIVE_INT_TYPE)); return new ListExpression(preserveStubRefId, value, callback.apply(value)); } return new ListExpression(value, callback.apply(value)); @@ -2008,7 +2084,7 @@ protected Expression deserializeField( if (serializerCallsReference) { Expression preserveStubRefId = - new Invoke(refResolverRef, "preserveRefId", new Literal(-1, PRIMITIVE_INT_TYPE)); + invokeReadContext("preserveRefId", new Literal(-1, PRIMITIVE_INT_TYPE)); return new ListExpression(preserveStubRefId, readNullableExpr); } return readNullableExpr; @@ -2023,7 +2099,8 @@ private Expression deserializeForNotNullForField( return deserializePrimitiveField(buffer, descriptor); } else { if (cls == String.class) { - return fory.getStringSerializer().readStringExpr(stringSerializerRef, buffer); + return new StringSerializer(typeResolver.getConfig()) + .readStringExpr(getOrCreateStringSerializer(), buffer); } Expression obj; if (useCollectionSerialization(typeRef)) { @@ -2111,9 +2188,9 @@ private Expression deserializePrimitive(Expression buffer, Class cls) { } else if (cls == short.class || cls == Short.class) { return readInt16(buffer); } else if (cls == int.class || cls == Integer.class) { - return fory.compressInt() ? readVarInt32(buffer) : readInt32(buffer); + return typeResolver.getConfig().compressInt() ? readVarInt32(buffer) : readInt32(buffer); } else if (cls == long.class || cls == Long.class) { - return LongSerializer.readInt64(buffer, fory.longEncoding()); + return LongSerializer.readInt64(buffer, typeResolver.getConfig().longEncoding()); } else if (cls == float.class || cls == Float.class) { return readFloat32(buffer); } else if (cls == double.class || cls == Double.class) { @@ -2132,10 +2209,11 @@ protected Expression read( Class type = returnType.getRawType(); Expression read; if (refMode == RefMode.NONE) { - read = new Invoke(serializer, readMethodName, returnType, buffer); + read = new Invoke(serializer, readMethodName, returnType, readContextRef()); } else { // janino take inherited generic method return type as return type of erased `T`. - read = new Invoke(serializer, readMethodName, OBJECT_TYPE, buffer, ofEnum(refMode)); + read = + new Invoke(serializer, readMethodName, OBJECT_TYPE, readContextRef(), ofEnum(refMode)); read = cast(inline(read), returnType); } if (ReflectionUtils.isMonomorphic(type) && !TypeUtils.hasExpandableLeafs(type)) { @@ -2143,7 +2221,10 @@ protected Expression read( } read = uninline(read); return new ListExpression( - new Invoke(foryRef, "incReadDepth"), read, new Invoke(foryRef, "decDepth"), read); + invokeReadContext("increaseDepth"), + read, + invokeReadContext("decreaseDepth"), + read); } protected Expression readForNotNullNonFinal( @@ -2151,7 +2232,7 @@ protected Expression readForNotNullNonFinal( if (serializer == null) { Expression classInfo; Class rawType = typeRef.getRawType(); - if (fory.isCompatible() && rawType != Object.class) { + if (typeResolver.isCompatible() && rawType != Object.class) { TypeInfo typeInfo = typeResolver(r -> r.getTypeInfo(rawType, false)); if (typeInfo == null || (typeInfo.getTypeId() == Types.COMPATIBLE_STRUCT @@ -2159,7 +2240,8 @@ protected Expression readForNotNullNonFinal( String name = ctx.newName(StringUtils.uncapitalize(rawType.getSimpleName()) + "Class"); Expression clsExpr = staticClassFieldExpr(rawType, name); classInfo = - inlineInvoke(typeResolverRef, "readTypeInfo", classInfoTypeRef, buffer, clsExpr); + inlineInvoke( + typeResolverRef, "readTypeInfo", classInfoTypeRef, readContextRef, clsExpr); } else { classInfo = readTypeInfo(getRawType(typeRef), buffer); } @@ -2194,7 +2276,8 @@ protected Expression deserializeForCollection( serializer.type()); } Invoke supportHook = inlineInvoke(serializer, "supportCodegenHook", PRIMITIVE_BOOLEAN_TYPE); - Expression collection = new Invoke(serializer, "newCollection", COLLECTION_TYPE, buffer); + Expression collection = + new Invoke(serializer, "newCollection", COLLECTION_TYPE, readContextRef, buffer); Expression size = new Invoke(serializer, "getAndClearNumElements", "size", PRIMITIVE_INT_TYPE); // if add branch by `ArrayList`, generated code will be > 325 bytes. // and List#add is more likely be inlined if there is only one subclass. @@ -2208,6 +2291,7 @@ protected Expression deserializeForCollection( false); if (invokeHint != null && invokeHint.genNewMethod) { invokeHint.add(buffer); + invokeHint.add(readContextRef()); return invokeGenerated( ctx, invokeHint.cutPoints, @@ -2226,7 +2310,7 @@ protected Expression readCollectionCodegen( Class elemClass = TypeUtils.getRawType(elementType); walkPath.add(elementType.toString()); boolean finalType = isMonomorphic(elemClass); - boolean trackingRef = fory.trackingRef() && !(isPrimitive(elemClass) || isBoxed(elemClass)); + boolean trackingRef = typeResolver.trackingRef() && !(isPrimitive(elemClass) || isBoxed(elemClass)); Literal trackingRefFlag = ofInt(CollectionFlags.TRACKING_REF); Expression trackRef = eq(new BitAnd(flags, trackingRefFlag), trackingRefFlag, "trackRef"); if (finalType) { @@ -2275,7 +2359,7 @@ protected Expression readCollectionCodegen( Literal hasNullFlag = ofInt(CollectionFlags.HAS_NULL); Expression hasNull = eq(new BitAnd(flags, hasNullFlag), hasNullFlag, "hasNull"); builder.add(hasNull); - Set cutPoint = ofHashSet(buffer, collection, size); + Set cutPoint = readCutPoints(buffer, collection, size); Expression differentElemTypeRead = invokeGenerated( ctx, @@ -2283,7 +2367,7 @@ protected Expression readCollectionCodegen( readContainerElements(elementType, true, null, null, buffer, collection, size), "differentTypeElemsRead", false); - Set noRefCutPoint = ofHashSet(buffer, collection, size, hasNull); + Set noRefCutPoint = readCutPoints(buffer, collection, size, hasNull); Expression noRefDifferentTypeElemsRead = invokeGenerated( ctx, @@ -2314,7 +2398,7 @@ protected Expression readCollectionCodegen( readContainerElements( elementType, false, elemSerializer, hasNull, buffer, collection, size)); // Same element class read end - Set cutPoint = ofHashSet(buffer, collection, size, hasNull); + Set cutPoint = readCutPoints(buffer, collection, size, hasNull); Expression differentTypeElemsRead = invokeGenerated( ctx, @@ -2441,7 +2525,7 @@ protected Expression deserializeForMap( Expression mapSerializer = serializer; Invoke supportHook = inlineInvoke(serializer, "supportCodegenHook", PRIMITIVE_BOOLEAN_TYPE); ListExpression expressions = new ListExpression(); - Expression newMap = new Invoke(serializer, "newMap", MAP_TYPE, buffer); + Expression newMap = new Invoke(serializer, "newMap", MAP_TYPE, readContextRef, buffer); Expression size = new Invoke(serializer, "getAndClearNumElements", "size", PRIMITIVE_INT_TYPE); Expression chunkHeader = new If( @@ -2477,6 +2561,7 @@ protected Expression deserializeForMap( "sizeAndHeader", PRIMITIVE_LONG_TYPE, false, + readContextRef, buffer, newMap, chunkHeader, @@ -2501,7 +2586,7 @@ chunkHeader, cast(bitand(sizeAndHeader2, ofInt(0xff)), PRIMITIVE_INT_TYPE)), return exprs; }); Set chunkLoopCutPoints = - ofHashSet(buffer, newMap, chunkHeader, size, mapSerializer, keySerializer, valueSerializer); + readCutPoints(buffer, newMap, chunkHeader, size, mapSerializer, keySerializer, valueSerializer); Expression chunkLoopExpr = invokeGenerated(ctx, chunkLoopCutPoints, chunksLoop, "readMapChunks", false); expressions.add(chunkLoopExpr, newMap); @@ -2511,6 +2596,7 @@ chunkHeader, cast(bitand(sizeAndHeader2, ofInt(0xff)), PRIMITIVE_INT_TYPE)), if (invokeHint != null && invokeHint.genNewMethod) { invokeHint.add(buffer); invokeHint.add(serializer); + invokeHint.add(readContextRef()); return invokeGenerated( ctx, invokeHint.cutPoints, @@ -2522,7 +2608,7 @@ chunkHeader, cast(bitand(sizeAndHeader2, ofInt(0xff)), PRIMITIVE_INT_TYPE)), } private boolean mayTrackRefForCollectionRead(Class type) { - if (!fory.trackingRef()) { + if (!typeResolver.trackingRef()) { return false; } if (type.isPrimitive() || isBoxed(type)) { @@ -2535,7 +2621,7 @@ private boolean mayTrackRefForCollectionRead(Class type) { // 2. we can't use `needWriteRef`, the collection/map read must follow ref track header // in serialized data. other language may write ref for elements even `needWriteRef` return // false - if (type == String.class && !fory.isCrossLanguage()) { + if (type == String.class && !typeResolver.isCrossLanguage()) { return !fory.getConfig().isStringRefIgnored(); } return true; @@ -2667,7 +2753,7 @@ private Expression readChunk( // Serializer keySerializer, // Serializer valueSerializer // ) - Set params = ofHashSet(buffer, size, chunkHeader, map); + Set params = readCutPoints(buffer, size, chunkHeader, map); return invokeGenerated(ctx, params, expressions, "readChunk", false); } } @@ -2697,4 +2783,62 @@ protected Expression beanClassExpr() { // Serializer has a `type` field. return new Reference("super.type", CLASS_TYPE); } + + protected Expression writeContextRef() { + return writeContextRef; + } + + protected Expression readContextRef() { + return readContextRef; + } + + protected Invoke invokeWriteContext(String methodName, Expression... arguments) { + return invokeWriteContext(methodName, PRIMITIVE_VOID_TYPE, arguments); + } + + protected Invoke invokeWriteContext( + String methodName, TypeRef returnType, Expression... arguments) { + return new Invoke(writeContextRef(), methodName, "", returnType, false, false, arguments); + } + + protected Invoke inlineInvokeWriteContext( + String methodName, TypeRef returnType, Expression... arguments) { + return Invoke.inlineInvoke(writeContextRef(), methodName, returnType, false, arguments); + } + + protected Invoke invokeReadContext(String methodName, Expression... arguments) { + return invokeReadContext(methodName, "", PRIMITIVE_VOID_TYPE, arguments); + } + + protected Invoke invokeReadContext( + String methodName, TypeRef returnType, Expression... arguments) { + return invokeReadContext(methodName, "", returnType, arguments); + } + + protected Invoke invokeReadContext( + String methodName, String returnNamePrefix, TypeRef returnType, Expression... arguments) { + return new Invoke(readContextRef(), methodName, returnNamePrefix, returnType, false, false, arguments); + } + + protected Invoke inlineInvokeReadContext( + String methodName, TypeRef returnType, Expression... arguments) { + return Invoke.inlineInvoke(readContextRef(), methodName, returnType, false, arguments); + } + + protected Set writeCutPoints(Expression... expressions) { + Set cutPoints = ofHashSet(expressions); + cutPoints.add(writeContextRef()); + cutPoints.add(refWriterRef()); + return cutPoints; + } + + protected Set readCutPoints(Expression... expressions) { + Set cutPoints = ofHashSet(expressions); + cutPoints.add(readContextRef()); + return cutPoints; + } + + protected Expression refWriterRef() { + return refWriterRef; + } } diff --git a/java/fory-core/src/main/java/org/apache/fory/builder/CodecBuilder.java b/java/fory-core/src/main/java/org/apache/fory/builder/CodecBuilder.java index ff81a2511f..7489f92ad3 100644 --- a/java/fory-core/src/main/java/org/apache/fory/builder/CodecBuilder.java +++ b/java/fory-core/src/main/java/org/apache/fory/builder/CodecBuilder.java @@ -43,7 +43,6 @@ import java.util.Set; import java.util.function.Supplier; import java.util.stream.Collectors; -import org.apache.fory.Fory; import org.apache.fory.codegen.CodegenContext; import org.apache.fory.codegen.Expression; import org.apache.fory.codegen.Expression.Cast; @@ -83,8 +82,6 @@ @SuppressWarnings("UnstableApiUsage") public abstract class CodecBuilder { protected static final String ROOT_OBJECT_NAME = "_f_obj"; - // avoid user class has field with name fory. - protected static final String FORY_NAME = "_f_fory"; static TypeRef objectArrayTypeRef = TypeRef.of(Object[].class); static TypeRef bufferTypeRef = TypeRef.of(MemoryBuffer.class); static TypeRef classInfoTypeRef = TypeRef.of(TypeInfo.class); @@ -96,7 +93,6 @@ public abstract class CodecBuilder { protected final boolean isRecord; protected final boolean isInterface; private final Set duplicatedFields; - protected Reference foryRef = Reference.fieldRef(FORY_NAME, TypeRef.of(Fory.class)); public static final Reference recordComponentDefaultValues = new Reference("recordComponentDefaultValues", OBJECT_ARRAY_TYPE); protected final Map fieldMap = new HashMap<>(); @@ -113,7 +109,6 @@ public CodecBuilder(CodegenContext ctx, TypeRef beanType) { } duplicatedFields = Descriptor.getSortedDuplicatedMembers(beanClass).keySet(); // don't ctx.addImport beanClass, because it maybe causes name collide. - ctx.reserveName(FORY_NAME); ctx.reserveName(ROOT_OBJECT_NAME); // Don't import other packages to avoid class conflicts. // For example user class named as `Date`/`List`/`MemoryBuffer` @@ -459,13 +454,7 @@ private Reference getReflectField(Class cls, Field field, boolean setAccessib inlineInvoke( classExpr, "getDeclaredField", fieldTypeRef, Literal.ofString(fieldName)); } else { - fieldExpr = - new StaticInvoke( - ReflectionUtils.class, - "getField", - fieldTypeRef, - classExpr, - Literal.ofString(fieldName)); + fieldExpr = reflectionUtilsInvoke("getField", fieldTypeRef, classExpr, Literal.ofString(fieldName)); } if (!setAccessible) { return fieldExpr; @@ -549,13 +538,7 @@ protected Expression beanClassExpr(Class cls) { true, Class.class, name, - () -> - new StaticInvoke( - ReflectionUtils.class, - "loadClass", - CLASS_TYPE, - Literal.ofString(cls.getName())) - .inline()); + () -> inlineReflectionUtilsInvoke("loadClass", CLASS_TYPE, Literal.ofString(cls.getName()))); } throw new UnsupportedOperationException(); } @@ -581,11 +564,21 @@ protected Expression staticClassFieldExpr(Class cls, String fieldName) { return getOrCreateField( true, Class.class, - fieldName, - () -> - new StaticInvoke( - ReflectionUtils.class, "loadClass", CLASS_TYPE, Literal.ofString(cls.getName())) - .inline()); + fieldName, + () -> + inlineReflectionUtilsInvoke("loadClass", CLASS_TYPE, Literal.ofString(cls.getName()))); + } + + private StaticInvoke reflectionUtilsInvoke( + String methodName, TypeRef returnType, Expression... arguments) { + return new StaticInvoke( + ReflectionUtils.class, methodName, "", returnType, false, false, false, arguments); + } + + private StaticInvoke inlineReflectionUtilsInvoke( + String methodName, TypeRef returnType, Expression... arguments) { + return new StaticInvoke( + ReflectionUtils.class, methodName, "", returnType, false, true, false, arguments); } /** Build unsafePut operation. */ diff --git a/java/fory-core/src/main/java/org/apache/fory/builder/CodecUtils.java b/java/fory-core/src/main/java/org/apache/fory/builder/CodecUtils.java index a65a6dbdaa..6148df85eb 100644 --- a/java/fory-core/src/main/java/org/apache/fory/builder/CodecUtils.java +++ b/java/fory-core/src/main/java/org/apache/fory/builder/CodecUtils.java @@ -66,6 +66,11 @@ public static Class> loadOrGenMetaSharedCodecClass( cls, fory, new MetaSharedCodecBuilder(TypeRef.of(cls), fory, typeDef))); } + public static Class> loadOrGenMetaSharedCodecClass( + TypeResolver typeResolver, Class cls, TypeDef typeDef) { + return typeResolver.getJITContext().asyncVisitFory(f -> loadOrGenMetaSharedCodecClass(f, cls, typeDef)); + } + /** * Load or generate a JIT serializer class for single-layer meta-shared serialization. * @@ -108,7 +113,7 @@ static Class> loadOrGenCodecClass( beanClassClassLoader = fory.getClass().getClassLoader(); } TypeResolver typeResolver = fory.getTypeResolver(); - codeGenerator = getCodeGenerator(fory, beanClassClassLoader, typeResolver); + codeGenerator = getCodeGenerator(beanClassClassLoader, typeResolver); ClassLoader classLoader = codeGenerator.compile( Collections.singletonList(compileUnit), compileState -> compileState.lock.lock()); @@ -121,7 +126,7 @@ static Class> loadOrGenCodecClass( } private static CodeGenerator getCodeGenerator( - Fory fory, ClassLoader beanClassClassLoader, TypeResolver typeResolver) { + ClassLoader beanClassClassLoader, TypeResolver typeResolver) { CodeGenerator codeGenerator; try { // generated code imported fory classes. @@ -133,7 +138,7 @@ private static CodeGenerator getCodeGenerator( new ClassLoader[] {beanClassClassLoader}, loaders -> CodeGenerator.getSharedCodeGenerator(loaders[0])); } catch (ClassNotFoundException e) { - ClassLoader[] loaders = {beanClassClassLoader, fory.getClass().getClassLoader()}; + ClassLoader[] loaders = {beanClassClassLoader, Fory.class.getClassLoader()}; codeGenerator = typeResolver.getOrCreateCodeGenerator( loaders, diff --git a/java/fory-core/src/main/java/org/apache/fory/builder/Generated.java b/java/fory-core/src/main/java/org/apache/fory/builder/Generated.java index f7737f1ee9..7bb7d15ade 100644 --- a/java/fory-core/src/main/java/org/apache/fory/builder/Generated.java +++ b/java/fory-core/src/main/java/org/apache/fory/builder/Generated.java @@ -23,11 +23,12 @@ import java.util.HashMap; import java.util.Map; import java.util.Objects; -import org.apache.fory.Fory; -import org.apache.fory.memory.MemoryBuffer; +import org.apache.fory.context.WriteContext; import org.apache.fory.meta.TypeDef; import org.apache.fory.reflect.ReflectionUtils; +import org.apache.fory.resolver.TypeResolver; import org.apache.fory.serializer.AbstractObjectSerializer; +import org.apache.fory.serializer.MetaSharedLayerSerializerBase; import org.apache.fory.serializer.Serializer; import org.apache.fory.util.Preconditions; @@ -40,8 +41,8 @@ public interface Generated { /** Base class for all generated serializers. */ abstract class GeneratedSerializer extends AbstractObjectSerializer implements Generated { - public GeneratedSerializer(Fory fory, Class cls) { - super(fory, cls); + public GeneratedSerializer(TypeResolver typeResolver, Class cls) { + super(typeResolver, cls); } /** @@ -66,14 +67,15 @@ public void registerJITNotifyCallback( String serializerFieldName = (String) serializerFieldInfos[i]; Class beanFieldType = (Class) serializerFieldInfos[i + 1]; Field field = Objects.requireNonNull(fieldsMap.get(serializerFieldName)); - fory.getJITContext() + typeResolver + .getJITContext() .registerJITNotifyCallback( beanFieldType, new JITContext.NotifyCallback() { @Override public void onNotifyResult(Object result) { Serializer fieldSerializer = - fory.getTypeResolver().getSerializer(beanFieldType); + typeResolver.getSerializer(beanFieldType); Preconditions.checkState(beanFieldType == fieldSerializer.getType()); Preconditions.checkState(result == fieldSerializer.getClass()); ReflectionUtils.setObjectFieldValue( @@ -83,7 +85,7 @@ public void onNotifyResult(Object result) { @Override public void onNotifyMissed() { Serializer fieldSerializer = - fory.getTypeResolver().getSerializer(beanFieldType); + typeResolver.getSerializer(beanFieldType); ReflectionUtils.setObjectFieldValue( subclassSerializer, field, fieldSerializer); } @@ -95,8 +97,8 @@ public void onNotifyMissed() { /** Base class for all type consist serializers. */ abstract class GeneratedObjectSerializer extends GeneratedSerializer implements Generated { - public GeneratedObjectSerializer(Fory fory, Class cls) { - super(fory, cls); + public GeneratedObjectSerializer(TypeResolver typeResolver, Class cls) { + super(typeResolver, cls); } } @@ -107,13 +109,13 @@ abstract class GeneratedMetaSharedSerializer extends GeneratedSerializer impleme /** Will be set in generated constructor by {@link MetaSharedCodecBuilder}. */ public Serializer serializer; - public GeneratedMetaSharedSerializer(Fory fory, Class cls) { - super(fory, cls); + public GeneratedMetaSharedSerializer(TypeResolver typeResolver, Class cls) { + super(typeResolver, cls); } @Override - public void write(MemoryBuffer buffer, Object value) { - serializer.write(buffer, value); + public void write(WriteContext writeContext, Object value) { + serializer.write(writeContext, value); } } @@ -123,9 +125,9 @@ public void write(MemoryBuffer buffer, Object value) { * and does not include parent class fields. */ abstract class GeneratedMetaSharedLayerSerializer - extends org.apache.fory.serializer.MetaSharedLayerSerializerBase implements Generated { - public GeneratedMetaSharedLayerSerializer(Fory fory, Class cls) { - super(fory, cls); + extends MetaSharedLayerSerializerBase implements Generated { + public GeneratedMetaSharedLayerSerializer(TypeResolver typeResolver, Class cls) { + super(typeResolver, cls); } } } diff --git a/java/fory-core/src/main/java/org/apache/fory/builder/LayerMarkerClassGenerator.java b/java/fory-core/src/main/java/org/apache/fory/builder/LayerMarkerClassGenerator.java index 48c4781c47..b022fcb25c 100644 --- a/java/fory-core/src/main/java/org/apache/fory/builder/LayerMarkerClassGenerator.java +++ b/java/fory-core/src/main/java/org/apache/fory/builder/LayerMarkerClassGenerator.java @@ -22,7 +22,6 @@ import java.lang.reflect.Array; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; -import org.apache.fory.Fory; /** * Creates unique marker classes for each layer in a class hierarchy. These marker classes serve as @@ -117,15 +116,8 @@ private static void prefillCommonClasses() { } } - /** - * Get or create a unique marker class for the given target class and layer index. - * - * @param fory the Fory instance (unused, kept for API compatibility) - * @param targetClass the target class this marker represents - * @param layerIndex the layer index in the class hierarchy (0 = topmost parent) - * @return a unique class representing this layer marker - */ - public static Class getOrCreate(Fory fory, Class targetClass, int layerIndex) { + /** Get or create a unique marker class for the given target class and layer index. */ + public static Class getOrCreate(Class targetClass, int layerIndex) { if (layerIndex >= MAX_LAYER_DEPTH) { throw new IllegalArgumentException( "Layer index " + layerIndex + " exceeds maximum depth " + MAX_LAYER_DEPTH); diff --git a/java/fory-core/src/main/java/org/apache/fory/builder/MetaSharedCodecBuilder.java b/java/fory-core/src/main/java/org/apache/fory/builder/MetaSharedCodecBuilder.java index 3e63c01761..38e9c82fe9 100644 --- a/java/fory-core/src/main/java/org/apache/fory/builder/MetaSharedCodecBuilder.java +++ b/java/fory-core/src/main/java/org/apache/fory/builder/MetaSharedCodecBuilder.java @@ -23,6 +23,8 @@ import static org.apache.fory.type.TypeUtils.OBJECT_TYPE; import static org.apache.fory.type.TypeUtils.STRING_TYPE; +import org.apache.fory.context.ReadContext; + import java.lang.reflect.Field; import java.lang.reflect.Member; import java.util.List; @@ -51,6 +53,7 @@ import org.apache.fory.type.Descriptor; import org.apache.fory.type.DescriptorBuilder; import org.apache.fory.type.DescriptorGrouper; +import org.apache.fory.resolver.TypeResolver; import org.apache.fory.util.DefaultValueUtils; import org.apache.fory.util.ExceptionUtils; import org.apache.fory.util.GraalvmSupport; @@ -158,15 +161,18 @@ public String genCode() { ctx.extendsClasses(ctx.type(parentSerializerClass)); ctx.reserveName(POJO_CLASS_TYPE_NAME); ctx.reserveName(SERIALIZER_FIELD_NAME); - ctx.addField(ctx.type(Fory.class), FORY_NAME); String constructorCode = StringUtils.format( "" - + "super(${fory}, ${cls});\n" - + "this.${fory} = ${fory};\n" - + "${serializer} = ${builderClass}.setCodegenSerializer(${fory}, ${cls}, this);\n", - "fory", - FORY_NAME, + + "super(${typeResolver}, ${cls});\n" + + "this.${generatedTypeResolver} = (${generatedTypeResolverType}) ${typeResolver};\n" + + "${serializer} = ${builderClass}.setCodegenSerializer(${typeResolver}, ${cls}, this);\n", + "typeResolver", + CONSTRUCTOR_TYPE_RESOLVER_NAME, + "generatedTypeResolver", + TYPE_RESOLVER_NAME, + "generatedTypeResolverType", + ctx.type(concreteTypeResolverType), "cls", POJO_CLASS_TYPE_NAME, "builderClass", @@ -177,9 +183,26 @@ public String genCode() { Expression decodeExpr = buildDecodeExpression(); String decodeCode = decodeExpr.genCode(ctx).code(); decodeCode = ctx.optimizeMethodCode(decodeCode); - ctx.overrideMethod(readMethodName, decodeCode, Object.class, MemoryBuffer.class, BUFFER_NAME); + decodeCode = decodeCode == null ? "" : decodeCode; + decodeCode = + StringUtils.format( + "${bufferType} ${buffer} = ${readContext}.getBuffer();\n${code}", + "bufferType", + ctx.type(MemoryBuffer.class), + "buffer", + BUFFER_NAME, + "readContext", + READ_CONTEXT_NAME, + "code", + decodeCode); + ctx.overrideMethod(readMethodName, decodeCode, Object.class, ReadContext.class, READ_CONTEXT_NAME); registerJITNotifyCallback(); - ctx.addConstructor(constructorCode, Fory.class, FORY_NAME, Class.class, POJO_CLASS_TYPE_NAME); + ctx.addConstructor( + constructorCode, + TypeResolver.class, + CONSTRUCTOR_TYPE_RESOLVER_NAME, + Class.class, + POJO_CLASS_TYPE_NAME); return ctx.genCode(); } @@ -192,18 +215,20 @@ protected void addCommonImports() { // Invoked by JIT. @SuppressWarnings({"unchecked", "rawtypes"}) public static Serializer setCodegenSerializer( - Fory fory, Class cls, GeneratedMetaSharedSerializer s) { + TypeResolver typeResolver, Class cls, GeneratedMetaSharedSerializer s) { if (GraalvmSupport.isGraalRuntime()) { - return typeResolver(fory, r -> r.getSerializer(s.getType())); + return typeResolver + .getJITContext() + .asyncVisitFory(f -> f.getTypeResolver().getSerializer(s.getType())); } // This method hold jit lock, so create jit serializer async to avoid block serialization. Class serializerClass = - fory.getJITContext() + typeResolver.getJITContext() .registerSerializerJITCallback( () -> ObjectSerializer.class, - () -> CodegenSerializer.loadCodegenSerializer(fory, s.getType()), - c -> s.serializer = Serializers.newSerializer(fory, s.getType(), c)); - return Serializers.newSerializer(fory, cls, serializerClass); + () -> CodegenSerializer.loadCodegenSerializer(typeResolver, s.getType()), + c -> s.serializer = Serializers.newSerializer(typeResolver, s.getType(), c)); + return Serializers.newSerializer(typeResolver, cls, serializerClass); } @Override diff --git a/java/fory-core/src/main/java/org/apache/fory/builder/MetaSharedLayerCodecBuilder.java b/java/fory-core/src/main/java/org/apache/fory/builder/MetaSharedLayerCodecBuilder.java index 134ec5f87e..f09bd72e5a 100644 --- a/java/fory-core/src/main/java/org/apache/fory/builder/MetaSharedLayerCodecBuilder.java +++ b/java/fory-core/src/main/java/org/apache/fory/builder/MetaSharedLayerCodecBuilder.java @@ -32,10 +32,11 @@ import org.apache.fory.codegen.Expression.Reference; import org.apache.fory.codegen.Expression.StaticInvoke; import org.apache.fory.codegen.ExpressionUtils; +import org.apache.fory.context.ReadContext; +import org.apache.fory.context.WriteContext; import org.apache.fory.memory.MemoryBuffer; import org.apache.fory.meta.TypeDef; import org.apache.fory.reflect.TypeRef; -import org.apache.fory.serializer.MetaSharedLayerSerializer; import org.apache.fory.type.Descriptor; import org.apache.fory.type.DescriptorGrouper; import org.apache.fory.util.ExceptionUtils; @@ -50,7 +51,7 @@ *

This is used by {@link org.apache.fory.serializer.ObjectStreamSerializer} to generate JIT * serializers for each layer in the class hierarchy. * - * @see MetaSharedLayerSerializer + * @see org.apache.fory.serializer.MetaSharedLayerSerializer * @see MetaSharedCodecBuilder * @see GeneratedMetaSharedLayerSerializer */ @@ -71,13 +72,10 @@ public MetaSharedLayerCodecBuilder( objectCodecOptimizer = new ObjectCodecOptimizer(beanClass, grouper, false, ctx); } - // Must be static to be shared across the whole process life. private static final Map idGenerator = new ConcurrentHashMap<>(); @Override protected String codecSuffix() { - // For every class def sent from different peer, if the class def are different, then - // a new serializer needs being generated. Integer id = idGenerator.get(layerTypeDef.getId()); if (id == null) { synchronized (idGenerator) { @@ -92,29 +90,51 @@ public String genCode() { ctx.setPackage(CodeGenerator.getPackage(beanClass)); String className = codecClassName(beanClass); ctx.setClassName(className); - // don't addImport(beanClass), because user class may name collide. ctx.extendsClasses(ctx.type(parentSerializerClass)); ctx.reserveName(POJO_CLASS_TYPE_NAME); - ctx.addField(ctx.type(Fory.class), FORY_NAME); String constructorCode = StringUtils.format( - "super(${fory}, ${cls});\nthis.${fory} = ${fory};\n", - "fory", - FORY_NAME, + "" + + "super(${typeResolver}, ${cls});\n" + + "this.${generatedTypeResolver} = (${generatedTypeResolverType}) ${typeResolver};\n" + + "${typeResolver}.setSerializerIfAbsent(${cls}, this);\n", + "typeResolver", + CONSTRUCTOR_TYPE_RESOLVER_NAME, + "generatedTypeResolver", + TYPE_RESOLVER_NAME, + "generatedTypeResolverType", + ctx.type(concreteTypeResolverType), "cls", POJO_CLASS_TYPE_NAME); + ctx.clearExprState(); - Expression encodeExpr = buildEncodeExpression(); - String encodeCode = encodeExpr.genCode(ctx).code(); + String encodeCode = buildEncodeExpression().genCode(ctx).code(); encodeCode = ctx.optimizeMethodCode(encodeCode); + encodeCode = encodeCode == null ? "" : encodeCode; + if (encodeCode.contains(REF_WRITER_NAME)) { + encodeCode = + StringUtils.format( + "${refWriterType} ${refWriter} = (${refWriterType}) ${writeContext}.getRefWriter();\n", + "refWriterType", + ctx.type(concreteRefWriterType), + "refWriter", + REF_WRITER_NAME, + "writeContext", + WRITE_CONTEXT_NAME) + + encodeCode; + } + ctx.clearExprState(); - Expression decodeExpr = buildReadAndSetFieldsExpression(); - String decodeCode = decodeExpr.genCode(ctx).code(); + String decodeCode = buildReadAndSetFieldsExpression().genCode(ctx).code(); decodeCode = ctx.optimizeMethodCode(decodeCode); + decodeCode = decodeCode == null ? "" : decodeCode; + ctx.overrideMethod( "writeFieldsOnly", encodeCode, void.class, + WriteContext.class, + WRITE_CONTEXT_NAME, MemoryBuffer.class, BUFFER_NAME, Object.class, @@ -123,12 +143,19 @@ public String genCode() { "readAndSetFields", decodeCode, Object.class, + ReadContext.class, + READ_CONTEXT_NAME, MemoryBuffer.class, BUFFER_NAME, Object.class, ROOT_OBJECT_NAME); registerJITNotifyCallback(); - ctx.addConstructor(constructorCode, Fory.class, FORY_NAME, Class.class, POJO_CLASS_TYPE_NAME); + ctx.addConstructor( + constructorCode, + org.apache.fory.resolver.TypeResolver.class, + CONSTRUCTOR_TYPE_RESOLVER_NAME, + Class.class, + POJO_CLASS_TYPE_NAME); return ctx.genCode(); } @@ -185,16 +212,11 @@ protected Expression serializeForNullable( @Override protected Expression setFieldValue(Expression bean, Descriptor descriptor, Expression value) { if (descriptor.getField() == null) { - // Field doesn't exist in current class (e.g., from serialPersistentFields). - // Skip setting this field value but still consume the read value. return new StaticInvoke(ExceptionUtils.class, "ignore", value); } return super.setFieldValue(bean, descriptor, value); } - // Note: Layer class meta is read by ObjectStreamSerializer before calling this serializer. - // The generated read() method only reads field data, not the layer class meta. - private Expression buildReadAndSetFieldsExpression() { Reference buffer = new Reference(BUFFER_NAME, bufferTypeRef, false); Reference inputObject = new Reference(ROOT_OBJECT_NAME, OBJECT_TYPE, false); @@ -207,17 +229,21 @@ private Expression buildReadAndSetFieldsExpression() { objectCodecOptimizer.boxedReadGroups, numGroups, expressions, bean, buffer); deserializeReadGroup( objectCodecOptimizer.buildInReadGroups, numGroups, expressions, bean, buffer); - for (Descriptor d : objectCodecOptimizer.descriptorGrouper.getCollectionDescriptors()) { - expressions.add( - deserializeGroup(java.util.Collections.singletonList(d), bean, buffer, false)); + for (Descriptor descriptor : + objectCodecOptimizer.descriptorGrouper.getCollectionDescriptors()) { + expressions.add(deserializeGroup(java.util.Collections.singletonList(descriptor), bean, buffer, false)); } - for (Descriptor d : objectCodecOptimizer.descriptorGrouper.getMapDescriptors()) { - expressions.add( - deserializeGroup(java.util.Collections.singletonList(d), bean, buffer, false)); + for (Descriptor descriptor : objectCodecOptimizer.descriptorGrouper.getMapDescriptors()) { + expressions.add(deserializeGroup(java.util.Collections.singletonList(descriptor), bean, buffer, false)); } deserializeReadGroup( objectCodecOptimizer.otherReadGroups, numGroups, expressions, bean, buffer); expressions.add(new Expression.Return(bean)); return expressions; } + + @Override + protected Expression buildComponentsArray() { + return buildDefaultComponentsArray(); + } } diff --git a/java/fory-core/src/main/java/org/apache/fory/builder/ObjectCodecBuilder.java b/java/fory-core/src/main/java/org/apache/fory/builder/ObjectCodecBuilder.java index 5620d778ce..0c3ef3eefa 100644 --- a/java/fory-core/src/main/java/org/apache/fory/builder/ObjectCodecBuilder.java +++ b/java/fory-core/src/main/java/org/apache/fory/builder/ObjectCodecBuilder.java @@ -118,8 +118,9 @@ public ObjectCodecBuilder(Class beanClass, Fory fory) { } } classVersionHash = - fory.checkClassVersion() - ? new Literal(ObjectSerializer.computeStructHash(fory, grouper), PRIMITIVE_INT_TYPE) + typeResolver.checkClassVersion() + ? new Literal( + ObjectSerializer.computeStructHash(typeResolver, grouper), PRIMITIVE_INT_TYPE) : null; objectCodecOptimizer = new ObjectCodecOptimizer(beanClass, grouper, false, ctx); if (isRecord) { @@ -163,7 +164,7 @@ public Expression buildEncodeExpression() { ListExpression expressions = new ListExpression(); Expression bean = tryCastIfPublic(inputObject, beanType, ctx.newName(beanClass)); expressions.add(bean); - if (fory.checkClassVersion()) { + if (typeResolver.checkClassVersion()) { expressions.add(new Invoke(buffer, "writeInt32", classVersionHash)); } expressions.addAll(serializePrimitives(bean, buffer, objectCodecOptimizer.primitiveGroups)); @@ -225,7 +226,8 @@ private Expression serializeGroup( if (inline) { return expressionSupplier.get(); } - return objectCodecOptimizer.invokeGenerated(expressionSupplier, "writeFields"); + return objectCodecOptimizer.invokeGenerated( + writeCutPoints(bean, buffer), expressionSupplier.get(), "writeFields"); } /** @@ -238,7 +240,7 @@ private List serializePrimitives( if (totalSize == 0) { return new ArrayList<>(); } - if (fory.compressInt() || fory.compressLong()) { + if (typeResolver.getConfig().compressInt() || typeResolver.getConfig().compressLong()) { return serializePrimitivesCompressed(bean, buffer, primitiveGroups, totalSize); } else { return serializePrimitivesUnCompressed(bean, buffer, primitiveGroups, totalSize); @@ -465,14 +467,13 @@ private Expression getWriterPos(Expression writerPos, long acc) { public Expression buildDecodeExpression() { Reference buffer = new Reference(BUFFER_NAME, bufferTypeRef, false); ListExpression expressions = new ListExpression(); - if (fory.checkClassVersion()) { + if (typeResolver.checkClassVersion()) { expressions.add(checkClassVersion(buffer)); } Expression bean; if (!isRecord) { bean = newBean(); - Expression referenceObject = - new Invoke(refResolverRef, "reference", PRIMITIVE_VOID_TYPE, bean); + Expression referenceObject = invokeReadContext("reference", bean); expressions.add(bean); expressions.add(referenceObject); } else { @@ -602,7 +603,8 @@ protected Expression deserializeGroup( if (inline) { return exprSupplier.get(); } else { - return objectCodecOptimizer.invokeGenerated(exprSupplier, "readFields"); + return objectCodecOptimizer.invokeGenerated( + readCutPoints(bean, buffer), exprSupplier.get(), "readFields"); } } @@ -640,7 +642,7 @@ protected List deserializePrimitives( if (totalSize == 0) { return new ArrayList<>(); } - if (fory.compressInt() || fory.compressLong()) { + if (typeResolver.getConfig().compressInt() || typeResolver.getConfig().compressLong()) { return deserializeCompressedPrimitives(bean, buffer, primitiveGroups); } else { return deserializeUnCompressedPrimitives(bean, buffer, primitiveGroups, totalSize); diff --git a/java/fory-core/src/main/java/org/apache/fory/builder/ObjectCodecOptimizer.java b/java/fory-core/src/main/java/org/apache/fory/builder/ObjectCodecOptimizer.java index cc3bbf230e..0c40514dc4 100644 --- a/java/fory-core/src/main/java/org/apache/fory/builder/ObjectCodecOptimizer.java +++ b/java/fory-core/src/main/java/org/apache/fory/builder/ObjectCodecOptimizer.java @@ -117,7 +117,7 @@ private void buildGroups() { boxedReadWeight, boxedReadGroups), MutableTuple3.of( - new ArrayList<>(descriptorGrouper.getBuildInDescriptors()), 9, buildInWriteGroups), + new ArrayList<>(descriptorGrouper.getBuildInDescriptors()), 8, buildInWriteGroups), MutableTuple3.of( new ArrayList<>(descriptorGrouper.getBuildInDescriptors()), 5, buildInReadGroups), MutableTuple3.of( diff --git a/java/fory-core/src/main/java/org/apache/fory/codegen/Expression.java b/java/fory-core/src/main/java/org/apache/fory/codegen/Expression.java index 7bd76da388..a3ede1c414 100644 --- a/java/fory-core/src/main/java/org/apache/fory/codegen/Expression.java +++ b/java/fory-core/src/main/java/org/apache/fory/codegen/Expression.java @@ -1236,7 +1236,15 @@ public StaticInvoke( TypeRef type, boolean returnNullable, Expression... arguments) { - this(staticObject, functionName, returnNamePrefix, type, returnNullable, false, arguments); + this( + staticObject, + functionName, + returnNamePrefix, + type, + returnNullable, + false, + ReflectionUtils.hasException(staticObject, functionName), + arguments); } /** @@ -1257,6 +1265,7 @@ public StaticInvoke( TypeRef type, boolean returnNullable, boolean inline, + boolean needTryCatch, Expression... arguments) { super( functionName, @@ -1265,7 +1274,7 @@ public StaticInvoke( returnNamePrefix, returnNullable, inline, - ReflectionUtils.hasException(staticObject, functionName)); + needTryCatch); this.staticObject = staticObject; if (inline && needTryCatch) { throw new UnsupportedOperationException( @@ -1275,6 +1284,25 @@ public StaticInvoke( } } + public StaticInvoke( + Class staticObject, + String functionName, + String returnNamePrefix, + TypeRef type, + boolean returnNullable, + boolean inline, + Expression... arguments) { + this( + staticObject, + functionName, + returnNamePrefix, + type, + returnNullable, + inline, + ReflectionUtils.hasException(staticObject, functionName), + arguments); + } + @Override public TypeRef type() { return type; @@ -2307,11 +2335,11 @@ public String toString() { class ForEach extends AbstractExpression { private Expression inputObject; - - @ClosureVisitable final SerializableBiFunction action; - private final TypeRef elementType; private final boolean elementNullable; + private final Reference indexRef; + private final Reference elementRef; + private final Expression loopAction; /** * inputObject.type() must be multi-dimension array or Collection, not allowed to be primitive @@ -2324,7 +2352,6 @@ public ForEach( super(inputObject); this.inputObject = inputObject; this.elementNullable = elementNullable; - this.action = action; TypeRef elementType; if (inputObject.type().isArray()) { elementType = inputObject.type().getComponentType(); @@ -2332,6 +2359,11 @@ public ForEach( elementType = getElementType(inputObject.type()); } this.elementType = ReflectionUtils.getPublicSuperType(elementType); + String refNamePrefix = String.valueOf(System.identityHashCode(this)); + indexRef = new Reference(refNamePrefix + "_i", PRIMITIVE_INT_TYPE); + elementRef = + new Reference(refNamePrefix + "_elemValue", this.elementType, this.elementNullable); + loopAction = action.apply(indexRef, elementRef); } public ForEach( @@ -2341,9 +2373,12 @@ public ForEach( SerializableBiFunction action) { super(inputObject); this.inputObject = inputObject; - this.action = action; this.elementType = beanType; this.elementNullable = elementNullable; + String refNamePrefix = String.valueOf(System.identityHashCode(this)); + indexRef = new Reference(refNamePrefix + "_i", PRIMITIVE_INT_TYPE); + elementRef = new Reference(refNamePrefix + "_elemValue", this.elementType, elementNullable); + loopAction = action.apply(indexRef, elementRef); } @Override @@ -2360,9 +2395,9 @@ public ExprCode doGenCode(CodegenContext ctx) { } String i = ctx.newName("i"); String elemValue = ctx.newName("elemValue"); - Expression elementExpr = - action.apply(new Reference(i), new Reference(elemValue, elementType, elementNullable)); - ExprCode elementExprCode = elementExpr.genCode(ctx); + indexRef.setName(i); + elementRef.setName(elemValue); + ExprCode elementExprCode = loopAction.genCode(ctx); if (inputObject.type().isArray()) { String code = @@ -2432,16 +2467,19 @@ public ExprCode doGenCode(CodegenContext ctx) { @Override public String toString() { - return String.format("ForEach(%s, %s)", inputObject, action); + return String.format("ForEach(%s, %s)", inputObject, loopAction); } } class ZipForEach extends AbstractExpression { private Expression left; private Expression right; - - @ClosureVisitable - private final SerializableTriFunction action; + private final TypeRef leftElemType; + private final TypeRef rightElemType; + private final Reference indexRef; + private final Reference leftElementRef; + private final Reference rightElementRef; + private final Expression loopAction; public ZipForEach( Expression left, @@ -2450,7 +2488,6 @@ public ZipForEach( super(new Expression[] {left, right}); this.left = left; this.right = right; - this.action = action; Preconditions.checkArgument( left.type().isArray() || TypeRef.of(Collection.class).isSupertypeOf(left.type())); Preconditions.checkArgument( @@ -2459,6 +2496,28 @@ public ZipForEach( Preconditions.checkArgument( right.type().isArray(), "Should both be array or neither be array"); } + TypeRef leftElemType; + if (left.type().isArray()) { + leftElemType = left.type().getComponentType(); + } else { + leftElemType = getElementType(left.type()); + } + this.leftElemType = ReflectionUtils.getPublicSuperType(leftElemType); + TypeRef rightElemType; + if (right.type().isArray()) { + rightElemType = right.type().getComponentType(); + } else { + rightElemType = getElementType(right.type()); + } + this.rightElemType = ReflectionUtils.getPublicSuperType(rightElemType); + String refNamePrefix = String.valueOf(System.identityHashCode(this)); + indexRef = new Reference(refNamePrefix + "_i", PRIMITIVE_INT_TYPE); + leftElementRef = new Reference(refNamePrefix + "_leftElemValue", this.leftElemType, true); + // elemValue nullability check uses isNullAt inside action, so elemValueRef's nullable is + // false. + rightElementRef = + new Reference(refNamePrefix + "_rightElemValue", this.rightElemType, false); + loopAction = action.apply(indexRef, leftElementRef, rightElementRef); } @Override @@ -2481,28 +2540,10 @@ public ExprCode doGenCode(CodegenContext ctx) { String i = ctx.newName("i"); String leftElemValue = ctx.newName("leftElemValue"); String rightElemValue = ctx.newName("rightElemValue"); - TypeRef leftElemType; - if (left.type().isArray()) { - leftElemType = left.type().getComponentType(); - } else { - leftElemType = getElementType(left.type()); - } - leftElemType = ReflectionUtils.getPublicSuperType(leftElemType); - TypeRef rightElemType; - if (right.type().isArray()) { - rightElemType = right.type().getComponentType(); - } else { - rightElemType = getElementType(right.type()); - } - rightElemType = ReflectionUtils.getPublicSuperType(rightElemType); - Expression elemExpr = - action.apply( - new Reference(i), - new Reference(leftElemValue, leftElemType, true), - // elemValue nullability check uses isNullAt inside action, so elemValueRef's nullable - // is false. - new Reference(rightElemValue, rightElemType, false)); - ExprCode elementExprCode = elemExpr.genCode(ctx); + indexRef.setName(i); + leftElementRef.setName(leftElemValue); + rightElementRef.setName(rightElemValue); + ExprCode elementExprCode = loopAction.genCode(ctx); if (left.type().isArray()) { String code = diff --git a/java/fory-core/src/main/java/org/apache/fory/codegen/ExpressionOptimizer.java b/java/fory-core/src/main/java/org/apache/fory/codegen/ExpressionOptimizer.java index 6d4747b61a..a9c3568e6f 100644 --- a/java/fory-core/src/main/java/org/apache/fory/codegen/ExpressionOptimizer.java +++ b/java/fory-core/src/main/java/org/apache/fory/codegen/ExpressionOptimizer.java @@ -41,7 +41,6 @@ * org.apache.fory.codegen.Expression} to invoke the new generated method. */ public class ExpressionOptimizer { - public static Expression invokeGenerated( CodegenContext ctx, SerializableSupplier groupExpressionsGenerator, @@ -89,11 +88,14 @@ public static Expression invokeGenerated( String modifier, String methodPrefix, boolean inlineInvoke) { - LinkedHashMap cutExprMap = new LinkedHashMap<>(); + LinkedHashMap availableCutExprMap = new LinkedHashMap<>(); for (Expression expression : cutPoint) { if (expression == null) { continue; } + if (expression instanceof Reference && ((Reference) expression).isFieldRef()) { + continue; + } if (expression instanceof Literal) { continue; } @@ -106,18 +108,20 @@ public static Expression invokeGenerated( Preconditions.checkArgument( expression.type() != PRIMITIVE_VOID_TYPE, "Cut on block is not supported currently."); String param = ctx.newName(getRawType(expression.type())); - cutExprMap.put(expression, new Reference(param, expression.type())); + availableCutExprMap.put(expression, new Reference(param, expression.type())); } + LinkedHashMap cutExprMap = new LinkedHashMap<>(); // iterate groupExpressions dag to update cutoff point to `Reference`. new ExpressionVisitor() .traverseExpression( groupExpressions, exprSite -> { if (cutPoint.contains((exprSite.current))) { - Reference newExpr = cutExprMap.get(exprSite.current); + Reference newExpr = availableCutExprMap.get(exprSite.current); // cutpoint may pass null, or we remove some expr from cutpoint if we think // it's ok to use original expr such as Literal or Enum expr. if (exprSite.current != newExpr && newExpr != null) { + cutExprMap.putIfAbsent(exprSite.current, newExpr); exprSite.update(newExpr); } return false; diff --git a/java/fory-core/src/main/java/org/apache/fory/collection/MultiKeyWeakMap.java b/java/fory-core/src/main/java/org/apache/fory/collection/MultiKeyWeakMap.java index 42b36a2c84..096497293f 100644 --- a/java/fory-core/src/main/java/org/apache/fory/collection/MultiKeyWeakMap.java +++ b/java/fory-core/src/main/java/org/apache/fory/collection/MultiKeyWeakMap.java @@ -41,17 +41,8 @@ * @see java.util.WeakHashMap */ public class MultiKeyWeakMap { - private static final FinalizableReferenceQueue REFERENCE_QUEUE; - - static { - if (GraalvmSupport.isGraalBuildtime()) { - REFERENCE_QUEUE = null; - } else { - REFERENCE_QUEUE = new FinalizableReferenceQueue(); - } - } - private static final Set REFERENCES = ConcurrentHashMap.newKeySet(); + private static volatile FinalizableReferenceQueue referenceQueue; private final Map map; public MultiKeyWeakMap() { @@ -85,6 +76,20 @@ private List createKey(Object[] keys) { return keyRefs; } + private static FinalizableReferenceQueue getReferenceQueue() { + FinalizableReferenceQueue queue = referenceQueue; + if (queue == null) { + synchronized (MultiKeyWeakMap.class) { + queue = referenceQueue; + if (queue == null) { + queue = new FinalizableReferenceQueue(); + referenceQueue = queue; + } + } + } + return queue; + } + private interface KeyReference {} private static final class NoCallbackRef implements KeyReference { @@ -121,7 +126,7 @@ private final class FinalizableKeyReference extends FinalizableWeakReference keyRefs, boolean[] reclaimedFlags, int index) { - super(obj, REFERENCE_QUEUE); + super(obj, getReferenceQueue()); this.reclaimedFlags = reclaimedFlags; this.index = index; this.keyRefs = keyRefs; diff --git a/java/fory-core/src/main/java/org/apache/fory/config/Config.java b/java/fory-core/src/main/java/org/apache/fory/config/Config.java index 3c8096d074..f786f9d90c 100644 --- a/java/fory-core/src/main/java/org/apache/fory/config/Config.java +++ b/java/fory-core/src/main/java/org/apache/fory/config/Config.java @@ -162,7 +162,7 @@ public boolean serializeEnumByName() { * Fory#registerSerializer(Class, Serializer)}, ex: * *

-   *   fory.registerSerializer(Date.class, new DateSerializer(fory, true));
+   *   fory.registerSerializer(Date.class, new DateSerializer(fory.getConfig(), true));
    * 
* *

Note that enabling ref tracking should happen before serializer codegen of any types which @@ -321,7 +321,7 @@ public boolean equals(Object o) { return false; } Config config = (Config) o; - return name == config.name + return Objects.equals(name, config.name) && trackingRef == config.trackingRef && mapRefLoadFactor == config.mapRefLoadFactor && stringRefIgnored == config.stringRefIgnored diff --git a/java/fory-core/src/main/java/org/apache/fory/config/ForyBuilder.java b/java/fory-core/src/main/java/org/apache/fory/config/ForyBuilder.java index 7b3a553c85..ad1a6255d5 100644 --- a/java/fory-core/src/main/java/org/apache/fory/config/ForyBuilder.java +++ b/java/fory-core/src/main/java/org/apache/fory/config/ForyBuilder.java @@ -661,10 +661,7 @@ private Function factory(ClassLoader loader) { return builder -> { builder.replayActions(actions); builder.finish(); - if (sharedRegistry != null) { - return newFory(builder, loader, sharedRegistry); - } - return newFory(builder, loader); + return newFory(builder, loader, builder.sharedRegistry); }; } } diff --git a/java/fory-core/src/main/java/org/apache/fory/context/CopyContext.java b/java/fory-core/src/main/java/org/apache/fory/context/CopyContext.java new file mode 100644 index 0000000000..aa9418d24e --- /dev/null +++ b/java/fory-core/src/main/java/org/apache/fory/context/CopyContext.java @@ -0,0 +1,169 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.fory.context; + +import java.util.Arrays; +import org.apache.fory.resolver.ClassResolver; +import org.apache.fory.resolver.TypeInfo; +import org.apache.fory.resolver.TypeResolver; +import org.apache.fory.serializer.Serializer; +import org.apache.fory.type.Types; +import org.apache.fory.collection.IdentityMap; + +@SuppressWarnings("unchecked") +public final class CopyContext { + private final TypeResolver typeResolver; + private final boolean copyRefTracking; + private final IdentityMap originToCopyMap; + private int depth; + + public CopyContext(TypeResolver typeResolver, boolean copyRefTracking) { + this.typeResolver = typeResolver; + this.copyRefTracking = copyRefTracking; + originToCopyMap = new IdentityMap<>(2); + } + + public void reset() { + if (copyRefTracking) { + originToCopyMap.clear(); + } + depth = 0; + } + + public int getDepth() { + return depth; + } + + public TypeResolver getTypeResolver() { + return typeResolver; + } + + public void increaseDepth(int diff) { + depth += diff; + } + + public boolean copyTrackingRef() { + return copyRefTracking; + } + + public void reference(T origin, T copied) { + if (copyRefTracking && origin != null) { + originToCopyMap.put(origin, copied); + } + } + + public T getCopyObject(T origin) { + return (T) originToCopyMap.get(origin); + } + + public T copyObject(T obj) { + if (obj == null) { + return null; + } + TypeInfo typeInfo = typeResolver.getTypeInfo(obj.getClass(), true); + int typeId = typeInfo.getTypeId(); + switch (typeId) { + case Types.BOOL: + case Types.INT8: + case ClassResolver.CHAR_ID: + case Types.INT16: + case Types.INT32: + case Types.FLOAT32: + case Types.INT64: + case Types.FLOAT64: + return obj; + case Types.STRING: + if (typeInfo.getCls() == String.class) { + return obj; + } + return copyObject(obj, typeInfo.getSerializer()); + case ClassResolver.PRIMITIVE_BOOLEAN_ARRAY_ID: + return (T) Arrays.copyOf((boolean[]) obj, ((boolean[]) obj).length); + case ClassResolver.PRIMITIVE_BYTE_ARRAY_ID: + return (T) Arrays.copyOf((byte[]) obj, ((byte[]) obj).length); + case ClassResolver.PRIMITIVE_CHAR_ARRAY_ID: + return (T) Arrays.copyOf((char[]) obj, ((char[]) obj).length); + case ClassResolver.PRIMITIVE_SHORT_ARRAY_ID: + return (T) Arrays.copyOf((short[]) obj, ((short[]) obj).length); + case ClassResolver.PRIMITIVE_INT_ARRAY_ID: + return (T) Arrays.copyOf((int[]) obj, ((int[]) obj).length); + case ClassResolver.PRIMITIVE_FLOAT_ARRAY_ID: + return (T) Arrays.copyOf((float[]) obj, ((float[]) obj).length); + case ClassResolver.PRIMITIVE_LONG_ARRAY_ID: + return (T) Arrays.copyOf((long[]) obj, ((long[]) obj).length); + case ClassResolver.PRIMITIVE_DOUBLE_ARRAY_ID: + return (T) Arrays.copyOf((double[]) obj, ((double[]) obj).length); + case ClassResolver.STRING_ARRAY_ID: + return (T) Arrays.copyOf((String[]) obj, ((String[]) obj).length); + default: + return copyObject(obj, typeInfo.getSerializer()); + } + } + + public T copyObject(T obj, int classId) { + if (obj == null) { + return null; + } + switch (classId) { + case ClassResolver.PRIMITIVE_BOOL_ID: + case ClassResolver.PRIMITIVE_INT8_ID: + case ClassResolver.PRIMITIVE_CHAR_ID: + case ClassResolver.PRIMITIVE_INT16_ID: + case ClassResolver.PRIMITIVE_INT32_ID: + case ClassResolver.PRIMITIVE_FLOAT32_ID: + case ClassResolver.PRIMITIVE_INT64_ID: + case ClassResolver.PRIMITIVE_FLOAT64_ID: + case Types.BOOL: + case Types.INT8: + case ClassResolver.CHAR_ID: + case Types.INT16: + case Types.INT32: + case Types.FLOAT32: + case Types.INT64: + case Types.FLOAT64: + return obj; + case Types.STRING: + if (obj.getClass() == String.class) { + return obj; + } + return copyObject(obj, typeResolver.getTypeInfo(obj.getClass(), true).getSerializer()); + default: + return copyObject(obj, typeResolver.getTypeInfo(obj.getClass(), true).getSerializer()); + } + } + + public T copyObject(T obj, Serializer serializer) { + depth++; + try { + if (serializer.needToCopyRef()) { + T existing = getCopyObject(obj); + if (existing != null) { + return existing; + } + T copied = serializer.copy(this, obj); + originToCopyMap.put(obj, copied); + return copied; + } + return serializer.copy(this, obj); + } finally { + depth--; + } + } +} diff --git a/java/fory-core/src/main/java/org/apache/fory/context/MapRefReader.java b/java/fory-core/src/main/java/org/apache/fory/context/MapRefReader.java new file mode 100644 index 0000000000..258832e8c4 --- /dev/null +++ b/java/fory-core/src/main/java/org/apache/fory/context/MapRefReader.java @@ -0,0 +1,130 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.fory.context; + +import org.apache.fory.Fory; +import org.apache.fory.collection.IntArray; +import org.apache.fory.collection.ObjectArray; +import org.apache.fory.memory.MemoryBuffer; + +public final class MapRefReader implements RefReader { + private static final int DEFAULT_ARRAY_CAPACITY = 3; + + private long readCounter; + private long readTotalObjectSize = 0; + private final ObjectArray readObjects = new ObjectArray(DEFAULT_ARRAY_CAPACITY); + private final IntArray readRefIds = new IntArray(DEFAULT_ARRAY_CAPACITY); + private Object readObject; + + @Override + public byte readRefOrNull(MemoryBuffer buffer) { + byte headFlag = buffer.readByte(); + if (headFlag == Fory.REF_FLAG) { + readObject = getReadObject(buffer.readVarUint32Small14()); + } else { + readObject = null; + } + return headFlag; + } + + @Override + public int preserveRefId() { + int nextReadRefId = readObjects.size(); + readObjects.add(null); + readRefIds.add(nextReadRefId); + return nextReadRefId; + } + + @Override + public int preserveRefId(int refId) { + readRefIds.add(refId); + return refId; + } + + @Override + public int tryPreserveRefId(MemoryBuffer buffer) { + byte headFlag = buffer.readByte(); + if (headFlag == Fory.REF_FLAG) { + readObject = getReadObject(buffer.readVarUint32Small14()); + } else { + readObject = null; + if (headFlag == Fory.REF_VALUE_FLAG) { + return preserveRefId(); + } + } + return headFlag; + } + + @Override + public int lastPreservedRefId() { + return readRefIds.get(readRefIds.size - 1); + } + + @Override + public boolean hasPreservedRefId() { + return readRefIds.size > 0; + } + + @Override + public void reference(Object object) { + int refId = readRefIds.pop(); + setReadObject(refId, object); + } + + @Override + public Object getReadObject(int id) { + return readObjects.get(id); + } + + @Override + public Object getReadObject() { + return readObject; + } + + @Override + public void setReadObject(int id, Object object) { + if (id >= 0) { + readObjects.set(id, object); + } + } + + public ObjectArray getReadObjects() { + return readObjects; + } + + @Override + public void reset() { + long totalObjectSize = this.readTotalObjectSize + readObjects.size(); + long counter = this.readCounter + 1; + if (counter < 0 || totalObjectSize < 0) { + counter = 1; + totalObjectSize = readObjects.size(); + } + this.readCounter = counter; + this.readTotalObjectSize = totalObjectSize; + int avg = (int) (totalObjectSize / counter); + if (avg <= DEFAULT_ARRAY_CAPACITY) { + avg = DEFAULT_ARRAY_CAPACITY; + } + readObjects.clearApproximate(avg); + readRefIds.clear(); + readObject = null; + } +} diff --git a/java/fory-core/src/main/java/org/apache/fory/context/MapRefWriter.java b/java/fory-core/src/main/java/org/apache/fory/context/MapRefWriter.java new file mode 100644 index 0000000000..2767a74e6e --- /dev/null +++ b/java/fory-core/src/main/java/org/apache/fory/context/MapRefWriter.java @@ -0,0 +1,111 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.fory.context; + +import org.apache.fory.Fory; +import org.apache.fory.collection.IdentityObjectIntMap; +import org.apache.fory.memory.MemoryBuffer; +import org.apache.fory.util.Preconditions; + +public final class MapRefWriter implements RefWriter { + private static final boolean ENABLE_FORY_REF_PROFILING = + "true".equalsIgnoreCase(System.getProperty("fory.enable_ref_profiling")); + private static final int DEFAULT_MAP_CAPACITY = 3; + + private long writeCounter; + private long writeTotalObjectSize = 0; + private final IdentityObjectIntMap writtenObjects; + + public MapRefWriter(float loadFactor) { + writtenObjects = new IdentityObjectIntMap<>(DEFAULT_MAP_CAPACITY, loadFactor); + } + + @Override + public boolean writeRefOrNull(MemoryBuffer buffer, Object obj) { + buffer.grow(10); + if (obj == null) { + buffer._unsafeWriteByte(Fory.NULL_FLAG); + return true; + } + int newWriteRefId = writtenObjects.size; + int writtenRefId = + ENABLE_FORY_REF_PROFILING + ? writtenObjects.profilingPutOrGet(obj, newWriteRefId) + : writtenObjects.putOrGet(obj, newWriteRefId); + if (writtenRefId >= 0) { + buffer._unsafeWriteByte(Fory.REF_FLAG); + buffer._unsafeWriteVarUint32(writtenRefId); + return true; + } + buffer._unsafeWriteByte(Fory.REF_VALUE_FLAG); + return false; + } + + @Override + public boolean writeRefValueFlag(MemoryBuffer buffer, Object obj) { + assert obj != null; + buffer.grow(10); + int newWriteRefId = writtenObjects.size; + int writtenRefId = + ENABLE_FORY_REF_PROFILING + ? writtenObjects.profilingPutOrGet(obj, newWriteRefId) + : writtenObjects.putOrGet(obj, newWriteRefId); + if (writtenRefId >= 0) { + buffer._unsafeWriteByte(Fory.REF_FLAG); + buffer._unsafeWriteVarUint32(writtenRefId); + return false; + } + buffer._unsafeWriteByte(Fory.REF_VALUE_FLAG); + return true; + } + + @Override + public boolean writeNullFlag(MemoryBuffer buffer, Object obj) { + if (obj == null) { + buffer._unsafeWriteByte(Fory.NULL_FLAG); + return true; + } + return false; + } + + @Override + public void replaceRef(Object original, Object newObject) { + int newObjectId = writtenObjects.get(newObject, -1); + Preconditions.checkArgument(newObjectId != -1); + writtenObjects.put(original, newObjectId); + } + + @Override + public void reset() { + long totalObjectSize = this.writeTotalObjectSize + writtenObjects.size; + long counter = this.writeCounter + 1; + if (counter < 0 || totalObjectSize < 0) { + counter = 1; + totalObjectSize = writtenObjects.size; + } + this.writeCounter = counter; + this.writeTotalObjectSize = totalObjectSize; + int avg = (int) (totalObjectSize / counter); + if (avg <= DEFAULT_MAP_CAPACITY) { + avg = DEFAULT_MAP_CAPACITY; + } + writtenObjects.clearApproximate(avg); + } +} diff --git a/java/fory-core/src/main/java/org/apache/fory/resolver/MetaContext.java b/java/fory-core/src/main/java/org/apache/fory/context/MetaContext.java similarity index 94% rename from java/fory-core/src/main/java/org/apache/fory/resolver/MetaContext.java rename to java/fory-core/src/main/java/org/apache/fory/context/MetaContext.java index ececa3c691..94f5320402 100644 --- a/java/fory-core/src/main/java/org/apache/fory/resolver/MetaContext.java +++ b/java/fory-core/src/main/java/org/apache/fory/context/MetaContext.java @@ -17,10 +17,11 @@ * under the License. */ -package org.apache.fory.resolver; +package org.apache.fory.context; import org.apache.fory.collection.IdentityObjectIntMap; import org.apache.fory.collection.ObjectArray; +import org.apache.fory.resolver.TypeInfo; /** * Context for sharing class meta across multiple serialization. Class name, field name and field diff --git a/java/fory-core/src/main/java/org/apache/fory/context/MetaStringReader.java b/java/fory-core/src/main/java/org/apache/fory/context/MetaStringReader.java new file mode 100644 index 0000000000..d7ef45f285 --- /dev/null +++ b/java/fory-core/src/main/java/org/apache/fory/context/MetaStringReader.java @@ -0,0 +1,232 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.fory.context; + +import java.util.Arrays; +import org.apache.fory.annotation.Internal; +import org.apache.fory.collection.LongLongByteMap; +import org.apache.fory.collection.LongMap; +import org.apache.fory.collection.ObjectMap; +import org.apache.fory.memory.LittleEndian; +import org.apache.fory.memory.MemoryBuffer; +import org.apache.fory.meta.EncodedMetaString; +import org.apache.fory.meta.Encoders; +import org.apache.fory.meta.MetaStringDecoder; +import org.apache.fory.resolver.MetaStringRef; +import org.apache.fory.util.MurmurHash3; + +/** Read-side state for meta-string references. */ +@Internal +public final class MetaStringReader { + private static final int INITIAL_CAPACITY = 2; + private static final float LOAD_FACTOR = 0.5f; + private static final int SMALL_STRING_THRESHOLD = 16; + + private final ObjectMap metaString2StringMap = + new ObjectMap<>(INITIAL_CAPACITY, LOAD_FACTOR); + private final LongMap hash2MetaStringMap = + new LongMap<>(INITIAL_CAPACITY, LOAD_FACTOR); + private final LongLongByteMap longLongMetaStringMap = + new LongLongByteMap<>(INITIAL_CAPACITY, LOAD_FACTOR); + private final MetaStringRef emptyMetaStringRef; + private MetaStringRef[] dynamicReadStringIds = new MetaStringRef[INITIAL_CAPACITY]; + private short dynamicReadStringId; + + public MetaStringReader() { + emptyMetaStringRef = new MetaStringRef(EncodedMetaString.EMPTY); + metaString2StringMap.put(EncodedMetaString.EMPTY, ""); + } + + public String readMetaString(MemoryBuffer buffer) { + MetaStringRef metaStringRef = readMetaStringBytes(buffer); + EncodedMetaString encodedMetaString = metaStringRef.getEncoded(); + String str = metaString2StringMap.get(encodedMetaString); + if (str == null) { + str = metaStringRef.decode(Encoders.GENERIC_DECODER); + metaString2StringMap.put(encodedMetaString, str); + } + return str; + } + + public MetaStringRef readMetaStringBytesWithFlag(MemoryBuffer buffer, int header) { + int len = header >>> 2; + if ((header & 0b10) == 0) { + MetaStringRef metaStringRef = + len <= SMALL_STRING_THRESHOLD + ? readSmallMetaStringBytes(buffer, len) + : readBigMetaStringBytes(buffer, len, buffer.readInt64()); + updateDynamicString(metaStringRef); + return metaStringRef; + } + return dynamicReadStringIds[len - 1]; + } + + public MetaStringRef readMetaStringBytesWithFlag( + MemoryBuffer buffer, MetaStringRef cache, int header) { + int len = header >>> 2; + if ((header & 0b10) == 0) { + MetaStringRef metaStringRef = + len <= SMALL_STRING_THRESHOLD + ? readSmallMetaStringBytes(buffer, cache, len) + : readBigMetaStringBytes(buffer, cache, len); + updateDynamicString(metaStringRef); + return metaStringRef; + } + return dynamicReadStringIds[len - 1]; + } + + public MetaStringRef readMetaStringBytes(MemoryBuffer buffer) { + int header = buffer.readVarUint32Small7(); + int len = header >>> 1; + if ((header & 0b1) == 0) { + MetaStringRef metaStringRef = + len > SMALL_STRING_THRESHOLD + ? readBigMetaStringBytes(buffer, len, buffer.readInt64()) + : readSmallMetaStringBytes(buffer, len); + updateDynamicString(metaStringRef); + return metaStringRef; + } + return dynamicReadStringIds[len - 1]; + } + + public MetaStringRef readMetaStringBytes(MemoryBuffer buffer, MetaStringRef cache) { + int header = buffer.readVarUint32Small7(); + int len = header >>> 1; + if ((header & 0b1) == 0) { + MetaStringRef metaStringRef = + len <= SMALL_STRING_THRESHOLD + ? readSmallMetaStringBytes(buffer, cache, len) + : readBigMetaStringBytes(buffer, cache, len); + updateDynamicString(metaStringRef); + return metaStringRef; + } + return dynamicReadStringIds[len - 1]; + } + + private MetaStringRef readBigMetaStringBytes(MemoryBuffer buffer, MetaStringRef cache, int len) { + long hashCode = buffer.readInt64(); + if (cache.getEncoded().hash == hashCode) { + buffer.increaseReaderIndex(len); + return cache; + } + return readBigMetaStringBytes(buffer, len, hashCode); + } + + private MetaStringRef readBigMetaStringBytes(MemoryBuffer buffer, int len, long hashCode) { + EncodedMetaString encodedMetaString = hash2MetaStringMap.get(hashCode); + if (encodedMetaString == null) { + EncodedMetaString newMetaString = new EncodedMetaString(buffer.readBytes(len), hashCode); + hash2MetaStringMap.put(hashCode, newMetaString); + return new MetaStringRef(newMetaString); + } + buffer.increaseReaderIndex(len); + return new MetaStringRef(encodedMetaString); + } + + private MetaStringRef readSmallMetaStringBytes(MemoryBuffer buffer, int len) { + if (len == 0) { + return emptyMetaStringRef; + } + byte encoding = buffer.readByte(); + long v1; + long v2 = 0; + if (len <= 8) { + v1 = buffer.readBytesAsInt64(len); + } else { + v1 = buffer.readInt64(); + v2 = buffer.readBytesAsInt64(len - 8); + } + EncodedMetaString encodedMetaString = longLongMetaStringMap.get(v1, v2, encoding); + if (encodedMetaString == null) { + return createSmallMetaStringBytes(len, encoding, v1, v2); + } + return new MetaStringRef(encodedMetaString); + } + + private MetaStringRef readSmallMetaStringBytes( + MemoryBuffer buffer, MetaStringRef cache, int len) { + if (len == 0) { + return emptyMetaStringRef; + } + byte encoding = buffer.readByte(); + long v1; + long v2 = 0; + if (len <= 8) { + v1 = buffer.readBytesAsInt64(len); + } else { + v1 = buffer.readInt64(); + v2 = buffer.readBytesAsInt64(len - 8); + } + EncodedMetaString cachedMetaString = cache.getEncoded(); + if (cachedMetaString.first8Bytes == v1 && cachedMetaString.second8Bytes == v2) { + return cache; + } + EncodedMetaString encodedMetaString = longLongMetaStringMap.get(v1, v2, encoding); + if (encodedMetaString == null) { + return createSmallMetaStringBytes(len, encoding, v1, v2); + } + return new MetaStringRef(encodedMetaString); + } + + private MetaStringRef createSmallMetaStringBytes(int len, byte encoding, long v1, long v2) { + byte[] data = new byte[16]; + LittleEndian.putInt64(data, 0, v1); + LittleEndian.putInt64(data, 8, v2); + long hashCode = MurmurHash3.murmurhash3_x64_128(data, 0, len, 47)[0]; + hashCode = Math.abs(hashCode); + hashCode = (hashCode & 0xffffffffffffff00L) | encoding; + EncodedMetaString encodedMetaString = new EncodedMetaString(Arrays.copyOf(data, len), hashCode); + longLongMetaStringMap.put(v1, v2, encoding, encodedMetaString); + return new MetaStringRef(encodedMetaString); + } + + private void updateDynamicString(MetaStringRef metaStringRef) { + short currentDynamicReadId = dynamicReadStringId++; + MetaStringRef[] readStringIds = dynamicReadStringIds; + if (readStringIds.length <= currentDynamicReadId) { + readStringIds = dynamicReadStringIds = growRead(readStringIds, currentDynamicReadId); + } + readStringIds[currentDynamicReadId] = metaStringRef; + } + + private MetaStringRef[] growRead(MetaStringRef[] current, int id) { + int newLength = current.length; + while (newLength <= id) { + newLength <<= 1; + } + MetaStringRef[] expanded = new MetaStringRef[newLength]; + System.arraycopy(current, 0, expanded, 0, current.length); + return expanded; + } + + public void reset() { + int dynamicReadId = dynamicReadStringId; + if (dynamicReadId != 0) { + for (int i = 0; i < dynamicReadId; i++) { + dynamicReadStringIds[i] = null; + } + dynamicReadStringId = 0; + } + } + + public String decode(MetaStringRef metaStringRef, MetaStringDecoder decoder) { + return metaStringRef.decode(decoder); + } +} diff --git a/java/fory-core/src/main/java/org/apache/fory/context/MetaStringWriter.java b/java/fory-core/src/main/java/org/apache/fory/context/MetaStringWriter.java new file mode 100644 index 0000000000..24e14ce3cb --- /dev/null +++ b/java/fory-core/src/main/java/org/apache/fory/context/MetaStringWriter.java @@ -0,0 +1,167 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.fory.context; + +import java.util.Objects; +import org.apache.fory.annotation.Internal; +import org.apache.fory.memory.MemoryBuffer; +import org.apache.fory.meta.EncodedMetaString; +import org.apache.fory.meta.Encoders; +import org.apache.fory.meta.MetaString; +import org.apache.fory.meta.MetaStringEncoder; +import org.apache.fory.resolver.MetaStringRef; +import org.apache.fory.resolver.SharedRegistry; + +/** Write-side state for meta-string references. */ +@Internal +public final class MetaStringWriter { + private static final int INITIAL_CAPACITY = 2; + private static final int SMALL_STRING_THRESHOLD = 16; + + private final SharedRegistry sharedRegistry; + private final MetaStringRef emptyMetaStringRef; + private MetaStringRef[] dynamicWrittenStrings = new MetaStringRef[INITIAL_CAPACITY]; + private short dynamicWriteStringId; + + public MetaStringWriter(SharedRegistry sharedRegistry) { + this.sharedRegistry = Objects.requireNonNull(sharedRegistry); + emptyMetaStringRef = new MetaStringRef(EncodedMetaString.EMPTY); + } + + public MetaStringRef getOrCreateGenericMetaStringBytes(String str) { + return getOrCreateGenericMetaStringBytes(str, Encoders.computeGenericEncoding(str)); + } + + public MetaStringRef getOrCreateGenericMetaStringBytes( + String str, MetaString.Encoding encoding) { + return getOrCreateMetaStringBytes( + str, Encoders.GENERIC_ENCODER, encoding, Encoders.GENERIC_ENCODER_TYPE_KEY); + } + + public MetaStringRef getOrCreatePackageMetaStringBytes(String str) { + return getOrCreateMetaStringBytes( + str, + Encoders.PACKAGE_ENCODER, + Encoders.computePackageEncoding(str), + Encoders.PACKAGE_ENCODER_TYPE_KEY); + } + + public MetaStringRef getOrCreateTypeNameMetaStringBytes(String str) { + return getOrCreateMetaStringBytes( + str, + Encoders.TYPE_NAME_ENCODER, + Encoders.computeTypeNameEncoding(str), + Encoders.TYPE_NAME_ENCODER_TYPE_KEY); + } + + public MetaStringRef getOrCreateMetaStringBytes( + String str, MetaStringEncoder encoder, MetaString.Encoding encoding, String encoderTypeKey) { + return new MetaStringRef( + sharedRegistry.getOrCreateEncodedMetaString(str, encoder, encoding, encoderTypeKey)); + } + + public MetaStringRef getEmptyMetaStringRef() { + return emptyMetaStringRef; + } + + public void writeMetaStringBytesWithFlag(MemoryBuffer buffer, MetaStringRef metaStringRef) { + Objects.requireNonNull(metaStringRef); + short id = metaStringRef.dynamicWriteStringId; + if (id == MetaStringRef.DEFAULT_DYNAMIC_WRITE_STRING_ID) { + id = dynamicWriteStringId; + dynamicWriteStringId = (short) (id + 1); + metaStringRef.dynamicWriteStringId = id; + MetaStringRef[] writtenStrings = dynamicWrittenStrings; + if (writtenStrings.length <= id) { + writtenStrings = dynamicWrittenStrings = growWrite(writtenStrings, id); + } + writtenStrings[id] = metaStringRef; + writeNewMetaStringBytesWithFlag(buffer, metaStringRef); + } else { + buffer.writeVarUint32Small7(((id + 1) << 2) | 0b11); + } + } + + public void writeMetaStringBytes(MemoryBuffer buffer, MetaStringRef metaStringRef) { + Objects.requireNonNull(metaStringRef); + short id = metaStringRef.dynamicWriteStringId; + if (id == MetaStringRef.DEFAULT_DYNAMIC_WRITE_STRING_ID) { + id = dynamicWriteStringId; + dynamicWriteStringId = (short) (id + 1); + metaStringRef.dynamicWriteStringId = id; + MetaStringRef[] writtenStrings = dynamicWrittenStrings; + if (writtenStrings.length <= id) { + writtenStrings = dynamicWrittenStrings = growWrite(writtenStrings, id); + } + writtenStrings[id] = metaStringRef; + writeNewMetaStringBytes(buffer, metaStringRef); + } else { + buffer.writeVarUint32Small7(((id + 1) << 1) | 1); + } + } + + public void reset() { + int dynamicId = dynamicWriteStringId; + if (dynamicId != 0) { + for (int i = 0; i < dynamicId; i++) { + MetaStringRef metaStringRef = dynamicWrittenStrings[i]; + if (metaStringRef != null) { + metaStringRef.dynamicWriteStringId = MetaStringRef.DEFAULT_DYNAMIC_WRITE_STRING_ID; + dynamicWrittenStrings[i] = null; + } + } + dynamicWriteStringId = 0; + } + } + + private void writeNewMetaStringBytesWithFlag(MemoryBuffer buffer, MetaStringRef metaStringRef) { + EncodedMetaString encodedMetaString = metaStringRef.getEncoded(); + int length = encodedMetaString.bytes.length; + buffer.writeVarUint32Small7(length << 2 | 0b1); + if (length > SMALL_STRING_THRESHOLD) { + buffer.writeInt64(encodedMetaString.hash); + } else if (length != 0) { + buffer.writeByte(encodedMetaString.encoding.getValue()); + } + buffer.writeBytes(encodedMetaString.bytes); + } + + private void writeNewMetaStringBytes(MemoryBuffer buffer, MetaStringRef metaStringRef) { + EncodedMetaString encodedMetaString = metaStringRef.getEncoded(); + int length = encodedMetaString.bytes.length; + buffer.writeVarUint32Small7(length << 1); + if (length > SMALL_STRING_THRESHOLD) { + buffer.writeInt64(encodedMetaString.hash); + } else if (length != 0) { + buffer.writeByte(encodedMetaString.encoding.getValue()); + } + buffer.writeBytes(encodedMetaString.bytes); + } + + private static MetaStringRef[] growWrite(MetaStringRef[] current, int id) { + int newLength = current.length; + while (newLength <= id) { + newLength <<= 1; + } + MetaStringRef[] expanded = new MetaStringRef[newLength]; + System.arraycopy(current, 0, expanded, 0, current.length); + return expanded; + } +} diff --git a/java/fory-core/src/main/java/org/apache/fory/context/ReadContext.java b/java/fory-core/src/main/java/org/apache/fory/context/ReadContext.java new file mode 100644 index 0000000000..f59fe47d46 --- /dev/null +++ b/java/fory-core/src/main/java/org/apache/fory/context/ReadContext.java @@ -0,0 +1,431 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.fory.context; + +import java.util.IdentityHashMap; +import java.util.Iterator; +import org.apache.fory.Fory; +import org.apache.fory.config.Config; +import org.apache.fory.config.LongEncoding; +import org.apache.fory.exception.InsecureException; +import org.apache.fory.memory.MemoryBuffer; +import org.apache.fory.resolver.ClassResolver; +import org.apache.fory.resolver.TypeInfo; +import org.apache.fory.resolver.TypeInfoHolder; +import org.apache.fory.resolver.TypeResolver; +import org.apache.fory.serializer.PrimitiveSerializers.LongSerializer; +import org.apache.fory.serializer.Serializer; +import org.apache.fory.serializer.StringSerializer; +import org.apache.fory.type.Generics; +import org.apache.fory.type.Types; +import org.apache.fory.util.Preconditions; + +@SuppressWarnings({"rawtypes", "unchecked"}) +public final class ReadContext { + private final Config config; + private final Generics generics; + private final TypeResolver typeResolver; + private final RefReader refReader; + private final MetaStringReader metaStringReader; + private final StringSerializer stringSerializer; + private final boolean crossLanguage; + private final boolean compressInt; + private final LongEncoding longEncoding; + private final int maxDepth; + private final boolean scopedMetaShareEnabled; + private final boolean forVirtualThread; + private final IdentityHashMap contextObjects = new IdentityHashMap<>(); + private MemoryBuffer buffer; + private Iterator outOfBandBuffers; + private MetaContext metaContext; + private boolean peerOutOfBandEnabled; + private int depth; + + public ReadContext( + Config config, + Generics generics, + TypeResolver typeResolver, + RefReader refReader, + MetaStringReader metaStringReader) { + this.config = config; + this.generics = generics; + this.typeResolver = typeResolver; + this.refReader = refReader; + this.metaStringReader = metaStringReader; + stringSerializer = new StringSerializer(config); + crossLanguage = config.isXlang(); + compressInt = config.compressInt(); + longEncoding = config.longEncoding(); + maxDepth = config.maxDepth(); + forVirtualThread = config.forVirtualThread(); + scopedMetaShareEnabled = config.isScopedMetaShareEnabled(); + if (scopedMetaShareEnabled) { + metaContext = new MetaContext(); + } + } + + public void prepare( + MemoryBuffer buffer, Iterable outOfBandBuffers, boolean peerOutOfBandEnabled) { + this.buffer = buffer; + this.peerOutOfBandEnabled = peerOutOfBandEnabled; + this.outOfBandBuffers = outOfBandBuffers == null ? null : outOfBandBuffers.iterator(); + } + + public MemoryBuffer getBuffer() { + return buffer; + } + + public void reset() { + refReader.reset(); + metaStringReader.reset(); + if (!contextObjects.isEmpty()) { + contextObjects.clear(); + } + if (scopedMetaShareEnabled) { + metaContext.readTypeInfos.size = 0; + } else { + metaContext = null; + } + if (forVirtualThread) { + stringSerializer.clearBuffer(config.bufferSizeLimitBytes()); + } + buffer = null; + outOfBandBuffers = null; + peerOutOfBandEnabled = false; + depth = 0; + } + + public Config getConfig() { + return config; + } + + public Generics getGenerics() { + return generics; + } + + public TypeResolver getTypeResolver() { + return typeResolver; + } + + public RefReader getRefReader() { + return refReader; + } + + public byte readRefOrNull() { + return refReader.readRefOrNull(buffer); + } + + public int preserveRefId() { + return refReader.preserveRefId(); + } + + public int preserveRefId(int refId) { + return refReader.preserveRefId(refId); + } + + public int tryPreserveRefId() { + return refReader.tryPreserveRefId(buffer); + } + + public int lastPreservedRefId() { + return refReader.lastPreservedRefId(); + } + + public boolean hasPreservedRefId() { + return refReader.hasPreservedRefId(); + } + + public void reference(Object object) { + refReader.reference(object); + } + + public Object getReadObject(int id) { + return refReader.getReadObject(id); + } + + public Object getReadObject() { + return refReader.getReadObject(); + } + + public void setReadObject(int id, Object object) { + refReader.setReadObject(id, object); + } + + public MetaStringReader getMetaStringReader() { + return metaStringReader; + } + + public StringSerializer getStringSerializer() { + return stringSerializer; + } + + public Object putContextObject(Object key, Object value) { + return contextObjects.put(key, value); + } + + public boolean hasContextObject(Object key) { + return contextObjects.containsKey(key); + } + + public Object getContextObject(Object key) { + return contextObjects.get(key); + } + + public MetaContext getMetaContext() { + return metaContext; + } + + public void setMetaContext(MetaContext metaContext) { + Preconditions.checkArgument(!scopedMetaShareEnabled); + this.metaContext = metaContext; + } + + public boolean isPeerOutOfBandEnabled() { + return peerOutOfBandEnabled; + } + + public int getDepth() { + return depth; + } + + public void setDepth(int depth) { + this.depth = depth; + } + + public void increaseDepth() { + if ((depth += 1) > maxDepth) { + throw new InsecureException( + String.format( + "Read depth exceed max depth %s, the deserialization data may be malicious. If " + + "it's not malicious, please increase max read depth by " + + "ForyBuilder#withMaxDepth(largerDepth)", + maxDepth)); + } + } + + public void increaseDepth(int diff) { + depth += diff; + } + + public void decreaseDepth() { + depth -= 1; + } + + public MemoryBuffer readBufferObject() { + MemoryBuffer buffer = this.buffer; + boolean inBand = buffer.readBoolean(); + if (inBand) { + int size; + if (!crossLanguage) { + size = buffer.readAlignedVarUint32(); + } else { + size = buffer.readVarUint32(); + } + if (buffer.readerIndex() + size > buffer.size() && buffer.getStreamReader() != null) { + buffer.getStreamReader().fillBuffer(buffer.readerIndex() + size - buffer.size()); + } + MemoryBuffer slice = buffer.slice(buffer.readerIndex(), size); + buffer.readerIndex(buffer.readerIndex() + size); + return slice; + } + Preconditions.checkArgument(outOfBandBuffers.hasNext()); + return outOfBandBuffers.next(); + } + + public String readString() { + MemoryBuffer buffer = this.buffer; + return stringSerializer.readString(buffer); + } + + public String readStringRef() { + MemoryBuffer buffer = this.buffer; + if (stringSerializer.needToWriteRef()) { + int nextReadRefId = refReader.tryPreserveRefId(buffer); + if (nextReadRefId >= Fory.NOT_NULL_VALUE_FLAG) { + String obj = stringSerializer.read(this); + refReader.setReadObject(nextReadRefId, obj); + return obj; + } + return (String) refReader.getReadObject(); + } + byte headFlag = buffer.readByte(); + if (headFlag == Fory.NULL_FLAG) { + return null; + } + return stringSerializer.read(this); + } + + public long readInt64() { + MemoryBuffer buffer = this.buffer; + return LongSerializer.readInt64(buffer, longEncoding); + } + + /** Deserialize nullable referencable object from the current buffer. */ + public Object readRef() { + MemoryBuffer buffer = this.buffer; + int nextReadRefId = refReader.tryPreserveRefId(buffer); + if (nextReadRefId >= Fory.NOT_NULL_VALUE_FLAG) { + TypeInfo typeInfo = typeResolver.readTypeInfo(this); + Object o = readNonRef(typeInfo); + refReader.setReadObject(nextReadRefId, o); + return o; + } + return refReader.getReadObject(); + } + + public Object readRef(TypeInfo typeInfo) { + int nextReadRefId = refReader.tryPreserveRefId(buffer); + if (nextReadRefId >= Fory.NOT_NULL_VALUE_FLAG) { + Object o = readNonRef(typeInfo); + refReader.setReadObject(nextReadRefId, o); + return o; + } + return refReader.getReadObject(); + } + + public Object readRef(TypeInfoHolder classInfoHolder) { + int nextReadRefId = refReader.tryPreserveRefId(buffer); + if (nextReadRefId >= Fory.NOT_NULL_VALUE_FLAG) { + TypeInfo typeInfo = typeResolver.readTypeInfo(this, classInfoHolder); + Object o = readNonRef(typeInfo); + refReader.setReadObject(nextReadRefId, o); + return o; + } + return refReader.getReadObject(); + } + + public T readRef(Serializer serializer) { + if (serializer.needToWriteRef()) { + int nextReadRefId = refReader.tryPreserveRefId(buffer); + if (nextReadRefId >= Fory.NOT_NULL_VALUE_FLAG) { + Object o = readNonRef(serializer); + refReader.setReadObject(nextReadRefId, o); + return (T) o; + } + return (T) refReader.getReadObject(); + } + byte headFlag = buffer.readByte(); + if (headFlag == Fory.NULL_FLAG) { + return null; + } + return (T) readNonRef(serializer); + } + + /** Deserialize not-null and non-reference object from the current buffer. */ + public Object readNonRef() { + TypeInfo typeInfo = typeResolver.readTypeInfo(this); + return readNonRef(typeInfo); + } + + public Object readNonRef(TypeInfoHolder classInfoHolder) { + TypeInfo typeInfo = typeResolver.readTypeInfo(this, classInfoHolder); + return readNonRef(typeInfo); + } + + public Object readNonRef(TypeInfo typeInfo) { + return readDataInternal(typeInfo); + } + + public Object readNonRef(Serializer serializer) { + increaseDepth(); + Object o = serializer.read(this); + decreaseDepth(); + return o; + } + + /** Read object class and data without tracking ref. */ + public Object readNullable() { + byte headFlag = buffer.readByte(); + if (headFlag == Fory.NULL_FLAG) { + return null; + } + return readNonRef(); + } + + public Object readNullable(Serializer serializer) { + byte headFlag = buffer.readByte(); + if (headFlag == Fory.NULL_FLAG) { + return null; + } + return serializer.read(this); + } + + public Object readNullable(TypeInfoHolder classInfoHolder) { + byte headFlag = buffer.readByte(); + if (headFlag == Fory.NULL_FLAG) { + return null; + } + return readNonRef(classInfoHolder); + } + + /** Class should be read already. */ + public Object readData(TypeInfo typeInfo) { + increaseDepth(); + Serializer serializer = typeInfo.getSerializer(); + Object read = serializer.read(this); + decreaseDepth(); + return read; + } + + private Object readDataInternal(TypeInfo typeInfo) { + MemoryBuffer buffer = this.buffer; + int typeId = typeInfo.getTypeId(); + switch (typeId) { + case Types.BOOL: + return buffer.readBoolean(); + case Types.INT8: + return buffer.readByte(); + case ClassResolver.CHAR_ID: + return buffer.readChar(); + case Types.INT16: + return buffer.readInt16(); + case Types.INT32: + if (compressInt) { + return buffer.readVarInt32(); + } + return buffer.readInt32(); + case Types.VARINT32: + return buffer.readVarInt32(); + case Types.FLOAT32: + return buffer.readFloat32(); + case Types.INT64: + return LongSerializer.readInt64(buffer, longEncoding); + case Types.VARINT64: + return buffer.readVarInt64(); + case Types.TAGGED_INT64: + return buffer.readTaggedInt64(); + case Types.FLOAT64: + return buffer.readFloat64(); + case Types.STRING: + if (typeInfo.getCls() == String.class) { + return stringSerializer.readString(buffer); + } + increaseDepth(); + Object stringLike = typeInfo.getSerializer().read(this); + decreaseDepth(); + return stringLike; + default: + increaseDepth(); + Object read = typeInfo.getSerializer().read(this); + decreaseDepth(); + return read; + } + } +} diff --git a/java/fory-core/src/main/java/org/apache/fory/context/RefReader.java b/java/fory-core/src/main/java/org/apache/fory/context/RefReader.java new file mode 100644 index 0000000000..1e65025e33 --- /dev/null +++ b/java/fory-core/src/main/java/org/apache/fory/context/RefReader.java @@ -0,0 +1,97 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.fory.context; + +import org.apache.fory.memory.MemoryBuffer; + +public interface RefReader { + byte readRefOrNull(MemoryBuffer buffer); + + int preserveRefId(); + + int preserveRefId(int refId); + + int tryPreserveRefId(MemoryBuffer buffer); + + int lastPreservedRefId(); + + boolean hasPreservedRefId(); + + void reference(Object object); + + Object getReadObject(int id); + + Object getReadObject(); + + void setReadObject(int id, Object object); + + void reset(); + + final class NoRefReader implements RefReader { + @Override + public byte readRefOrNull(MemoryBuffer buffer) { + return buffer.readByte(); + } + + @Override + public int preserveRefId() { + return -1; + } + + @Override + public int preserveRefId(int refId) { + return -1; + } + + @Override + public int tryPreserveRefId(MemoryBuffer buffer) { + return buffer.readByte(); + } + + @Override + public int lastPreservedRefId() { + return -1; + } + + @Override + public boolean hasPreservedRefId() { + return false; + } + + @Override + public void reference(Object object) {} + + @Override + public Object getReadObject(int id) { + return null; + } + + @Override + public Object getReadObject() { + return null; + } + + @Override + public void setReadObject(int id, Object object) {} + + @Override + public void reset() {} + } +} diff --git a/java/fory-core/src/main/java/org/apache/fory/context/RefWriter.java b/java/fory-core/src/main/java/org/apache/fory/context/RefWriter.java new file mode 100644 index 0000000000..3b679bc16d --- /dev/null +++ b/java/fory-core/src/main/java/org/apache/fory/context/RefWriter.java @@ -0,0 +1,69 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.fory.context; + +import org.apache.fory.Fory; +import org.apache.fory.memory.MemoryBuffer; + +public interface RefWriter { + boolean writeRefOrNull(MemoryBuffer buffer, Object obj); + + boolean writeRefValueFlag(MemoryBuffer buffer, Object obj); + + boolean writeNullFlag(MemoryBuffer buffer, Object obj); + + void replaceRef(Object original, Object newObject); + + void reset(); + + final class NoRefWriter implements RefWriter { + @Override + public boolean writeRefOrNull(MemoryBuffer buffer, Object obj) { + if (obj == null) { + buffer.writeByte(Fory.NULL_FLAG); + return true; + } + buffer.writeByte(Fory.NOT_NULL_VALUE_FLAG); + return false; + } + + @Override + public boolean writeRefValueFlag(MemoryBuffer buffer, Object obj) { + assert obj != null; + buffer.writeByte(Fory.NOT_NULL_VALUE_FLAG); + return true; + } + + @Override + public boolean writeNullFlag(MemoryBuffer buffer, Object obj) { + if (obj == null) { + buffer.writeByte(Fory.NULL_FLAG); + return true; + } + return false; + } + + @Override + public void replaceRef(Object original, Object newObject) {} + + @Override + public void reset() {} + } +} diff --git a/java/fory-core/src/main/java/org/apache/fory/context/WriteContext.java b/java/fory-core/src/main/java/org/apache/fory/context/WriteContext.java new file mode 100644 index 0000000000..880ec7e810 --- /dev/null +++ b/java/fory-core/src/main/java/org/apache/fory/context/WriteContext.java @@ -0,0 +1,458 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.fory.context; + +import java.util.IdentityHashMap; +import org.apache.fory.Fory; +import org.apache.fory.config.Config; +import org.apache.fory.config.LongEncoding; +import org.apache.fory.memory.MemoryBuffer; +import org.apache.fory.resolver.ClassResolver; +import org.apache.fory.resolver.TypeInfo; +import org.apache.fory.resolver.TypeInfoHolder; +import org.apache.fory.resolver.TypeResolver; +import org.apache.fory.serializer.ArraySerializers; +import org.apache.fory.serializer.BufferCallback; +import org.apache.fory.serializer.BufferObject; +import org.apache.fory.serializer.PrimitiveSerializers.LongSerializer; +import org.apache.fory.serializer.Serializer; +import org.apache.fory.serializer.StringSerializer; +import org.apache.fory.serializer.UnknownClass.UnknownStruct; +import org.apache.fory.type.Generics; +import org.apache.fory.type.Types; +import org.apache.fory.util.Preconditions; + +@SuppressWarnings({"rawtypes", "unchecked"}) +public final class WriteContext { + private final Config config; + private final Generics generics; + private final TypeResolver typeResolver; + private final RefWriter refWriter; + private final MetaStringWriter metaStringWriter; + private final StringSerializer stringSerializer; + private final boolean crossLanguage; + private final boolean compressInt; + private final LongEncoding longEncoding; + private final boolean forVirtualThread; + private final boolean scopedMetaShareEnabled; + private final IdentityHashMap contextObjects = new IdentityHashMap<>(); + private MemoryBuffer buffer; + private BufferCallback bufferCallback; + private MetaContext metaContext; + private int depth; + + public WriteContext( + Config config, + Generics generics, + TypeResolver typeResolver, + RefWriter refWriter, + MetaStringWriter metaStringWriter) { + this.config = config; + this.generics = generics; + this.typeResolver = typeResolver; + this.refWriter = refWriter; + this.metaStringWriter = metaStringWriter; + stringSerializer = new StringSerializer(config); + crossLanguage = config.isXlang(); + compressInt = config.compressInt(); + longEncoding = config.longEncoding(); + forVirtualThread = config.forVirtualThread(); + scopedMetaShareEnabled = config.isScopedMetaShareEnabled(); + if (scopedMetaShareEnabled) { + metaContext = new MetaContext(); + } + } + + public void prepare(MemoryBuffer buffer, BufferCallback callback) { + this.buffer = buffer; + bufferCallback = callback; + } + + public MemoryBuffer getBuffer() { + return Preconditions.checkNotNull(buffer); + } + + public MemoryBuffer getBufferOrNull() { + return buffer; + } + + public void reset() { + refWriter.reset(); + metaStringWriter.reset(); + if (!contextObjects.isEmpty()) { + contextObjects.clear(); + } + if (scopedMetaShareEnabled) { + metaContext.classMap.clear(); + } else { + metaContext = null; + } + buffer = null; + bufferCallback = null; + depth = 0; + if (forVirtualThread) { + stringSerializer.clearBuffer(config.bufferSizeLimitBytes()); + } + } + + public Config getConfig() { + return config; + } + + public Generics getGenerics() { + return generics; + } + + public TypeResolver getTypeResolver() { + return typeResolver; + } + + public RefWriter getRefWriter() { + return refWriter; + } + + public boolean writeRefOrNull(Object obj) { + return refWriter.writeRefOrNull(buffer, obj); + } + + public boolean writeRefValueFlag(Object obj) { + return refWriter.writeRefValueFlag(buffer, obj); + } + + public boolean writeNullFlag(Object obj) { + return refWriter.writeNullFlag(buffer, obj); + } + + public void replaceRef(Object original, Object newObject) { + refWriter.replaceRef(original, newObject); + } + + public MetaStringWriter getMetaStringWriter() { + return metaStringWriter; + } + + public StringSerializer getStringSerializer() { + return stringSerializer; + } + + public Object putContextObject(Object key, Object value) { + return contextObjects.put(key, value); + } + + public boolean hasContextObject(Object key) { + return contextObjects.containsKey(key); + } + + public Object getContextObject(Object key) { + return contextObjects.get(key); + } + + public MetaContext getMetaContext() { + return metaContext; + } + + public void setMetaContext(MetaContext metaContext) { + Preconditions.checkArgument(!scopedMetaShareEnabled); + this.metaContext = metaContext; + } + + public boolean isCrossLanguage() { + return crossLanguage; + } + + public boolean compressInt() { + return compressInt; + } + + public LongEncoding longEncoding() { + return longEncoding; + } + + public int getDepth() { + return depth; + } + + public void setDepth(int depth) { + this.depth = depth; + } + + public void increaseDepth(int diff) { + depth += diff; + } + + public void increaseDepth() { + depth += 1; + } + + public void decreaseDepth() { + depth -= 1; + } + + public BufferCallback getBufferCallback() { + return bufferCallback; + } + + /** Serialize a nullable referencable object to the current buffer. */ + public void writeRef(Object obj) { + MemoryBuffer buffer = this.buffer; + if (!refWriter.writeRefOrNull(buffer, obj)) { + TypeResolver resolver = typeResolver; + TypeInfo typeInfo = resolver.getTypeInfo(obj.getClass()); + if (crossLanguage && typeInfo.getCls() == UnknownStruct.class) { + depth++; + typeInfo.getSerializer().write(this, obj); + depth--; + return; + } + resolver.writeTypeInfo(this, typeInfo); + writeData(typeInfo, obj); + } + } + + public void writeRef(Object obj, TypeInfoHolder classInfoHolder) { + MemoryBuffer buffer = this.buffer; + if (!refWriter.writeRefOrNull(buffer, obj)) { + TypeResolver resolver = typeResolver; + TypeInfo typeInfo = resolver.getTypeInfo(obj.getClass(), classInfoHolder); + if (crossLanguage && typeInfo.getCls() == UnknownStruct.class) { + depth++; + typeInfo.getSerializer().write(this, obj); + depth--; + return; + } + resolver.writeTypeInfo(this, typeInfo); + writeData(typeInfo, obj); + } + } + + public void writeRef(Object obj, TypeInfo typeInfo) { + MemoryBuffer buffer = this.buffer; + if (crossLanguage && typeInfo.getCls() == UnknownStruct.class) { + if (!refWriter.writeRefOrNull(buffer, obj)) { + depth++; + typeInfo.getSerializer().write(this, obj); + depth--; + } + return; + } + TypeResolver resolver = typeResolver; + Serializer serializer = typeInfo.getSerializer(); + if (serializer.needToWriteRef()) { + if (!refWriter.writeRefOrNull(buffer, obj)) { + resolver.writeTypeInfo(this, typeInfo); + depth++; + serializer.write(this, obj); + depth--; + } + } else if (obj == null) { + buffer.writeByte(Fory.NULL_FLAG); + } else { + buffer.writeByte(Fory.NOT_NULL_VALUE_FLAG); + resolver.writeTypeInfo(this, typeInfo); + depth++; + serializer.write(this, obj); + depth--; + } + } + + public void writeRef(T obj, Serializer serializer) { + MemoryBuffer buffer = this.buffer; + if (serializer.needToWriteRef()) { + if (!refWriter.writeRefOrNull(buffer, obj)) { + depth++; + serializer.write(this, obj); + depth--; + } + } else if (obj == null) { + buffer.writeByte(Fory.NULL_FLAG); + } else { + buffer.writeByte(Fory.NOT_NULL_VALUE_FLAG); + depth++; + serializer.write(this, obj); + depth--; + } + } + + /** + * Serialize a not-null and non-reference object to the current buffer. + * + *

If reference is enabled, this method should be called only when the object is first seen in + * the object graph. + */ + public void writeNonRef(Object obj) { + MemoryBuffer buffer = this.buffer; + TypeResolver resolver = typeResolver; + TypeInfo typeInfo = resolver.getTypeInfo(obj.getClass()); + if (crossLanguage && typeInfo.getCls() == UnknownStruct.class) { + depth++; + typeInfo.getSerializer().write(this, obj); + depth--; + return; + } + resolver.writeTypeInfo(this, typeInfo); + writeData(typeInfo, obj); + } + + public void writeNonRef(Object obj, Serializer serializer) { + depth++; + serializer.write(this, obj); + depth--; + } + + public void writeNonRef(Object obj, TypeInfoHolder holder) { + MemoryBuffer buffer = this.buffer; + TypeResolver resolver = typeResolver; + TypeInfo typeInfo = resolver.getTypeInfo(obj.getClass(), holder); + if (crossLanguage && typeInfo.getCls() == UnknownStruct.class) { + depth++; + typeInfo.getSerializer().write(this, obj); + depth--; + return; + } + resolver.writeTypeInfo(this, typeInfo); + writeData(typeInfo, obj); + } + + public void writeNonRef(Object obj, TypeInfo typeInfo) { + MemoryBuffer buffer = this.buffer; + if (crossLanguage && typeInfo.getCls() == UnknownStruct.class) { + depth++; + typeInfo.getSerializer().write(this, obj); + depth--; + return; + } + typeResolver.writeTypeInfo(this, typeInfo); + writeData(typeInfo, obj); + } + + /** Class/type info should be written already. */ + public void writeData(TypeInfo typeInfo, Object obj) { + MemoryBuffer buffer = this.buffer; + int typeId = typeInfo.getTypeId(); + switch (typeId) { + case Types.BOOL: + buffer.writeBoolean((Boolean) obj); + break; + case Types.INT8: + buffer.writeByte((Byte) obj); + break; + case Types.INT16: + buffer.writeInt16((Short) obj); + break; + case ClassResolver.CHAR_ID: + buffer.writeChar((Character) obj); + break; + case Types.INT32: + case Types.VARINT32: + if (compressInt) { + buffer.writeVarInt32((Integer) obj); + } else { + buffer.writeInt32((Integer) obj); + } + break; + case Types.INT64: + LongSerializer.writeInt64(buffer, (Long) obj, longEncoding); + break; + case Types.VARINT64: + buffer.writeVarInt64((Long) obj); + break; + case Types.TAGGED_INT64: + buffer.writeTaggedInt64((Long) obj); + break; + case Types.FLOAT32: + buffer.writeFloat32((Float) obj); + break; + case Types.FLOAT64: + buffer.writeFloat64((Double) obj); + break; + case Types.STRING: + if (typeInfo.getCls() == String.class) { + stringSerializer.writeString(buffer, (String) obj); + break; + } + depth++; + typeInfo.getSerializer().write(this, obj); + depth--; + break; + default: + depth++; + typeInfo.getSerializer().write(this, obj); + depth--; + } + } + + public void writeBufferObject(BufferObject bufferObject) { + MemoryBuffer buffer = this.buffer; + if (bufferCallback == null || bufferCallback.apply(bufferObject)) { + buffer.writeBoolean(true); + int totalBytes = bufferObject.totalBytes(); + if (!crossLanguage) { + buffer.writeVarUint32Aligned(totalBytes); + } else { + buffer.writeVarUint32(totalBytes); + } + int writerIndex = buffer.writerIndex(); + buffer.ensure(writerIndex + bufferObject.totalBytes()); + bufferObject.writeTo(buffer); + int size = buffer.writerIndex() - writerIndex; + Preconditions.checkArgument(size == totalBytes); + } else { + buffer.writeBoolean(false); + } + } + + public void writeBufferObject(ArraySerializers.PrimitiveArrayBufferObject bufferObject) { + MemoryBuffer buffer = this.buffer; + if (bufferCallback == null || bufferCallback.apply(bufferObject)) { + buffer.writeBoolean(true); + int totalBytes = bufferObject.totalBytes(); + if (!crossLanguage) { + buffer.writeVarUint32Aligned(totalBytes); + } else { + buffer.writeVarUint32(totalBytes); + } + bufferObject.writeTo(buffer); + } else { + buffer.writeBoolean(false); + } + } + + public void writeString(String str) { + stringSerializer.writeString(buffer, str); + } + + public void writeStringRef(String str) { + MemoryBuffer buffer = this.buffer; + if (stringSerializer.needToWriteRef()) { + if (!refWriter.writeRefOrNull(buffer, str)) { + stringSerializer.writeString(buffer, str); + } + } else if (str == null) { + buffer.writeByte(Fory.NULL_FLAG); + } else { + buffer.writeByte(Fory.NOT_NULL_VALUE_FLAG); + stringSerializer.write(this, str); + } + } + + public void writeInt64(long value) { + LongSerializer.writeInt64(buffer, value, longEncoding); + } +} diff --git a/java/fory-core/src/main/java/org/apache/fory/io/BlockedStreamUtils.java b/java/fory-core/src/main/java/org/apache/fory/io/BlockedStreamUtils.java index 62cbffbccb..2ca2ca76d9 100644 --- a/java/fory-core/src/main/java/org/apache/fory/io/BlockedStreamUtils.java +++ b/java/fory-core/src/main/java/org/apache/fory/io/BlockedStreamUtils.java @@ -85,17 +85,15 @@ public static T deserialize(Fory fory, ReadableByteChannel channel, Class private static Object readFromChannel( Fory fory, ReadableByteChannel channel, Function action) { try { - MemoryBuffer buf = fory.getBuffer(); - buf.readerIndex(0); ByteBuffer byteBuffer = ByteBuffer.allocate(4); byteBuffer.order(ByteOrder.LITTLE_ENDIAN); readByteBuffer(channel, byteBuffer, 4); int size = byteBuffer.getInt(); - buf.ensure(size); - readByteBuffer(channel, buf.sliceAsByteBuffer(), size); + MemoryBuffer buf = MemoryBuffer.newHeapBuffer(size); + readByteBuffer(channel, buf.sliceAsByteBuffer(0, size), size); return action.apply(buf); - } finally { - fory.resetBuffer(); + } catch (Throwable t) { + throw ExceptionUtils.handleReadFailed(fory, t); } } @@ -120,7 +118,7 @@ private static void readByteBuffer(ReadableByteChannel channel, ByteBuffer buffe private static void serializeToStream( Fory fory, OutputStream outputStream, Consumer function) { - MemoryBuffer buf = fory.getBuffer(); + MemoryBuffer buf = MemoryBuffer.newHeapBuffer(32); buf.writerIndex(0); try { buf.writeInt32(-1); @@ -135,21 +133,17 @@ private static void serializeToStream( outputStream.flush(); } catch (IOException e) { throw new RuntimeException(e); - } finally { - fory.resetBuffer(); } } private static Object deserializeFromStream( Fory fory, InputStream inputStream, Function function) { - MemoryBuffer buf = fory.getBuffer(); + MemoryBuffer buf = MemoryBuffer.newHeapBuffer(32); try { readToBufferFromStream(inputStream, buf); return function.apply(buf); } catch (Throwable t) { throw ExceptionUtils.handleReadFailed(fory, t); - } finally { - fory.resetBuffer(); } } diff --git a/java/fory-core/src/main/java/org/apache/fory/io/MemoryBufferObjectInput.java b/java/fory-core/src/main/java/org/apache/fory/io/MemoryBufferObjectInput.java index 0187e1b795..062bcb13a0 100644 --- a/java/fory-core/src/main/java/org/apache/fory/io/MemoryBufferObjectInput.java +++ b/java/fory-core/src/main/java/org/apache/fory/io/MemoryBufferObjectInput.java @@ -22,27 +22,27 @@ import java.io.IOException; import java.io.InputStream; import java.io.ObjectInput; -import org.apache.fory.Fory; +import org.apache.fory.config.Config; import org.apache.fory.config.LongEncoding; +import org.apache.fory.context.ReadContext; import org.apache.fory.memory.MemoryBuffer; import org.apache.fory.serializer.PrimitiveSerializers.LongSerializer; import org.apache.fory.serializer.StringSerializer; import org.apache.fory.util.Preconditions; -/** ObjectInput based on {@link Fory} and {@link MemoryBuffer}. */ +/** ObjectInput based on {@link MemoryBuffer}. */ public class MemoryBufferObjectInput extends InputStream implements ObjectInput { - private final Fory fory; private final boolean compressInt; private final LongEncoding longEncoding; - private MemoryBuffer buffer; private final StringSerializer stringSerializer; + private MemoryBuffer buffer; + private ReadContext readContext; - public MemoryBufferObjectInput(Fory fory, MemoryBuffer buffer) { - this.fory = fory; - this.compressInt = fory.compressInt(); - this.longEncoding = fory.longEncoding(); + public MemoryBufferObjectInput(Config config, MemoryBuffer buffer) { + this.compressInt = config.compressInt(); + this.longEncoding = config.longEncoding(); + this.stringSerializer = new StringSerializer(config); this.buffer = buffer; - this.stringSerializer = fory.getStringSerializer(); } public MemoryBuffer getBuffer() { @@ -53,9 +53,13 @@ public void setBuffer(MemoryBuffer buffer) { this.buffer = buffer; } + public void setReadContext(ReadContext readContext) { + this.readContext = readContext; + } + @Override public Object readObject() throws ClassNotFoundException, IOException { - return fory.readRef(buffer); + return readContext.readRef(); } @Override @@ -165,6 +169,9 @@ public String readLine() throws IOException { @Override public String readUTF() throws IOException { + if (readContext != null) { + return readContext.getStringSerializer().readString(buffer); + } return stringSerializer.readString(buffer); } } diff --git a/java/fory-core/src/main/java/org/apache/fory/io/MemoryBufferObjectOutput.java b/java/fory-core/src/main/java/org/apache/fory/io/MemoryBufferObjectOutput.java index 415c5fd8df..db0fac677d 100644 --- a/java/fory-core/src/main/java/org/apache/fory/io/MemoryBufferObjectOutput.java +++ b/java/fory-core/src/main/java/org/apache/fory/io/MemoryBufferObjectOutput.java @@ -22,27 +22,27 @@ import java.io.IOException; import java.io.ObjectOutput; import java.io.OutputStream; -import org.apache.fory.Fory; +import org.apache.fory.config.Config; import org.apache.fory.config.LongEncoding; +import org.apache.fory.context.WriteContext; import org.apache.fory.memory.MemoryBuffer; import org.apache.fory.serializer.PrimitiveSerializers.LongSerializer; import org.apache.fory.serializer.StringSerializer; import org.apache.fory.util.Preconditions; -/** ObjectOutput based on {@link Fory} and {@link MemoryBuffer}. */ +/** ObjectOutput based on {@link MemoryBuffer}. */ public class MemoryBufferObjectOutput extends OutputStream implements ObjectOutput { - private final Fory fory; private final boolean compressInt; private final LongEncoding longEncoding; private final StringSerializer stringSerializer; + private WriteContext writeContext; private MemoryBuffer buffer; - public MemoryBufferObjectOutput(Fory fory, MemoryBuffer buffer) { - this.fory = fory; - this.compressInt = fory.compressInt(); - this.longEncoding = fory.longEncoding(); + public MemoryBufferObjectOutput(Config config, MemoryBuffer buffer) { + this.compressInt = config.compressInt(); + this.longEncoding = config.longEncoding(); + this.stringSerializer = new StringSerializer(config); this.buffer = buffer; - this.stringSerializer = fory.getStringSerializer(); } public MemoryBuffer getBuffer() { @@ -53,9 +53,13 @@ public void setBuffer(MemoryBuffer buffer) { this.buffer = buffer; } + public void setWriteContext(WriteContext writeContext) { + this.writeContext = writeContext; + } + @Override public void writeObject(Object obj) throws IOException { - fory.writeRef(buffer, obj); + writeContext.writeRef(obj); } @Override @@ -129,13 +133,21 @@ public void writeBytes(String s) throws IOException { @Override public void writeChars(String s) throws IOException { Preconditions.checkNotNull(s); - stringSerializer.writeString(buffer, s); + if (writeContext != null) { + writeContext.getStringSerializer().writeString(buffer, s); + } else { + stringSerializer.writeString(buffer, s); + } } @Override public void writeUTF(String s) throws IOException { Preconditions.checkNotNull(s); - stringSerializer.writeString(buffer, s); + if (writeContext != null) { + writeContext.getStringSerializer().writeString(buffer, s); + } else { + stringSerializer.writeString(buffer, s); + } } @Override diff --git a/java/fory-core/src/main/java/org/apache/fory/memory/MemoryBuffer.java b/java/fory-core/src/main/java/org/apache/fory/memory/MemoryBuffer.java index fd1ef155be..87b8dc9115 100644 --- a/java/fory-core/src/main/java/org/apache/fory/memory/MemoryBuffer.java +++ b/java/fory-core/src/main/java/org/apache/fory/memory/MemoryBuffer.java @@ -793,7 +793,7 @@ public int _unsafeWriteVarInt32(int v) { */ public int writeVarUint32(int v) { // ensure at least 8 bytes are writable at once, so jvm-jit - // generated code is smaller. Otherwise, `MapRefResolver.writeRefOrNull` + // generated code is smaller. Otherwise, the reference writer fast path // may be `callee is too large`/`already compiled into a big method` ensure(writerIndex + 8); int varintBytes = _unsafePutVarUint32(writerIndex, v); diff --git a/java/fory-core/src/main/java/org/apache/fory/meta/FieldTypes.java b/java/fory-core/src/main/java/org/apache/fory/meta/FieldTypes.java index 1edb55d871..52ff01a7b6 100644 --- a/java/fory-core/src/main/java/org/apache/fory/meta/FieldTypes.java +++ b/java/fory-core/src/main/java/org/apache/fory/meta/FieldTypes.java @@ -93,20 +93,20 @@ private static FieldType buildFieldType( TypeResolver resolver, Field field, GenericType genericType) { Preconditions.checkNotNull(genericType); Class rawType = genericType.getCls(); - boolean isXlang = resolver.getFory().isCrossLanguage(); + boolean isXlang = resolver.isCrossLanguage(); // Get type ID for both xlang and native mode // This supports unsigned types and field-configurable compression in both modes int typeId; if (TypeUtils.unwrap(rawType).isPrimitive()) { if (field != null) { - typeId = Types.getDescriptorTypeId(resolver.getFory(), field); + typeId = Types.getDescriptorTypeId(resolver, field); } else { - typeId = Types.getTypeId(resolver.getFory(), rawType); + typeId = Types.getTypeId(resolver, rawType); } } else if (rawType.isArray() && rawType.getComponentType().isPrimitive() && field != null) { // For primitive arrays with type annotations, use getDescriptorTypeId to parse annotation // This allows @Uint8ArrayType etc. to override the default INT8_ARRAY type - typeId = Types.getDescriptorTypeId(resolver.getFory(), field); + typeId = Types.getDescriptorTypeId(resolver, field); } else { TypeInfo info = isXlang && rawType == Object.class ? null : resolver.getTypeInfo(rawType, false); @@ -119,7 +119,7 @@ private static FieldType buildFieldType( if (rawType.isArray()) { Class componentType = rawType.getComponentType(); if (componentType.isPrimitive()) { - int elemTypeId = Types.getTypeId(resolver.getFory(), componentType); + int elemTypeId = Types.getTypeId(resolver, componentType); typeId = Types.getPrimitiveArrayTypeId(elemTypeId); } else { typeId = Types.LIST; @@ -557,7 +557,7 @@ public TypeRef toTypeToken(TypeResolver resolver, TypeRef declared) { } LOG.warn("Class {} not registered, take it as Struct type for deserialization.", typeId); boolean isEnum = internalTypeId == Types.ENUM; - cls = UnknownClass.getUnknowClass(isEnum, 0, resolver.getFory().isShareMeta()); + cls = UnknownClass.getUnknowClass(isEnum, 0, resolver.isShareMeta()); return TypeRef.of(cls, new TypeExtMeta(typeId, nullable, trackingRef)); } if (resolver instanceof XtypeResolver) { @@ -570,7 +570,7 @@ public TypeRef toTypeToken(TypeResolver resolver, TypeRef declared) { if (cls == null) { LOG.warn("Class {} not registered, take it as Struct type for deserialization.", typeId); boolean isEnum = internalTypeId == Types.ENUM; - cls = UnknownClass.getUnknowClass(isEnum, 0, resolver.getFory().isShareMeta()); + cls = UnknownClass.getUnknowClass(isEnum, 0, resolver.isShareMeta()); } return TypeRef.of(cls, new TypeExtMeta(typeId, nullable, trackingRef)); } diff --git a/java/fory-core/src/main/java/org/apache/fory/meta/NativeTypeDefDecoder.java b/java/fory-core/src/main/java/org/apache/fory/meta/NativeTypeDefDecoder.java index aa99478651..1ca6559f04 100644 --- a/java/fory-core/src/main/java/org/apache/fory/meta/NativeTypeDefDecoder.java +++ b/java/fory-core/src/main/java/org/apache/fory/meta/NativeTypeDefDecoder.java @@ -60,7 +60,7 @@ static Tuple2 decodeTypeDefBuf( encoded.writeBytes(encodedTypeDef); if ((id & COMPRESS_META_FLAG) != 0) { encodedTypeDef = - resolver.getFory().getConfig().getMetaCompressor().decompress(encodedTypeDef, 0, size); + resolver.getConfig().getMetaCompressor().decompress(encodedTypeDef, 0, size); } return Tuple2.of(encodedTypeDef, encoded.getBytes(0, encoded.writerIndex())); } @@ -116,10 +116,7 @@ public static TypeDef decodeTypeDef(ClassResolver resolver, MemoryBuffer buffer, if (decodedSpec.isEnum) { typeId = Types.NAMED_ENUM; } else { - typeId = - resolver.getFory().isCompatible() - ? Types.NAMED_COMPATIBLE_STRUCT - : Types.NAMED_STRUCT; + typeId = resolver.isCompatible() ? Types.NAMED_COMPATIBLE_STRUCT : Types.NAMED_STRUCT; } classSpec = new ClassSpec( diff --git a/java/fory-core/src/main/java/org/apache/fory/meta/NativeTypeDefEncoder.java b/java/fory-core/src/main/java/org/apache/fory/meta/NativeTypeDefEncoder.java index 61eed78043..f86d15ce41 100644 --- a/java/fory-core/src/main/java/org/apache/fory/meta/NativeTypeDefEncoder.java +++ b/java/fory-core/src/main/java/org/apache/fory/meta/NativeTypeDefEncoder.java @@ -35,7 +35,6 @@ import java.util.List; import java.util.Map; import java.util.Set; -import org.apache.fory.Fory; import org.apache.fory.annotation.ForyField; import org.apache.fory.annotation.Internal; import org.apache.fory.collection.Tuple2; @@ -75,10 +74,9 @@ private static boolean needsUserTypeId(int typeId) { } } - static List buildFields(Fory fory, Class cls, boolean resolveParent) { + static List buildFields(TypeResolver typeResolver, Class cls, boolean resolveParent) { DescriptorGrouper descriptorGrouper = - fory.getTypeResolver() - .getFieldDescriptorGrouper(cls, resolveParent, false, IDENTITY_DESCRIPTOR); + typeResolver.getFieldDescriptorGrouper(cls, resolveParent, false, IDENTITY_DESCRIPTOR); List fields = new ArrayList<>(); descriptorGrouper .getPrimitiveDescriptors() @@ -101,7 +99,7 @@ static List buildFields(Fory fory, Class cls, boolean resolveParent) { } public static List buildFieldsInfo(ClassResolver resolver, Class cls) { - return buildFieldsInfo(resolver, buildFields(resolver.getFory(), cls, true)); + return buildFieldsInfo(resolver, buildFields(resolver, cls, true)); } public static List buildFieldsInfo(TypeResolver resolver, List fields) { @@ -217,7 +215,6 @@ public static MemoryBuffer encodeTypeDef( } byte[] compressed = classResolver - .getFory() .getConfig() .getMetaCompressor() .compress(typeDefBuf.getHeapMemory(), 0, typeDefBuf.writerIndex()); diff --git a/java/fory-core/src/main/java/org/apache/fory/meta/TypeDef.java b/java/fory-core/src/main/java/org/apache/fory/meta/TypeDef.java index 63fb419d6a..8436bc40b5 100644 --- a/java/fory-core/src/main/java/org/apache/fory/meta/TypeDef.java +++ b/java/fory-core/src/main/java/org/apache/fory/meta/TypeDef.java @@ -32,7 +32,6 @@ import java.util.Map; import java.util.Objects; import java.util.stream.Collectors; -import org.apache.fory.Fory; import org.apache.fory.builder.MetaSharedCodecBuilder; import org.apache.fory.config.CompatibleMode; import org.apache.fory.config.ForyBuilder; @@ -346,22 +345,19 @@ public void writeTypeDef(MemoryBuffer buffer) { } /** Read class definition from buffer. */ - public static TypeDef readTypeDef(Fory fory, MemoryBuffer buffer) { - if (fory.isCrossLanguage()) { - return TypeDefDecoder.decodeTypeDef( - (XtypeResolver) fory.getTypeResolver(), buffer, buffer.readInt64()); + public static TypeDef readTypeDef(TypeResolver resolver, MemoryBuffer buffer) { + if (resolver.isCrossLanguage()) { + return TypeDefDecoder.decodeTypeDef((XtypeResolver) resolver, buffer, buffer.readInt64()); } - return NativeTypeDefDecoder.decodeTypeDef( - (ClassResolver) fory.getTypeResolver(), buffer, buffer.readInt64()); + return NativeTypeDefDecoder.decodeTypeDef((ClassResolver) resolver, buffer, buffer.readInt64()); } /** Read class definition from buffer. */ - public static TypeDef readTypeDef(Fory fory, MemoryBuffer buffer, long header) { - if (fory.isCrossLanguage()) { - return TypeDefDecoder.decodeTypeDef((XtypeResolver) fory.getTypeResolver(), buffer, header); + public static TypeDef readTypeDef(TypeResolver resolver, MemoryBuffer buffer, long header) { + if (resolver.isCrossLanguage()) { + return TypeDefDecoder.decodeTypeDef((XtypeResolver) resolver, buffer, header); } - return NativeTypeDefDecoder.decodeTypeDef( - (ClassResolver) fory.getTypeResolver(), buffer, header); + return NativeTypeDefDecoder.decodeTypeDef((ClassResolver) resolver, buffer, header); } /** @@ -373,7 +369,7 @@ public static TypeDef readTypeDef(Fory fory, MemoryBuffer buffer, long header) { * @param cls class load in current process. */ public List getDescriptors(TypeResolver resolver, Class cls) { - SharedRegistry sharedRegistry = resolver.getFory().getSharedRegistry(); + SharedRegistry sharedRegistry = resolver.getSharedRegistry(); return sharedRegistry.getOrCreateTypeDefDescriptors( this, cls, () -> buildDescriptors(resolver, cls)); } @@ -405,7 +401,7 @@ private List buildDescriptors(TypeResolver resolver, Class cls) { } } List descriptors = new ArrayList<>(fieldsInfo.size()); - boolean isXlang = resolver.getFory().isCrossLanguage(); + boolean isXlang = resolver.isCrossLanguage(); for (FieldInfo fieldInfo : fieldsInfo) { Descriptor descriptor; if (fieldInfo.hasFieldId()) { @@ -425,16 +421,16 @@ private List buildDescriptors(TypeResolver resolver, Class cls) { return descriptors; } - public static TypeDef buildTypeDef(Fory fory, Class cls) { - return buildTypeDef(fory, cls, true); + public static TypeDef buildTypeDef(TypeResolver resolver, Class cls) { + return buildTypeDef(resolver, cls, true); } - public static TypeDef buildTypeDef(Fory fory, Class cls, boolean resolveParent) { - if (fory.isCrossLanguage()) { - return TypeDefEncoder.buildTypeDef(fory, cls); + public static TypeDef buildTypeDef(TypeResolver resolver, Class cls, boolean resolveParent) { + if (resolver.isCrossLanguage()) { + return TypeDefEncoder.buildTypeDef((XtypeResolver) resolver, cls); } return NativeTypeDefEncoder.buildTypeDef( - (ClassResolver) fory.getTypeResolver(), cls, buildFields(fory, cls, resolveParent), true); + (ClassResolver) resolver, cls, buildFields(resolver, cls, resolveParent), true); } /** Build class definition from fields of class. */ @@ -460,7 +456,7 @@ public TypeDef replaceRootClassTo(TypeResolver resolver, Class targetCls) { } }) .collect(Collectors.toList()); - if (resolver.getFory().isCrossLanguage()) { + if (resolver.isCrossLanguage()) { return TypeDefEncoder.buildTypeDefWithFieldInfos( (XtypeResolver) resolver, targetCls, fieldInfos); } diff --git a/java/fory-core/src/main/java/org/apache/fory/meta/TypeDefDecoder.java b/java/fory-core/src/main/java/org/apache/fory/meta/TypeDefDecoder.java index 1053bad9f3..c338e7e142 100644 --- a/java/fory-core/src/main/java/org/apache/fory/meta/TypeDefDecoder.java +++ b/java/fory-core/src/main/java/org/apache/fory/meta/TypeDefDecoder.java @@ -91,7 +91,7 @@ public static TypeDef decodeTypeDef(XtypeResolver resolver, MemoryBuffer inputBu // Compute and print diff with local TypeDef Class cls = classSpec.type; if (cls != null && cls != UnknownClass.UnknownStruct.class) { - TypeDef localDef = TypeDef.buildTypeDef(resolver.getFory(), cls); + TypeDef localDef = TypeDef.buildTypeDef(resolver, cls); String diff = typeDef.computeDiff(localDef); if (diff != null) { LOG.info("[Java TypeDef DIFF] " + classSpec.entireClassName + ":\n" + diff); diff --git a/java/fory-core/src/main/java/org/apache/fory/meta/TypeDefEncoder.java b/java/fory-core/src/main/java/org/apache/fory/meta/TypeDefEncoder.java index 86a7bbaac5..e4bf3d5881 100644 --- a/java/fory-core/src/main/java/org/apache/fory/meta/TypeDefEncoder.java +++ b/java/fory-core/src/main/java/org/apache/fory/meta/TypeDefEncoder.java @@ -32,7 +32,6 @@ import java.util.Map; import java.util.Set; import java.util.stream.Collectors; -import org.apache.fory.Fory; import org.apache.fory.annotation.ForyField; import org.apache.fory.logging.Logger; import org.apache.fory.logging.LoggerFactory; @@ -61,8 +60,7 @@ class TypeDefEncoder { descriptor -> descriptor; /** Build class definition from fields of class. */ - static TypeDef buildTypeDef(Fory fory, Class type) { - XtypeResolver resolver = (XtypeResolver) fory.getTypeResolver(); + static TypeDef buildTypeDef(XtypeResolver resolver, Class type) { DescriptorGrouper descriptorGrouper = resolver.getFieldDescriptorGrouper(type, true, false, IDENTITY_DESCRIPTOR); TypeInfo typeInfo = resolver.getTypeInfo(type); diff --git a/java/fory-core/src/main/java/org/apache/fory/reflect/ReflectionUtils.java b/java/fory-core/src/main/java/org/apache/fory/reflect/ReflectionUtils.java index f76168bbd9..ece694c186 100644 --- a/java/fory-core/src/main/java/org/apache/fory/reflect/ReflectionUtils.java +++ b/java/fory-core/src/main/java/org/apache/fory/reflect/ReflectionUtils.java @@ -242,6 +242,9 @@ public static List> getAllClasses(Class cls, boolean topToLeaf) { public static boolean hasException(Class cls, String methodName) { List methods = findMethods(cls, methodName); if (methods.isEmpty()) { + if (GraalvmSupport.IN_GRAALVM_NATIVE_IMAGE) { + return false; + } String msg = String.format("class %s doesn't have method %s", cls, methodName); throw new IllegalArgumentException(msg); } @@ -257,6 +260,9 @@ public static boolean hasException(Class cls, String methodName) { public static boolean hasCheckedException(Class cls, String methodName) { List methods = findMethods(cls, methodName); if (methods.isEmpty()) { + if (GraalvmSupport.IN_GRAALVM_NATIVE_IMAGE) { + return false; + } String msg = String.format("class %s doesn't have method %s", cls, methodName); throw new IllegalArgumentException(msg); } diff --git a/java/fory-core/src/main/java/org/apache/fory/resolver/AllowListChecker.java b/java/fory-core/src/main/java/org/apache/fory/resolver/AllowListChecker.java index 116e21da9b..4a3fa0f2a8 100644 --- a/java/fory-core/src/main/java/org/apache/fory/resolver/AllowListChecker.java +++ b/java/fory-core/src/main/java/org/apache/fory/resolver/AllowListChecker.java @@ -26,11 +26,12 @@ import java.util.concurrent.locks.ReadWriteLock; import java.util.concurrent.locks.ReentrantReadWriteLock; import javax.annotation.concurrent.ThreadSafe; -import org.apache.fory.Fory; +import org.apache.fory.config.Config; +import org.apache.fory.context.ReadContext; +import org.apache.fory.context.WriteContext; import org.apache.fory.exception.InsecureException; import org.apache.fory.logging.Logger; import org.apache.fory.logging.LoggerFactory; -import org.apache.fory.memory.MemoryBuffer; import org.apache.fory.serializer.Serializer; /** White/black list based class checker. */ @@ -209,22 +210,22 @@ private void disallow(String classNameOrPrefix) { disallowListPrefix.add(prefix); for (ClassResolver classResolver : listeners.keySet()) { try { - classResolver.getFory().getJITContext().lock(); + classResolver.getJITContext().lock(); // clear serializer may throw NullPointerException for field serialization. classResolver.setSerializers(prefix, DisallowSerializer.class); } finally { - classResolver.getFory().getJITContext().unlock(); + classResolver.getJITContext().unlock(); } } } else { disallowList.add(classNameOrPrefix); for (ClassResolver classResolver : listeners.keySet()) { try { - classResolver.getFory().getJITContext().lock(); + classResolver.getJITContext().lock(); // clear serializer may throw NullPointerException for field serialization. classResolver.setSerializer(classNameOrPrefix, DisallowSerializer.class); } finally { - classResolver.getFory().getJITContext().unlock(); + classResolver.getJITContext().unlock(); } } } @@ -246,17 +247,17 @@ public void addListener(ClassResolver classResolver) { @SuppressWarnings({"rawtypes", "unchecked"}) private static class DisallowSerializer extends Serializer { - public DisallowSerializer(Fory fory, Class type) { - super(fory, type); + public DisallowSerializer(Config config, Class type) { + super(config, type); } @Override - public void write(MemoryBuffer buffer, Object value) { + public void write(WriteContext writeContext, Object value) { throw new InsecureException(String.format("Class %s not allowed for serialization.", type)); } @Override - public Object read(MemoryBuffer buffer) { + public Object read(ReadContext readContext) { throw new InsecureException(String.format("Class %s not allowed for serialization.", type)); } } diff --git a/java/fory-core/src/main/java/org/apache/fory/resolver/ClassResolver.java b/java/fory-core/src/main/java/org/apache/fory/resolver/ClassResolver.java index 6c93a9ad73..3431e818ca 100644 --- a/java/fory-core/src/main/java/org/apache/fory/resolver/ClassResolver.java +++ b/java/fory-core/src/main/java/org/apache/fory/resolver/ClassResolver.java @@ -21,7 +21,6 @@ import static org.apache.fory.meta.Encoders.PACKAGE_DECODER; import static org.apache.fory.meta.Encoders.TYPE_NAME_DECODER; -import static org.apache.fory.serializer.CodegenSerializer.loadCodegenSerializer; import static org.apache.fory.serializer.CodegenSerializer.supportCodegenForJavaSerialization; import static org.apache.fory.type.TypeUtils.OBJECT_TYPE; import static org.apache.fory.type.Types.INVALID_USER_TYPE_ID; @@ -52,7 +51,6 @@ import java.util.List; import java.util.Locale; import java.util.Map; -import java.util.Objects; import java.util.Optional; import java.util.OptionalInt; import java.util.TimeZone; @@ -66,12 +64,10 @@ import java.util.concurrent.atomic.AtomicLong; import java.util.concurrent.atomic.AtomicReference; import java.util.function.Supplier; -import org.apache.fory.Fory; import org.apache.fory.ForyCopyable; import org.apache.fory.annotation.CodegenInvoke; import org.apache.fory.annotation.ForyField; import org.apache.fory.annotation.Internal; -import org.apache.fory.builder.CodecUtils; import org.apache.fory.builder.JITContext; import org.apache.fory.collection.BoolList; import org.apache.fory.collection.Float16List; @@ -87,7 +83,10 @@ import org.apache.fory.collection.Uint32List; import org.apache.fory.collection.Uint64List; import org.apache.fory.collection.Uint8List; +import org.apache.fory.config.Config; import org.apache.fory.config.Language; +import org.apache.fory.context.ReadContext; +import org.apache.fory.context.WriteContext; import org.apache.fory.exception.InsecureException; import org.apache.fory.logging.Logger; import org.apache.fory.logging.LoggerFactory; @@ -220,22 +219,22 @@ public class ClassResolver extends TypeResolver { public static final short REPLACE_STUB_ID = INTERNAL_TYPE_START_ID + 28; public static final int NONEXISTENT_META_SHARED_ID = REPLACE_STUB_ID + 1; - private final Fory fory; private TypeInfo typeInfoCache; // Every deserialization for unregistered class will query it, performance is important. private final ObjectMap compositeNameBytes2TypeInfo = new ObjectMap<>(16, foryMapLoadFactor); - // typeDefMap is inherited from TypeResolver - private Class currentReadClass; private final ShimDispatcher shimDispatcher; - public ClassResolver(Fory fory) { - super(fory); - this.fory = fory; + public ClassResolver( + Config config, + ClassLoader classLoader, + SharedRegistry sharedRegistry, + JITContext jitContext) { + super(config, classLoader, sharedRegistry, jitContext); typeInfoCache = NIL_TYPE_INFO; extRegistry.classIdGenerator = NONEXISTENT_META_SHARED_ID + 1; - shimDispatcher = new ShimDispatcher(fory); - _addGraalvmClassRegistry(fory.getConfig().getConfigHash(), this); + shimDispatcher = new ShimDispatcher(this); + _addGraalvmClassRegistry(config.getConfigHash(), this); } @Override @@ -311,55 +310,49 @@ public void initialize() { } private void addDefaultSerializers() { + Config config = this.config; // primitive types will be boxed. - addDefaultSerializer(void.class, NoneSerializer.class); - addDefaultSerializer(String.class, fory.getStringSerializer()); - PrimitiveSerializers.registerDefaultSerializers(fory); - UnsignedSerializers.registerDefaultSerializers(fory); - Serializers.registerDefaultSerializers(fory); - ArraySerializers.registerDefaultSerializers(fory); - PrimitiveListSerializers.registerDefaultSerializers(fory); - TimeSerializers.registerDefaultSerializers(fory); - OptionalSerializers.registerDefaultSerializers(fory); - CollectionSerializers.registerDefaultSerializers(fory); - MapSerializers.registerDefaultSerializers(fory); + addDefaultSerializer(void.class, new NoneSerializer(config, void.class)); + addDefaultSerializer(String.class, new org.apache.fory.serializer.StringSerializer(config)); + PrimitiveSerializers.registerDefaultSerializers(this); + UnsignedSerializers.registerDefaultSerializers(this); + Serializers.registerDefaultSerializers(this); + ArraySerializers.registerDefaultSerializers(this); + PrimitiveListSerializers.registerDefaultSerializers(this); + TimeSerializers.registerDefaultSerializers(this); + OptionalSerializers.registerDefaultSerializers(this); + CollectionSerializers.registerDefaultSerializers(this); + MapSerializers.registerDefaultSerializers(this); + addDefaultSerializer(Locale.class, new LocaleSerializer(config)); addDefaultSerializer( - StackTraceElement[].class, - new ArraySerializers.ObjectArraySerializer<>(fory, StackTraceElement[].class)); - addDefaultSerializer(Locale.class, new LocaleSerializer(fory)); - addDefaultSerializer( - SerializedLambda.class, new SerializedLambdaSerializer(fory, SerializedLambda.class)); + SerializedLambda.class, new SerializedLambdaSerializer(this, SerializedLambda.class)); addDefaultSerializer( LambdaSerializer.ReplaceStub.class, - new LambdaSerializer(fory, LambdaSerializer.ReplaceStub.class)); + new LambdaSerializer(this, LambdaSerializer.ReplaceStub.class)); addDefaultSerializer( JdkProxySerializer.ReplaceStub.class, - new JdkProxySerializer(fory, JdkProxySerializer.ReplaceStub.class)); + new JdkProxySerializer(this, JdkProxySerializer.ReplaceStub.class)); addDefaultSerializer( ReplaceResolveSerializer.ReplaceStub.class, - new ReplaceResolveSerializer(fory, ReplaceResolveSerializer.ReplaceStub.class)); - SynchronizedSerializers.registerSerializers(fory); - UnmodifiableSerializers.registerSerializers(fory); - ImmutableCollectionSerializers.registerSerializers(fory); - SubListSerializers.registerSerializers(fory, true); - if (fory.getConfig().registerGuavaTypes()) { - GuavaCollectionSerializers.registerDefaultSerializers(fory); - } - if (fory.getConfig().deserializeUnknownClass()) { + new ReplaceResolveSerializer(this, ReplaceResolveSerializer.ReplaceStub.class)); + SynchronizedSerializers.registerSerializers(this); + UnmodifiableSerializers.registerSerializers(this); + ImmutableCollectionSerializers.registerSerializers(this); + SubListSerializers.registerSerializers(this, true); + if (config.registerGuavaTypes()) { + GuavaCollectionSerializers.registerDefaultSerializers(this); + } + if (config.deserializeUnknownClass()) { if (metaContextShareEnabled) { registerInternal(UnknownStruct.class, NONEXISTENT_META_SHARED_ID); registerInternalSerializer( - UnknownStruct.class, new UnknownClassSerializers.UnknownStructSerializer(fory, null)); + UnknownStruct.class, new UnknownClassSerializers.UnknownStructSerializer(this, null)); } else { registerInternal(UnknownEmptyStruct.class); } } } - private void addDefaultSerializer(Class type, Class serializerClass) { - addDefaultSerializer(type, Serializers.newSerializer(fory, type, serializerClass)); - } - private void addDefaultSerializer(Class type, Serializer serializer) { registerInternalSerializer(type, serializer); registerInternal(type); @@ -486,8 +479,8 @@ public void register(Class cls, String namespace, String name) { fullname = namespace + "." + name; } checkRegistration(cls, -1, fullname, false); - MetaStringRef nsBytes = metaStringResolver.getOrCreatePackageMetaStringBytes(namespace); - MetaStringRef nameBytes = metaStringResolver.getOrCreateTypeNameMetaStringBytes(name); + MetaStringRef nsBytes = getOrCreatePackageMetaStringBytes(namespace); + MetaStringRef nameBytes = getOrCreateTypeNameMetaStringBytes(name); TypeInfo existingInfo = classInfoMap.get(cls); int typeId = buildUnregisteredTypeId(cls, existingInfo == null ? null : existingInfo.serializer); @@ -496,7 +489,7 @@ public void register(Class cls, String namespace, String name) { compositeNameBytes2TypeInfo.put( new TypeNameBytes(nsBytes.encoded.hash, nameBytes.encoded.hash), typeInfo); extRegistry.registeredClasses.put(fullname, cls); - registerGraalvmClass(cls); + GraalvmSupport.registerClass(cls); } @Override @@ -509,14 +502,22 @@ public void registerUnion(Class cls, long userId, Serializer serializer) { int typeId = Types.TYPED_UNION; TypeInfo typeInfo = classInfoMap.get(cls); if (typeInfo == null) { - typeInfo = new TypeInfo(this, cls, serializer, typeId, checkedUserId); + typeInfo = new TypeInfo(this, cls, null, typeId, checkedUserId); } else { - typeInfo = typeInfo.copy(typeId, checkedUserId); - typeInfo.setSerializer(this, serializer); - } + typeInfo = + new TypeInfo( + typeInfo.cls, + typeInfo.namespaceBytes, + typeInfo.typeNameBytes, + typeInfo.isDynamicGeneratedClass, + typeInfo.serializer, + typeId, + checkedUserId); + } + typeInfo.setSerializer(this, sharedRegistry.setSerializer(cls, serializer)); updateTypeInfo(cls, typeInfo); extRegistry.registeredClasses.put(cls.getName(), cls); - registerGraalvmClass(cls); + GraalvmSupport.registerClass(cls); } @Override @@ -534,16 +535,16 @@ public void registerUnion(Class cls, String namespace, String name, Serialize fullname = namespace + "." + name; } checkRegistration(cls, -1, fullname, false); - MetaStringRef nsBytes = metaStringResolver.getOrCreatePackageMetaStringBytes(namespace); - MetaStringRef nameBytes = metaStringResolver.getOrCreateTypeNameMetaStringBytes(name); + MetaStringRef nsBytes = getOrCreatePackageMetaStringBytes(namespace); + MetaStringRef nameBytes = getOrCreateTypeNameMetaStringBytes(name); int typeId = Types.NAMED_UNION; - TypeInfo typeInfo = new TypeInfo(cls, nsBytes, nameBytes, false, serializer, typeId, -1); - typeInfo.setSerializer(this, serializer); + TypeInfo typeInfo = new TypeInfo(cls, nsBytes, nameBytes, false, null, typeId, -1); + typeInfo.setSerializer(this, sharedRegistry.setSerializer(cls, serializer)); classInfoMap.put(cls, typeInfo); compositeNameBytes2TypeInfo.put( new TypeNameBytes(nsBytes.encoded.hash, nameBytes.encoded.hash), typeInfo); extRegistry.registeredClasses.put(fullname, cls); - registerGraalvmClass(cls); + GraalvmSupport.registerClass(cls); } /** @@ -610,13 +611,21 @@ private void registerInternalImpl(Class cls, int typeId) { extRegistry.registeredClassIdMap.put(cls, typeId); TypeInfo typeInfo = classInfoMap.get(cls); if (typeInfo != null) { - typeInfo = typeInfo.copy(typeId, INVALID_USER_TYPE_ID); + typeInfo = + new TypeInfo( + typeInfo.cls, + typeInfo.namespaceBytes, + typeInfo.typeNameBytes, + typeInfo.isDynamicGeneratedClass, + typeInfo.serializer, + typeId, + INVALID_USER_TYPE_ID); } else { typeInfo = new TypeInfo(this, cls, null, typeId, INVALID_USER_TYPE_ID); } updateTypeInfo(cls, typeInfo); extRegistry.registeredClasses.put(cls.getName(), cls); - registerGraalvmClass(cls); + GraalvmSupport.registerClass(cls); } private void registerUserImpl(Class cls, int userId) { @@ -627,13 +636,21 @@ private void registerUserImpl(Class cls, int userId) { int typeId = buildUserTypeId(cls, null); TypeInfo typeInfo = classInfoMap.get(cls); if (typeInfo != null) { - typeInfo = typeInfo.copy(typeId, userId); + typeInfo = + new TypeInfo( + typeInfo.cls, + typeInfo.namespaceBytes, + typeInfo.typeNameBytes, + typeInfo.isDynamicGeneratedClass, + typeInfo.serializer, + typeId, + userId); } else { typeInfo = new TypeInfo(this, cls, null, typeId, userId); } updateTypeInfo(cls, typeInfo); extRegistry.registeredClasses.put(cls.getName(), cls); - registerGraalvmClass(cls); + GraalvmSupport.registerClass(cls); } private int buildUserTypeId(Class cls, Serializer serializer) { @@ -875,7 +892,7 @@ public boolean isMonomorphic(Class clz) { if (TypeUtils.isPrimitiveListClass(clz)) { return true; } - if (fory.getConfig().isMetaShareEnabled()) { + if (config.isMetaShareEnabled()) { // can't create final map/collection type using TypeUtils.mapOf(TypeToken, // TypeToken) if (!ReflectionUtils.isMonomorphic(clz)) { @@ -976,7 +993,7 @@ public static boolean requireJavaSerialization(Class clz) { public void registerSerializer(Class type, Class serializerClass) { checkRegisterAllowed(); checkSerializerRegistration(type, serializerClass); - registerSerializer(type, Serializers.newSerializer(fory, type, serializerClass)); + registerSerializer(type, Serializers.newSerializer(this, type, serializerClass)); } @Override @@ -1013,17 +1030,31 @@ public void registerInternalSerializer(Class type, Serializer serializer) if (classId == null) { registerInternal(type); } - registerSerializerImpl(type, serializer); - } - - private void registerSerializerImpl(Class type, Serializer serializer) { + TypeInfo currentTypeInfo = classInfoMap.get(type); + if (currentTypeInfo != null + && currentTypeInfo.serializer != null + && currentTypeInfo.serializer.getClass() == serializer.getClass()) { + recordRegisteredTypeInfo(type, currentTypeInfo); + return; + } checkRegisterAllowed(); if (!serializer.getClass().getPackage().getName().startsWith("org.apache.fory")) { SerializationUtils.validate(type, serializer.getClass()); } - addSerializer(type, serializer); + addSerializer(type, serializer, false); + TypeInfo typeInfo = classInfoMap.get(type); + updateTypeInfo(type, typeInfo); + recordRegisteredTypeInfo(type, typeInfo); + } + + private void registerSerializerImpl(Class type, Serializer serializer) { + addSerializer(type, serializer, true); TypeInfo typeInfo = classInfoMap.get(type); - classInfoMap.put(type, typeInfo); + recordRegisteredTypeInfo(type, typeInfo); + } + + private void recordRegisteredTypeInfo(Class type, TypeInfo typeInfo) { + extRegistry.registeredTypeInfos.add(typeInfo); // in order to support customized serializer for abstract or interface. if (!type.isPrimitive() && (ReflectionUtils.isAbstract(type) || type.isInterface())) { extRegistry.abstractTypeInfo.put(type, typeInfo); @@ -1032,25 +1063,21 @@ private void registerSerializerImpl(Class type, Serializer serializer) { } private void checkSerializerRegistration(Class type, Class serializerClass) { - if (isCollection(type)) { - if (!CollectionLikeSerializer.class.isAssignableFrom(serializerClass)) { - throw new IllegalArgumentException( - String.format( - "Serializer %s is not supported for collection type %s. Use %s instead.", - serializerClass.getName(), - type.getName(), - CollectionSerializers.JDKCompatibleCollectionSerializer.class.getName())); - } + if (isCollection(type) && !CollectionLikeSerializer.class.isAssignableFrom(serializerClass)) { + throw new IllegalArgumentException( + String.format( + "Serializer %s is not supported for collection type %s. Use %s instead.", + serializerClass.getName(), + type.getName(), + CollectionSerializers.JDKCompatibleCollectionSerializer.class.getName())); } - if (isMap(type)) { - if (!MapLikeSerializer.class.isAssignableFrom(serializerClass)) { - throw new IllegalArgumentException( - String.format( - "Serializer %s is not supported for map type %s. Use %s instead.", - serializerClass.getName(), - type.getName(), - MapSerializers.JDKCompatibleMapSerializer.class.getName())); - } + if (isMap(type) && !MapLikeSerializer.class.isAssignableFrom(serializerClass)) { + throw new IllegalArgumentException( + String.format( + "Serializer %s is not supported for map type %s. Use %s instead.", + serializerClass.getName(), + type.getName(), + MapSerializers.JDKCompatibleMapSerializer.class.getName())); } } @@ -1070,7 +1097,7 @@ public SerializerFactory getSerializerFactory() { */ @Override public void setSerializer(Class cls, Serializer serializer) { - addSerializer(cls, serializer); + addSerializer(cls, serializer, true); } /** Set serializer for class whose name is {@code className}. */ @@ -1083,7 +1110,7 @@ public void setSerializer(String className, Class serializ Class cls = entry.getKey(); if (cls.getName().equals(className)) { LOG.info("Clear serializer for class {}.", className); - entry.getValue().setSerializer(this, Serializers.newSerializer(fory, cls, serializer)); + setSerializer((Class) cls, Serializers.newSerializer(this, cls, serializer)); typeInfoCache = NIL_TYPE_INFO; return; } @@ -1100,7 +1127,7 @@ public void setSerializers(String classNamePrefix, Class s } if (className.startsWith(classNamePrefix)) { LOG.info("Clear serializer for class {}.", className); - entry.getValue().setSerializer(this, Serializers.newSerializer(fory, cls, serializer)); + setSerializer((Class) cls, Serializers.newSerializer(this, cls, serializer)); typeInfoCache = NIL_TYPE_INFO; } } @@ -1122,15 +1149,18 @@ public void setSerializerIfAbsent(Class cls, Serializer serializer) { /** Clear serializer associated with cls if not null. */ public void clearSerializer(Class cls) { - TypeInfo typeInfo = classInfoMap.get(cls); - if (typeInfo != null) { - typeInfo.setSerializer(this, null); - } + resetSerializer((Class) cls, null); } /** Add serializer for specified class. */ public void addSerializer(Class type, Serializer serializer) { + addSerializer(type, serializer, false); + } + + public void addSerializer( + Class type, Serializer serializer, boolean replaceSharedThreadSafe) { Preconditions.checkNotNull(serializer); + TypeInfo currentTypeInfo = getCachedTypeInfo(type); TypeInfo typeInfo; Integer classId = getRegisteredClassIdLocalOrFrozen(type); boolean registered = classId != null; @@ -1138,41 +1168,54 @@ public void addSerializer(Class type, Serializer serializer) { int id = classId; boolean internal = isInternalRegisteredClassId(type, id); int typeId = internal ? id : buildUserTypeId(type, serializer); - typeInfo = classInfoMap.get(type); - if (typeInfo == null) { + int userTypeId = internal ? INVALID_USER_TYPE_ID : id; + if (currentTypeInfo == null) { typeInfo = new TypeInfo(this, type, null, typeId, internal ? INVALID_USER_TYPE_ID : id); + } else if (currentTypeInfo.getTypeId() != typeId + || currentTypeInfo.getUserTypeId() != userTypeId) { + typeInfo = + new TypeInfo( + currentTypeInfo.cls, + currentTypeInfo.namespaceBytes, + currentTypeInfo.typeNameBytes, + currentTypeInfo.isDynamicGeneratedClass, + currentTypeInfo.serializer, + typeId, + userTypeId); } else { - typeInfo = typeInfo.copy(typeId); + typeInfo = currentTypeInfo; } - updateTypeInfo(type, typeInfo); } else { int typeId = buildUnregisteredTypeId(type, serializer); - typeInfo = classInfoMap.get(type); - if (typeInfo == null) { + if (currentTypeInfo == null) { typeInfo = new TypeInfo(this, type, null, typeId, INVALID_USER_TYPE_ID); + } else if (currentTypeInfo.getTypeId() != typeId + || currentTypeInfo.getUserTypeId() != INVALID_USER_TYPE_ID) { + typeInfo = + new TypeInfo( + currentTypeInfo.cls, + currentTypeInfo.namespaceBytes, + currentTypeInfo.typeNameBytes, + currentTypeInfo.isDynamicGeneratedClass, + currentTypeInfo.serializer, + typeId, + INVALID_USER_TYPE_ID); } else { - typeInfo = typeInfo.copy(typeId); - } - updateTypeInfo(type, typeInfo); - // Add to compositeNameBytes2TypeInfo for unregistered classes so that - // readTypeInfo can find the TypeInfo by name bytes during deserialization. - // This is important for dynamically created classes that can't be loaded by name. - if (typeInfo.namespaceBytes != null && typeInfo.typeNameBytes != null) { - TypeNameBytes typeNameBytes = - new TypeNameBytes( - typeInfo.namespaceBytes.encoded.hash, typeInfo.typeNameBytes.encoded.hash); - compositeNameBytes2TypeInfo.put(typeNameBytes, typeInfo); + typeInfo = currentTypeInfo; } } + typeInfo.setSerializer( + this, + replaceSharedThreadSafe + ? sharedRegistry.setSerializer(type, serializer) + : sharedRegistry.shareSerializer(type, serializer)); + updateTypeInfo(type, typeInfo); if (typeInfo.namespaceBytes != null && typeInfo.typeNameBytes != null) { TypeNameBytes typeNameBytes = new TypeNameBytes( typeInfo.namespaceBytes.encoded.hash, typeInfo.typeNameBytes.encoded.hash); compositeNameBytes2TypeInfo.put(typeNameBytes, typeInfo); } - - // 2. Set `Serializer` for `TypeInfo`. - typeInfo.setSerializer(this, serializer); } @SuppressWarnings("unchecked") @@ -1181,7 +1224,7 @@ public Serializer getSerializer(Class cls, boolean createIfNotExist) { if (createIfNotExist) { return getSerializer(cls); } - TypeInfo typeInfo = classInfoMap.get(cls); + TypeInfo typeInfo = getCachedTypeInfo(cls); return typeInfo == null ? null : (Serializer) typeInfo.serializer; } @@ -1208,7 +1251,9 @@ public Serializer getRawSerializer(Class cls) { @Override public Class getSerializerClass(Class cls) { boolean codegen = - fory.getConfig().isCodeGenEnabled() && supportCodegenForJavaSerialization(cls); + config.isCodeGenEnabled() + && !GraalvmSupport.isGraalRuntime() + && supportCodegenForJavaSerialization(cls); return getSerializerClass(cls, codegen); } @@ -1217,12 +1262,6 @@ public Class getSerializerClass(Class cls, boolean code throw new UnsupportedOperationException( String.format("Class %s doesn't support serialization.", cls)); } - if (cls == StackTraceElement.class) { - return ExceptionSerializers.StackTraceElementSerializer.class; - } - if (Throwable.class.isAssignableFrom(cls)) { - return ExceptionSerializers.ExceptionSerializer.class; - } Class serializerClass = getSerializerClassFromGraalvmRegistry(cls); if (serializerClass != null) { return serializerClass; @@ -1232,16 +1271,16 @@ public Class getSerializerClass(Class cls, boolean code if (typeInfo != null && typeInfo.serializer != null) { // Note: need to check `classInfo.serializer != null`, because sometimes `cls` is already // serialized, which will create a class info with serializer null, see `#writeClassInternal` - return getGraalvmSerializerClass(typeInfo.serializer); + return typeInfo.serializer.getClass(); } else { if (getSerializerFactory() != null) { - Serializer serializer = getSerializerFactory().createSerializer(fory, cls); + Serializer serializer = getSerializerFactory().createSerializer(this, cls); if (serializer != null) { return serializer.getClass(); } } if (UnknownClass.isUnknowClass(cls)) { - return UnknownClassSerializers.getSerializer(fory, "Unknown", cls).getClass(); + return UnknownClassSerializers.getSerializer(this, "Unknown", cls).getClass(); } if (cls.isArray()) { return ArraySerializers.ObjectArraySerializer.class; @@ -1252,6 +1291,10 @@ public Class getSerializerClass(Class cls, boolean code return EnumSerializer.class; } else if (EnumSet.class.isAssignableFrom(cls)) { return CollectionSerializers.EnumSetSerializer.class; + } else if (cls == StackTraceElement.class) { + return ExceptionSerializers.StackTraceElementSerializer.class; + } else if (Throwable.class.isAssignableFrom(cls)) { + return ExceptionSerializers.ExceptionSerializer.class; } else if (Charset.class.isAssignableFrom(cls)) { return Serializers.CharsetSerializer.class; } else if (ReflectionUtils.isJdkProxy(cls)) { @@ -1278,14 +1321,13 @@ public Class getSerializerClass(Class cls, boolean code if (serializerClass != null) { return serializerClass; } - if (fory.getConfig().checkJdkClassSerializable()) { + if (config.checkJdkClassSerializable()) { if (cls.getName().startsWith("java") && !(Serializable.class.isAssignableFrom(cls))) { throw new UnsupportedOperationException( String.format("Class %s doesn't support serialization.", cls)); } } - if (fory.getConfig().isScalaOptimizationEnabled() - && ReflectionUtils.isScalaSingletonObject(cls)) { + if (config.isScalaOptimizationEnabled() && ReflectionUtils.isScalaSingletonObject(cls)) { if (isCollection(cls)) { return SingletonCollectionSerializer.class; } else if (isMap(cls)) { @@ -1304,7 +1346,7 @@ public Class getSerializerClass(Class cls, boolean code if (requireJavaSerialization(cls) || useReplaceResolveSerializer(cls)) { return CollectionSerializers.JDKCompatibleCollectionSerializer.class; } - if (!fory.isCrossLanguage()) { + if (!isCrossLanguage()) { return CollectionSerializers.DefaultJavaCollectionSerializer.class; } else { return CollectionSerializer.class; @@ -1318,13 +1360,13 @@ public Class getSerializerClass(Class cls, boolean code if (requireJavaSerialization(cls) || useReplaceResolveSerializer(cls)) { return MapSerializers.JDKCompatibleMapSerializer.class; } - if (!fory.isCrossLanguage()) { + if (!isCrossLanguage()) { return MapSerializers.DefaultJavaMapSerializer.class; } else { return MapSerializer.class; } } - if (fory.isCrossLanguage()) { + if (isCrossLanguage()) { LOG.warn("Class {} isn't supported for cross-language serialization.", cls); } if (useReplaceResolveSerializer(cls)) { @@ -1344,7 +1386,7 @@ public Class getSerializerClass(Class cls, boolean code new JITContext.SerializerJITCallback>() { @Override public void onSuccess(Class result) { - setSerializer(clz, Serializers.newSerializer(fory, clz, result)); + setSerializer(clz, Serializers.newSerializer(ClassResolver.this, clz, result)); if (typeInfoCache.cls == clz) { typeInfoCache = NIL_TYPE_INFO; // clear class info cache } @@ -1362,7 +1404,9 @@ public Object id() { public Class getObjectSerializerClass( Class cls, JITContext.SerializerJITCallback> callback) { boolean codegen = - fory.getConfig().isCodeGenEnabled() && supportCodegenForJavaSerialization(cls); + config.isCodeGenEnabled() + && !GraalvmSupport.isGraalRuntime() + && supportCodegenForJavaSerialization(cls); return getObjectSerializerClass(cls, false, codegen, callback); } @@ -1371,13 +1415,6 @@ public Class getObjectSerializerClass( boolean shareMeta, boolean codegen, JITContext.SerializerJITCallback> callback) { - if (GraalvmSupport.isGraalRuntime()) { - Class serializerClass = - getObjectSerializerClassFromGraalvmRegistry(cls); - if (serializerClass != null) { - return serializerClass; - } - } if (codegen) { if (extRegistry.getClassCtx.contains(cls)) { // avoid potential recursive call for seq codec generation. @@ -1385,9 +1422,34 @@ public Class getObjectSerializerClass( } else { try { extRegistry.getClassCtx.add(cls); - return fory.getJITContext() - .registerSerializerJITCallback( - () -> ObjectSerializer.class, () -> loadCodegenSerializer(fory, cls), callback); + Class sc; + switch (getCompatibleMode()) { + case SCHEMA_CONSISTENT: + sc = + getJITContext() + .registerSerializerJITCallback( + () -> ObjectSerializer.class, + () -> + org.apache.fory.serializer.CodegenSerializer.loadCodegenSerializer( + ClassResolver.this, cls), + callback); + return sc; + case COMPATIBLE: + // Always use ObjectSerializer for compatible mode. + // Class definition will be sent to peer to create serializer for deserialization. + sc = + getJITContext() + .registerSerializerJITCallback( + () -> ObjectSerializer.class, + () -> + org.apache.fory.serializer.CodegenSerializer.loadCodegenSerializer( + ClassResolver.this, cls), + callback); + return sc; + default: + throw new UnsupportedOperationException( + String.format("Unsupported mode %s", getCompatibleMode())); + } } finally { extRegistry.getClassCtx.remove(cls); } @@ -1410,43 +1472,55 @@ public Class getJavaSerializer(Class clz) { if (useReplaceResolveSerializer(clz)) { return ReplaceResolveSerializer.class; } - return fory.getDefaultJDKStreamSerializerType(); + return getDefaultJDKStreamSerializerType(); + } + } + + @Override + protected void updateTypeInfo(Class cls, TypeInfo typeInfo) { + super.updateTypeInfo(cls, typeInfo); + if (typeInfo.namespaceBytes != null && typeInfo.typeNameBytes != null) { + compositeNameBytes2TypeInfo.put( + new TypeNameBytes( + typeInfo.namespaceBytes.encoded.hash, typeInfo.typeNameBytes.encoded.hash), + typeInfo); } } + private TypeInfo getCachedTypeInfo(Class cls) { + return classInfoMap.get(cls); + } + + private TypeInfo ensureTypeInfoWithSerializer(Class cls, TypeInfo typeInfo) { + if (typeInfo != null && typeInfo.serializer != null) { + return typeInfo; + } + addSerializer(cls, createSerializer(cls)); + return classInfoMap.get(cls); + } + // Invoked by fory JIT. @Override public TypeInfo getTypeInfo(Class cls) { - TypeInfo typeInfo = classInfoMap.get(cls); - if (typeInfo == null || typeInfo.serializer == null) { - addSerializer(cls, createSerializer(cls)); - typeInfo = classInfoMap.get(cls); - } - return typeInfo; + return ensureTypeInfoWithSerializer(cls, getCachedTypeInfo(cls)); } public TypeInfo getTypeInfo(short classId) { TypeInfo typeInfo = typeIdToTypeInfo[classId]; assert typeInfo != null : classId; - if (typeInfo.serializer == null) { - addSerializer(typeInfo.cls, createSerializer(typeInfo.cls)); - typeInfo = classInfoMap.get(typeInfo.cls); - } - return typeInfo; + return ensureTypeInfoWithSerializer(typeInfo.cls, typeInfo); } /** Get classinfo by cache, update cache if miss. */ @Override public TypeInfo getTypeInfo(Class cls, TypeInfoHolder classInfoHolder) { TypeInfo typeInfo = classInfoHolder.typeInfo; - if (typeInfo.getCls() != cls) { - typeInfo = classInfoMap.get(cls); - if (typeInfo == null || typeInfo.serializer == null) { - addSerializer(cls, createSerializer(cls)); - typeInfo = Objects.requireNonNull(classInfoMap.get(cls)); - } - classInfoHolder.typeInfo = typeInfo; + if (typeInfo.getCls() == cls) { + typeInfo = ensureTypeInfoWithSerializer(cls, typeInfo); + } else { + typeInfo = ensureTypeInfoWithSerializer(cls, getCachedTypeInfo(cls)); } + classInfoHolder.typeInfo = typeInfo; assert typeInfo.serializer != null; return typeInfo; } @@ -1466,21 +1540,19 @@ public TypeInfo getTypeInfo(Class cls, boolean createTypeInfoIfNotFound) { if (extRegistry.getClassCtx.contains(cls)) { return null; } else { - return classInfoMap.get(cls); + return getCachedTypeInfo(cls); } } @Internal public TypeInfo getOrUpdateTypeInfo(Class cls) { TypeInfo typeInfo = typeInfoCache; - if (typeInfo.cls != cls) { - typeInfo = classInfoMap.get(cls); - if (typeInfo == null || typeInfo.serializer == null) { - addSerializer(cls, createSerializer(cls)); - typeInfo = classInfoMap.get(cls); - } - typeInfoCache = typeInfo; + if (typeInfo.cls == cls) { + typeInfo = ensureTypeInfoWithSerializer(cls, typeInfo); + } else { + typeInfo = ensureTypeInfoWithSerializer(cls, getCachedTypeInfo(cls)); } + typeInfoCache = typeInfo; return typeInfo; } @@ -1489,14 +1561,8 @@ private TypeInfo getOrUpdateTypeInfo(short classId) { TypeInfo internalInfo = classId < typeIdToTypeInfo.length ? typeIdToTypeInfo[classId] : null; Preconditions.checkArgument( internalInfo != null, "Internal class id %s is not registered", classId); - if (typeInfo != internalInfo) { - typeInfo = internalInfo; - if (typeInfo.serializer == null) { - addSerializer(typeInfo.cls, createSerializer(typeInfo.cls)); - typeInfo = classInfoMap.get(typeInfo.cls); - } - typeInfoCache = typeInfo; - } + typeInfo = ensureTypeInfoWithSerializer(internalInfo.cls, internalInfo); + typeInfoCache = typeInfo; return typeInfo; } @@ -1518,20 +1584,18 @@ private Serializer createSerializer(Class cls) { DisallowedList.checkNotInDisallowedList(cls.getName()); if (!isSecure(cls)) { if (getSerializerClass(cls, false) == ObjectSerializer.class) { - Serializer serializer = new CopyOnlyObjectSerializer<>(fory, cls); + Serializer serializer = new CopyOnlyObjectSerializer<>(this, cls); if (ForyCopyable.class.isAssignableFrom(cls)) { - serializer = new ForyCopyableSerializer<>(fory, cls, serializer); + serializer = new ForyCopyableSerializer<>(config, cls, serializer); } return serializer; } throw new InsecureException(generateSecurityMsg(cls)); } else { - if (!fory.getConfig().suppressClassRegistrationWarnings() + if (!config.suppressClassRegistrationWarnings() && !Functions.isLambda(cls) && !ReflectionUtils.isJdkProxy(cls) && !isRegisteredByIdLocalOrFrozen(cls) - && !isRegisteredByNameLocalOrFrozen(cls) - && !hasGraalvmSerializerClass(cls) && !shimDispatcher.contains(cls) && !extRegistry.isTypeCheckerSet()) { LOG.warn(generateSecurityMsg(cls)); @@ -1548,7 +1612,7 @@ private Serializer createSerializer(Class cls) { } if (extRegistry.serializerFactory != null) { - Serializer serializer = extRegistry.serializerFactory.createSerializer(fory, cls); + Serializer serializer = extRegistry.serializerFactory.createSerializer(this, cls); if (serializer != null) { return serializer; } @@ -1577,96 +1641,39 @@ private Serializer createSerializer(Class cls) { } Class serializerClass = getSerializerClass(cls); - Serializer serializer; - serializer = Serializers.newSerializer(fory, cls, serializerClass); + Serializer serializer = Serializers.newSerializer(this, cls, serializerClass); if (ForyCopyable.class.isAssignableFrom(cls)) { - serializer = new ForyCopyableSerializer<>(fory, cls, serializer); + serializer = new ForyCopyableSerializer<>(config, cls, serializer); } return serializer; } - private Class getSerializerClassForGraalvmBuild(Class cls) { - Class serializerClass = getSerializerClass(cls, false); - boolean canUseCodegen = - serializerClass == ObjectSerializer.class - && fory.getConfig().isCodeGenEnabled() - && supportCodegenForJavaSerialization(cls); - if (canUseCodegen) { - serializerClass = loadCodegenSerializer(fory, cls); - } - return serializerClass; - } - - private Class getObjectSerializerClassForGraalvmBuild(Class cls) { - if (fory.getConfig().isCodeGenEnabled() && supportCodegenForJavaSerialization(cls)) { - return loadCodegenSerializer(fory, cls); - } - return ObjectSerializer.class; - } - - private boolean needsGraalvmObjectSerializerClass( - Class cls, Class serializerClass) { - if (serializerClass == ReplaceResolveSerializer.class) { - return !Externalizable.class.isAssignableFrom(cls) - && JavaSerializer.getReadObjectMethod(cls, true) == null - && JavaSerializer.getWriteObjectMethod(cls, true) == null; - } - return serializerClass == CollectionSerializers.DefaultJavaCollectionSerializer.class - || serializerClass == MapSerializers.DefaultJavaMapSerializer.class; - } - - private Class getMetaSharedDeserializerClassForGraalvmBuild( - Class cls, TypeDef typeDef) { - Class serializerClass = - getMetaSharedDeserializerClassFromGraalvmRegistry(cls, typeDef); - if (serializerClass != null) { - return serializerClass; - } - return CodecUtils.loadOrGenMetaSharedCodecClass(fory, cls, typeDef); - } - - private void registerGraalvmSerializerClass(Class cls) { - TypeInfo typeInfo = classInfoMap.get(cls); - Preconditions.checkNotNull(typeInfo); - Class serializerClass = - typeInfo.serializer != null - ? getGraalvmSerializerClass(typeInfo.serializer) - : getSerializerClassForGraalvmBuild(cls); - getGraalvmClassRegistry().putSerializerClass(cls, serializerClass); - if (needsGraalvmObjectSerializerClass(cls, serializerClass)) { - getGraalvmClassRegistry() - .putObjectSerializerClass(cls, getObjectSerializerClassForGraalvmBuild(cls)); - } - if (metaContextShareEnabled && needToWriteTypeDef(serializerClass)) { - TypeDef typeDef = typeInfo.typeDef; - if (typeDef == null) { - typeDef = buildTypeDef(typeInfo, serializerClass); - } - getGraalvmClassRegistry() - .putDeserializerClass( - typeDef.getId(), getMetaSharedDeserializerClassForGraalvmBuild(cls, typeDef)); - extRegistry.typeInfoByTypeDefId.remove(typeDef.getId()); - } - typeInfoCache = NIL_TYPE_INFO; - if (RecordUtils.isRecord(cls)) { - RecordUtils.getRecordConstructor(cls); - RecordUtils.getRecordComponents(cls); - } - ObjectCreators.getObjectCreator(cls); - } - private void createSerializer0(Class cls) { - if (GraalvmSupport.isGraalBuildtime()) { - registerGraalvmSerializerClass(cls); - return; - } TypeInfo typeInfo = getTypeInfo(cls); + TypeInfo deserializationTypeInfo; if (metaContextShareEnabled && needToWriteTypeDef(typeInfo.serializer)) { TypeDef typeDef = typeInfo.typeDef; if (typeDef == null) { typeDef = buildTypeDef(typeInfo); } - buildMetaSharedTypeInfo(typeDef); + deserializationTypeInfo = buildMetaSharedTypeInfo(typeDef); + if (deserializationTypeInfo != null && GraalvmSupport.isGraalBuildtime()) { + getGraalvmClassRegistry() + .putDeserializerClass( + typeDef.getId(), getGraalvmSerializerClass(deserializationTypeInfo.serializer)); + typeInfoCache = NIL_TYPE_INFO; + extRegistry.typeInfoByTypeDefId.remove(typeDef.getId()); + } + } + if (GraalvmSupport.isGraalBuildtime()) { + // Instance for generated class should be hold at graalvm runtime only. + getGraalvmClassRegistry().putSerializerClass(cls, getGraalvmSerializerClass(typeInfo.serializer)); + typeInfoCache = NIL_TYPE_INFO; + if (RecordUtils.isRecord(cls)) { + RecordUtils.getRecordConstructor(cls); + RecordUtils.getRecordComponents(cls); + } + ObjectCreators.getObjectCreator(cls); } } @@ -1681,9 +1688,7 @@ private String generateSecurityMsg(Class cls) { } private boolean isSecure(Class cls) { - if (isRegisteredByNameLocalOrFrozen(cls) - || hasGraalvmSerializerClass(cls) - || shimDispatcher.contains(cls)) { + if (isRegisteredByNameLocalOrFrozen(cls) || shimDispatcher.contains(cls)) { return true; } if (cls.isArray()) { @@ -1697,7 +1702,7 @@ private boolean isSecure(Class cls) { return isSecure(enclosingClass); } } - if (fory.getConfig().requireClassRegistration()) { + if (config.requireClassRegistration()) { return Functions.isLambda(cls) || ReflectionUtils.isJdkProxy(cls) || isRegisteredByIdLocalOrFrozen(cls) @@ -1705,6 +1710,12 @@ private boolean isSecure(Class cls) { } else { return extRegistry.typeChecker.checkType(this, cls.getName()); } + // Don't take java Exception as secure in case future JDK introduce insecure JDK exception. + // if (Exception.class.isAssignableFrom(cls) + // && cls.getName().startsWith("java.") + // && !cls.getName().startsWith("java.sql")) { + // return true; + // } } /** @@ -1713,14 +1724,15 @@ private boolean isSecure(Class cls) { * serializing object, if this writes are aligned, then later serialization will be more * efficient. */ - public void writeClassAndUpdateCache(MemoryBuffer buffer, Class cls) { + public void writeClassAndUpdateCache( + WriteContext writeContext, MemoryBuffer buffer, Class cls) { // fast path for common type if (cls == Integer.class) { buffer.writeVarUint32Small7(Types.INT32); } else if (cls == Long.class) { buffer.writeVarUint32Small7(Types.INT64); } else { - writeTypeInfo(buffer, getOrUpdateTypeInfo(cls)); + writeTypeInfo(writeContext, getOrUpdateTypeInfo(cls)); } } @@ -1737,17 +1749,14 @@ public void writeClassAndUpdateCache(MemoryBuffer buffer, Class cls) { @Override protected TypeDef buildTypeDef(TypeInfo typeInfo) { - return buildTypeDef(typeInfo, typeInfo.serializer.getClass()); - } - - private TypeDef buildTypeDef(TypeInfo typeInfo, Class serializerClass) { TypeDef typeDef; + Serializer serializer = typeInfo.serializer; Preconditions.checkArgument( - serializerClass != UnknownClassSerializers.UnknownStructSerializer.class); - if (needToWriteTypeDef(serializerClass)) { + serializer.getClass() != UnknownClassSerializers.UnknownStructSerializer.class); + if (needToWriteTypeDef(serializer)) { typeDef = cacheTypeDef( - typeDefMap.computeIfAbsent(typeInfo.cls, cls -> TypeDef.buildTypeDef(fory, cls))); + typeDefMap.computeIfAbsent(typeInfo.cls, cls -> TypeDef.buildTypeDef(this, cls))); } else { // Some type will use other serializers such MapSerializer and so on. typeDef = @@ -1762,17 +1771,18 @@ private TypeDef buildTypeDef(TypeInfo typeInfo, Class seri /** * Write classname for java serialization. Note that the object of provided class can be * non-serializable, and class with writeReplace/readResolve defined won't be skipped. For - * serializable object, {@link #writeTypeInfo(MemoryBuffer, TypeInfo)} should be invoked. + * serializable object, {@link #writeTypeInfo(WriteContext, TypeInfo)} should be invoked. */ - public void writeClassInternal(MemoryBuffer buffer, Class cls) { + public void writeClassInternal(WriteContext writeContext, Class cls) { TypeInfo typeInfo = classInfoMap.get(cls); if (typeInfo == null) { typeInfo = buildClassInfo(cls); } - writeClassInternal(buffer, typeInfo); + writeClassInternal(writeContext, typeInfo); } - public void writeClassInternal(MemoryBuffer buffer, TypeInfo typeInfo) { + public void writeClassInternal(WriteContext writeContext, TypeInfo typeInfo) { + MemoryBuffer buffer = writeContext.getBuffer(); int typeId = typeInfo.typeId; boolean writeById = typeId != REPLACE_STUB_ID && !Types.isNamedType(typeId); if (writeById) { @@ -1791,8 +1801,10 @@ public void writeClassInternal(MemoryBuffer buffer, TypeInfo typeInfo) { } else { // let the lowermost bit of next byte be set, so the deserialization can know // whether need to read class by name in advance - metaStringResolver.writeMetaStringBytesWithFlag(buffer, typeInfo.namespaceBytes); - metaStringResolver.writeMetaStringBytes(buffer, typeInfo.typeNameBytes); + writeContext + .getMetaStringWriter() + .writeMetaStringBytesWithFlag(buffer, typeInfo.namespaceBytes); + writeContext.getMetaStringWriter().writeMetaStringBytes(buffer, typeInfo.typeNameBytes); } } @@ -1815,16 +1827,19 @@ private TypeInfo buildClassInfo(Class cls) { /** * Read serialized java classname. Note that the object of the class can be non-serializable. For - * serializable object, {@link #readTypeInfo(MemoryBuffer)} or {@link #readTypeInfo(MemoryBuffer, + * serializable object, {@link #readTypeInfo(ReadContext)} or {@link #readTypeInfo(ReadContext, * TypeInfoHolder)} should be invoked. */ - public Class readClassInternal(MemoryBuffer buffer) { + public Class readClassInternal(ReadContext readContext) { + MemoryBuffer buffer = readContext.getBuffer(); int header = buffer.readVarUint32Small14(); if ((header & 0b1) != 0) { // let the lowermost bit of next byte be set, so the deserialization can know // whether need to read class by name in advance - MetaStringRef packageBytes = metaStringResolver.readMetaStringBytesWithFlag(buffer, header); - MetaStringRef simpleClassNameBytes = metaStringResolver.readMetaStringBytes(buffer); + MetaStringRef packageBytes = + readContext.getMetaStringReader().readMetaStringBytesWithFlag(buffer, header); + MetaStringRef simpleClassNameBytes = + readContext.getMetaStringReader().readMetaStringBytes(buffer); return loadBytesToTypeInfo(packageBytes, simpleClassNameBytes).cls; } int typeId = header >>> 1; @@ -1901,7 +1916,7 @@ private TypeInfo populateBytesToTypeInfo( cls, packageBytes, simpleClassNameBytes, false, null, typeId, INVALID_USER_TYPE_ID); if (UnknownClass.class.isAssignableFrom(TypeUtils.getComponentIfArray(cls))) { typeInfo.serializer = - UnknownClassSerializers.getSerializer(fory, classSpec.entireClassName, cls); + UnknownClassSerializers.getSerializer(this, classSpec.entireClassName, cls); } else { // don't create serializer here, if the class is an interface, // there won't be serializer since interface has no instance. @@ -1916,30 +1931,17 @@ private TypeInfo populateBytesToTypeInfo( public Class loadClassForMeta(String className, boolean isEnum, int arrayDims) { String pkg = ReflectionUtils.getPackage(className); String typeName = ReflectionUtils.getClassNameWithoutPackage(className); - MetaStringRef pkgBytes = metaStringResolver.getOrCreatePackageMetaStringBytes(pkg); - MetaStringRef typeBytes = metaStringResolver.getOrCreateTypeNameMetaStringBytes(typeName); + MetaStringRef pkgBytes = getOrCreatePackageMetaStringBytes(pkg); + MetaStringRef typeBytes = getOrCreateTypeNameMetaStringBytes(typeName); TypeInfo cachedInfo = compositeNameBytes2TypeInfo.get( new TypeNameBytes(pkgBytes.encoded.hash, typeBytes.encoded.hash)); if (cachedInfo != null) { return cachedInfo.cls; } - return loadClass(className, isEnum, arrayDims, fory.getConfig().deserializeUnknownClass()); - } - - public Class getCurrentReadClass() { - return currentReadClass; - } - - public void reset() { - resetRead(); - resetWrite(); + return loadClass(className, isEnum, arrayDims, config.deserializeUnknownClass()); } - public void resetRead() {} - - public void resetWrite() {} - // buildGenericType, nilTypeInfo, nilTypeInfoHolder are inherited from TypeResolver public GenericType getObjectGenericType() { @@ -2016,21 +2018,18 @@ public Comparator getDescriptorComparator() { */ @Override public void ensureSerializersCompiled() { + if (GraalvmSupport.isGraalRuntime()) { + return; + } if (extRegistry.ensureSerializersCompiled) { return; } extRegistry.ensureSerializersCompiled = true; try { - fory.getJITContext().lock(); - // Lambda and JdkProxy serializers use java.lang.Class which is not supported in xlang mode - if (!fory.isCrossLanguage()) { - Serializers.newSerializer(fory, LambdaSerializer.STUB_LAMBDA_CLASS, LambdaSerializer.class); - Serializers.newSerializer( - fory, JdkProxySerializer.SUBT_PROXY.getClass(), JdkProxySerializer.class); - } + getJITContext().lock(); classInfoMap.forEach( (cls, classInfo) -> { - registerGraalvmClass(cls); + GraalvmSupport.registerClass(cls); if (classInfo.serializer == null) { if (isSerializable(classInfo.cls)) { createSerializer0(cls); @@ -2066,22 +2065,21 @@ public void ensureSerializersCompiled() { } } } - if (GraalvmSupport.isGraalBuildtime() && classInfo.serializer != null) { - getGraalvmClassRegistry() - .putSerializerClass(cls, getGraalvmSerializerClass(classInfo.serializer)); - } }); if (GraalvmSupport.isGraalBuildtime()) { typeInfoCache = NIL_TYPE_INFO; - clearGraalvmGeneratedTypeInfoSerializers(); - compositeNameBytes2TypeInfo.forEach( - (typeNameBytes, typeInfo) -> clearGraalvmTypeInfoSerializer(typeInfo)); - getGraalvmClassRegistry().clearResolvers(); + classInfoMap.forEach( + (cls, classInfo) -> { + if (classInfo.serializer != null + && !extRegistry.registeredTypeInfos.contains(classInfo)) { + classInfo.serializer = null; + } + }); } // clear it to reduce memory footprint for massive virtual threads. extRegistry.registeredTypeInfos.clear(); } finally { - fory.getJITContext().unlock(); + getJITContext().unlock(); } } } diff --git a/java/fory-core/src/main/java/org/apache/fory/resolver/MapRefResolver.java b/java/fory-core/src/main/java/org/apache/fory/resolver/MapRefResolver.java deleted file mode 100644 index 8741fe597f..0000000000 --- a/java/fory-core/src/main/java/org/apache/fory/resolver/MapRefResolver.java +++ /dev/null @@ -1,320 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -package org.apache.fory.resolver; - -import java.util.ArrayList; -import java.util.HashMap; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Map; -import org.apache.fory.Fory; -import org.apache.fory.collection.IdentityObjectIntMap; -import org.apache.fory.collection.IntArray; -import org.apache.fory.collection.MapStatistics; -import org.apache.fory.collection.ObjectArray; -import org.apache.fory.memory.MemoryBuffer; -import org.apache.fory.util.Preconditions; - -/** Resolving reference by tracking reference by an IdentityMap. */ -// FIXME Will binding a separate reference resolver to every type have better performance? -// If so, we can have sophisticated reference control for every type. -public final class MapRefResolver implements RefResolver { - private static final boolean ENABLE_FORY_REF_PROFILING = - "true".equalsIgnoreCase(System.getProperty("fory.enable_ref_profiling")); - - private static final int DEFAULT_MAP_CAPACITY = 3; - private static final int DEFAULT_ARRAY_CAPACITY = 3; - // use average size to amortise resize/clear cost. - // exponential smoothing can't reflect overall reference size, thus not - // suitable for amortization. - // FIXME median may be better, but calculate median streaming is complicated. - // FIXME is there a more accurate way to predict reference size? - // maybe more complicated exponential smoothing? - private long writeCounter; - private long writeTotalObjectSize = 0; - private long readCounter; - private long readTotalObjectSize = 0; - private final IdentityObjectIntMap writtenObjects; - private final ObjectArray readObjects = new ObjectArray(DEFAULT_ARRAY_CAPACITY); - private final IntArray readRefIds = new IntArray(DEFAULT_ARRAY_CAPACITY); - - // last read object which is not a reference - private Object readObject; - - public MapRefResolver(float loadFactor) { - writtenObjects = new IdentityObjectIntMap<>(DEFAULT_MAP_CAPACITY, loadFactor); - } - - @Override - public boolean writeRefOrNull(MemoryBuffer buffer, Object obj) { - buffer.grow(10); - if (obj == null) { - buffer._unsafeWriteByte(Fory.NULL_FLAG); - return true; - } else { - // The id should be consistent with `#nextReadRefId` - int newWriteRefId = writtenObjects.size; - int writtenRefId; - if (ENABLE_FORY_REF_PROFILING) { - // replaceRef is rare, just ignore it for profiling. - writtenRefId = writtenObjects.profilingPutOrGet(obj, newWriteRefId); - } else { - writtenRefId = writtenObjects.putOrGet(obj, newWriteRefId); - } - if (writtenRefId >= 0) { - // The obj has been written previously. - buffer._unsafeWriteByte(Fory.REF_FLAG); - buffer._unsafeWriteVarUint32(writtenRefId); - return true; - } else { - // The object is being written for the first time. - buffer._unsafeWriteByte(Fory.REF_VALUE_FLAG); - return false; - } - } - } - - @Override - public boolean writeRefValueFlag(MemoryBuffer buffer, Object obj) { - assert obj != null; - buffer.grow(10); - // The id should be consistent with `#nextReadRefId` - int newWriteRefId = writtenObjects.size; - int writtenRefId; - if (ENABLE_FORY_REF_PROFILING) { - // replaceRef is rare, just ignore it for profiling. - writtenRefId = writtenObjects.profilingPutOrGet(obj, newWriteRefId); - } else { - writtenRefId = writtenObjects.putOrGet(obj, newWriteRefId); - } - if (writtenRefId >= 0) { - // The obj has been written previously. - buffer._unsafeWriteByte(Fory.REF_FLAG); - buffer._unsafeWriteVarUint32(writtenRefId); - return false; - } else { - // The object is being written for the first time. - buffer._unsafeWriteByte(Fory.REF_VALUE_FLAG); - return true; - } - } - - @Override - public boolean writeNullFlag(MemoryBuffer buffer, Object obj) { - if (obj == null) { - buffer._unsafeWriteByte(Fory.NULL_FLAG); - return true; - } - return false; - } - - @Override - public void replaceRef(Object original, Object newObject) { - int newObjectId = writtenObjects.get(newObject, -1); - Preconditions.checkArgument(newObjectId != -1); - writtenObjects.put(original, newObjectId); - } - - /** - * Returns {@link Fory#NULL_FLAG} if the object is null and set {@link #readObject} to null. - * - *

Returns {@link Fory#NOT_NULL_VALUE_FLAG} if the object is not null and the object isn't a - * referencable value and first read. - * - *

Returns {@link Fory#REF_FLAG} if a reference to a previously read object was read, which is - * stored in {@link #readObject}. - * - *

Returns {@link Fory#REF_VALUE_FLAG} if the object is a referencable value and not null and - * the object is first read. - */ - @Override - public byte readRefOrNull(MemoryBuffer buffer) { - byte headFlag = buffer.readByte(); - if (headFlag == Fory.REF_FLAG) { - // read reference id and get object from reference resolver - int referenceId = buffer.readVarUint32Small14(); - readObject = getReadObject(referenceId); - } else { - readObject = null; - } - return headFlag; - } - - @Override - public int preserveRefId() { - int nextReadRefId = readObjects.size(); - readObjects.add(null); - readRefIds.add(nextReadRefId); - return nextReadRefId; - } - - @Override - public int preserveRefId(int refId) { - readRefIds.add(refId); - return refId; - } - - @Override - public int tryPreserveRefId(MemoryBuffer buffer) { - byte headFlag = buffer.readByte(); - if (headFlag == Fory.REF_FLAG) { - // read reference id and get object from reference resolver - readObject = getReadObject(buffer.readVarUint32Small14()); - } else { - readObject = null; - if (headFlag == Fory.REF_VALUE_FLAG) { - return preserveRefId(); - } - } - // `headFlag` except `REF_FLAG` can be used as stub reference id because we use - // `refId >= NOT_NULL_VALUE_FLAG` to read data. - return headFlag; - } - - @Override - public int lastPreservedRefId() { - return readRefIds.get(readRefIds.size - 1); - } - - public boolean hasPreservedRefId() { - return readRefIds.size > 0; - } - - @Override - public void reference(Object object) { - int refId = readRefIds.pop(); - setReadObject(refId, object); - } - - @Override - public Object getReadObject(int id) { - return readObjects.get(id); - } - - @Override - public Object getReadObject() { - return readObject; - } - - @Override - public void setReadObject(int id, Object object) { - if (id >= 0) { - readObjects.set(id, object); - } - } - - public ObjectArray getReadObjects() { - return readObjects; - } - - @Override - public void reset() { - resetWrite(); - resetRead(); - } - - @Override - public void resetWrite() { - IdentityObjectIntMap writtenObjects = this.writtenObjects; - // TODO handle outlier big size. - long writeTotalObjectSize = this.writeTotalObjectSize + writtenObjects.size; - long writeCounter = this.writeCounter + 1; - if (writeCounter < 0 || writeTotalObjectSize < 0) { // overflow; - writeCounter = 1; - writeTotalObjectSize = writtenObjects.size; - } - this.writeCounter = writeCounter; - this.writeTotalObjectSize = writeTotalObjectSize; - int avg = (int) (writeTotalObjectSize / writeCounter); - if (avg <= DEFAULT_MAP_CAPACITY) { - avg = DEFAULT_MAP_CAPACITY; - } - writtenObjects.clearApproximate(avg); - } - - @Override - public void resetRead() { - ObjectArray readObjects = this.readObjects; - // TODO handle outlier big size. - long readTotalObjectSize = this.readTotalObjectSize + readObjects.size(); - long readCounter = this.readCounter + 1; - if (readCounter < 0 || readTotalObjectSize < 0) { // overflow; - readCounter = 1; - readTotalObjectSize = readObjects.size(); - } - this.readCounter = readCounter; - this.readTotalObjectSize = readTotalObjectSize; - int avg = (int) (readTotalObjectSize / readCounter); - if (avg <= DEFAULT_ARRAY_CAPACITY) { - avg = DEFAULT_ARRAY_CAPACITY; - } - readObjects.clearApproximate(avg); - readRefIds.clear(); - readObject = null; - } - - public static class RefStatistics { - LinkedHashMap, Integer> refTypeSummary; - int refCount; - MapStatistics mapStatistics; - - public RefStatistics( - LinkedHashMap, Integer> refTypeSummary, MapStatistics mapStatistics) { - this.refTypeSummary = refTypeSummary; - this.mapStatistics = mapStatistics; - refCount = refTypeSummary.values().stream().reduce(0, Integer::sum, Integer::sum); - } - - @Override - public String toString() { - return "RefStatistics{" - + "referenceTypeSummary=" - + refTypeSummary - + ", referenceCount=" - + refCount - + ", mapProbeStatistics=" - + mapStatistics - + '}'; - } - } - - public RefStatistics referenceStatistics() { - return new RefStatistics(referenceTypeSummary(), writtenObjects.getAndResetStatistics()); - } - - /** Returns a map which indicates counter for reference object type. */ - public LinkedHashMap, Integer> referenceTypeSummary() { - Map, Integer> typeCounter = new HashMap<>(); - writtenObjects.forEach( - (k, v) -> typeCounter.compute(k.getClass(), (key, value) -> value == null ? 1 : value + 1)); - List, Integer>> entries = new ArrayList<>(typeCounter.entrySet()); - entries.sort( - (o1, o2) -> { - if (o1.getValue().equals(o2.getValue())) { - return o1.getKey().getName().compareTo(o2.getKey().getName()); - } else { - return o2.getValue() - o1.getValue(); - } - }); - LinkedHashMap, Integer> result = new LinkedHashMap<>(entries.size()); - entries.forEach(e -> result.put(e.getKey(), e.getValue())); - return result; - } -} diff --git a/java/fory-core/src/main/java/org/apache/fory/resolver/MetaStringRef.java b/java/fory-core/src/main/java/org/apache/fory/resolver/MetaStringRef.java index 3916651604..46f038150b 100644 --- a/java/fory-core/src/main/java/org/apache/fory/resolver/MetaStringRef.java +++ b/java/fory-core/src/main/java/org/apache/fory/resolver/MetaStringRef.java @@ -19,18 +19,24 @@ package org.apache.fory.resolver; +import java.util.Objects; import org.apache.fory.annotation.Internal; import org.apache.fory.meta.EncodedMetaString; import org.apache.fory.meta.MetaStringDecoder; @Internal public final class MetaStringRef { - static final short DEFAULT_DYNAMIC_WRITE_STRING_ID = -1; + public static final short DEFAULT_DYNAMIC_WRITE_STRING_ID = -1; + final EncodedMetaString encoded; - short dynamicWriteStringId = DEFAULT_DYNAMIC_WRITE_STRING_ID; + public short dynamicWriteStringId = DEFAULT_DYNAMIC_WRITE_STRING_ID; + + public MetaStringRef(EncodedMetaString encoded) { + this.encoded = Objects.requireNonNull(encoded); + } - MetaStringRef(EncodedMetaString encoded) { - this.encoded = encoded; + public EncodedMetaString getEncoded() { + return encoded; } public String decode(char specialChar1, char specialChar2) { @@ -60,11 +66,6 @@ public int hashCode() { @Override public String toString() { - return "MetaStringRef{" - + "encoded=" - + encoded - + ", dynamicWriteStringId=" - + dynamicWriteStringId - + '}'; + return "MetaStringRef{" + "encoded=" + encoded + '}'; } } diff --git a/java/fory-core/src/main/java/org/apache/fory/resolver/MetaStringResolver.java b/java/fory-core/src/main/java/org/apache/fory/resolver/MetaStringResolver.java deleted file mode 100644 index deed8075b5..0000000000 --- a/java/fory-core/src/main/java/org/apache/fory/resolver/MetaStringResolver.java +++ /dev/null @@ -1,370 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -package org.apache.fory.resolver; - -import java.util.Arrays; -import java.util.Objects; -import org.apache.fory.collection.LongLongByteMap; -import org.apache.fory.collection.LongMap; -import org.apache.fory.collection.ObjectMap; -import org.apache.fory.memory.LittleEndian; -import org.apache.fory.memory.MemoryBuffer; -import org.apache.fory.meta.EncodedMetaString; -import org.apache.fory.meta.Encoders; -import org.apache.fory.meta.MetaString; -import org.apache.fory.meta.MetaStringEncoder; -import org.apache.fory.util.MurmurHash3; - -/** - * A resolver for limited string value writing. Currently, we only support classname dynamic - * writing. In the future, we may profile string field value dynamically and writing by this - * resolver to reduce string cost. TODO add common inner package names and classnames here. TODO - * share common immutable datastructure globally across multiple fory. - */ -public final class MetaStringResolver { - private static final int initialCapacity = 4; - // use a lower load factor to minimize hash collision - private static final float foryMapLoadFactor = 0.5f; - private static final int SMALL_STRING_THRESHOLD = 16; - - // Every deserialization for unregistered string will query it, performance is important. - private final ObjectMap metaString2StringMap = - new ObjectMap<>(initialCapacity, foryMapLoadFactor); - private final LongMap hash2MetaStringMap = - new LongMap<>(initialCapacity, foryMapLoadFactor); - private final LongLongByteMap longLongMetaStringMap = - new LongLongByteMap<>(initialCapacity, foryMapLoadFactor); - private final ObjectMap metaString2RefMap = - new ObjectMap<>(initialCapacity, foryMapLoadFactor); - private final SharedRegistry sharedRegistry; - private final MetaStringRef emptyMetaStringRef; - private MetaStringRef[] dynamicWrittenString = new MetaStringRef[initialCapacity]; - private MetaStringRef[] dynamicReadStringIds = new MetaStringRef[initialCapacity]; - private short dynamicWriteStringId; - private short dynamicReadStringId; - - public MetaStringResolver() { - this(new SharedRegistry()); - } - - public MetaStringResolver(SharedRegistry sharedRegistry) { - this.sharedRegistry = Objects.requireNonNull(sharedRegistry); - emptyMetaStringRef = new MetaStringRef(EncodedMetaString.EMPTY); - metaString2RefMap.put(EncodedMetaString.EMPTY, emptyMetaStringRef); - metaString2StringMap.put(EncodedMetaString.EMPTY, ""); - dynamicWriteStringId = 0; - dynamicReadStringId = 0; - } - - public MetaStringRef getOrCreateGenericMetaStringBytes(String str) { - return getOrCreateGenericMetaStringBytes(str, Encoders.computeGenericEncoding(str)); - } - - public MetaStringRef getOrCreateGenericMetaStringBytes(String str, MetaString.Encoding encoding) { - return getOrCreateMetaStringBytes( - str, Encoders.GENERIC_ENCODER, encoding, Encoders.GENERIC_ENCODER_TYPE_KEY); - } - - public MetaStringRef getOrCreatePackageMetaStringBytes(String str) { - return getOrCreateMetaStringBytes( - str, - Encoders.PACKAGE_ENCODER, - Encoders.computePackageEncoding(str), - Encoders.PACKAGE_ENCODER_TYPE_KEY); - } - - public MetaStringRef getOrCreateTypeNameMetaStringBytes(String str) { - return getOrCreateMetaStringBytes( - str, - Encoders.TYPE_NAME_ENCODER, - Encoders.computeTypeNameEncoding(str), - Encoders.TYPE_NAME_ENCODER_TYPE_KEY); - } - - MetaStringRef getOrCreateMetaStringBytes( - String str, MetaStringEncoder encoder, MetaString.Encoding encoding, String encoderTypeKey) { - EncodedMetaString encodedMetaString = - sharedRegistry.getOrCreateEncodedMetaString(str, encoder, encoding, encoderTypeKey); - return getOrCreateMetaStringRef(encodedMetaString); - } - - private MetaStringRef getOrCreateMetaStringRef(EncodedMetaString encodedMetaString) { - MetaStringRef metaStringRef = metaString2RefMap.get(encodedMetaString); - if (metaStringRef == null) { - metaStringRef = new MetaStringRef(encodedMetaString); - metaString2RefMap.put(encodedMetaString, metaStringRef); - } - return metaStringRef; - } - - public void writeMetaStringBytesWithFlag(MemoryBuffer buffer, MetaStringRef metaStringRef) { - Objects.requireNonNull(metaStringRef); - short id = metaStringRef.dynamicWriteStringId; - if (id == MetaStringRef.DEFAULT_DYNAMIC_WRITE_STRING_ID) { - // noinspection Duplicates - id = dynamicWriteStringId++; - metaStringRef.dynamicWriteStringId = id; - MetaStringRef[] dynamicWrittenMetaString = this.dynamicWrittenString; - if (dynamicWrittenMetaString.length <= id) { - dynamicWrittenMetaString = growWrite(id); - } - dynamicWrittenMetaString[id] = metaStringRef; - EncodedMetaString encodedMetaString = metaStringRef.encoded; - int length = encodedMetaString.bytes.length; - // last bit `1` indicates class is written by name instead of registered id. - buffer.writeVarUint32Small7(length << 2 | 0b1); - if (length > SMALL_STRING_THRESHOLD) { - buffer.writeInt64(encodedMetaString.hash); - } else if (length != 0) { - buffer.writeByte(encodedMetaString.encoding.getValue()); - } - buffer.writeBytes(encodedMetaString.bytes); - } else { - // last bit `1` indicates class is written by name instead of registered id. - buffer.writeVarUint32Small7(((id + 1) << 2) | 0b11); - } - } - - public void writeMetaStringBytes(MemoryBuffer buffer, MetaStringRef metaStringRef) { - short id = metaStringRef.dynamicWriteStringId; - if (id == MetaStringRef.DEFAULT_DYNAMIC_WRITE_STRING_ID) { - // noinspection Duplicates - id = dynamicWriteStringId++; - metaStringRef.dynamicWriteStringId = id; - MetaStringRef[] dynamicWrittenMetaString = this.dynamicWrittenString; - if (dynamicWrittenMetaString.length <= id) { - dynamicWrittenMetaString = growWrite(id); - } - dynamicWrittenMetaString[id] = metaStringRef; - EncodedMetaString encodedMetaString = metaStringRef.encoded; - int length = encodedMetaString.bytes.length; - buffer.writeVarUint32Small7(length << 1); - if (length > SMALL_STRING_THRESHOLD) { - buffer.writeInt64(encodedMetaString.hash); - } else if (length != 0) { - buffer.writeByte(encodedMetaString.encoding.getValue()); - } - buffer.writeBytes(encodedMetaString.bytes); - } else { - buffer.writeVarUint32Small7(((id + 1) << 1) | 1); - } - } - - private MetaStringRef[] growWrite(int id) { - MetaStringRef[] tmp = new MetaStringRef[id * 2]; - System.arraycopy(dynamicWrittenString, 0, tmp, 0, dynamicWrittenString.length); - return this.dynamicWrittenString = tmp; - } - - public String readMetaString(MemoryBuffer buffer) { - MetaStringRef metaStringRef = readMetaStringBytes(buffer); - EncodedMetaString encodedMetaString = metaStringRef.encoded; - String str = metaString2StringMap.get(encodedMetaString); - if (str == null) { - // TODO support meta string in other languages. - str = metaStringRef.decode(Encoders.GENERIC_DECODER); - metaString2StringMap.put(encodedMetaString, str); - } - return str; - } - - public MetaStringRef readMetaStringBytesWithFlag(MemoryBuffer buffer, int header) { - int len = header >>> 2; - if ((header & 0b10) == 0) { - MetaStringRef metaStringRef = - len <= SMALL_STRING_THRESHOLD - ? readSmallMetaStringBytes(buffer, len) - : readBigMetaStringBytes(buffer, len, buffer.readInt64()); - updateDynamicString(metaStringRef); - return metaStringRef; - } else { - return dynamicReadStringIds[len - 1]; - } - } - - public MetaStringRef readMetaStringBytesWithFlag( - MemoryBuffer buffer, MetaStringRef cache, int header) { - int len = header >>> 2; - if ((header & 0b10) == 0) { - MetaStringRef metaStringRef = - len <= SMALL_STRING_THRESHOLD - ? readSmallMetaStringBytes(buffer, cache, len) - : readBigMetaStringBytes(buffer, cache, len); - updateDynamicString(metaStringRef); - return metaStringRef; - } else { - return dynamicReadStringIds[len - 1]; - } - } - - public MetaStringRef readMetaStringBytes(MemoryBuffer buffer) { - int header = buffer.readVarUint32Small7(); - int len = header >>> 1; - if ((header & 0b1) == 0) { - MetaStringRef metaStringRef = - len > SMALL_STRING_THRESHOLD - ? readBigMetaStringBytes(buffer, len, buffer.readInt64()) - : readSmallMetaStringBytes(buffer, len); - updateDynamicString(metaStringRef); - return metaStringRef; - } else { - return dynamicReadStringIds[len - 1]; - } - } - - MetaStringRef readMetaStringBytes(MemoryBuffer buffer, MetaStringRef cache) { - int header = buffer.readVarUint32Small7(); - int len = header >>> 1; - if ((header & 0b1) == 0) { - MetaStringRef metaStringRef = - len <= SMALL_STRING_THRESHOLD - ? readSmallMetaStringBytes(buffer, cache, len) - : readBigMetaStringBytes(buffer, cache, len); - updateDynamicString(metaStringRef); - return metaStringRef; - } else { - return dynamicReadStringIds[len - 1]; - } - } - - private MetaStringRef readBigMetaStringBytes(MemoryBuffer buffer, MetaStringRef cache, int len) { - long hashCode = buffer.readInt64(); - if (cache.encoded.hash == hashCode) { - // skip byteString data - buffer.increaseReaderIndex(len); - return cache; - } else { - return readBigMetaStringBytes(buffer, len, hashCode); - } - } - - /** Read enum string by try to reuse previous read {@link MetaStringRef} object. */ - private MetaStringRef readBigMetaStringBytes(MemoryBuffer buffer, int len, long hashCode) { - EncodedMetaString encodedMetaString = hash2MetaStringMap.get(hashCode); - if (encodedMetaString == null) { - EncodedMetaString newMetaString = new EncodedMetaString(buffer.readBytes(len), hashCode); - MetaStringRef metaStringRef = getOrCreateMetaStringRef(newMetaString); - hash2MetaStringMap.put(hashCode, metaStringRef.encoded); - return metaStringRef; - } else { - // skip byteString data - buffer.increaseReaderIndex(len); - return getOrCreateMetaStringRef(encodedMetaString); - } - } - - private MetaStringRef readSmallMetaStringBytes(MemoryBuffer buffer, int len) { - if (len == 0) { - return emptyMetaStringRef; - } - byte encoding = buffer.readByte(); - long v1, v2 = 0; - if (len <= 8) { - v1 = buffer.readBytesAsInt64(len); - } else { - v1 = buffer.readInt64(); - v2 = buffer.readBytesAsInt64(len - 8); - } - EncodedMetaString encodedMetaString = longLongMetaStringMap.get(v1, v2, encoding); - if (encodedMetaString == null) { - return createSmallMetaStringBytes(len, encoding, v1, v2); - } - return getOrCreateMetaStringRef(encodedMetaString); - } - - private MetaStringRef readSmallMetaStringBytes( - MemoryBuffer buffer, MetaStringRef cache, int len) { - if (len == 0) { - return emptyMetaStringRef; - } - byte encoding = buffer.readByte(); - long v1, v2 = 0; - if (len <= 8) { - v1 = buffer.readBytesAsInt64(len); - } else { - v1 = buffer.readInt64(); - v2 = buffer.readBytesAsInt64(len - 8); - } - EncodedMetaString cachedMetaString = cache.encoded; - if (cachedMetaString.first8Bytes == v1 && cachedMetaString.second8Bytes == v2) { - return cache; - } - EncodedMetaString encodedMetaString = longLongMetaStringMap.get(v1, v2, encoding); - if (encodedMetaString == null) { - return createSmallMetaStringBytes(len, encoding, v1, v2); - } - return getOrCreateMetaStringRef(encodedMetaString); - } - - private MetaStringRef createSmallMetaStringBytes(int len, byte encoding, long v1, long v2) { - byte[] data = new byte[16]; - LittleEndian.putInt64(data, 0, v1); - LittleEndian.putInt64(data, 8, v2); - long hashCode = MurmurHash3.murmurhash3_x64_128(data, 0, len, 47)[0]; - hashCode = Math.abs(hashCode); - hashCode = (hashCode & 0xffffffffffffff00L) | encoding; - EncodedMetaString encodedMetaString = new EncodedMetaString(Arrays.copyOf(data, len), hashCode); - MetaStringRef metaStringRef = getOrCreateMetaStringRef(encodedMetaString); - longLongMetaStringMap.put(v1, v2, encoding, metaStringRef.encoded); - return metaStringRef; - } - - private void updateDynamicString(MetaStringRef metaStringRef) { - short currentDynamicReadId = dynamicReadStringId++; - MetaStringRef[] dynamicReadStringIds = this.dynamicReadStringIds; - if (dynamicReadStringIds.length <= currentDynamicReadId) { - dynamicReadStringIds = growRead(currentDynamicReadId); - } - dynamicReadStringIds[currentDynamicReadId] = metaStringRef; - } - - private MetaStringRef[] growRead(int id) { - MetaStringRef[] tmp = new MetaStringRef[id * 2]; - System.arraycopy(dynamicReadStringIds, 0, tmp, 0, dynamicReadStringIds.length); - return this.dynamicReadStringIds = tmp; - } - - public void reset() { - resetRead(); - resetWrite(); - } - - public void resetRead() { - int dynamicReadId = this.dynamicReadStringId; - if (dynamicReadId != 0) { - for (int i = 0; i < dynamicReadId; i++) { - dynamicReadStringIds[i] = null; - } - this.dynamicReadStringId = 0; - } - } - - public void resetWrite() { - int dynamicWriteStringId = this.dynamicWriteStringId; - if (dynamicWriteStringId != 0) { - for (int i = 0; i < dynamicWriteStringId; i++) { - dynamicWrittenString[i].dynamicWriteStringId = - MetaStringRef.DEFAULT_DYNAMIC_WRITE_STRING_ID; - dynamicWrittenString[i] = null; - } - this.dynamicWriteStringId = 0; - } - } -} diff --git a/java/fory-core/src/main/java/org/apache/fory/resolver/NoRefResolver.java b/java/fory-core/src/main/java/org/apache/fory/resolver/NoRefResolver.java deleted file mode 100644 index 22f8c6df23..0000000000 --- a/java/fory-core/src/main/java/org/apache/fory/resolver/NoRefResolver.java +++ /dev/null @@ -1,109 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -package org.apache.fory.resolver; - -import org.apache.fory.Fory; -import org.apache.fory.memory.MemoryBuffer; - -/** A no-op resolver which ignore reference, only handle null/non-null. */ -public final class NoRefResolver implements RefResolver { - - @Override - public boolean writeRefOrNull(MemoryBuffer buffer, Object obj) { - if (obj == null) { - buffer.writeByte(Fory.NULL_FLAG); - return true; - } else { - buffer.writeByte(Fory.NOT_NULL_VALUE_FLAG); - return false; - } - } - - @Override - public boolean writeRefValueFlag(MemoryBuffer buffer, Object obj) { - assert obj != null; - buffer.writeByte(Fory.NOT_NULL_VALUE_FLAG); - return true; - } - - @Override - public boolean writeNullFlag(MemoryBuffer buffer, Object obj) { - if (obj == null) { - buffer.writeByte(Fory.NULL_FLAG); - return true; - } - return false; - } - - @Override - public void replaceRef(Object original, Object newObject) {} - - @Override - public byte readRefOrNull(MemoryBuffer buffer) { - return buffer.readByte(); - } - - @Override - public int preserveRefId() { - return -1; - } - - @Override - public int preserveRefId(int refId) { - return -1; - } - - @Override - public int tryPreserveRefId(MemoryBuffer buffer) { - // `NOT_NULL_VALUE_FLAG` can be used as stub reference id because we use - // `refId >= NOT_NULL_VALUE_FLAG` to read data. - return buffer.readByte(); - } - - @Override - public int lastPreservedRefId() { - return -1; - } - - @Override - public void reference(Object object) {} - - @Override - public Object getReadObject(int id) { - return null; - } - - @Override - public Object getReadObject() { - return null; - } - - @Override - public void setReadObject(int id, Object object) {} - - @Override - public void reset() {} - - @Override - public void resetWrite() {} - - @Override - public void resetRead() {} -} diff --git a/java/fory-core/src/main/java/org/apache/fory/resolver/RefResolver.java b/java/fory-core/src/main/java/org/apache/fory/resolver/RefResolver.java deleted file mode 100644 index d40f3e9d3c..0000000000 --- a/java/fory-core/src/main/java/org/apache/fory/resolver/RefResolver.java +++ /dev/null @@ -1,116 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -package org.apache.fory.resolver; - -import org.apache.fory.Fory; -import org.apache.fory.memory.MemoryBuffer; - -/** This class is used to track objects that have already been read or written. */ -public interface RefResolver { - /** - * Write reference and tag for the obj if the obj has been written previously, write null/not-null - * tag otherwise. - * - * @return true if no bytes need to be written for the object. - */ - boolean writeRefOrNull(MemoryBuffer buffer, Object obj); - - /** - * Write reference and tag for the obj if the obj has been written previously, otherwise do - * nothing. - * - * @param buffer data buffer for writing flag. - * @param obj object. - * @return true if bytes need to be written for the object. - */ - boolean writeRefValueFlag(MemoryBuffer buffer, Object obj); - - /** - * Write null tag for the obj if the obj is null, otherwise do nothing. - * - * @param buffer data buffer for writing flag. - * @param obj object. - * @return true if no bytes need to be written for the object. - */ - boolean writeNullFlag(MemoryBuffer buffer, Object obj); - - /** - * Replace reference id of original with newObject. - * - * @param original original object - * @param newObject new object - */ - void replaceRef(Object original, Object newObject); - - /** - * Returns {@link Fory#REF_FLAG} if a reference to a previously read object was read - * - *

Returns {@link Fory#NULL_FLAG} if the object is null. - * - *

Returns {@link Fory#REF_VALUE_FLAG} if the object is not null and reference tracking is not - * enabled or the object is first read. - */ - byte readRefOrNull(MemoryBuffer buffer); - - /** - * Preserve a reference id, which is used by {@link #reference}/{@link #setReadObject} to set up - * reference for object that is first deserialized. - * - * @return a reference id or -1 if reference is not enabled. - */ - int preserveRefId(); - - int preserveRefId(int refId); - - /** - * Preserve and return a {@code refId} which is {@code >=} {@link Fory#NOT_NULL_VALUE_FLAG} if the - * value is not null. If the value is referencable value, the {@code refId} will be {@link - * #preserveRefId}. - */ - int tryPreserveRefId(MemoryBuffer buffer); - - /** Returns last preserved reference id. */ - int lastPreservedRefId(); - - /** - * Call this method immediately after composited object such as object array/map/collection/bean - * is created so that circular reference can be deserialized correctly. - */ - void reference(Object object); - - /** Returns the object for the specified id. */ - Object getReadObject(int id); - - Object getReadObject(); - - /** - * Sets the id for an object that has been read. - * - * @param id The id from {@link #preserveRefId}. - * @param object the object that has been read - */ - void setReadObject(int id, Object object); - - void reset(); - - void resetWrite(); - - void resetRead(); -} diff --git a/java/fory-core/src/main/java/org/apache/fory/resolver/SerializationContext.java b/java/fory-core/src/main/java/org/apache/fory/resolver/SerializationContext.java deleted file mode 100644 index 86b69b12b6..0000000000 --- a/java/fory-core/src/main/java/org/apache/fory/resolver/SerializationContext.java +++ /dev/null @@ -1,104 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -package org.apache.fory.resolver; - -import java.util.IdentityHashMap; -import org.apache.fory.config.Config; -import org.apache.fory.config.ForyBuilder; - -/** - * A context is used to add some context-related information, so that the serializers can set up - * relation between serializing different objects. The context will be reset after finished - * serializing/deserializing the object tree. - */ -public final class SerializationContext { - private final IdentityHashMap objects = new IdentityHashMap<>(); - private final boolean scopedMetaShareEnabled; - private MetaContext metaContext; - - public SerializationContext(Config config) { - scopedMetaShareEnabled = config.isScopedMetaShareEnabled(); - if (scopedMetaShareEnabled) { - metaContext = new MetaContext(); - } - } - - /** Return the previous value associated with key, or null. */ - public Object add(Object key, Object value) { - return objects.put(key, value); - } - - public boolean containsKey(Object key) { - return objects.containsKey(key); - } - - public Object get(Object key) { - return objects.get(key); - } - - public MetaContext getMetaContext() { - return metaContext; - } - - /** - * Set meta context, which can be used to share data across multiple serialization call. Note that - * {@code metaContext} will be cleared after the serialization is finished. Please set the context - * before every serialization if metaShare is enabled by {@link - * ForyBuilder#withMetaShare(boolean)} - */ - public void setMetaContext(MetaContext metaContext) { - assert !scopedMetaShareEnabled; - this.metaContext = metaContext; - } - - public void resetWrite() { - if (!objects.isEmpty()) { - objects.clear(); - } - if (scopedMetaShareEnabled) { - metaContext.classMap.clear(); - } else { - metaContext = null; - } - } - - public void resetRead() { - if (!objects.isEmpty()) { - objects.clear(); - } - if (scopedMetaShareEnabled) { - metaContext.readTypeInfos.size = 0; - } else { - metaContext = null; - } - } - - public void reset() { - if (!objects.isEmpty()) { - objects.clear(); - } - if (scopedMetaShareEnabled) { - metaContext.classMap.clear(); - metaContext.readTypeInfos.size = 0; - } else { - metaContext = null; - } - } -} diff --git a/java/fory-core/src/main/java/org/apache/fory/resolver/SharedRegistry.java b/java/fory-core/src/main/java/org/apache/fory/resolver/SharedRegistry.java index 5dfb9ba769..38e8dc78b4 100644 --- a/java/fory-core/src/main/java/org/apache/fory/resolver/SharedRegistry.java +++ b/java/fory-core/src/main/java/org/apache/fory/resolver/SharedRegistry.java @@ -36,11 +36,20 @@ import org.apache.fory.meta.MetaString; import org.apache.fory.meta.MetaStringEncoder; import org.apache.fory.meta.TypeDef; +import org.apache.fory.serializer.Serializer; import org.apache.fory.type.Descriptor; import org.apache.fory.type.DescriptorGrouper; import org.apache.fory.util.GraalvmSupport; -/** Shared caches reused by multiple equivalent {@link org.apache.fory.Fory} instances. */ +/** + * Shared caches reused by multiple equivalent {@link org.apache.fory.Fory} instances. + * + *

A {@code SharedRegistry} is scoped to one effective config hash. Do not share it across + * incompatible configs. + * + *

Only thread-safe serializers live in this registry. {@link TypeInfo} stays local to each + * {@link TypeResolver}. + */ @Internal public final class SharedRegistry { final ConcurrentIdentityMap, TypeDef> typeDefMap = new ConcurrentIdentityMap<>(); @@ -61,6 +70,8 @@ public final class SharedRegistry { new ConcurrentHashMap<>(); final ConcurrentHashMap metaStringMap = new ConcurrentHashMap<>(); + private final ConcurrentHashMap> serializers = + new ConcurrentHashMap<>(); volatile IdentityHashMap, Integer> registeredClassIdMap; volatile BiMap> registeredClasses; @@ -83,7 +94,7 @@ synchronized BiMap> getRegisteredClasses() { return Objects.requireNonNull(registeredClasses); } - EncodedMetaString getOrCreateEncodedMetaString( + public EncodedMetaString getOrCreateEncodedMetaString( String string, MetaStringEncoder encoder, MetaString.Encoding encoding, @@ -95,6 +106,51 @@ EncodedMetaString getOrCreateEncodedMetaString( return metaStringMap.computeIfAbsent(key, ignored -> encoder.encodeBinary(string, encoding)); } + @FunctionalInterface + public interface SerializerBuilder> { + T build(); + } + + @SuppressWarnings("unchecked") + public Serializer getSerializer( + Class type, Class serializerClass) { + return (Serializer) serializers.get(new SerializerCacheKey(type, serializerClass)); + } + + @SuppressWarnings("unchecked") + public > T getOrCreateSerializer( + Class type, Class serializerClass, SerializerBuilder builder) { + SerializerCacheKey key = new SerializerCacheKey(type, serializerClass); + Serializer existing = serializers.get(key); + if (existing != null) { + return (T) existing; + } + T serializer = builder.build(); + if (serializer == null || !serializer.threadSafe()) { + return serializer; + } + Serializer winner = serializers.putIfAbsent(key, serializer); + return winner == null ? serializer : (T) winner; + } + + @SuppressWarnings("unchecked") + public > T shareSerializer(Class type, T serializer) { + if (serializer == null || !serializer.threadSafe()) { + return serializer; + } + SerializerCacheKey key = new SerializerCacheKey(type, serializerClass(serializer)); + Serializer existing = serializers.putIfAbsent(key, serializer); + return existing == null ? serializer : (T) existing; + } + + public > T setSerializer(Class type, T serializer) { + if (serializer == null || !serializer.threadSafe()) { + return serializer; + } + serializers.put(new SerializerCacheKey(type, serializerClass(serializer)), serializer); + return serializer; + } + TypeDef getOrCreateTypeDef(TypeDef typeDef) { TypeDef existingTypeDef = typeDefById.putIfAbsent(typeDef.getId(), typeDef); return existingTypeDef == null ? typeDef : existingTypeDef; @@ -115,7 +171,8 @@ public List getOrCreateTypeDefDescriptors( if (GraalvmSupport.isGraalBuildtime()) { return Collections.unmodifiableList(new ArrayList<>(factory.get())); } - TypeDefDescriptorsKey key = new TypeDefDescriptorsKey(typeDef.getId(), type); + TypeDefDescriptorsKey key = + new TypeDefDescriptorsKey(typeDef.getId(), type, typeDef.getClassSpec().type); return typeDefDescriptorsCache.computeIfAbsent( key, ignored -> Collections.unmodifiableList(new ArrayList<>(factory.get()))); } @@ -148,12 +205,44 @@ DescriptorGrouper getOrCreateTypeDefDescriptorGrouper( } TypeDefDescriptorGrouperKey key = new TypeDefDescriptorGrouperKey( - new TypeDefDescriptorsKey(typeDef.getId(), type), + new TypeDefDescriptorsKey(typeDef.getId(), type, typeDef.getClassSpec().type), descriptorsGroupedOrdered, descriptorUpdator); return typeDefDescriptorGrouperCache.computeIfAbsent(key, ignored -> factory.get()); } + @SuppressWarnings("unchecked") + private static Class serializerClass(Serializer serializer) { + return (Class) serializer.getClass(); + } + + private static final class SerializerCacheKey { + private final Class type; + private final Class serializerClass; + + private SerializerCacheKey(Class type, Class serializerClass) { + this.type = Objects.requireNonNull(type); + this.serializerClass = Objects.requireNonNull(serializerClass); + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (!(o instanceof SerializerCacheKey)) { + return false; + } + SerializerCacheKey that = (SerializerCacheKey) o; + return type == that.type && serializerClass == that.serializerClass; + } + + @Override + public int hashCode() { + return 31 * System.identityHashCode(type) + System.identityHashCode(serializerClass); + } + } + private static final class MetaStringKey { private final String string; private final String encoderTypeKey; @@ -215,10 +304,17 @@ public int hashCode() { private static final class TypeDefDescriptorsKey { private final long typeDefId; private final Class type; + private final Class typeDefClass; - private TypeDefDescriptorsKey(long typeDefId, Class type) { + private TypeDefDescriptorsKey(long typeDefId, Class type, Class typeDefClass) { this.typeDefId = typeDefId; this.type = Objects.requireNonNull(type); + this.typeDefClass = typeDefClass; + } + + private boolean referencesClassLoader(ClassLoader loader) { + return type.getClassLoader() == loader + || (typeDefClass != null && typeDefClass.getClassLoader() == loader); } @Override diff --git a/java/fory-core/src/main/java/org/apache/fory/resolver/TypeInfo.java b/java/fory-core/src/main/java/org/apache/fory/resolver/TypeInfo.java index ca0bbf9b36..7458ae8031 100644 --- a/java/fory-core/src/main/java/org/apache/fory/resolver/TypeInfo.java +++ b/java/fory-core/src/main/java/org/apache/fory/resolver/TypeInfo.java @@ -93,7 +93,6 @@ public TypeInfo(Class cls, TypeDef typeDef) { this.cls = cls; this.serializer = serializer; needToWriteTypeDef = serializer != null && classResolver.needToWriteTypeDef(serializer); - MetaStringResolver metaStringResolver = classResolver.getMetaStringResolver(); // When typeId indicates a named type, we need to create classname bytes for serialization. // - NAMED_STRUCT: unregistered struct classes // - NAMED_COMPATIBLE_STRUCT: unregistered classes in compatible mode @@ -107,8 +106,8 @@ public TypeInfo(Class cls, TypeDef typeDef) { || typeId == ClassResolver.REPLACE_STUB_ID; if (cls != null && isNamedType) { Tuple2 tuple2 = Encoders.encodePkgAndClass(cls); - this.namespaceBytes = metaStringResolver.getOrCreatePackageMetaStringBytes(tuple2.f0); - this.typeNameBytes = metaStringResolver.getOrCreateTypeNameMetaStringBytes(tuple2.f1); + this.namespaceBytes = classResolver.getOrCreatePackageMetaStringBytes(tuple2.f0); + this.typeNameBytes = classResolver.getOrCreateTypeNameMetaStringBytes(tuple2.f1); } else { this.namespaceBytes = null; this.typeNameBytes = null; @@ -130,34 +129,6 @@ public TypeInfo(Class cls, TypeDef typeDef) { this.userTypeId = userTypeId; } - public TypeInfo copy(int typeId) { - if (typeId == this.typeId) { - return this; - } - return new TypeInfo( - cls, - namespaceBytes, - typeNameBytes, - isDynamicGeneratedClass, - serializer, - typeId, - userTypeId); - } - - public TypeInfo copy(int typeId, int userTypeId) { - if (typeId == this.typeId && userTypeId == this.userTypeId) { - return this; - } - return new TypeInfo( - cls, - namespaceBytes, - typeNameBytes, - isDynamicGeneratedClass, - serializer, - typeId, - userTypeId); - } - public Class getCls() { return cls; } @@ -166,10 +137,6 @@ public TypeDef getTypeDef() { return typeDef; } - void setTypeDef(TypeDef typeDef) { - this.typeDef = typeDef; - } - /** Returns the fory type ID for this class. */ public int getTypeId() { return typeId; diff --git a/java/fory-core/src/main/java/org/apache/fory/resolver/TypeResolver.java b/java/fory-core/src/main/java/org/apache/fory/resolver/TypeResolver.java index 61b66cf146..df19914010 100644 --- a/java/fory-core/src/main/java/org/apache/fory/resolver/TypeResolver.java +++ b/java/fory-core/src/main/java/org/apache/fory/resolver/TypeResolver.java @@ -42,7 +42,6 @@ import java.util.SortedMap; import java.util.concurrent.ConcurrentHashMap; import java.util.function.Function; -import org.apache.fory.Fory; import org.apache.fory.annotation.CodegenInvoke; import org.apache.fory.annotation.ForyField; import org.apache.fory.annotation.ForyObject; @@ -59,12 +58,18 @@ import org.apache.fory.collection.IdentityObjectIntMap; import org.apache.fory.collection.LongMap; import org.apache.fory.collection.Tuple2; +import org.apache.fory.config.CompatibleMode; +import org.apache.fory.config.Config; +import org.apache.fory.context.MetaContext; +import org.apache.fory.context.ReadContext; +import org.apache.fory.context.WriteContext; import org.apache.fory.exception.ForyException; import org.apache.fory.exception.SerializerUnregisteredException; import org.apache.fory.logging.Logger; import org.apache.fory.logging.LoggerFactory; import org.apache.fory.memory.MemoryBuffer; import org.apache.fory.meta.ClassSpec; +import org.apache.fory.meta.Encoders; import org.apache.fory.meta.TypeDef; import org.apache.fory.meta.TypeExtMeta; import org.apache.fory.reflect.ReflectionUtils; @@ -103,18 +108,18 @@ public abstract class TypeResolver { // use a lower load factor to minimize hash collision static final float foryMapLoadFactor = 0.5f; static final String SET_META__CONTEXT_MSG = - "Meta context must be set before serialization, " - + "please set meta context by SerializationContext.setMetaContext"; + "Meta context must be set on the active context before serialization/deserialization."; // reserved last 5 internal type ids for future use static final int INTERNAL_NATIVE_ID_LIMIT = 250; private static final GenericType OBJECT_GENERIC_TYPE = GenericType.build(Object.class); private static final float TYPE_ID_MAP_LOAD_FACTOR = 0.5f; static final long MAX_USER_TYPE_ID = 0xffff_fffEL; - final Fory fory; + final Config config; + final ClassLoader classLoader; final boolean metaContextShareEnabled; - final MetaStringResolver metaStringResolver; final SharedRegistry sharedRegistry; + final JITContext jitContext; // IdentityMap has better lookup performance, when loadFactor is 0.05f, performance is better final IdentityMap, TypeInfo> classInfoMap = new IdentityMap<>(64, foryMapLoadFactor); final ExtRegistry extRegistry; @@ -126,20 +131,82 @@ public abstract class TypeResolver { // Cache for readTypeInfo(MemoryBuffer) - persists between calls to avoid reloading // dynamically created classes that can't be found by Class.forName private TypeInfo typeInfoCache; - - protected TypeResolver(Fory fory) { - this.fory = fory; - metaContextShareEnabled = fory.getConfig().isMetaShareEnabled(); - sharedRegistry = fory.getSharedRegistry(); + private boolean registrationFinished; + + protected TypeResolver( + Config config, + ClassLoader classLoader, + SharedRegistry sharedRegistry, + JITContext jitContext) { + this.config = config; + this.classLoader = classLoader; + this.sharedRegistry = sharedRegistry; + this.jitContext = jitContext; + metaContextShareEnabled = config.isMetaShareEnabled(); extRegistry = new ExtRegistry(sharedRegistry); typeDefMap = sharedRegistry.typeDefMap; - metaStringResolver = fory.getMetaStringResolver(); - int length = fory.isCrossLanguage() ? Types.BOUND : INTERNAL_NATIVE_ID_LIMIT; + int length = isCrossLanguage() ? Types.BOUND : INTERNAL_NATIVE_ID_LIMIT; typeIdToTypeInfo = new TypeInfo[length]; } + public final Config getConfig() { + return config; + } + + public final ClassLoader getClassLoader() { + return classLoader; + } + + public final SharedRegistry getSharedRegistry() { + return sharedRegistry; + } + + public final JITContext getJITContext() { + return jitContext; + } + + public final boolean isRegistrationFinished() { + return registrationFinished; + } + + protected final void setRegistrationFinished() { + registrationFinished = true; + } + + public final boolean isCrossLanguage() { + return config.isXlang(); + } + + public final boolean isCompatible() { + return config.getCompatibleMode() == CompatibleMode.COMPATIBLE; + } + + public final boolean isShareMeta() { + return metaContextShareEnabled; + } + + public final boolean trackingRef() { + return config.trackingRef(); + } + + public final boolean isStringRefIgnored() { + return config.isStringRefIgnored(); + } + + public final boolean checkClassVersion() { + return config.checkClassVersion(); + } + + public final CompatibleMode getCompatibleMode() { + return config.getCompatibleMode(); + } + + public final Class getDefaultJDKStreamSerializerType() { + return config.getDefaultJDKStreamSerializerType(); + } + protected final void checkRegisterAllowed() { - if (fory.isRegistrationFinished()) { + if (registrationFinished) { throw new ForyException( "Cannot register class/serializer after registration has been frozen. Please register " + "all classes before invoking top-level `serialize/deserialize/copy` methods of " @@ -243,14 +310,14 @@ public abstract void registerSerializer( * points can call it defensively. */ public final void finishRegistration() { - if (fory.isRegistrationFinished()) { + if (registrationFinished) { return; } sharedRegistry.setRegistrationIfAbsent( extRegistry.registeredClassIdMap, extRegistry.registeredClasses); extRegistry.finishRegistration( sharedRegistry.getRegisteredClassIdMap(), sharedRegistry.getRegisteredClasses()); - fory.setRegistrationFinished(); + setRegistrationFinished(); } /** @@ -286,13 +353,13 @@ public void registerSerializerAndType(Class type, Serializer serializer) { * ignored too. */ public final boolean needToWriteRef(TypeRef typeRef) { - if (!fory.trackingRef()) { + if (!trackingRef()) { return false; } Class cls = typeRef.getRawType(); - if (cls == String.class && !fory.isCrossLanguage()) { + if (cls == String.class && !isCrossLanguage()) { // for string, ignore `TypeExtMeta` for java native mode - return !fory.getConfig().isStringRefIgnored(); + return !isStringRefIgnored(); } TypeExtMeta meta = typeRef.getTypeExtMeta(); if (meta != null) { @@ -309,14 +376,14 @@ public final boolean needToWriteRef(TypeRef typeRef) { } public final boolean needToWriteTypeDef(Serializer serializer) { - if (!fory.getConfig().isCompatible()) { + if (getCompatibleMode() != CompatibleMode.COMPATIBLE) { return false; } return isStructSerializer(serializer); } public final boolean needToWriteTypeDef(Class serializerClass) { - if (!fory.getConfig().isCompatible()) { + if (getCompatibleMode() != CompatibleMode.COMPATIBLE) { return false; } return isStructSerializerClass(serializerClass); @@ -361,7 +428,8 @@ protected DescriptorGrouper configureDescriptorGrouper(DescriptorGrouper descrip *

  • Other types: just the type ID * */ - public final void writeTypeInfo(MemoryBuffer buffer, TypeInfo typeInfo) { + public final void writeTypeInfo(WriteContext writeContext, TypeInfo typeInfo) { + MemoryBuffer buffer = writeContext.getBuffer(); int typeId = typeInfo.getTypeId(); buffer.writeUint8(typeId); switch (typeId) { @@ -373,7 +441,7 @@ public final void writeTypeInfo(MemoryBuffer buffer, TypeInfo typeInfo) { break; case Types.COMPATIBLE_STRUCT: case Types.NAMED_COMPATIBLE_STRUCT: - writeSharedClassMeta(buffer, typeInfo); + writeSharedClassMeta(writeContext, typeInfo); break; case Types.NAMED_ENUM: case Types.NAMED_STRUCT: @@ -381,11 +449,11 @@ public final void writeTypeInfo(MemoryBuffer buffer, TypeInfo typeInfo) { case Types.NAMED_UNION: if (!metaContextShareEnabled) { Preconditions.checkNotNull(typeInfo.namespaceBytes); - metaStringResolver.writeMetaStringBytes(buffer, typeInfo.namespaceBytes); + writeContext.getMetaStringWriter().writeMetaStringBytes(buffer, typeInfo.namespaceBytes); Preconditions.checkNotNull(typeInfo.typeNameBytes); - metaStringResolver.writeMetaStringBytes(buffer, typeInfo.typeNameBytes); + writeContext.getMetaStringWriter().writeMetaStringBytes(buffer, typeInfo.typeNameBytes); } else { - writeSharedClassMeta(buffer, typeInfo); + writeSharedClassMeta(writeContext, typeInfo); } break; default: @@ -408,8 +476,8 @@ static int toUserTypeId(long userTypeId) { */ // Note: Thread safe for jit thread to call. public Expression writeClassExpr( - Expression classResolverRef, Expression buffer, Expression classInfo) { - return new Invoke(classResolverRef, "writeTypeInfo", buffer, classInfo); + Expression classResolverRef, Expression writeContext, Expression classInfo) { + return new Invoke(classResolverRef, "writeTypeInfo", writeContext, classInfo); } /** @@ -419,8 +487,9 @@ public Expression writeClassExpr( * *

    This method is shared between XtypeResolver and ClassResolver. */ - protected final void writeSharedClassMeta(MemoryBuffer buffer, TypeInfo typeInfo) { - MetaContext metaContext = fory.getSerializationContext().getMetaContext(); + protected final void writeSharedClassMeta(WriteContext writeContext, TypeInfo typeInfo) { + MemoryBuffer buffer = writeContext.getBuffer(); + MetaContext metaContext = writeContext.getMetaContext(); assert metaContext != null : SET_META__CONTEXT_MSG; IdentityObjectIntMap> classMap = metaContext.classMap; int newId = classMap.size; @@ -449,10 +518,11 @@ protected final void writeSharedClassMeta(MemoryBuffer buffer, TypeInfo typeInfo * Reads class info from buffer using the unified type system. This is the single implementation * shared by both ClassResolver and XtypeResolver. * - *

    Note: {@link #readTypeInfo(MemoryBuffer, TypeInfo)} is faster since it uses a non-global + *

    Note: {@link #readTypeInfo(ReadContext, TypeInfo)} is faster since it uses a non-global * class info cache. */ - public final TypeInfo readTypeInfo(MemoryBuffer buffer) { + public final TypeInfo readTypeInfo(ReadContext readContext) { + MemoryBuffer buffer = readContext.getBuffer(); int typeId = buffer.readUint8(); TypeInfo typeInfo; switch (typeId) { @@ -464,23 +534,23 @@ public final TypeInfo readTypeInfo(MemoryBuffer buffer) { break; case Types.COMPATIBLE_STRUCT: case Types.NAMED_COMPATIBLE_STRUCT: - typeInfo = readSharedClassMeta(buffer); + typeInfo = readSharedClassMeta(readContext); break; case Types.NAMED_ENUM: case Types.NAMED_STRUCT: case Types.NAMED_EXT: case Types.NAMED_UNION: if (!metaContextShareEnabled) { - typeInfo = readTypeInfoFromBytes(buffer, typeInfoCache, typeId); + typeInfo = readTypeInfoFromBytes(readContext, typeInfoCache, typeId); } else { - typeInfo = readSharedClassMeta(buffer); + typeInfo = readSharedClassMeta(readContext); } break; case Types.LIST: - typeInfo = getListTypeInfo(); + typeInfo = getListTypeInfo(readContext); break; case Types.TIMESTAMP: - typeInfo = getTimestampTypeInfo(); + typeInfo = getTimestampTypeInfo(readContext); break; default: typeInfo = Objects.requireNonNull(getInternalTypeInfoByTypeId(typeId)); @@ -496,7 +566,8 @@ public final TypeInfo readTypeInfo(MemoryBuffer buffer) { * Read class info from buffer using a target class. This is used by java serialization APIs that * pass an expected class for meta share resolution. */ - public final TypeInfo readTypeInfo(MemoryBuffer buffer, Class targetClass) { + public final TypeInfo readTypeInfo(ReadContext readContext, Class targetClass) { + MemoryBuffer buffer = readContext.getBuffer(); int typeId = buffer.readUint8(); TypeInfo typeInfo; switch (typeId) { @@ -508,23 +579,23 @@ public final TypeInfo readTypeInfo(MemoryBuffer buffer, Class targetClass) { break; case Types.COMPATIBLE_STRUCT: case Types.NAMED_COMPATIBLE_STRUCT: - typeInfo = readSharedClassMeta(buffer, targetClass); + typeInfo = readSharedClassMeta(readContext, targetClass); break; case Types.NAMED_ENUM: case Types.NAMED_STRUCT: case Types.NAMED_EXT: case Types.NAMED_UNION: if (!metaContextShareEnabled) { - typeInfo = readTypeInfoFromBytes(buffer, typeInfoCache, typeId); + typeInfo = readTypeInfoFromBytes(readContext, typeInfoCache, typeId); } else { - typeInfo = readSharedClassMeta(buffer, targetClass); + typeInfo = readSharedClassMeta(readContext, targetClass); } break; case Types.LIST: - typeInfo = getListTypeInfo(); + typeInfo = getListTypeInfo(readContext); break; case Types.TIMESTAMP: - typeInfo = getTimestampTypeInfo(); + typeInfo = getTimestampTypeInfo(readContext); break; default: typeInfo = Objects.requireNonNull(getInternalTypeInfoByTypeId(typeId)); @@ -538,15 +609,15 @@ public final TypeInfo readTypeInfo(MemoryBuffer buffer, Class targetClass) { /** * Read class info from buffer with TypeInfo cache. This version is faster than {@link - * #readTypeInfo(MemoryBuffer)} because it uses the provided classInfoCache to reduce map lookups + * #readTypeInfo(ReadContext)} because it uses the provided classInfoCache to reduce map lookups * when reading class from binary. * - * @param buffer the buffer to read from * @param typeInfoCache cache for class info to speed up repeated reads * @return the TypeInfo read from buffer */ @CodegenInvoke - public final TypeInfo readTypeInfo(MemoryBuffer buffer, TypeInfo typeInfoCache) { + public final TypeInfo readTypeInfo(ReadContext readContext, TypeInfo typeInfoCache) { + MemoryBuffer buffer = readContext.getBuffer(); int typeId = buffer.readUint8(); TypeInfo typeInfo; switch (typeId) { @@ -558,23 +629,23 @@ public final TypeInfo readTypeInfo(MemoryBuffer buffer, TypeInfo typeInfoCache) break; case Types.COMPATIBLE_STRUCT: case Types.NAMED_COMPATIBLE_STRUCT: - typeInfo = readSharedClassMeta(buffer); + typeInfo = readSharedClassMeta(readContext); break; case Types.NAMED_ENUM: case Types.NAMED_STRUCT: case Types.NAMED_EXT: case Types.NAMED_UNION: if (!metaContextShareEnabled) { - typeInfo = readTypeInfoFromBytes(buffer, typeInfoCache, typeId); + typeInfo = readTypeInfoFromBytes(readContext, typeInfoCache, typeId); } else { - typeInfo = readSharedClassMeta(buffer); + typeInfo = readSharedClassMeta(readContext); } break; case Types.LIST: - typeInfo = getListTypeInfo(); + typeInfo = getListTypeInfo(readContext); break; case Types.TIMESTAMP: - typeInfo = getTimestampTypeInfo(); + typeInfo = getTimestampTypeInfo(readContext); break; default: typeInfo = Objects.requireNonNull(getInternalTypeInfoByTypeId(typeId)); @@ -589,12 +660,12 @@ public final TypeInfo readTypeInfo(MemoryBuffer buffer, TypeInfo typeInfoCache) * Read class info from buffer with TypeInfoHolder cache. This version updates the classInfoHolder * if the cache doesn't hit, allowing callers to maintain the cache across calls. * - * @param buffer the buffer to read from * @param classInfoHolder holder containing cache, will be updated on cache miss * @return the TypeInfo read from buffer */ @CodegenInvoke - public final TypeInfo readTypeInfo(MemoryBuffer buffer, TypeInfoHolder classInfoHolder) { + public final TypeInfo readTypeInfo(ReadContext readContext, TypeInfoHolder classInfoHolder) { + MemoryBuffer buffer = readContext.getBuffer(); int typeId = buffer.readUint8(); TypeInfo typeInfo; boolean updateCache = false; @@ -607,24 +678,24 @@ public final TypeInfo readTypeInfo(MemoryBuffer buffer, TypeInfoHolder classInfo break; case Types.COMPATIBLE_STRUCT: case Types.NAMED_COMPATIBLE_STRUCT: - typeInfo = readSharedClassMeta(buffer); + typeInfo = readSharedClassMeta(readContext); break; case Types.NAMED_ENUM: case Types.NAMED_STRUCT: case Types.NAMED_EXT: case Types.NAMED_UNION: if (!metaContextShareEnabled) { - typeInfo = readTypeInfoFromBytes(buffer, classInfoHolder.typeInfo, typeId); + typeInfo = readTypeInfoFromBytes(readContext, classInfoHolder.typeInfo, typeId); updateCache = true; } else { - typeInfo = readSharedClassMeta(buffer); + typeInfo = readSharedClassMeta(readContext); } break; case Types.LIST: - typeInfo = getListTypeInfo(); + typeInfo = getListTypeInfo(readContext); break; case Types.TIMESTAMP: - typeInfo = getTimestampTypeInfo(); + typeInfo = getTimestampTypeInfo(readContext); break; default: typeInfo = Objects.requireNonNull(getInternalTypeInfoByTypeId(typeId)); @@ -643,8 +714,8 @@ public final TypeInfo readTypeInfo(MemoryBuffer buffer, TypeInfoHolder classInfo * name bytes match. */ protected final TypeInfo readTypeInfoByCache( - MemoryBuffer buffer, TypeInfo typeInfoCache, int header) { - return readTypeInfoFromBytes(buffer, typeInfoCache, header); + ReadContext readContext, TypeInfo typeInfoCache, int header) { + return readTypeInfoFromBytes(readContext, typeInfoCache, header); } /** @@ -652,7 +723,8 @@ protected final TypeInfo readTypeInfoByCache( * bytes to avoid map lookups when the class is the same as the cached one (hash comparison). */ protected final TypeInfo readTypeInfoFromBytes( - MemoryBuffer buffer, TypeInfo typeInfoCache, int header) { + ReadContext readContext, TypeInfo typeInfoCache, int header) { + MemoryBuffer buffer = readContext.getBuffer(); MetaStringRef typeNameBytesCache = typeInfoCache != null ? typeInfoCache.typeNameBytes : null; MetaStringRef namespaceBytes; MetaStringRef simpleClassNameBytes; @@ -660,9 +732,11 @@ protected final TypeInfo readTypeInfoFromBytes( if (typeNameBytesCache != null) { // Use cache for faster comparison MetaStringRef packageNameBytesCache = typeInfoCache.namespaceBytes; - namespaceBytes = metaStringResolver.readMetaStringBytes(buffer, packageNameBytesCache); + namespaceBytes = + readContext.getMetaStringReader().readMetaStringBytes(buffer, packageNameBytesCache); assert packageNameBytesCache != null; - simpleClassNameBytes = metaStringResolver.readMetaStringBytes(buffer, typeNameBytesCache); + simpleClassNameBytes = + readContext.getMetaStringReader().readMetaStringBytes(buffer, typeNameBytesCache); // Fast path: if hashes match, return cached TypeInfo (already has serializer) if (typeNameBytesCache.encoded.hash == simpleClassNameBytes.encoded.hash @@ -671,8 +745,8 @@ protected final TypeInfo readTypeInfoFromBytes( } } else { // No cache available, read fresh - namespaceBytes = metaStringResolver.readMetaStringBytes(buffer); - simpleClassNameBytes = metaStringResolver.readMetaStringBytes(buffer); + namespaceBytes = readContext.getMetaStringReader().readMetaStringBytes(buffer); + simpleClassNameBytes = readContext.getMetaStringReader().readMetaStringBytes(buffer); } // Load class info from bytes (subclass-specific). @@ -683,8 +757,9 @@ protected final TypeInfo readTypeInfoFromBytes( * Reads shared class metadata from buffer. This is the shared implementation used by both * ClassResolver and XtypeResolver. */ - protected final TypeInfo readSharedClassMeta(MemoryBuffer buffer) { - MetaContext metaContext = fory.getSerializationContext().getMetaContext(); + protected final TypeInfo readSharedClassMeta(ReadContext readContext) { + MemoryBuffer buffer = readContext.getBuffer(); + MetaContext metaContext = readContext.getMetaContext(); assert metaContext != null : SET_META__CONTEXT_MSG; int indexMarker = buffer.readVarUint32Small14(); boolean isRef = (indexMarker & 1) == 1; @@ -714,8 +789,8 @@ protected final TypeInfo readSharedClassMeta(MemoryBuffer buffer) { return typeInfo; } - public final TypeInfo readSharedClassMeta(MemoryBuffer buffer, Class targetClass) { - TypeInfo typeInfo = readSharedClassMeta(buffer); + public final TypeInfo readSharedClassMeta(ReadContext readContext, Class targetClass) { + TypeInfo typeInfo = readSharedClassMeta(readContext); Class readClass = typeInfo.getCls(); // replace target class if needed if (targetClass != readClass) { @@ -790,11 +865,11 @@ protected TypeInfo loadBytesToTypeInfo( */ protected abstract TypeInfo ensureSerializerForTypeInfo(TypeInfo typeInfo); - protected TypeInfo getListTypeInfo() { + protected TypeInfo getListTypeInfo(ReadContext readContext) { return getInternalTypeInfoByTypeId(Types.LIST); } - protected TypeInfo getTimestampTypeInfo() { + protected TypeInfo getTimestampTypeInfo(ReadContext readContext) { return getInternalTypeInfoByTypeId(Types.TIMESTAMP); } @@ -818,6 +893,11 @@ protected void updateTypeInfo(Class cls, TypeInfo typeInfo) { } putInternalTypeInfo(typeInfo.typeId, typeInfo); } + cacheTypeInfo(typeInfo); + } + + protected final void cacheTypeInfo(TypeInfo typeInfo) { + typeInfoCache = typeInfo; } protected final void putInternalTypeInfo(int typeId, TypeInfo typeInfo) { @@ -892,15 +972,15 @@ private TypeInfo getMetaSharedTypeInfo(TypeDef typeDef, Class clz) { if (UnknownClass.class.isAssignableFrom(TypeUtils.getComponentIfArray(cls))) { if (cls == UnknownStruct.class) { typeInfo.setSerializer( - this, new UnknownClassSerializers.UnknownStructSerializer(fory, typeDef)); + this, new UnknownClassSerializers.UnknownStructSerializer(this, typeDef)); // Ensure UnknownStruct is registered so writeTypeInfo emits a placeholder typeId // that UnknownStructSerializer can rewrite to the original typeId. - if (!fory.isCrossLanguage()) { + if (!isCrossLanguage()) { Preconditions.checkNotNull(classId); } } else { typeInfo.serializer = - UnknownClassSerializers.getSerializer(fory, typeDef.getClassName(), cls); + UnknownClassSerializers.getSerializer(this, typeDef.getClassName(), cls); } return typeInfo; } @@ -918,23 +998,17 @@ private TypeInfo getMetaSharedTypeInfo(TypeDef typeDef, Class clz) { sc); } else { sc = - fory.getJITContext() + getJITContext() .registerSerializerJITCallback( () -> MetaSharedSerializer.class, - () -> CodecUtils.loadOrGenMetaSharedCodecClass(fory, cls, typeDef), - c -> typeInfo.setSerializer(this, Serializers.newSerializer(fory, cls, c))); + () -> loadMetaSharedCodecClass(cls, typeDef), + c -> typeInfo.setSerializer(this, Serializers.newSerializer(this, cls, c))); } } - if (GraalvmSupport.isGraalBuildtime() - && GeneratedMetaSharedSerializer.class.isAssignableFrom(sc)) { - getGraalvmClassRegistry().putIfAbsentDeserializerClass(typeDef.getId(), sc); - typeInfo.setSerializer(this, new MetaSharedSerializer(fory, cls, typeDef)); - return typeInfo; - } if (sc == MetaSharedSerializer.class) { - typeInfo.setSerializer(this, new MetaSharedSerializer(fory, cls, typeDef)); + typeInfo.setSerializer(this, new MetaSharedSerializer(this, cls, typeDef)); } else { - typeInfo.setSerializer(this, Serializers.newSerializer(fory, cls, sc)); + typeInfo.setSerializer(this, Serializers.newSerializer(this, cls, sc)); } return typeInfo; } @@ -946,7 +1020,7 @@ protected int buildUnregisteredTypeId(Class cls, Serializer serializer) { if (serializer != null && !isStructSerializer(serializer)) { return Types.NAMED_EXT; } - if (fory.isCompatible() && isStructEvolving(cls)) { + if (isCompatible() && isStructEvolving(cls)) { return metaContextShareEnabled ? Types.NAMED_COMPATIBLE_STRUCT : Types.NAMED_STRUCT; } return Types.NAMED_STRUCT; @@ -977,7 +1051,7 @@ protected static boolean isStructSerializerClass(Class ser } protected TypeDef readTypeDef(MemoryBuffer buffer, long header) { - TypeDef readTypeDef = TypeDef.readTypeDef(fory, buffer, header); + TypeDef readTypeDef = TypeDef.readTypeDef(this, buffer, header); return cacheTypeDef(readTypeDef); } @@ -989,7 +1063,7 @@ final Class loadClass(ClassSpec classSpec) { } final Class loadClass(String className, boolean isEnum, int arrayDims) { - return loadClass(className, isEnum, arrayDims, fory.getConfig().deserializeUnknownClass()); + return loadClass(className, isEnum, arrayDims, config.deserializeUnknownClass()); } final Class loadClass(String className) { @@ -1004,7 +1078,7 @@ final Class loadClass( return cls; } try { - return Class.forName(className, false, fory.getClassLoader()); + return Class.forName(className, false, classLoader); } catch (ClassNotFoundException e) { try { return Class.forName(className, false, Thread.currentThread().getContextClassLoader()); @@ -1012,7 +1086,7 @@ final Class loadClass( String msg = String.format( "Class %s not found from classloaders [%s, %s]", - className, fory.getClassLoader(), Thread.currentThread().getContextClassLoader()); + className, classLoader, Thread.currentThread().getContextClassLoader()); if (deserializeUnknownClass) { LOG.warn(msg); return UnknownClass.getUnknowClass(className, isEnum, arrayDims, metaContextShareEnabled); @@ -1025,7 +1099,7 @@ final Class loadClass( public abstract Serializer getSerializer(Class cls); public final Serializer getSerializer(TypeRef typeRef) { - if (!fory.isCrossLanguage()) { + if (!isCrossLanguage()) { return getSerializer(typeRef.getRawType()); } Class rawType = typeRef.getRawType(); @@ -1046,6 +1120,7 @@ public void resetSerializer(Class cls, Serializer serializer) { TypeInfo typeInfo = getTypeInfo(cls, false); if (typeInfo != null) { typeInfo.setSerializer(this, null); + updateTypeInfo(cls, typeInfo); } } else { setSerializer(cls, serializer); @@ -1114,13 +1189,21 @@ public GenericType getGenericTypeInStruct(Class cls, String genericTypeStr) { public abstract void ensureSerializersCompiled(); + protected Class loadCodegenSerializerClass(Class cls) { + return CodegenSerializer.loadCodegenSerializer(this, cls); + } + + protected Class loadMetaSharedCodecClass(Class cls, TypeDef typeDef) { + return CodecUtils.loadOrGenMetaSharedCodecClass(this, cls, typeDef); + } + public final TypeDef getTypeDef(Class cls, boolean resolveParent) { if (resolveParent) { - return cacheTypeDef(typeDefMap.computeIfAbsent(cls, k -> TypeDef.buildTypeDef(fory, cls))); + return cacheTypeDef(typeDefMap.computeIfAbsent(cls, k -> TypeDef.buildTypeDef(this, cls))); } return cacheTypeDef( extRegistry.currentLayerTypeDef.computeIfAbsent( - cls, key -> TypeDef.buildTypeDef(fory, key, false))); + cls, key -> TypeDef.buildTypeDef(this, key, false))); } public final TypeDef cacheTypeDef(TypeDef typeDef) { @@ -1171,11 +1254,30 @@ public Class getObjectSerializerClass( } else { try { extRegistry.getClassCtx.add(cls); - return fory.getJITContext() - .registerSerializerJITCallback( - () -> ObjectSerializer.class, - () -> CodegenSerializer.loadCodegenSerializer(fory, cls), - callback); + Class sc; + switch (getCompatibleMode()) { + case SCHEMA_CONSISTENT: + sc = + getJITContext() + .registerSerializerJITCallback( + () -> ObjectSerializer.class, + () -> loadCodegenSerializerClass(cls), + callback); + return sc; + case COMPATIBLE: + // Always use ObjectSerializer for compatible mode. + // Class definition will be sent to peer to create serializer for deserialization. + sc = + getJITContext() + .registerSerializerJITCallback( + () -> ObjectSerializer.class, + () -> loadCodegenSerializerClass(cls), + callback); + return sc; + default: + throw new UnsupportedOperationException( + String.format("Unsupported mode %s", getCompatibleMode())); + } } finally { extRegistry.getClassCtx.remove(cls); } @@ -1193,7 +1295,7 @@ public final boolean isCollection(Class cls) { if (Collection.class.isAssignableFrom(cls)) { return true; } - if (fory.getConfig().isScalaOptimizationEnabled()) { + if (config.isScalaOptimizationEnabled()) { // Scala map is scala iterable too. if (ScalaTypes.getScalaMapType().isAssignableFrom(cls)) { return false; @@ -1208,7 +1310,7 @@ public final boolean isSet(Class cls) { if (Set.class.isAssignableFrom(cls)) { return true; } - if (fory.getConfig().isScalaOptimizationEnabled()) { + if (config.isScalaOptimizationEnabled()) { // Scala map is scala iterable too. if (ScalaTypes.getScalaMapType().isAssignableFrom(cls)) { return false; @@ -1224,7 +1326,7 @@ public final boolean isMap(Class cls) { return false; } return Map.class.isAssignableFrom(cls) - || (fory.getConfig().isScalaOptimizationEnabled() + || (config.isScalaOptimizationEnabled() && ScalaTypes.getScalaMapType().isAssignableFrom(cls)); } @@ -1298,8 +1400,8 @@ private List buildFieldDescriptors(Class clz, boolean searchParen SortedMap allDescriptors = getAllDescriptorsMap(clz, searchParent); List result = new ArrayList<>(allDescriptors.size()); - boolean globalRefTracking = fory.trackingRef(); - boolean isXlang = fory.isCrossLanguage(); + boolean globalRefTracking = trackingRef(); + boolean isXlang = isCrossLanguage(); for (Map.Entry entry : allDescriptors.entrySet()) { Member member = entry.getKey(); @@ -1378,8 +1480,8 @@ public Comparator getPrimitiveComparator() { return (d1, d2) -> { Class t1 = TypeUtils.unwrap(d1.getRawType()); Class t2 = TypeUtils.unwrap(d2.getRawType()); - int typeId1 = Types.getDescriptorTypeId(fory, d1); - int typeId2 = Types.getDescriptorTypeId(fory, d2); + int typeId1 = Types.getDescriptorTypeId(this, d1); + int typeId2 = Types.getDescriptorTypeId(this, d2); boolean t1Compress = Types.isCompressedType(typeId1); boolean t2Compress = Types.isCompressedType(typeId2); if ((t1Compress && t2Compress) || (!t1Compress && !t2Compress)) { @@ -1432,7 +1534,7 @@ private boolean isFieldNullable(Descriptor descriptor) { if (rawType.isPrimitive()) { return false; } - if (fory.isCrossLanguage()) { + if (isCrossLanguage()) { // For xlang mode: apply xlang defaults // This must match what TypeDefEncoder.buildFieldType uses for TypeDef metadata ForyField foryField = descriptor.getForyField(); @@ -1468,7 +1570,7 @@ protected final Map buildGenericMap(Class cls) { Type type = field.getGenericType(); GenericType genericType = buildGenericType(type); AnnotatedType annotatedType = field.getAnnotatedType(); - TypeUtils.applyRefTrackingOverride(genericType, annotatedType, fory.trackingRef()); + TypeUtils.applyRefTrackingOverride(genericType, annotatedType, trackingRef()); buildGenericMap(map, genericType); TypeRef typeRef = TypeRef.of(type); buildGenericMap(map2, typeRef); @@ -1540,53 +1642,17 @@ public SerializerFactory getSerializerFactory() { return extRegistry.serializerFactory; } - public void resetRead() {} - - public void resetWrite() {} - - protected final void clearGraalvmTypeInfoSerializer(TypeInfo typeInfo) { - if (typeInfo != null - && typeInfo.serializer != null - && !extRegistry.registeredTypeInfos.contains(typeInfo)) { - typeInfo.serializer = null; - } - } - - protected final void clearGraalvmGeneratedTypeInfoSerializers() { - classInfoMap.forEach((cls, typeInfo) -> clearGraalvmTypeInfoSerializer(typeInfo)); - for (TypeInfo typeInfo : typeIdToTypeInfo) { - clearGraalvmTypeInfoSerializer(typeInfo); - } - userTypeIdToTypeInfo.forEach((id, typeInfo) -> clearGraalvmTypeInfoSerializer(typeInfo)); - extRegistry.typeInfoByTypeDefId.forEach( - (typeDefId, typeInfo) -> clearGraalvmTypeInfoSerializer(typeInfo)); - } - - protected final void registerGraalvmClass(Class cls) { - GraalvmSupport.registerClass(cls); - } - - protected final boolean hasGraalvmSerializerClass(Class cls) { - return GraalvmSupport.isGraalRuntime() && getGraalvmClassRegistry().hasSerializerClass(cls); - } - - protected final Class getObjectSerializerClassFromGraalvmRegistry( - Class cls) { - return getGraalvmClassRegistry().getObjectSerializerClass(cls); - } - // CHECKSTYLE.OFF:MethodName - public static void _addGraalvmClassRegistry(int foryConfigHash, ClassResolver classResolver) { + public static void _addGraalvmClassRegistry(int configHash, ClassResolver classResolver) { // CHECKSTYLE.ON:MethodName if (GraalvmSupport.isGraalBuildtime()) { - GraalvmSupport.GraalvmClassRegistry registry = - GraalvmSupport.getClassRegistry(foryConfigHash); + GraalvmSupport.GraalvmClassRegistry registry = GraalvmSupport.getClassRegistry(configHash); registry.addResolver(classResolver); } } final GraalvmSupport.GraalvmClassRegistry getGraalvmClassRegistry() { - return GraalvmSupport.getClassRegistry(fory.getConfig().getConfigHash()); + return GraalvmSupport.getClassRegistry(config.getConfigHash()); } final Class getGraalvmSerializerClass(Serializer serializer) { @@ -1597,20 +1663,30 @@ final Class getGraalvmSerializerClass(Serializer serialize return serializer.getClass(); } + protected final boolean hasGraalvmSerializerClass(Class cls) { + return GraalvmSupport.isGraalRuntime() && getGraalvmClassRegistry().hasSerializerClass(cls); + } + + protected final Class getObjectSerializerClassFromGraalvmRegistry( + Class cls) { + return getGraalvmClassRegistry().getObjectSerializerClass(cls); + } + final Class getSerializerClassFromGraalvmRegistry(Class cls) { GraalvmSupport.GraalvmClassRegistry registry = getGraalvmClassRegistry(); Class serializerClass = registry.getSerializerClass(cls); if (serializerClass != null) { return serializerClass; } + if (!registry.hasResolvers()) { + return null; + } List resolvers = registry.getResolvers(); - if (!resolvers.isEmpty()) { - for (TypeResolver resolver : resolvers) { - if (resolver != this) { - TypeInfo typeInfo = resolver.getTypeInfo(cls, false); - if (typeInfo != null && typeInfo.serializer != null) { - return resolver.getGraalvmSerializerClass(typeInfo.serializer); - } + for (TypeResolver resolver : resolvers) { + if (resolver != this) { + TypeInfo typeInfo = resolver.getTypeInfo(cls, false); + if (typeInfo != null && typeInfo.serializer != null) { + return resolver.getGraalvmSerializerClass(typeInfo.serializer); } } } @@ -1618,7 +1694,10 @@ final Class getSerializerClassFromGraalvmRegistry(Class if (Functions.isLambda(cls) || ReflectionUtils.isJdkProxy(cls)) { return null; } - throw new RuntimeException(String.format("Class %s is not registered", cls)); + throw new RuntimeException( + String.format( + "Class %s is not registered, registered classes: %s", + cls, registry.getRegisteredSerializerClasses())); } return null; } @@ -1645,12 +1724,31 @@ protected final Class getMetaSharedDeserializerClassFromGr return null; } - public final Fory getFory() { - return fory; + public final MetaStringRef getOrCreateGenericMetaStringBytes(String string) { + return new MetaStringRef( + sharedRegistry.getOrCreateEncodedMetaString( + string, + Encoders.GENERIC_ENCODER, + Encoders.computeGenericEncoding(string), + Encoders.GENERIC_ENCODER_TYPE_KEY)); + } + + public final MetaStringRef getOrCreatePackageMetaStringBytes(String string) { + return new MetaStringRef( + sharedRegistry.getOrCreateEncodedMetaString( + string, + Encoders.PACKAGE_ENCODER, + Encoders.computePackageEncoding(string), + Encoders.PACKAGE_ENCODER_TYPE_KEY)); } - public final MetaStringResolver getMetaStringResolver() { - return metaStringResolver; + public final MetaStringRef getOrCreateTypeNameMetaStringBytes(String string) { + return new MetaStringRef( + sharedRegistry.getOrCreateEncodedMetaString( + string, + Encoders.TYPE_NAME_ENCODER, + Encoders.computeTypeNameEncoding(string), + Encoders.TYPE_NAME_ENCODER_TYPE_KEY)); } /** @@ -1719,13 +1817,13 @@ class ExtRegistry { final Map genericTypes = new HashMap<>(); final Map> classGenericTypes = new HashMap<>(); // will be clear after ensureSerializersCompiled - final Set registeredTypeInfos = new HashSet<>(fory.isCrossLanguage() ? 4 : 180); + final Set registeredTypeInfos = new HashSet<>(isCrossLanguage() ? 4 : 180); boolean ensureSerializersCompiled; // shared across multiple fory instances. IdentityHashMap, Integer> registeredClassIdMap = - new IdentityHashMap<>(fory.isCrossLanguage() ? 4 : 200); - BiMap> registeredClasses = HashBiMap.create(fory.isCrossLanguage() ? 4 : 200); + new IdentityHashMap<>(isCrossLanguage() ? 4 : 200); + BiMap> registeredClasses = HashBiMap.create(isCrossLanguage() ? 4 : 200); final ConcurrentIdentityMap, TypeDef> currentLayerTypeDef; // TODO(chaokunyang) Better to use soft reference, see ObjectStreamClass. final ConcurrentHashMap, Boolean>, SortedMap> diff --git a/java/fory-core/src/main/java/org/apache/fory/resolver/XtypeResolver.java b/java/fory-core/src/main/java/org/apache/fory/resolver/XtypeResolver.java index 3be3645f49..95b849297f 100644 --- a/java/fory-core/src/main/java/org/apache/fory/resolver/XtypeResolver.java +++ b/java/fory-core/src/main/java/org/apache/fory/resolver/XtypeResolver.java @@ -29,6 +29,7 @@ import java.math.BigDecimal; import java.math.BigInteger; +import java.net.URI; import java.sql.Timestamp; import java.time.Duration; import java.time.Instant; @@ -37,6 +38,7 @@ import java.util.ArrayList; import java.util.Collection; import java.util.Comparator; +import java.util.Currency; import java.util.Date; import java.util.HashMap; import java.util.HashSet; @@ -45,13 +47,15 @@ import java.util.List; import java.util.Map; import java.util.Set; +import java.util.UUID; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicLong; import java.util.concurrent.atomic.AtomicReference; -import org.apache.fory.Fory; +import java.util.regex.Pattern; import org.apache.fory.annotation.ForyField; import org.apache.fory.annotation.Internal; +import org.apache.fory.builder.JITContext; import org.apache.fory.collection.BoolList; import org.apache.fory.collection.Float16List; import org.apache.fory.collection.Float32List; @@ -67,6 +71,7 @@ import org.apache.fory.collection.Uint64List; import org.apache.fory.collection.Uint8List; import org.apache.fory.config.Config; +import org.apache.fory.context.ReadContext; import org.apache.fory.exception.ClassUnregisteredException; import org.apache.fory.exception.SerializerUnregisteredException; import org.apache.fory.logging.Logger; @@ -76,6 +81,7 @@ import org.apache.fory.meta.TypeDef; import org.apache.fory.reflect.ReflectionUtils; import org.apache.fory.serializer.ArraySerializers; +import org.apache.fory.serializer.CodegenSerializer; import org.apache.fory.serializer.DeferedLazySerializer; import org.apache.fory.serializer.DeferedLazySerializer.DeferredLazyObjectSerializer; import org.apache.fory.serializer.EnumSerializer; @@ -106,7 +112,6 @@ import org.apache.fory.type.DescriptorGrouper; import org.apache.fory.type.Float16; import org.apache.fory.type.GenericType; -import org.apache.fory.type.Generics; import org.apache.fory.type.TypeUtils; import org.apache.fory.type.Types; import org.apache.fory.type.union.Union; @@ -126,10 +131,7 @@ public class XtypeResolver extends TypeResolver { // Most systems won't have so many types for serialization. private static final int MAX_TYPE_ID = 4096; - private final Config config; - private final Fory fory; private final TypeInfoHolder classInfoCache = new TypeInfoHolder(NIL_TYPE_INFO); - private final MetaStringResolver metaStringResolver; // Every deserialization for unregistered class will query it, performance is important. private final ObjectMap compositeClassNameBytes2TypeInfo = @@ -140,35 +142,75 @@ public class XtypeResolver extends TypeResolver { private final boolean shareMeta; private int xtypeIdGenerator = 64; - private final Generics generics; - - public XtypeResolver(Fory fory) { - super(fory); - this.config = fory.getConfig(); - this.fory = fory; - shareMeta = fory.getConfig().isMetaShareEnabled(); - this.generics = fory.getGenerics(); - this.metaStringResolver = fory.getMetaStringResolver(); + public XtypeResolver( + Config config, + ClassLoader classLoader, + SharedRegistry sharedRegistry, + JITContext jitContext) { + super(config, classLoader, sharedRegistry, jitContext); + shareMeta = config.isMetaShareEnabled(); } @Override public void initialize() { registerDefaultTypes(); - // Keep xlang default internal serializers aligned with previous bootstrap behavior. - // Before resolver unification, these were registered through ClassResolver initialization. - Serializers.registerDefaultSerializers(fory); + registerInternalSharedSerializer( + Class.class, + Serializers.ClassSerializer.class, + () -> new Serializers.ClassSerializer(config)); + registerInternalSharedSerializer( + AtomicReference.class, + Serializers.AtomicReferenceSerializer.class, + () -> new Serializers.AtomicReferenceSerializer(config)); + registerInternalSharedSerializer( + Currency.class, + Serializers.CurrencySerializer.class, + () -> new Serializers.CurrencySerializer(config)); + registerInternalSharedSerializer( + URI.class, Serializers.URISerializer.class, () -> new Serializers.URISerializer(config)); + registerInternalSharedSerializer( + Pattern.class, + Serializers.RegexSerializer.class, + () -> new Serializers.RegexSerializer(config)); + registerInternalSharedSerializer( + UUID.class, Serializers.UUIDSerializer.class, () -> new Serializers.UUIDSerializer(config)); + registerInternalSharedSerializer( + Object.class, + Serializers.EmptyObjectSerializer.class, + () -> new Serializers.EmptyObjectSerializer(config)); if (shareMeta) { - Serializer serializer = new UnknownStructSerializer(fory, null); + Serializer serializer = new UnknownStructSerializer(this, null); register(UnknownStruct.class, serializer, "", "unknown_struct", Types.COMPATIBLE_STRUCT, -1); } - if (GraalvmSupport.isGraalBuildtime()) { - classInfoMap.forEach( - (cls, classInfo) -> { - if (classInfo.serializer != null) { - extRegistry.registeredTypeInfos.add(classInfo); - } - }); + } + + private > void registerInternalSharedSerializer( + Class type, + Class serializerClass, + SharedRegistry.SerializerBuilder creator) { + registerInternalSerializer( + type, sharedRegistry.getOrCreateSerializer(type, serializerClass, creator)); + } + + @Override + protected void updateTypeInfo(Class cls, TypeInfo typeInfo) { + classInfoMap.put(cls, typeInfo); + if (typeInfo.userTypeId != INVALID_USER_TYPE_ID) { + putUserTypeInfo(typeInfo.userTypeId, typeInfo); + cacheTypeInfo(typeInfo); + return; + } + if (Types.isUserDefinedType((byte) typeInfo.typeId)) { + cacheTypeInfo(typeInfo); + return; + } + TypeInfo currentTypeInfo = getInternalTypeInfoByTypeId(typeInfo.typeId); + // Multiple Java classes can share one xlang built-in type id. Keep the canonical decode target + // stable for that id instead of replacing it with an alias type like StringBuffer. + if (currentTypeInfo == null || currentTypeInfo.getCls() == cls) { + putInternalTypeInfo(typeInfo.typeId, typeInfo); } + cacheTypeInfo(typeInfo); } @Override @@ -188,7 +230,7 @@ public void register(Class type, long userTypeId) { TypeInfo typeInfo = classInfoMap.get(type); if (type.isArray()) { buildTypeInfo(type); - registerGraalvmClass(type); + GraalvmSupport.registerClass(type); return; } Serializer serializer = null; @@ -288,20 +330,21 @@ private void register( String typeName, int typeId, int userTypeId) { - TypeInfo typeInfo = newTypeInfo(type, serializer, namespace, typeName, typeId, userTypeId); + TypeInfo currentTypeInfo = classInfoMap.get(type); + Serializer currentSerializer = + currentTypeInfo == null ? null : currentTypeInfo.getSerializer(); + TypeInfo typeInfo = + newTypeInfo(type, currentSerializer, namespace, typeName, typeId, userTypeId); String qualifiedName = qualifiedName(namespace, typeName); - qualifiedType2TypeInfo.put(qualifiedName, typeInfo); - extRegistry.registeredClasses.put(qualifiedName, type); - registerGraalvmClass(type); - if (serializer == null) { + if (serializer == null && typeInfo.getSerializer() == null) { if (type.isEnum()) { - typeInfo.serializer = new EnumSerializer(fory, (Class) type); + serializer = new EnumSerializer(config, (Class) type); } else { AtomicBoolean updated = new AtomicBoolean(false); AtomicReference ref = new AtomicReference(null); - typeInfo.serializer = + serializer = new DeferedLazySerializer.DeferredLazyObjectSerializer( - fory, + this, type, () -> { if (ref.get() == null) { @@ -309,10 +352,10 @@ private void register( getObjectSerializerClass( type, shareMeta, - fory.getConfig().isCodeGenEnabled(), - sc -> ref.set(Serializers.newSerializer(fory, type, sc))); - ref.set(Serializers.newSerializer(fory, type, c)); - if (!fory.getConfig().isAsyncCompilationEnabled()) { + config.isCodeGenEnabled(), + sc -> ref.set(Serializers.newSerializer(this, type, sc))); + ref.set(Serializers.newSerializer(this, type, c)); + if (!config.isAsyncCompilationEnabled()) { updated.set(true); } } @@ -320,6 +363,12 @@ private void register( }); } } + if (serializer != null) { + typeInfo.setSerializer(this, sharedRegistry.shareSerializer(type, serializer)); + } + qualifiedType2TypeInfo.put(qualifiedName, typeInfo); + extRegistry.registeredClasses.put(qualifiedName, type); + GraalvmSupport.registerClass(type); updateTypeInfo(type, typeInfo); } @@ -426,14 +475,14 @@ private TypeInfo newTypeInfo( String typeName, int typeId, int userTypeId) { - MetaStringRef nsBytes = metaStringResolver.getOrCreatePackageMetaStringBytes(namespace); - MetaStringRef classNameBytes = metaStringResolver.getOrCreateTypeNameMetaStringBytes(typeName); + MetaStringRef nsBytes = getOrCreatePackageMetaStringBytes(namespace); + MetaStringRef classNameBytes = getOrCreateTypeNameMetaStringBytes(typeName); return new TypeInfo(type, nsBytes, classNameBytes, false, serializer, typeId, userTypeId); } public void registerSerializer(Class type, Class serializerClass) { checkRegisterAllowed(); - registerSerializer(type, Serializers.newSerializer(fory, type, serializerClass)); + registerSerializer(type, Serializers.newSerializer(this, type, serializerClass)); } public void registerSerializer(Class type, Serializer serializer) { @@ -450,8 +499,16 @@ public void registerSerializer(Class type, Serializer serializer) { } else if (foryId == Types.NAMED_STRUCT || foryId == Types.NAMED_COMPATIBLE_STRUCT) { foryId = Types.NAMED_EXT; } - typeInfo = typeInfo.copy(foryId); - typeInfo.serializer = serializer; + typeInfo = + new TypeInfo( + typeInfo.cls, + typeInfo.namespaceBytes, + typeInfo.typeNameBytes, + typeInfo.isDynamicGeneratedClass, + typeInfo.serializer, + foryId, + typeInfo.userTypeId); + typeInfo.setSerializer(this, sharedRegistry.setSerializer(type, serializer)); updateTypeInfo(type, typeInfo); if (typeInfo.typeNameBytes != null) { String qualifiedName = qualifiedName(typeInfo.decodeNamespace(), typeInfo.decodeTypeName()); @@ -473,17 +530,39 @@ public void registerInternalSerializer(Class type, Serializer serializer) || type == Character[].class) { return; } - TypeInfo typeInfo = classInfoMap.get(type); + int typeId = determineTypeIdForClass(type); + TypeInfo currentTypeInfo = getCachedTypeInfo(type); + TypeInfo typeInfo = currentTypeInfo; if (typeInfo != null) { - if (typeInfo.serializer == null) { - typeInfo.serializer = serializer; - } + typeInfo = + new TypeInfo( + typeInfo.cls, + typeInfo.namespaceBytes, + typeInfo.typeNameBytes, + typeInfo.isDynamicGeneratedClass, + typeInfo.serializer, + typeInfo.getTypeId(), + typeInfo.getUserTypeId()); + typeInfo.setSerializer(this, sharedRegistry.shareSerializer(type, serializer)); + updateTypeInfo(type, typeInfo); return; } - // Determine appropriate type ID based on the type - int typeId = determineTypeIdForClass(type); - typeInfo = newTypeInfo(type, serializer, typeId); - classInfoMap.put(type, typeInfo); + typeInfo = newTypeInfo(type, null, typeId); + typeInfo.setSerializer(this, sharedRegistry.shareSerializer(type, serializer)); + updateTypeInfo(type, typeInfo); + } + + private TypeInfo getCachedTypeInfo(Class cls) { + TypeInfo typeInfo = classInfoMap.get(cls); + if (typeInfo != null && typeInfo.typeNameBytes != null) { + String qualifiedName = qualifiedName(typeInfo.decodeNamespace(), typeInfo.decodeTypeName()); + qualifiedType2TypeInfo.put(qualifiedName, typeInfo); + TypeNameBytes typeNameBytes = + new TypeNameBytes( + typeInfo.namespaceBytes.encoded.hash, typeInfo.typeNameBytes.encoded.hash); + compositeClassNameBytes2TypeInfo.put(typeNameBytes, typeInfo); + } + return typeInfo; } /** @@ -501,7 +580,7 @@ private int determineTypeIdForClass(Class type) { if (type.isArray()) { Class componentType = type.getComponentType(); if (componentType.isPrimitive()) { - int elemTypeId = Types.getTypeId(fory, componentType); + int elemTypeId = Types.getTypeId(this, componentType); return Types.getPrimitiveArrayTypeId(elemTypeId); } return Types.LIST; @@ -521,7 +600,7 @@ private int determineTypeIdForClass(Class type) { } private TypeInfo checkClassRegistration(Class type) { - TypeInfo typeInfo = classInfoMap.get(type); + TypeInfo typeInfo = getCachedTypeInfo(type); Preconditions.checkArgument( typeInfo != null && (typeInfo.typeId != 0 || !type.getSimpleName().equals(typeInfo.decodeTypeName())), @@ -600,7 +679,7 @@ public boolean isMonomorphic(Descriptor descriptor) { return false; } byte typeIdByte = getInternalTypeId(rawType); - if (fory.isCompatible()) { + if (isCompatible()) { return !Types.isUserDefinedType(typeIdByte) && typeIdByte != Types.UNKNOWN; } return typeIdByte != Types.UNKNOWN; @@ -659,8 +738,8 @@ public boolean isBuildIn(Descriptor descriptor) { @Override public TypeInfo getTypeInfo(Class cls) { - TypeInfo typeInfo = classInfoMap.get(cls); - if (typeInfo == null || typeInfo.serializer == null) { + TypeInfo typeInfo = getCachedTypeInfo(cls); + if (typeInfo == null) { typeInfo = buildTypeInfo(cls); } return typeInfo; @@ -671,40 +750,52 @@ public TypeInfo getTypeInfo(Class cls, boolean createIfAbsent) { if (createIfAbsent) { return getTypeInfo(cls); } - return classInfoMap.get(cls); + return getCachedTypeInfo(cls); } public TypeInfo getTypeInfo(Class cls, TypeInfoHolder classInfoHolder) { TypeInfo typeInfo = classInfoHolder.typeInfo; if (typeInfo.getCls() != cls) { - typeInfo = classInfoMap.get(cls); - if (typeInfo == null || typeInfo.serializer == null) { + typeInfo = getCachedTypeInfo(cls); + if (typeInfo == null) { typeInfo = buildTypeInfo(cls); } - classInfoHolder.typeInfo = typeInfo; } + classInfoHolder.typeInfo = typeInfo; assert typeInfo.serializer != null; return typeInfo; } - private TypeInfo buildTypeInfo(Class cls) { - TypeInfo typeInfo = classInfoMap.get(cls); - if (typeInfo != null && typeInfo.serializer != null) { - return typeInfo; + public TypeInfo getXtypeInfo(int typeId) { + return getInternalTypeInfoByTypeId(typeId); + } + + public TypeInfo getUserTypeInfo(String namespace, String typeName) { + String name = qualifiedName(namespace, typeName); + TypeInfo typeInfo = qualifiedType2TypeInfo.get(name); + if (typeInfo == null) { + return null; } - if (typeInfo != null) { - Class serializerClass = getSerializerClassFromGraalvmRegistry(cls); - if (serializerClass != null) { - typeInfo.setSerializer(this, Serializers.newSerializer(fory, cls, serializerClass)); - return typeInfo; - } + return getCachedTypeInfo(typeInfo.getCls()); + } + + public TypeInfo getUserTypeInfo(int userTypeId) { + TypeInfo typeInfo = userTypeIdToTypeInfo.get(userTypeId); + if (typeInfo == null) { + return null; } + return getCachedTypeInfo(typeInfo.getCls()); + } + + // buildGenericType methods are inherited from TypeResolver + + private TypeInfo buildTypeInfo(Class cls) { Serializer serializer; int typeId; if (isSet(cls)) { if (cls.isAssignableFrom(HashSet.class)) { cls = HashSet.class; - serializer = new HashSetSerializer(fory); + serializer = new HashSetSerializer(this); } else { serializer = getCollectionSerializer(cls); } @@ -712,32 +803,32 @@ private TypeInfo buildTypeInfo(Class cls) { } else if (isCollection(cls)) { if (cls.isAssignableFrom(ArrayList.class)) { cls = ArrayList.class; - serializer = new ArrayListSerializer(fory); + serializer = new ArrayListSerializer(this); } else { serializer = getCollectionSerializer(cls); } typeId = Types.LIST; } else if (cls.isArray() && !cls.getComponentType().isPrimitive()) { - serializer = new ArraySerializers.ObjectArraySerializer(fory, cls); + serializer = new ArraySerializers.ObjectArraySerializer(this, cls); typeId = Types.LIST; } else if (isMap(cls)) { if (cls.isAssignableFrom(HashMap.class)) { cls = HashMap.class; - serializer = new HashMapSerializer(fory); + serializer = new HashMapSerializer(this); } else { - TypeInfo cachedTypeInfo = classInfoMap.get(cls); - if (cachedTypeInfo != null - && cachedTypeInfo.serializer != null - && cachedTypeInfo.serializer instanceof MapLikeSerializer - && ((MapLikeSerializer) cachedTypeInfo.serializer).supportCodegenHook()) { - serializer = cachedTypeInfo.serializer; + TypeInfo typeInfo = classInfoMap.get(cls); + if (typeInfo != null + && typeInfo.serializer != null + && typeInfo.serializer instanceof MapLikeSerializer + && ((MapLikeSerializer) typeInfo.serializer).supportCodegenHook()) { + serializer = typeInfo.serializer; } else { - serializer = new MapSerializer(fory, cls); + serializer = new MapSerializer(this, cls); } } typeId = Types.MAP; } else if (UnknownClass.class.isAssignableFrom(cls)) { - serializer = UnknownClassSerializers.getSerializer(fory, "Unknown", cls); + serializer = UnknownClassSerializers.getSerializer(this, "Unknown", cls); if (cls.isEnum()) { typeId = Types.ENUM; } else { @@ -756,26 +847,12 @@ private TypeInfo buildTypeInfo(Class cls) { throw new ClassUnregisteredException(cls); } } - TypeInfo info = newTypeInfo(cls, serializer, typeId); + TypeInfo info = newTypeInfo(cls, null, typeId); + info.setSerializer(this, sharedRegistry.shareSerializer(cls, serializer)); classInfoMap.put(cls, info); return info; } - public TypeInfo getXtypeInfo(int typeId) { - return getInternalTypeInfoByTypeId(typeId); - } - - public TypeInfo getUserTypeInfo(String namespace, String typeName) { - String name = qualifiedName(namespace, typeName); - return qualifiedType2TypeInfo.get(name); - } - - public TypeInfo getUserTypeInfo(int userTypeId) { - return userTypeIdToTypeInfo.get(userTypeId); - } - - // buildGenericType methods are inherited from TypeResolver - private Serializer getCollectionSerializer(Class cls) { TypeInfo typeInfo = classInfoMap.get(cls); if (typeInfo != null @@ -784,122 +861,152 @@ private Serializer getCollectionSerializer(Class cls) { && ((CollectionLikeSerializer) (typeInfo.serializer)).supportCodegenHook()) { return typeInfo.serializer; } - return new CollectionSerializer(fory, cls); + return new CollectionSerializer(this, cls); } private void registerDefaultTypes() { + Config config = this.config; // Boolean types registerType( - Types.BOOL, Boolean.class, new PrimitiveSerializers.BooleanSerializer(fory, Boolean.class)); + Types.BOOL, + Boolean.class, + new PrimitiveSerializers.BooleanSerializer(config, Boolean.class)); registerType( - Types.BOOL, boolean.class, new PrimitiveSerializers.BooleanSerializer(fory, boolean.class)); - registerType(Types.BOOL, AtomicBoolean.class, new Serializers.AtomicBooleanSerializer(fory)); + Types.BOOL, + boolean.class, + new PrimitiveSerializers.BooleanSerializer(config, boolean.class)); + registerType(Types.BOOL, AtomicBoolean.class, new Serializers.AtomicBooleanSerializer(config)); // Byte types registerType( - Types.UINT8, Byte.class, new PrimitiveSerializers.ByteSerializer(fory, Byte.class)); + Types.UINT8, Byte.class, new PrimitiveSerializers.ByteSerializer(config, Byte.class)); registerType( - Types.UINT8, byte.class, new PrimitiveSerializers.ByteSerializer(fory, byte.class)); - registerType(Types.INT8, Byte.class, new PrimitiveSerializers.ByteSerializer(fory, Byte.class)); - registerType(Types.INT8, byte.class, new PrimitiveSerializers.ByteSerializer(fory, byte.class)); - registerType(Types.UINT8, Uint8.class, new UnsignedSerializers.Uint8Serializer(fory)); + Types.UINT8, byte.class, new PrimitiveSerializers.ByteSerializer(config, byte.class)); + registerType( + Types.INT8, Byte.class, new PrimitiveSerializers.ByteSerializer(config, Byte.class)); + registerType( + Types.INT8, byte.class, new PrimitiveSerializers.ByteSerializer(config, byte.class)); + registerType(Types.UINT8, Uint8.class, new UnsignedSerializers.Uint8Serializer(config)); // Short types registerType( - Types.UINT16, Short.class, new PrimitiveSerializers.ShortSerializer(fory, Short.class)); + Types.UINT16, Short.class, new PrimitiveSerializers.ShortSerializer(config, Short.class)); registerType( - Types.UINT16, short.class, new PrimitiveSerializers.ShortSerializer(fory, short.class)); + Types.UINT16, short.class, new PrimitiveSerializers.ShortSerializer(config, short.class)); registerType( - Types.INT16, Short.class, new PrimitiveSerializers.ShortSerializer(fory, Short.class)); + Types.INT16, Short.class, new PrimitiveSerializers.ShortSerializer(config, Short.class)); registerType( - Types.INT16, short.class, new PrimitiveSerializers.ShortSerializer(fory, short.class)); - registerType(Types.UINT16, Uint16.class, new UnsignedSerializers.Uint16Serializer(fory)); + Types.INT16, short.class, new PrimitiveSerializers.ShortSerializer(config, short.class)); + registerType(Types.UINT16, Uint16.class, new UnsignedSerializers.Uint16Serializer(config)); // Integer types registerType( - Types.UINT32, Integer.class, new PrimitiveSerializers.IntSerializer(fory, Integer.class)); - registerType(Types.UINT32, int.class, new PrimitiveSerializers.IntSerializer(fory, int.class)); - registerType(Types.UINT32, AtomicInteger.class, new Serializers.AtomicIntegerSerializer(fory)); + Types.UINT32, Integer.class, new PrimitiveSerializers.IntSerializer(config, Integer.class)); + registerType( + Types.UINT32, int.class, new PrimitiveSerializers.IntSerializer(config, int.class)); registerType( - Types.INT32, Integer.class, new PrimitiveSerializers.IntSerializer(fory, Integer.class)); - registerType(Types.INT32, int.class, new PrimitiveSerializers.IntSerializer(fory, int.class)); - registerType(Types.INT32, AtomicInteger.class, new Serializers.AtomicIntegerSerializer(fory)); + Types.UINT32, AtomicInteger.class, new Serializers.AtomicIntegerSerializer(config)); registerType( - Types.VAR_UINT32, Integer.class, new PrimitiveSerializers.VarUint32Serializer(fory)); - registerType(Types.VAR_UINT32, int.class, new PrimitiveSerializers.VarUint32Serializer(fory)); + Types.INT32, Integer.class, new PrimitiveSerializers.IntSerializer(config, Integer.class)); + registerType(Types.INT32, int.class, new PrimitiveSerializers.IntSerializer(config, int.class)); + registerType(Types.INT32, AtomicInteger.class, new Serializers.AtomicIntegerSerializer(config)); registerType( - Types.VARINT32, Integer.class, new PrimitiveSerializers.IntSerializer(fory, Integer.class)); + Types.VAR_UINT32, Integer.class, new PrimitiveSerializers.VarUint32Serializer(config)); + registerType(Types.VAR_UINT32, int.class, new PrimitiveSerializers.VarUint32Serializer(config)); registerType( - Types.VARINT32, int.class, new PrimitiveSerializers.IntSerializer(fory, int.class)); + Types.VARINT32, + Integer.class, + new PrimitiveSerializers.IntSerializer(config, Integer.class)); registerType( - Types.VARINT32, AtomicInteger.class, new Serializers.AtomicIntegerSerializer(fory)); - registerType(Types.UINT32, Uint32.class, new UnsignedSerializers.Uint32Serializer(fory)); - registerType(Types.UINT64, Uint64.class, new UnsignedSerializers.Uint64Serializer(fory)); + Types.VARINT32, int.class, new PrimitiveSerializers.IntSerializer(config, int.class)); + registerType( + Types.VARINT32, AtomicInteger.class, new Serializers.AtomicIntegerSerializer(config)); + registerType(Types.UINT32, Uint32.class, new UnsignedSerializers.Uint32Serializer(config)); + registerType(Types.UINT64, Uint64.class, new UnsignedSerializers.Uint64Serializer(config)); // Long types registerType( - Types.UINT64, Long.class, new PrimitiveSerializers.LongSerializer(fory, Long.class)); + Types.UINT64, Long.class, new PrimitiveSerializers.LongSerializer(config, Long.class)); + registerType( + Types.UINT64, long.class, new PrimitiveSerializers.LongSerializer(config, long.class)); + registerType(Types.UINT64, AtomicLong.class, new Serializers.AtomicLongSerializer(config)); + registerType( + Types.TAGGED_UINT64, + Long.class, + new PrimitiveSerializers.LongSerializer(config, Long.class)); registerType( - Types.UINT64, long.class, new PrimitiveSerializers.LongSerializer(fory, long.class)); - registerType(Types.UINT64, AtomicLong.class, new Serializers.AtomicLongSerializer(fory)); + Types.TAGGED_UINT64, + long.class, + new PrimitiveSerializers.LongSerializer(config, long.class)); registerType( - Types.TAGGED_UINT64, Long.class, new PrimitiveSerializers.LongSerializer(fory, Long.class)); + Types.TAGGED_UINT64, AtomicLong.class, new Serializers.AtomicLongSerializer(config)); registerType( - Types.TAGGED_UINT64, long.class, new PrimitiveSerializers.LongSerializer(fory, long.class)); - registerType(Types.TAGGED_UINT64, AtomicLong.class, new Serializers.AtomicLongSerializer(fory)); + Types.INT64, Long.class, new PrimitiveSerializers.LongSerializer(config, Long.class)); registerType( - Types.INT64, Long.class, new PrimitiveSerializers.LongSerializer(fory, Long.class)); + Types.INT64, long.class, new PrimitiveSerializers.LongSerializer(config, long.class)); + registerType(Types.INT64, AtomicLong.class, new Serializers.AtomicLongSerializer(config)); registerType( - Types.INT64, long.class, new PrimitiveSerializers.LongSerializer(fory, long.class)); - registerType(Types.INT64, AtomicLong.class, new Serializers.AtomicLongSerializer(fory)); + Types.TAGGED_INT64, + Long.class, + new PrimitiveSerializers.LongSerializer(config, Long.class)); registerType( - Types.TAGGED_INT64, Long.class, new PrimitiveSerializers.LongSerializer(fory, Long.class)); + Types.TAGGED_INT64, + long.class, + new PrimitiveSerializers.LongSerializer(config, long.class)); registerType( - Types.TAGGED_INT64, long.class, new PrimitiveSerializers.LongSerializer(fory, long.class)); - registerType(Types.TAGGED_INT64, AtomicLong.class, new Serializers.AtomicLongSerializer(fory)); - registerType(Types.VAR_UINT64, Long.class, new PrimitiveSerializers.VarUint64Serializer(fory)); - registerType(Types.VAR_UINT64, long.class, new PrimitiveSerializers.VarUint64Serializer(fory)); + Types.TAGGED_INT64, AtomicLong.class, new Serializers.AtomicLongSerializer(config)); registerType( - Types.VARINT64, Long.class, new PrimitiveSerializers.LongSerializer(fory, Long.class)); + Types.VAR_UINT64, Long.class, new PrimitiveSerializers.VarUint64Serializer(config)); registerType( - Types.VARINT64, long.class, new PrimitiveSerializers.LongSerializer(fory, long.class)); - registerType(Types.VARINT64, AtomicLong.class, new Serializers.AtomicLongSerializer(fory)); + Types.VAR_UINT64, long.class, new PrimitiveSerializers.VarUint64Serializer(config)); + registerType( + Types.VARINT64, Long.class, new PrimitiveSerializers.LongSerializer(config, Long.class)); + registerType( + Types.VARINT64, long.class, new PrimitiveSerializers.LongSerializer(config, long.class)); + registerType(Types.VARINT64, AtomicLong.class, new Serializers.AtomicLongSerializer(config)); // Float types registerType( - Types.FLOAT32, Float.class, new PrimitiveSerializers.FloatSerializer(fory, Float.class)); + Types.FLOAT32, Float.class, new PrimitiveSerializers.FloatSerializer(config, Float.class)); registerType( - Types.FLOAT32, float.class, new PrimitiveSerializers.FloatSerializer(fory, float.class)); + Types.FLOAT32, float.class, new PrimitiveSerializers.FloatSerializer(config, float.class)); registerType( Types.FLOAT16, Float16.class, - new PrimitiveSerializers.Float16Serializer(fory, Float16.class)); + new PrimitiveSerializers.Float16Serializer(config, Float16.class)); registerType( - Types.FLOAT64, Double.class, new PrimitiveSerializers.DoubleSerializer(fory, Double.class)); + Types.FLOAT64, + Double.class, + new PrimitiveSerializers.DoubleSerializer(config, Double.class)); registerType( - Types.FLOAT64, double.class, new PrimitiveSerializers.DoubleSerializer(fory, double.class)); + Types.FLOAT64, + double.class, + new PrimitiveSerializers.DoubleSerializer(config, double.class)); // String types - registerType(Types.STRING, String.class, fory.getStringSerializer()); - registerType(Types.STRING, StringBuilder.class, new Serializers.StringBuilderSerializer(fory)); - registerType(Types.STRING, StringBuffer.class, new Serializers.StringBufferSerializer(fory)); + registerType( + Types.STRING, String.class, new org.apache.fory.serializer.StringSerializer(config)); + registerType( + Types.STRING, StringBuilder.class, new Serializers.StringBuilderSerializer(config)); + registerType(Types.STRING, StringBuffer.class, new Serializers.StringBufferSerializer(config)); // Time types - registerType(Types.DURATION, Duration.class, new TimeSerializers.DurationSerializer(fory)); - registerType(Types.TIMESTAMP, Instant.class, new TimeSerializers.InstantSerializer(fory)); - registerType(Types.TIMESTAMP, Date.class, new TimeSerializers.DateSerializer(fory)); - registerType(Types.TIMESTAMP, java.sql.Date.class, new TimeSerializers.SqlDateSerializer(fory)); - registerType(Types.TIMESTAMP, Timestamp.class, new TimeSerializers.TimestampSerializer(fory)); + registerType(Types.DURATION, Duration.class, new TimeSerializers.DurationSerializer(config)); + registerType(Types.TIMESTAMP, Instant.class, new TimeSerializers.InstantSerializer(config)); + registerType(Types.TIMESTAMP, Date.class, new TimeSerializers.DateSerializer(config)); + registerType( + Types.TIMESTAMP, java.sql.Date.class, new TimeSerializers.SqlDateSerializer(config)); + registerType(Types.TIMESTAMP, Timestamp.class, new TimeSerializers.TimestampSerializer(config)); registerType( - Types.TIMESTAMP, LocalDateTime.class, new TimeSerializers.LocalDateTimeSerializer(fory)); - registerType(Types.DATE, LocalDate.class, new TimeSerializers.LocalDateSerializer(fory)); + Types.TIMESTAMP, LocalDateTime.class, new TimeSerializers.LocalDateTimeSerializer(config)); + registerType(Types.DATE, LocalDate.class, new TimeSerializers.LocalDateSerializer(config)); // Decimal types - registerType(Types.DECIMAL, BigDecimal.class, new Serializers.BigDecimalSerializer(fory)); - registerType(Types.DECIMAL, BigInteger.class, new Serializers.BigIntegerSerializer(fory)); + registerType(Types.DECIMAL, BigDecimal.class, new Serializers.BigDecimalSerializer(config)); + registerType(Types.DECIMAL, BigInteger.class, new Serializers.BigIntegerSerializer(config)); // Binary types - registerType(Types.BINARY, byte[].class, new ArraySerializers.ByteArraySerializer(fory)); + registerType(Types.BINARY, byte[].class, new ArraySerializers.ByteArraySerializer(this)); @SuppressWarnings("unchecked") Class heapByteBufferClass = (Class) Platform.HEAP_BYTE_BUFFER_CLASS; @@ -907,7 +1014,7 @@ private void registerDefaultTypes() { Types.BINARY, Platform.HEAP_BYTE_BUFFER_CLASS, new org.apache.fory.serializer.BufferSerializers.ByteBufferSerializer( - fory, heapByteBufferClass)); + this, heapByteBufferClass)); @SuppressWarnings("unchecked") Class directByteBufferClass = (Class) Platform.DIRECT_BYTE_BUFFER_CLASS; @@ -915,94 +1022,103 @@ private void registerDefaultTypes() { Types.BINARY, Platform.DIRECT_BYTE_BUFFER_CLASS, new org.apache.fory.serializer.BufferSerializers.ByteBufferSerializer( - fory, directByteBufferClass)); + this, directByteBufferClass)); // Primitive arrays registerType( - Types.BOOL_ARRAY, boolean[].class, new ArraySerializers.BooleanArraySerializer(fory)); - registerType(Types.INT16_ARRAY, short[].class, new ArraySerializers.ShortArraySerializer(fory)); - registerType(Types.INT32_ARRAY, int[].class, new ArraySerializers.IntArraySerializer(fory)); - registerType(Types.INT64_ARRAY, long[].class, new ArraySerializers.LongArraySerializer(fory)); + Types.BOOL_ARRAY, boolean[].class, new ArraySerializers.BooleanArraySerializer(this)); + registerType(Types.INT16_ARRAY, short[].class, new ArraySerializers.ShortArraySerializer(this)); + registerType(Types.INT32_ARRAY, int[].class, new ArraySerializers.IntArraySerializer(this)); + registerType(Types.INT64_ARRAY, long[].class, new ArraySerializers.LongArraySerializer(this)); registerType( - Types.FLOAT32_ARRAY, float[].class, new ArraySerializers.FloatArraySerializer(fory)); + Types.FLOAT32_ARRAY, float[].class, new ArraySerializers.FloatArraySerializer(this)); registerType( - Types.FLOAT64_ARRAY, double[].class, new ArraySerializers.DoubleArraySerializer(fory)); + Types.FLOAT64_ARRAY, double[].class, new ArraySerializers.DoubleArraySerializer(this)); registerType( - Types.FLOAT16_ARRAY, Float16[].class, new ArraySerializers.Float16ArraySerializer(fory)); + Types.FLOAT16_ARRAY, Float16[].class, new ArraySerializers.Float16ArraySerializer(this)); // Primitive lists registerType( - Types.BOOL_ARRAY, BoolList.class, new PrimitiveListSerializers.BoolListSerializer(fory)); + Types.BOOL_ARRAY, BoolList.class, new PrimitiveListSerializers.BoolListSerializer(config)); registerType( - Types.INT8_ARRAY, Int8List.class, new PrimitiveListSerializers.Int8ListSerializer(fory)); + Types.INT8_ARRAY, Int8List.class, new PrimitiveListSerializers.Int8ListSerializer(config)); registerType( - Types.INT16_ARRAY, Int16List.class, new PrimitiveListSerializers.Int16ListSerializer(fory)); + Types.INT16_ARRAY, + Int16List.class, + new PrimitiveListSerializers.Int16ListSerializer(config)); registerType( - Types.INT32_ARRAY, Int32List.class, new PrimitiveListSerializers.Int32ListSerializer(fory)); + Types.INT32_ARRAY, + Int32List.class, + new PrimitiveListSerializers.Int32ListSerializer(config)); registerType( - Types.INT64_ARRAY, Int64List.class, new PrimitiveListSerializers.Int64ListSerializer(fory)); + Types.INT64_ARRAY, + Int64List.class, + new PrimitiveListSerializers.Int64ListSerializer(config)); registerType( - Types.UINT8_ARRAY, Uint8List.class, new PrimitiveListSerializers.Uint8ListSerializer(fory)); + Types.UINT8_ARRAY, + Uint8List.class, + new PrimitiveListSerializers.Uint8ListSerializer(config)); registerType( Types.UINT16_ARRAY, Uint16List.class, - new PrimitiveListSerializers.Uint16ListSerializer(fory)); + new PrimitiveListSerializers.Uint16ListSerializer(config)); registerType( Types.UINT32_ARRAY, Uint32List.class, - new PrimitiveListSerializers.Uint32ListSerializer(fory)); + new PrimitiveListSerializers.Uint32ListSerializer(config)); registerType( Types.UINT64_ARRAY, Uint64List.class, - new PrimitiveListSerializers.Uint64ListSerializer(fory)); + new PrimitiveListSerializers.Uint64ListSerializer(config)); registerType( Types.FLOAT32_ARRAY, Float32List.class, - new PrimitiveListSerializers.Float32ListSerializer(fory)); + new PrimitiveListSerializers.Float32ListSerializer(config)); registerType( Types.FLOAT64_ARRAY, Float64List.class, - new PrimitiveListSerializers.Float64ListSerializer(fory)); + new PrimitiveListSerializers.Float64ListSerializer(config)); registerType( Types.FLOAT16_ARRAY, Float16List.class, - new PrimitiveListSerializers.Float16ListSerializer(fory)); + new PrimitiveListSerializers.Float16ListSerializer(config)); // Collections - registerType(Types.LIST, ArrayList.class, new ArrayListSerializer(fory)); + registerType(Types.LIST, ArrayList.class, new ArrayListSerializer(this)); registerType( Types.LIST, Object[].class, - new ArraySerializers.ObjectArraySerializer(fory, Object[].class)); - registerType(Types.LIST, List.class, new XlangListDefaultSerializer(fory, List.class)); + new ArraySerializers.ObjectArraySerializer(this, Object[].class)); + registerType(Types.LIST, List.class, new XlangListDefaultSerializer(this, List.class)); registerType( - Types.LIST, Collection.class, new XlangListDefaultSerializer(fory, Collection.class)); + Types.LIST, Collection.class, new XlangListDefaultSerializer(this, Collection.class)); // Sets - registerType(Types.SET, HashSet.class, new HashSetSerializer(fory)); + registerType(Types.SET, HashSet.class, new HashSetSerializer(this)); registerType( Types.SET, LinkedHashSet.class, new org.apache.fory.serializer.collection.CollectionSerializers.LinkedHashSetSerializer( - fory)); - registerType(Types.SET, Set.class, new XlangSetDefaultSerializer(fory, Set.class)); + this)); + registerType(Types.SET, Set.class, new XlangSetDefaultSerializer(this, Set.class)); // Maps registerType( Types.MAP, HashMap.class, - new org.apache.fory.serializer.collection.MapSerializers.HashMapSerializer(fory)); + new org.apache.fory.serializer.collection.MapSerializers.HashMapSerializer(this)); registerType( Types.MAP, LinkedHashMap.class, - new org.apache.fory.serializer.collection.MapSerializers.LinkedHashMapSerializer(fory)); - registerType(Types.MAP, Map.class, new XlangMapSerializer(fory, Map.class)); + new org.apache.fory.serializer.collection.MapSerializers.LinkedHashMapSerializer(this)); + registerType(Types.MAP, Map.class, new XlangMapSerializer(this, Map.class)); registerUnionTypes(); } private void registerType(int xtypeId, Class type, Serializer serializer) { - TypeInfo typeInfo = newTypeInfo(type, serializer, xtypeId, INVALID_USER_TYPE_ID); + TypeInfo typeInfo = newTypeInfo(type, null, xtypeId, INVALID_USER_TYPE_ID); + typeInfo.setSerializer(this, sharedRegistry.shareSerializer(type, serializer)); classInfoMap.put(type, typeInfo); if (getInternalTypeInfoByTypeId(xtypeId) == null) { putInternalTypeInfo(xtypeId, typeInfo); @@ -1023,8 +1139,9 @@ private void registerUnionTypes() { @SuppressWarnings("unchecked") Class unionCls = (Class) cls; - UnionSerializer serializer = new UnionSerializer(fory, unionCls); - TypeInfo typeInfo = newTypeInfo(cls, serializer, Types.UNION, INVALID_USER_TYPE_ID); + UnionSerializer serializer = new UnionSerializer(this, unionCls); + TypeInfo typeInfo = newTypeInfo(cls, null, Types.UNION, INVALID_USER_TYPE_ID); + typeInfo.setSerializer(this, sharedRegistry.shareSerializer(cls, serializer)); classInfoMap.put(cls, typeInfo); } putInternalTypeInfo(Types.UNION, classInfoMap.get(org.apache.fory.type.union.Union.class)); @@ -1040,7 +1157,7 @@ public TypeInfo writeTypeInfo(MemoryBuffer buffer, Object obj) { protected TypeDef buildTypeDef(TypeInfo typeInfo) { TypeDef typeDef = cacheTypeDef( - typeDefMap.computeIfAbsent(typeInfo.cls, cls -> TypeDef.buildTypeDef(fory, cls))); + typeDefMap.computeIfAbsent(typeInfo.cls, cls -> TypeDef.buildTypeDef(this, cls))); typeInfo.typeDef = typeDef; return typeDef; } @@ -1057,25 +1174,32 @@ public Serializer getRawSerializer(Class cls) { @Override public void setSerializer(Class cls, Serializer serializer) { - getTypeInfo(cls).serializer = serializer; + TypeInfo currentTypeInfo = getTypeInfo(cls); + TypeInfo typeInfo = + new TypeInfo( + currentTypeInfo.cls, + currentTypeInfo.namespaceBytes, + currentTypeInfo.typeNameBytes, + currentTypeInfo.isDynamicGeneratedClass, + currentTypeInfo.serializer, + currentTypeInfo.getTypeId(), + currentTypeInfo.getUserTypeId()); + typeInfo.setSerializer(this, sharedRegistry.setSerializer(cls, serializer)); + updateTypeInfo(cls, typeInfo); } @Override public void setSerializerIfAbsent(Class cls, Serializer serializer) { TypeInfo typeInfo = classInfoMap.get(cls); Preconditions.checkNotNull(typeInfo); - if (typeInfo.serializer == null) { - typeInfo.serializer = serializer; - } + Preconditions.checkNotNull(typeInfo.serializer); } // nilTypeInfo and nilTypeInfoHolder are inherited from TypeResolver @Override - protected TypeInfo getListTypeInfo() { - fory.incReadDepth(); - GenericType genericType = generics.nextGenericType(); - fory.decDepth(); + protected TypeInfo getListTypeInfo(ReadContext readContext) { + GenericType genericType = readContext.getGenerics().nextGenericType(readContext.getDepth() + 1); if (genericType != null) { return getOrBuildTypeInfo(genericType.getCls()); } @@ -1083,10 +1207,8 @@ protected TypeInfo getListTypeInfo() { } @Override - protected TypeInfo getTimestampTypeInfo() { - fory.incReadDepth(); - GenericType genericType = generics.nextGenericType(); - fory.decDepth(); + protected TypeInfo getTimestampTypeInfo(ReadContext readContext) { + GenericType genericType = readContext.getGenerics().nextGenericType(readContext.getDepth() + 1); if (genericType != null) { return getOrBuildTypeInfo(genericType.getCls()); } @@ -1189,7 +1311,7 @@ private TypeInfo populateBytesToTypeInfo( NOT_SUPPORT_XLANG, INVALID_USER_TYPE_ID); if (UnknownClass.class.isAssignableFrom(TypeUtils.getComponentIfArray(type))) { - typeInfo.serializer = UnknownClassSerializers.getSerializer(fory, qualifiedName, type); + typeInfo.serializer = UnknownClassSerializers.getSerializer(this, qualifiedName, type); } } compositeClassNameBytes2TypeInfo.put(typeNameBytes, typeInfo); @@ -1218,7 +1340,7 @@ protected DescriptorGrouper configureDescriptorGrouper(DescriptorGrouper descrip private byte getInternalTypeId(Descriptor descriptor) { Class cls = descriptor.getRawType(); if (cls.isArray() && cls.getComponentType().isPrimitive()) { - return (byte) Types.getDescriptorTypeId(fory, descriptor); + return (byte) Types.getDescriptorTypeId(this, descriptor); } return getInternalTypeId(cls); } @@ -1279,15 +1401,20 @@ private boolean isEnum(int internalTypeId) { */ @Override public void ensureSerializersCompiled() { + if (GraalvmSupport.isGraalRuntime()) { + return; + } classInfoMap.forEach( (cls, classInfo) -> { - registerGraalvmClass(cls); + GraalvmSupport.registerClass(cls); if (classInfo.serializer != null) { // Trigger serializer initialization and resolution for deferred serializers if (classInfo.serializer instanceof DeferedLazySerializer.DeferredLazyObjectSerializer) { ((DeferedLazySerializer.DeferredLazyObjectSerializer) classInfo.serializer) .resolveSerializer(); + } else { + classInfo.serializer.getClass(); } } // For enums at GraalVM build time, also handle anonymous enum value classes @@ -1299,15 +1426,6 @@ public void ensureSerializersCompiled() { } } } - if (GraalvmSupport.isGraalBuildtime() && classInfo.serializer != null) { - getGraalvmClassRegistry() - .putSerializerClass(cls, getGraalvmSerializerClass(classInfo.serializer)); - } }); - if (GraalvmSupport.isGraalBuildtime()) { - clearGraalvmGeneratedTypeInfoSerializers(); - getGraalvmClassRegistry().clearResolvers(); - } - extRegistry.registeredTypeInfos.clear(); } } diff --git a/java/fory-core/src/main/java/org/apache/fory/serializer/AbstractObjectSerializer.java b/java/fory-core/src/main/java/org/apache/fory/serializer/AbstractObjectSerializer.java index cd66bcd750..0b2190e5ab 100644 --- a/java/fory-core/src/main/java/org/apache/fory/serializer/AbstractObjectSerializer.java +++ b/java/fory-core/src/main/java/org/apache/fory/serializer/AbstractObjectSerializer.java @@ -26,6 +26,12 @@ import java.util.List; import java.util.stream.Collectors; import org.apache.fory.Fory; +import org.apache.fory.config.Config; +import org.apache.fory.context.CopyContext; +import org.apache.fory.context.ReadContext; +import org.apache.fory.context.RefReader; +import org.apache.fory.context.RefWriter; +import org.apache.fory.context.WriteContext; import org.apache.fory.logging.Logger; import org.apache.fory.logging.LoggerFactory; import org.apache.fory.memory.MemoryBuffer; @@ -36,7 +42,6 @@ import org.apache.fory.reflect.ReflectionUtils; import org.apache.fory.reflect.TypeRef; import org.apache.fory.resolver.RefMode; -import org.apache.fory.resolver.RefResolver; import org.apache.fory.resolver.TypeInfo; import org.apache.fory.resolver.TypeResolver; import org.apache.fory.serializer.FieldGroups.SerializationFieldInfo; @@ -55,39 +60,40 @@ public abstract class AbstractObjectSerializer extends Serializer { private static final Logger LOG = LoggerFactory.getLogger(AbstractObjectSerializer.class); - protected final RefResolver refResolver; + protected final Config config; protected final TypeResolver typeResolver; protected final boolean isRecord; protected final ObjectCreator objectCreator; private SerializationFieldInfo[] fieldInfos; private RecordInfo copyRecordInfo; - public AbstractObjectSerializer(Fory fory, Class type) { - this(fory, type, ObjectCreators.getObjectCreator(type)); + public AbstractObjectSerializer(TypeResolver typeResolver, Class type) { + this(typeResolver, type, ObjectCreators.getObjectCreator(type)); } - public AbstractObjectSerializer(Fory fory, Class type, ObjectCreator objectCreator) { - super(fory, type); - this.refResolver = fory.getRefResolver(); - this.typeResolver = fory.getTypeResolver(); + public AbstractObjectSerializer( + TypeResolver typeResolver, Class type, ObjectCreator objectCreator) { + super(typeResolver.getConfig(), type); + this.config = typeResolver.getConfig(); + this.typeResolver = typeResolver; this.isRecord = RecordUtils.isRecord(type); this.objectCreator = objectCreator; } static void writeField( - Fory fory, + WriteContext writeContext, TypeResolver typeResolver, - RefResolver refResolver, + RefWriter refWriter, SerializationFieldInfo fieldInfo, MemoryBuffer buffer, Object fieldValue) { - writeField(fory, typeResolver, refResolver, fieldInfo, fieldInfo.refMode, buffer, fieldValue); + writeField(writeContext, typeResolver, refWriter, fieldInfo, fieldInfo.refMode, buffer, fieldValue); } static void writeField( - Fory fory, + WriteContext writeContext, TypeResolver typeResolver, - RefResolver refResolver, + RefWriter refWriter, SerializationFieldInfo fieldInfo, RefMode refMode, MemoryBuffer buffer, @@ -95,8 +101,8 @@ static void writeField( if (fieldInfo.useDeclaredTypeInfo) { Serializer serializer = fieldInfo.typeInfo.getSerializer(); if (refMode == RefMode.TRACKING) { - if (!refResolver.writeRefOrNull(buffer, fieldValue)) { - serializer.write(buffer, fieldValue); + if (!refWriter.writeRefOrNull(buffer, fieldValue)) { + serializer.write(writeContext, fieldValue); } } else if (refMode == RefMode.NULL_ONLY) { if (fieldValue == null) { @@ -104,65 +110,68 @@ static void writeField( return; } buffer.writeByte(Fory.NOT_NULL_VALUE_FLAG); - serializer.write(buffer, fieldValue); + serializer.write(writeContext, fieldValue); } else { - serializer.write(buffer, fieldValue); + serializer.write(writeContext, fieldValue); } return; } if (refMode == RefMode.TRACKING) { - fory.writeRef(buffer, fieldValue, fieldInfo.classInfoHolder); + writeContext.writeRef(fieldValue, fieldInfo.classInfoHolder); } else if (refMode == RefMode.NULL_ONLY) { if (fieldValue == null) { buffer.writeByte(Fory.NULL_FLAG); return; } buffer.writeByte(Fory.NOT_NULL_VALUE_FLAG); - fory.writeNonRef(buffer, fieldValue, fieldInfo.classInfoHolder); + writeContext.writeNonRef(fieldValue, fieldInfo.classInfoHolder); } else { - fory.writeNonRef(buffer, fieldValue, fieldInfo.classInfoHolder); + writeContext.writeNonRef(fieldValue, fieldInfo.classInfoHolder); } } static Object readField( - Fory fory, + ReadContext readContext, TypeResolver typeResolver, - RefResolver refResolver, + RefReader refReader, SerializationFieldInfo fieldInfo, MemoryBuffer buffer) { - return readField(fory, typeResolver, refResolver, fieldInfo, fieldInfo.refMode, buffer); + return readField(readContext, typeResolver, refReader, fieldInfo, fieldInfo.refMode, buffer); } static Object readField( - Fory fory, + ReadContext readContext, TypeResolver typeResolver, - RefResolver refResolver, + RefReader refReader, SerializationFieldInfo fieldInfo, RefMode refMode, MemoryBuffer buffer) { if (fieldInfo.useDeclaredTypeInfo) { if (refMode == RefMode.TRACKING) { - return fory.readRef(buffer, fieldInfo.typeInfo); + return readContext.readRef(fieldInfo.typeInfo); } if (refMode != RefMode.NULL_ONLY || buffer.readByte() != Fory.NULL_FLAG) { - refResolver.preserveRefId(-1); - return fory.readNonRef(buffer, fieldInfo.typeInfo); + refReader.preserveRefId(-1); + return readContext.readNonRef(fieldInfo.typeInfo); } return null; } if (refMode == RefMode.TRACKING) { - int nextReadRefId = refResolver.tryPreserveRefId(buffer); + int nextReadRefId = refReader.tryPreserveRefId(buffer); if (nextReadRefId >= Fory.NOT_NULL_VALUE_FLAG) { Object value = - typeResolver.readTypeInfo(buffer, fieldInfo.type).getSerializer().read(buffer); - refResolver.setReadObject(nextReadRefId, value); + typeResolver + .readTypeInfo(readContext, fieldInfo.type) + .getSerializer() + .read(readContext); + refReader.setReadObject(nextReadRefId, value); return value; } - return refResolver.getReadObject(); + return refReader.getReadObject(); } if (refMode != RefMode.NULL_ONLY || buffer.readByte() != Fory.NULL_FLAG) { - TypeInfo typeInfo = typeResolver.readTypeInfo(buffer, fieldInfo.type); - return typeInfo.getSerializer().read(buffer, RefMode.NONE); + TypeInfo typeInfo = typeResolver.readTypeInfo(readContext, fieldInfo.type); + return typeInfo.getSerializer().read(readContext, RefMode.NONE); } return null; } @@ -172,9 +181,9 @@ static Object readField( * types, unsigned/compressed numbers, and common types like String with optimized fast paths. */ static void writeBuildInField( - Fory fory, + WriteContext writeContext, TypeResolver typeResolver, - RefResolver refResolver, + RefWriter refWriter, SerializationFieldInfo fieldInfo, MemoryBuffer buffer, Object obj) { @@ -185,7 +194,7 @@ static void writeBuildInField( } // Handle non-primitive fields based on refMode Object fieldValue = fieldInfo.fieldAccessor.getObject(obj); - writeBuildInFieldValue(fory, typeResolver, refResolver, fieldInfo, buffer, fieldValue); + writeBuildInFieldValue(writeContext, typeResolver, refWriter, fieldInfo, buffer, fieldValue); } /** @@ -193,12 +202,12 @@ static void writeBuildInField( * types like String with optimized fast paths. * *

    This method is the write counterpart of {@link #readBuildInFieldValue(Fory, TypeResolver, - * RefResolver, SerializationFieldInfo, MemoryBuffer)}. + * RefReader, SerializationFieldInfo, MemoryBuffer)}. */ static void writeBuildInFieldValue( - Fory fory, + WriteContext writeContext, TypeResolver typeResolver, - RefResolver refResolver, + RefWriter refWriter, SerializationFieldInfo fieldInfo, MemoryBuffer buffer, Object fieldValue) { @@ -206,7 +215,8 @@ static void writeBuildInFieldValue( // Handle non-primitive fields based on refMode if (refMode == RefMode.NONE) { // No null flag - write value directly - writeNotPrimitiveFieldValue(fory, typeResolver, refResolver, buffer, fieldValue, fieldInfo); + writeNotPrimitiveFieldValue( + writeContext, typeResolver, refWriter, buffer, fieldValue, fieldInfo); } else if (refMode == RefMode.NULL_ONLY) { // Write null flag, then value if not null if (fieldValue == null) { @@ -214,9 +224,9 @@ static void writeBuildInFieldValue( return; } buffer.writeByte(Fory.NOT_NULL_VALUE_FLAG); - writeNotPrimitiveFieldValue(fory, typeResolver, refResolver, buffer, fieldValue, fieldInfo); + writeNotPrimitiveFieldValue(writeContext, typeResolver, refWriter, buffer, fieldValue, fieldInfo); } else { - writeField(fory, typeResolver, refResolver, fieldInfo, buffer, fieldValue); + writeField(writeContext, typeResolver, refWriter, fieldInfo, buffer, fieldValue); } } @@ -363,9 +373,9 @@ static boolean writePrimitiveFieldValue( * Write field value to buffer. This method handle the situation which all fields are not null. */ static void writeNotPrimitiveFieldValue( - Fory fory, + WriteContext writeContext, TypeResolver typeResolver, - RefResolver refResolver, + RefWriter refWriter, MemoryBuffer buffer, Object fieldValue, SerializationFieldInfo fieldInfo) { @@ -377,7 +387,7 @@ static void writeNotPrimitiveFieldValue( // add time types serialization here. switch (fieldInfo.dispatchId) { case DispatchId.STRING: // fastpath for string. - fory.writeString(buffer, (String) fieldValue); + writeContext.writeString((String) fieldValue); return; case DispatchId.BOOL: buffer.writeBoolean((Boolean) fieldValue); @@ -455,20 +465,20 @@ static void writeNotPrimitiveFieldValue( buffer.writeInt16(((Float16) fieldValue).toBits()); return; default: - writeField(fory, typeResolver, refResolver, fieldInfo, RefMode.NONE, buffer, fieldValue); + writeField(writeContext, typeResolver, refWriter, fieldInfo, RefMode.NONE, buffer, fieldValue); } } static void writeContainerFieldValue( - Fory fory, + WriteContext writeContext, TypeResolver typeResolver, - RefResolver refResolver, + RefWriter refWriter, Generics generics, SerializationFieldInfo fieldInfo, MemoryBuffer buffer, Object fieldValue) { if (fieldInfo.refMode == RefMode.TRACKING) { - if (refResolver.writeRefOrNull(buffer, fieldValue)) { + if (refWriter.writeRefOrNull(buffer, fieldValue)) { return; } } else if (fieldInfo.refMode == RefMode.NULL_ONLY) { @@ -478,64 +488,63 @@ static void writeContainerFieldValue( } buffer.writeByte(Fory.NOT_NULL_VALUE_FLAG); } - generics.pushGenericType(fieldInfo.genericType); - if (fory.isCrossLanguage() && fieldInfo.useDeclaredTypeInfo) { + generics.pushGenericType(fieldInfo.genericType, writeContext.getDepth()); + if (writeContext.isCrossLanguage() && fieldInfo.useDeclaredTypeInfo) { TypeInfo typeInfo = typeResolver.getTypeInfo(fieldValue.getClass(), fieldInfo.classInfoHolder); - fory.writeData(buffer, typeInfo, fieldValue); + writeContext.writeData(typeInfo, fieldValue); } else if (fieldInfo.useDeclaredTypeInfo) { TypeInfo typeInfo = typeResolver.getTypeInfo(fieldValue.getClass(), fieldInfo.classInfoHolder); - fory.writeNonRef(buffer, fieldValue, typeInfo); + writeContext.writeNonRef(fieldValue, typeInfo); } else { - fory.writeNonRef(buffer, fieldValue, fieldInfo.classInfoHolder); + writeContext.writeNonRef(fieldValue, fieldInfo.classInfoHolder); } - generics.popGenericType(); + generics.popGenericType(writeContext.getDepth()); } /** * Read a container field value (Collection or Map). Handles reference tracking, nullable fields, * and pushes/pops generic type information for proper deserialization of parameterized types. * - * @param fory fory runtime * @param typeResolver resolver used for type metadata read - * @param refResolver resolver used for reference tracking + * @param refReader resolver used for reference tracking * @param generics the generics context for tracking parameterized types * @param fieldInfo the field metadata including generic type info and nullability * @param buffer the buffer to read from * @return the deserialized container field value, or null if the field is nullable and was null */ static Object readContainerFieldValue( - Fory fory, + ReadContext readContext, TypeResolver typeResolver, - RefResolver refResolver, + RefReader refReader, Generics generics, SerializationFieldInfo fieldInfo, MemoryBuffer buffer) { Object fieldValue; switch (fieldInfo.refMode) { case NONE: - refResolver.preserveRefId(-1); - generics.pushGenericType(fieldInfo.genericType); - fieldValue = readContainerFieldValueNoRef(fory, fieldInfo, buffer); - generics.popGenericType(); + refReader.preserveRefId(-1); + generics.pushGenericType(fieldInfo.genericType, readContext.getDepth()); + fieldValue = readContainerFieldValueNoRef(readContext, fieldInfo); + generics.popGenericType(readContext.getDepth()); break; case NULL_ONLY: { - refResolver.preserveRefId(-1); + refReader.preserveRefId(-1); byte headFlag = buffer.readByte(); if (headFlag == Fory.NULL_FLAG) { return null; } - generics.pushGenericType(fieldInfo.genericType); - fieldValue = readContainerFieldValueNoRef(fory, fieldInfo, buffer); - generics.popGenericType(); + generics.pushGenericType(fieldInfo.genericType, readContext.getDepth()); + fieldValue = readContainerFieldValueNoRef(readContext, fieldInfo); + generics.popGenericType(readContext.getDepth()); } break; case TRACKING: - generics.pushGenericType(fieldInfo.genericType); - fieldValue = readContainerFieldValueRef(fory, typeResolver, refResolver, fieldInfo, buffer); - generics.popGenericType(); + generics.pushGenericType(fieldInfo.genericType, readContext.getDepth()); + fieldValue = readContainerFieldValueRef(readContext, typeResolver, refReader, fieldInfo, buffer); + generics.popGenericType(readContext.getDepth()); break; default: throw new IllegalStateException("Unknown refMode: " + fieldInfo.refMode); @@ -544,58 +553,57 @@ static Object readContainerFieldValue( } private static Object readContainerFieldValueNoRef( - Fory fory, SerializationFieldInfo fieldInfo, MemoryBuffer buffer) { - if (fory.isCrossLanguage()) { - return fory.readNonRef(buffer, fieldInfo.containerTypeInfo); + ReadContext readContext, SerializationFieldInfo fieldInfo) { + if (readContext.getConfig().isXlang()) { + return readContext.readNonRef(fieldInfo.containerTypeInfo); } - return fory.readNonRef(buffer, fieldInfo.classInfoHolder); + return readContext.readNonRef(fieldInfo.classInfoHolder); } private static Object readContainerFieldValueRef( - Fory fory, + ReadContext readContext, TypeResolver typeResolver, - RefResolver refResolver, + RefReader refReader, SerializationFieldInfo fieldInfo, MemoryBuffer buffer) { - int nextReadRefId = refResolver.tryPreserveRefId(buffer); + int nextReadRefId = refReader.tryPreserveRefId(buffer); if (nextReadRefId >= Fory.NOT_NULL_VALUE_FLAG) { Object value; - if (fory.isCrossLanguage()) { - value = fory.readNonRef(buffer, fieldInfo.containerTypeInfo); + if (readContext.getConfig().isXlang()) { + value = readContext.readNonRef(fieldInfo.containerTypeInfo); } else { - value = fory.readData(buffer, typeResolver.readTypeInfo(buffer)); + value = readContext.readData(typeResolver.readTypeInfo(readContext)); } - refResolver.setReadObject(nextReadRefId, value); + refReader.setReadObject(nextReadRefId, value); return value; } - return refResolver.getReadObject(); + return refReader.getReadObject(); } /** * Read field value from buffer and return it. Handles primitive types, unsigned/compressed * numbers, and common types like String with optimized fast paths. * - *

    This method is similar to {@link #readBuildInFieldValue(Fory, TypeResolver, RefResolver, - * SerializationFieldInfo, MemoryBuffer, Object)}, but returns the field value instead of setting - * it into the target object. + *

    This method is similar to {@link #readBuildInFieldValue(ReadContext, TypeResolver, + * RefReader, SerializationFieldInfo, MemoryBuffer)}, but returns the field value instead of + * setting it into the target object. * *

    The refMode determines how to read the value from buffer: - RefMode.NONE: read directly * without null flag - RefMode.NULL_ONLY: read null flag first, then value if not null - * RefMode.TRACKING: use reference tracking * - * @param fory fory runtime * @param typeResolver resolver used for type metadata read - * @param refResolver resolver used for reference tracking + * @param refReader resolver used for reference tracking * @param fieldInfo the field metadata including type and nullability info * @param buffer the buffer to read from * @return the deserialized field value, or null if the field is nullable and was null - * @see #readBuildInFieldValue(Fory, TypeResolver, RefResolver, SerializationFieldInfo, - * MemoryBuffer, Object) + * @see #readBuildInFieldValue(ReadContext, TypeResolver, RefReader, SerializationFieldInfo, + * MemoryBuffer) */ static Object readBuildInFieldValue( - Fory fory, + ReadContext readContext, TypeResolver typeResolver, - RefResolver refResolver, + RefReader refReader, SerializationFieldInfo fieldInfo, MemoryBuffer buffer) { int dispatchId = fieldInfo.dispatchId; @@ -603,17 +611,15 @@ static Object readBuildInFieldValue( // Use refMode to determine if there's a null flag prefix in the stream if (refMode == RefMode.NONE) { // No null flag in stream - read directly - return readNotNullBuildInFieldValue( - fory, typeResolver, refResolver, buffer, fieldInfo, dispatchId); + return readNotNullBuildInFieldValue(readContext, typeResolver, refReader, buffer, fieldInfo, dispatchId); } else if (refMode == RefMode.NULL_ONLY) { // Read null flag from buffer if (buffer.readByte() == Fory.NULL_FLAG) { return null; } - return readNotNullBuildInFieldValue( - fory, typeResolver, refResolver, buffer, fieldInfo, dispatchId); + return readNotNullBuildInFieldValue(readContext, typeResolver, refReader, buffer, fieldInfo, dispatchId); } - return readField(fory, typeResolver, refResolver, fieldInfo, buffer); + return readField(readContext, typeResolver, refReader, fieldInfo, buffer); } /** @@ -621,9 +627,9 @@ static Object readBuildInFieldValue( * fastpath for common type such as String. */ static void readBuildInFieldValue( - Fory fory, + ReadContext readContext, TypeResolver typeResolver, - RefResolver refResolver, + RefReader refReader, SerializationFieldInfo fieldInfo, MemoryBuffer buffer, Object targetObject) { @@ -634,7 +640,7 @@ static void readBuildInFieldValue( readPrimitiveFieldValue(buffer, targetObject, fieldAccessor, dispatchId); } else { readNotPrimitiveFieldValue( - fory, typeResolver, refResolver, buffer, targetObject, fieldInfo, dispatchId); + readContext, typeResolver, refReader, buffer, targetObject, fieldInfo, dispatchId); } } else if (fieldInfo.refMode == RefMode.NULL_ONLY) { if (buffer.readByte() == Fory.NULL_FLAG) { @@ -644,10 +650,10 @@ static void readBuildInFieldValue( readPrimitiveFieldValue(buffer, targetObject, fieldAccessor, dispatchId); } else { readNotPrimitiveFieldValue( - fory, typeResolver, refResolver, buffer, targetObject, fieldInfo, dispatchId); + readContext, typeResolver, refReader, buffer, targetObject, fieldInfo, dispatchId); } } else { - Object fieldValue = readField(fory, typeResolver, refResolver, fieldInfo, buffer); + Object fieldValue = readField(readContext, typeResolver, refReader, fieldInfo, buffer); fieldAccessor.putObject(targetObject, fieldValue); } } @@ -657,9 +663,9 @@ static void readBuildInFieldValue( * STRING dispatch IDs with optimized fast paths. */ private static Object readNotNullBuildInFieldValue( - Fory fory, + ReadContext readContext, TypeResolver typeResolver, - RefResolver refResolver, + RefReader refReader, MemoryBuffer buffer, SerializationFieldInfo fieldInfo, int dispatchId) { @@ -715,9 +721,9 @@ private static Object readNotNullBuildInFieldValue( case DispatchId.FLOAT16: return Float16.fromBits(buffer.readInt16()); case DispatchId.STRING: - return fory.readString(buffer); + return readContext.readString(); default: - return readField(fory, typeResolver, refResolver, fieldInfo, RefMode.NONE, buffer); + return readField(readContext, typeResolver, refReader, fieldInfo, RefMode.NONE, buffer); } } @@ -853,9 +859,9 @@ private static void readPrimitiveFieldValue( * and NOTNULL_BOXED_* dispatch IDs where null values are not allowed. */ private static void readNotPrimitiveFieldValue( - Fory fory, + ReadContext readContext, TypeResolver typeResolver, - RefResolver refResolver, + RefReader refReader, MemoryBuffer buffer, Object targetObject, SerializationFieldInfo fieldInfo, @@ -938,36 +944,35 @@ private static void readNotPrimitiveFieldValue( fieldAccessor.putObject(targetObject, Float16.fromBits(buffer.readInt16())); return; case DispatchId.STRING: - fieldAccessor.putObject(targetObject, fory.readString(buffer)); + fieldAccessor.putObject(targetObject, readContext.readString()); return; default: // Use RefMode.NONE because null flag was already handled by caller - Object fieldValue = - readField(fory, typeResolver, refResolver, fieldInfo, RefMode.NONE, buffer); + Object fieldValue = readField(readContext, typeResolver, refReader, fieldInfo, RefMode.NONE, buffer); fieldAccessor.putObject(targetObject, fieldValue); } } @Override - public T copy(T originObj) { + public T copy(CopyContext copyContext, T originObj) { if (immutable) { return originObj; } if (isRecord) { - return copyRecord(originObj); + return copyRecord(copyContext, originObj); } T newObj = newBean(); - fory.reference(originObj, newObj); - copyFields(originObj, newObj); + copyContext.reference(originObj, newObj); + copyFields(copyContext, originObj, newObj); return newObj; } - private T copyRecord(T originObj) { - Object[] fieldValues = copyFields(originObj); + private T copyRecord(CopyContext copyContext, T originObj) { + Object[] fieldValues = copyFields(copyContext, originObj); try { T t = objectCreator.newInstanceWithArguments(fieldValues); Arrays.fill(copyRecordInfo.getRecordComponents(), null); - fory.reference(originObj, t); + copyContext.reference(originObj, t); return t; } catch (Throwable e) { Platform.throwException(e); @@ -975,7 +980,7 @@ private T copyRecord(T originObj) { return originObj; } - private Object[] copyFields(T originObj) { + private Object[] copyFields(CopyContext copyContext, T originObj) { SerializationFieldInfo[] fieldInfos = this.fieldInfos; if (fieldInfos == null) { fieldInfos = buildFieldsInfo(); @@ -989,27 +994,31 @@ private Object[] copyFields(T originObj) { if (fieldInfo.isPrimitiveField) { fieldValues[i] = copyPrimitiveField(originObj, fieldOffset, fieldInfo.dispatchId); } else { - fieldValues[i] = copyNotPrimitiveField(originObj, fieldOffset, fieldInfo.dispatchId); + fieldValues[i] = copyNotPrimitiveField(copyContext, originObj, fieldOffset, fieldInfo.dispatchId); } } else { // field in record class has offset -1 Object fieldValue = fieldAccessor.get(originObj); - fieldValues[i] = fory.copyObject(fieldValue, fieldInfo.dispatchId); + fieldValues[i] = copyContext.copyObject(fieldValue, fieldInfo.dispatchId); } } return RecordUtils.remapping(copyRecordInfo, fieldValues); } - private void copyFields(T originObj, T newObj) { + private void copyFields( + CopyContext copyContext, T originObj, T newObj) { SerializationFieldInfo[] fieldInfos = this.fieldInfos; if (fieldInfos == null) { fieldInfos = buildFieldsInfo(); } - copyFields(fory, fieldInfos, originObj, newObj); + copyFields(copyContext, fieldInfos, originObj, newObj); } public static void copyFields( - Fory fory, SerializationFieldInfo[] fieldInfos, Object originObj, Object newObj) { + CopyContext copyContext, + SerializationFieldInfo[] fieldInfos, + Object originObj, + Object newObj) { for (SerializationFieldInfo fieldInfo : fieldInfos) { FieldAccessor fieldAccessor = fieldInfo.fieldAccessor; long fieldOffset = fieldAccessor.getFieldOffset(); @@ -1018,7 +1027,7 @@ public static void copyFields( if (fieldInfo.isPrimitiveField) { copySetPrimitiveField(originObj, newObj, fieldOffset, fieldInfo.dispatchId); } else { - copySetNotPrimitiveField(fory, originObj, newObj, fieldOffset, fieldInfo.dispatchId); + copySetNotPrimitiveField(copyContext, originObj, newObj, fieldOffset, fieldInfo.dispatchId); } } } @@ -1066,7 +1075,11 @@ private static void copySetPrimitiveField( } private static void copySetNotPrimitiveField( - Fory fory, Object originObj, Object newObj, long fieldOffset, int typeId) { + CopyContext copyContext, + Object originObj, + Object newObj, + long fieldOffset, + int typeId) { switch (typeId) { case DispatchId.BOOL: case DispatchId.INT8: @@ -1098,7 +1111,7 @@ private static void copySetNotPrimitiveField( break; default: Platform.putObject( - newObj, fieldOffset, fory.copyObject(Platform.getObject(originObj, fieldOffset))); + newObj, fieldOffset, copyContext.copyObject(Platform.getObject(originObj, fieldOffset))); } } @@ -1135,7 +1148,11 @@ private Object copyPrimitiveField(Object targetObject, long fieldOffset, int typ } } - private Object copyNotPrimitiveField(Object targetObject, long fieldOffset, int typeId) { + private Object copyNotPrimitiveField( + CopyContext copyContext, + Object targetObject, + long fieldOffset, + int typeId) { switch (typeId) { case DispatchId.BOOL: case DispatchId.INT8: @@ -1159,7 +1176,7 @@ private Object copyNotPrimitiveField(Object targetObject, long fieldOffset, int case DispatchId.STRING: return Platform.getObject(targetObject, fieldOffset); default: - return fory.copyObject(Platform.getObject(targetObject, fieldOffset)); + return copyContext.copyObject(Platform.getObject(targetObject, fieldOffset)); } } @@ -1187,8 +1204,8 @@ private SerializationFieldInfo[] buildFieldsInfo() { } } DescriptorGrouper descriptorGrouper = - FieldGroups.buildDescriptorGrouper(fory, descriptors, false, null); - FieldGroups fieldGroups = FieldGroups.buildFieldInfos(fory, descriptorGrouper); + FieldGroups.buildDescriptorGrouper(typeResolver, descriptors, false, null); + FieldGroups fieldGroups = FieldGroups.buildFieldInfos(typeResolver, descriptorGrouper); fieldInfos = fieldGroups.allFields; if (isRecord) { List fieldNames = diff --git a/java/fory-core/src/main/java/org/apache/fory/serializer/ArraySerializers.java b/java/fory-core/src/main/java/org/apache/fory/serializer/ArraySerializers.java index eedb16c776..fcc3ebd6de 100644 --- a/java/fory-core/src/main/java/org/apache/fory/serializer/ArraySerializers.java +++ b/java/fory-core/src/main/java/org/apache/fory/serializer/ArraySerializers.java @@ -22,11 +22,15 @@ import java.lang.reflect.Array; import java.util.Arrays; import org.apache.fory.Fory; +import org.apache.fory.config.CompatibleMode; +import org.apache.fory.config.Config; import org.apache.fory.config.LongEncoding; +import org.apache.fory.context.CopyContext; +import org.apache.fory.context.ReadContext; +import org.apache.fory.context.WriteContext; import org.apache.fory.memory.MemoryBuffer; import org.apache.fory.memory.Platform; import org.apache.fory.resolver.ClassResolver; -import org.apache.fory.resolver.RefResolver; import org.apache.fory.resolver.TypeInfo; import org.apache.fory.resolver.TypeInfoHolder; import org.apache.fory.resolver.TypeResolver; @@ -43,15 +47,19 @@ public class ArraySerializers { /** May be multi-dimension array, or multi-dimension primitive array. */ @SuppressWarnings({"unchecked", "rawtypes"}) public static final class ObjectArraySerializer extends Serializer { + private final Config config; private final Class innerType; + private final TypeResolver typeResolver; private final Serializer componentTypeSerializer; private final TypeInfoHolder classInfoHolder; private final int[] stubDims; private final GenericType componentGenericType; - public ObjectArraySerializer(Fory fory, Class cls) { - super(fory, cls); - TypeResolver resolver = fory.getTypeResolver(); + public ObjectArraySerializer(TypeResolver typeResolver, Class cls) { + super(typeResolver.getConfig(), cls); + this.config = typeResolver.getConfig(); + this.typeResolver = typeResolver; + TypeResolver resolver = typeResolver; if (resolver instanceof ClassResolver) { resolver.setSerializer(cls, this); } @@ -68,128 +76,126 @@ public ObjectArraySerializer(Fory fory, Class cls) { } this.innerType = (Class) innerType; Class componentType = cls.getComponentType(); - componentGenericType = fory.getTypeResolver().buildGenericType(componentType); - if (fory.getTypeResolver().isMonomorphic(componentType)) { - if (fory.isCrossLanguage()) { + componentGenericType = typeResolver.buildGenericType(componentType); + if (typeResolver.isMonomorphic(componentType)) { + if (config.isXlang()) { this.componentTypeSerializer = null; } else { - this.componentTypeSerializer = fory.getTypeResolver().getSerializer(componentType); + this.componentTypeSerializer = typeResolver.getSerializer(componentType); } } else { // TODO add TypeInfo cache for non-final component type. this.componentTypeSerializer = null; } this.stubDims = new int[dimension]; - classInfoHolder = fory.getTypeResolver().nilTypeInfoHolder(); + classInfoHolder = typeResolver.nilTypeInfoHolder(); } @Override - public void write(MemoryBuffer buffer, T[] arr) { - if (!isJava) { + public void write(WriteContext writeContext, T[] arr) { + MemoryBuffer buffer = writeContext.getBuffer(); + if (config.isXlang()) { int len = arr.length; buffer.writeVarUint32Small7(len); // TODO(chaokunyang) use generics by creating component serializers to multi-dimension // array. for (T t : arr) { - fory.writeRef(buffer, t); + writeContext.writeRef(t); } return; } int len = arr.length; - RefResolver refResolver = fory.getRefResolver(); Serializer componentSerializer = this.componentTypeSerializer; int header = componentSerializer != null ? 0b1 : 0b0; buffer.writeVarUint32Small7(len << 1 | header); if (componentSerializer != null) { for (T t : arr) { - if (!refResolver.writeRefOrNull(buffer, t)) { - componentSerializer.write(buffer, t); + if (!writeContext.writeRefOrNull(t)) { + componentSerializer.write(writeContext, t); } } } else { - Fory fory = this.fory; - ClassResolver classResolver = (ClassResolver) fory.getTypeResolver(); + ClassResolver classResolver = (ClassResolver) typeResolver; TypeInfo typeInfo = null; Class elemClass = null; for (T t : arr) { - if (!refResolver.writeRefOrNull(buffer, t)) { + if (!writeContext.writeRefOrNull(t)) { Class clz = t.getClass(); if (clz != elemClass) { elemClass = clz; typeInfo = classResolver.getTypeInfo(clz); } - fory.writeNonRef(buffer, t, typeInfo); + writeContext.writeNonRef(t, typeInfo); } } } } @Override - public T[] copy(T[] originArray) { + public T[] copy(CopyContext copyContext, T[] originArray) { int length = originArray.length; Object[] newArray = newArray(length); - fory.reference(originArray, newArray); + copyContext.reference(originArray, newArray); Serializer componentSerializer = this.componentTypeSerializer; if (componentSerializer != null) { if (componentSerializer.isImmutable()) { System.arraycopy(originArray, 0, newArray, 0, length); } else { for (int i = 0; i < length; i++) { - newArray[i] = componentSerializer.copy(originArray[i]); + newArray[i] = componentSerializer.copy(copyContext, originArray[i]); } } } else { for (int i = 0; i < length; i++) { - newArray[i] = fory.copyObject(originArray[i]); + newArray[i] = copyContext.copyObject(originArray[i]); } } return (T[]) newArray; } @Override - public T[] read(MemoryBuffer buffer) { - if (!isJava) { + public T[] read(ReadContext readContext) { + MemoryBuffer buffer = readContext.getBuffer(); + if (config.isXlang()) { int numElements = buffer.readVarUint32Small7(); Object[] value = newArray(numElements); - fory.getGenerics().pushGenericType(componentGenericType); + readContext.getGenerics().pushGenericType(componentGenericType, readContext.getDepth()); for (int i = 0; i < numElements; i++) { - Object x = fory.readRef(buffer); + Object x = readContext.readRef(); value[i] = x; } - fory.getGenerics().popGenericType(); + readContext.getGenerics().popGenericType(readContext.getDepth()); return (T[]) value; } int numElements = buffer.readVarUint32Small7(); boolean isFinal = (numElements & 0b1) != 0; numElements >>>= 1; Object[] value = newArray(numElements); - RefResolver refResolver = fory.getRefResolver(); - refResolver.reference(value); + readContext.reference(value); if (isFinal) { final Serializer componentTypeSerializer = this.componentTypeSerializer; for (int i = 0; i < numElements; i++) { Object elem; - int nextReadRefId = refResolver.tryPreserveRefId(buffer); + int nextReadRefId = readContext.tryPreserveRefId(); if (nextReadRefId >= Fory.NOT_NULL_VALUE_FLAG) { - elem = componentTypeSerializer.read(buffer); - refResolver.setReadObject(nextReadRefId, elem); + elem = componentTypeSerializer.read(readContext); + readContext.setReadObject(nextReadRefId, elem); } else { - elem = refResolver.getReadObject(); + elem = readContext.getReadObject(); } value[i] = elem; } } else { - Fory fory = this.fory; TypeInfoHolder classInfoHolder = this.classInfoHolder; for (int i = 0; i < numElements; i++) { - int nextReadRefId = refResolver.tryPreserveRefId(buffer); + int nextReadRefId = readContext.tryPreserveRefId(); Object o; if (nextReadRefId >= Fory.NOT_NULL_VALUE_FLAG) { // ref value or not-null value - o = fory.readNonRef(buffer, classInfoHolder); - refResolver.setReadObject(nextReadRefId, o); + o = readContext.readNonRef(classInfoHolder); + readContext.setReadObject(nextReadRefId, o); } else { - o = refResolver.getReadObject(); + o = readContext.getReadObject(); } value[i] = o; } @@ -247,41 +253,48 @@ public MemoryBuffer toBuffer() { // Implement all read/write methods in subclasses to avoid // virtual method call cost. - public abstract static class PrimitiveArraySerializer - extends Serializers.CrossLanguageCompatibleSerializer { + public abstract static class PrimitiveArraySerializer extends Serializer { + protected final Config config; - public PrimitiveArraySerializer(Fory fory, Class cls) { - super(fory, cls); + public PrimitiveArraySerializer(TypeResolver typeResolver, Class cls) { + super(typeResolver.getConfig(), cls); + this.config = typeResolver.getConfig(); + } + + @Override + public boolean threadSafe() { + return true; } } public static final class BooleanArraySerializer extends PrimitiveArraySerializer { - public BooleanArraySerializer(Fory fory) { - super(fory, boolean[].class); + public BooleanArraySerializer(TypeResolver typeResolver) { + super(typeResolver, boolean[].class); } @Override - public void write(MemoryBuffer buffer, boolean[] value) { - if (fory.getBufferCallback() == null) { + public void write(WriteContext writeContext, boolean[] value) { + MemoryBuffer buffer = writeContext.getBuffer(); + if (writeContext.getBufferCallback() == null) { int size = Math.multiplyExact(value.length, 1); buffer.writePrimitiveArrayWithSize(value, Platform.BOOLEAN_ARRAY_OFFSET, size); } else { - fory.writeBufferObject( - buffer, + writeContext.writeBufferObject( new PrimitiveArrayBufferObject(value, Platform.BOOLEAN_ARRAY_OFFSET, 1, value.length)); } } @Override - public boolean[] copy(boolean[] originArray) { + public boolean[] copy(CopyContext copyContext, boolean[] originArray) { return Arrays.copyOf(originArray, originArray.length); } @Override - public boolean[] read(MemoryBuffer buffer) { - if (fory.isPeerOutOfBandEnabled()) { - MemoryBuffer buf = fory.readBufferObject(buffer); + public boolean[] read(ReadContext readContext) { + MemoryBuffer buffer = readContext.getBuffer(); + if (readContext.isPeerOutOfBandEnabled()) { + MemoryBuffer buf = readContext.readBufferObject(); int size = buf.remaining(); int numElements = size; boolean[] values = new boolean[numElements]; @@ -299,31 +312,32 @@ public boolean[] read(MemoryBuffer buffer) { public static final class ByteArraySerializer extends PrimitiveArraySerializer { - public ByteArraySerializer(Fory fory) { - super(fory, byte[].class); + public ByteArraySerializer(TypeResolver typeResolver) { + super(typeResolver, byte[].class); } @Override - public void write(MemoryBuffer buffer, byte[] value) { - if (fory.getBufferCallback() == null) { + public void write(WriteContext writeContext, byte[] value) { + MemoryBuffer buffer = writeContext.getBuffer(); + if (writeContext.getBufferCallback() == null) { int size = Math.multiplyExact(value.length, 1); buffer.writePrimitiveArrayWithSize(value, Platform.BYTE_ARRAY_OFFSET, size); } else { - fory.writeBufferObject( - buffer, + writeContext.writeBufferObject( new PrimitiveArrayBufferObject(value, Platform.BYTE_ARRAY_OFFSET, 1, value.length)); } } @Override - public byte[] copy(byte[] originArray) { + public byte[] copy(CopyContext copyContext, byte[] originArray) { return Arrays.copyOf(originArray, originArray.length); } @Override - public byte[] read(MemoryBuffer buffer) { - if (fory.isPeerOutOfBandEnabled()) { - MemoryBuffer buf = fory.readBufferObject(buffer); + public byte[] read(ReadContext readContext) { + MemoryBuffer buffer = readContext.getBuffer(); + if (readContext.isPeerOutOfBandEnabled()) { + MemoryBuffer buf = readContext.readBufferObject(); int size = buf.remaining(); byte[] values = new byte[size]; buf.copyToUnsafe(0, values, Platform.BYTE_ARRAY_OFFSET, size); @@ -339,16 +353,17 @@ public byte[] read(MemoryBuffer buffer) { public static final class CharArraySerializer extends PrimitiveArraySerializer { - public CharArraySerializer(Fory fory) { - super(fory, char[].class); + public CharArraySerializer(TypeResolver typeResolver) { + super(typeResolver, char[].class); } @Override - public void write(MemoryBuffer buffer, char[] value) { - if (!isJava) { + public void write(WriteContext writeContext, char[] value) { + MemoryBuffer buffer = writeContext.getBuffer(); + if (config.isXlang()) { throw new UnsupportedOperationException(); } - if (fory.getBufferCallback() == null) { + if (writeContext.getBufferCallback() == null) { int size = Math.multiplyExact(value.length, 2); if (Platform.IS_LITTLE_ENDIAN) { buffer.writePrimitiveArrayWithSize(value, Platform.CHAR_ARRAY_OFFSET, size); @@ -356,8 +371,7 @@ public void write(MemoryBuffer buffer, char[] value) { writeCharBySwapEndian(buffer, value); } } else { - fory.writeBufferObject( - buffer, + writeContext.writeBufferObject( new PrimitiveArrayBufferObject(value, Platform.CHAR_ARRAY_OFFSET, 2, value.length)); } } @@ -374,17 +388,18 @@ private void writeCharBySwapEndian(MemoryBuffer buffer, char[] value) { } @Override - public char[] copy(char[] originArray) { + public char[] copy(CopyContext copyContext, char[] originArray) { return Arrays.copyOf(originArray, originArray.length); } @Override - public char[] read(MemoryBuffer buffer) { - if (!isJava) { + public char[] read(ReadContext readContext) { + MemoryBuffer buffer = readContext.getBuffer(); + if (config.isXlang()) { throw new UnsupportedOperationException(); } - if (fory.isPeerOutOfBandEnabled()) { - MemoryBuffer buf = fory.readBufferObject(buffer); + if (readContext.isPeerOutOfBandEnabled()) { + MemoryBuffer buf = readContext.readBufferObject(); int size = buf.remaining(); int numElements = size / 2; char[] values = new char[numElements]; @@ -420,13 +435,14 @@ private void readCharBySwapEndian(MemoryBuffer buffer, char[] values, int numEle public static final class ShortArraySerializer extends PrimitiveArraySerializer { - public ShortArraySerializer(Fory fory) { - super(fory, short[].class); + public ShortArraySerializer(TypeResolver typeResolver) { + super(typeResolver, short[].class); } @Override - public void write(MemoryBuffer buffer, short[] value) { - if (fory.getBufferCallback() == null) { + public void write(WriteContext writeContext, short[] value) { + MemoryBuffer buffer = writeContext.getBuffer(); + if (writeContext.getBufferCallback() == null) { int size = Math.multiplyExact(value.length, 2); if (Platform.IS_LITTLE_ENDIAN) { buffer.writePrimitiveArrayWithSize(value, Platform.SHORT_ARRAY_OFFSET, size); @@ -434,8 +450,7 @@ public void write(MemoryBuffer buffer, short[] value) { writeInt16BySwapEndian(buffer, value); } } else { - fory.writeBufferObject( - buffer, + writeContext.writeBufferObject( new PrimitiveArrayBufferObject(value, Platform.SHORT_ARRAY_OFFSET, 2, value.length)); } } @@ -452,14 +467,15 @@ private void writeInt16BySwapEndian(MemoryBuffer buffer, short[] value) { } @Override - public short[] copy(short[] originArray) { + public short[] copy(CopyContext copyContext, short[] originArray) { return Arrays.copyOf(originArray, originArray.length); } @Override - public short[] read(MemoryBuffer buffer) { - if (fory.isPeerOutOfBandEnabled()) { - MemoryBuffer buf = fory.readBufferObject(buffer); + public short[] read(ReadContext readContext) { + MemoryBuffer buffer = readContext.getBuffer(); + if (readContext.isPeerOutOfBandEnabled()) { + MemoryBuffer buf = readContext.readBufferObject(); int size = buf.remaining(); int numElements = size / 2; short[] values = new short[numElements]; @@ -495,14 +511,15 @@ private void readInt16BySwapEndian(MemoryBuffer buffer, short[] values, int numE public static final class IntArraySerializer extends PrimitiveArraySerializer { - public IntArraySerializer(Fory fory) { - super(fory, int[].class); + public IntArraySerializer(TypeResolver typeResolver) { + super(typeResolver, int[].class); } @Override - public void write(MemoryBuffer buffer, int[] value) { - if (fory.getBufferCallback() == null) { - if (fory.getConfig().compressIntArray()) { + public void write(WriteContext writeContext, int[] value) { + MemoryBuffer buffer = writeContext.getBuffer(); + if (writeContext.getBufferCallback() == null) { + if (config.compressIntArray()) { writeInt32Compressed(buffer, value); return; } @@ -513,8 +530,7 @@ public void write(MemoryBuffer buffer, int[] value) { writeInt32BySwapEndian(buffer, value); } } else { - fory.writeBufferObject( - buffer, + writeContext.writeBufferObject( new PrimitiveArrayBufferObject(value, Platform.INT_ARRAY_OFFSET, 4, value.length)); } } @@ -531,14 +547,15 @@ private void writeInt32BySwapEndian(MemoryBuffer buffer, int[] value) { } @Override - public int[] copy(int[] originArray) { + public int[] copy(CopyContext copyContext, int[] originArray) { return Arrays.copyOf(originArray, originArray.length); } @Override - public int[] read(MemoryBuffer buffer) { - if (fory.isPeerOutOfBandEnabled()) { - MemoryBuffer buf = fory.readBufferObject(buffer); + public int[] read(ReadContext readContext) { + MemoryBuffer buffer = readContext.getBuffer(); + if (readContext.isPeerOutOfBandEnabled()) { + MemoryBuffer buf = readContext.readBufferObject(); int size = buf.remaining(); int numElements = size / 4; int[] values = new int[numElements]; @@ -551,7 +568,7 @@ public int[] read(MemoryBuffer buffer) { } return values; } - if (fory.getConfig().compressIntArray()) { + if (config.compressIntArray()) { return readInt32Compressed(buffer); } int size = buffer.readVarUint32Small7(); @@ -598,18 +615,19 @@ private int[] readInt32Compressed(MemoryBuffer buffer) { public static final class LongArraySerializer extends PrimitiveArraySerializer { private final boolean compressLongArray; - public LongArraySerializer(Fory fory) { - super(fory, long[].class); + public LongArraySerializer(TypeResolver typeResolver) { + super(typeResolver, long[].class); compressLongArray = - fory.getConfig().compressLongArray() - && fory.getConfig().longEncoding() != LongEncoding.FIXED; + typeResolver.getConfig().compressLongArray() + && typeResolver.getConfig().longEncoding() != LongEncoding.FIXED; } @Override - public void write(MemoryBuffer buffer, long[] value) { - if (fory.getBufferCallback() == null) { + public void write(WriteContext writeContext, long[] value) { + MemoryBuffer buffer = writeContext.getBuffer(); + if (writeContext.getBufferCallback() == null) { if (compressLongArray) { - writeInt64Compressed(buffer, value, fory.getConfig().longEncoding()); + writeInt64Compressed(buffer, value, config.longEncoding()); return; } int size = Math.multiplyExact(value.length, 8); @@ -619,8 +637,7 @@ public void write(MemoryBuffer buffer, long[] value) { writeInt64BySwapEndian(buffer, value); } } else { - fory.writeBufferObject( - buffer, + writeContext.writeBufferObject( new PrimitiveArrayBufferObject(value, Platform.LONG_ARRAY_OFFSET, 8, value.length)); } } @@ -637,14 +654,15 @@ private void writeInt64BySwapEndian(MemoryBuffer buffer, long[] value) { } @Override - public long[] copy(long[] originArray) { + public long[] copy(CopyContext copyContext, long[] originArray) { return Arrays.copyOf(originArray, originArray.length); } @Override - public long[] read(MemoryBuffer buffer) { - if (fory.isPeerOutOfBandEnabled()) { - MemoryBuffer buf = fory.readBufferObject(buffer); + public long[] read(ReadContext readContext) { + MemoryBuffer buffer = readContext.getBuffer(); + if (readContext.isPeerOutOfBandEnabled()) { + MemoryBuffer buf = readContext.readBufferObject(); int size = buf.remaining(); int numElements = size / 8; long[] values = new long[numElements]; @@ -658,7 +676,7 @@ public long[] read(MemoryBuffer buffer) { return values; } if (compressLongArray) { - return readInt64Compressed(buffer, fory.getConfig().longEncoding()); + return readInt64Compressed(buffer, config.longEncoding()); } int size = buffer.readVarUint32Small7(); int numElements = size / 8; @@ -718,13 +736,14 @@ private long[] readInt64Compressed(MemoryBuffer buffer, LongEncoding longEncodin public static final class FloatArraySerializer extends PrimitiveArraySerializer { - public FloatArraySerializer(Fory fory) { - super(fory, float[].class); + public FloatArraySerializer(TypeResolver typeResolver) { + super(typeResolver, float[].class); } @Override - public void write(MemoryBuffer buffer, float[] value) { - if (fory.getBufferCallback() == null) { + public void write(WriteContext writeContext, float[] value) { + MemoryBuffer buffer = writeContext.getBuffer(); + if (writeContext.getBufferCallback() == null) { int size = Math.multiplyExact(value.length, 4); if (Platform.IS_LITTLE_ENDIAN) { buffer.writePrimitiveArrayWithSize(value, Platform.FLOAT_ARRAY_OFFSET, size); @@ -732,8 +751,7 @@ public void write(MemoryBuffer buffer, float[] value) { writeFloat32BySwapEndian(buffer, value); } } else { - fory.writeBufferObject( - buffer, + writeContext.writeBufferObject( new PrimitiveArrayBufferObject(value, Platform.FLOAT_ARRAY_OFFSET, 4, value.length)); } } @@ -750,14 +768,15 @@ private void writeFloat32BySwapEndian(MemoryBuffer buffer, float[] value) { } @Override - public float[] copy(float[] originArray) { + public float[] copy(CopyContext copyContext, float[] originArray) { return Arrays.copyOf(originArray, originArray.length); } @Override - public float[] read(MemoryBuffer buffer) { - if (fory.isPeerOutOfBandEnabled()) { - MemoryBuffer buf = fory.readBufferObject(buffer); + public float[] read(ReadContext readContext) { + MemoryBuffer buffer = readContext.getBuffer(); + if (readContext.isPeerOutOfBandEnabled()) { + MemoryBuffer buf = readContext.readBufferObject(); int size = buf.remaining(); int numElements = size / 4; float[] values = new float[numElements]; @@ -793,13 +812,14 @@ private void readFloat32BySwapEndian(MemoryBuffer buffer, float[] values, int nu public static final class DoubleArraySerializer extends PrimitiveArraySerializer { - public DoubleArraySerializer(Fory fory) { - super(fory, double[].class); + public DoubleArraySerializer(TypeResolver typeResolver) { + super(typeResolver, double[].class); } @Override - public void write(MemoryBuffer buffer, double[] value) { - if (fory.getBufferCallback() == null) { + public void write(WriteContext writeContext, double[] value) { + MemoryBuffer buffer = writeContext.getBuffer(); + if (writeContext.getBufferCallback() == null) { int size = Math.multiplyExact(value.length, 8); if (Platform.IS_LITTLE_ENDIAN) { buffer.writePrimitiveArrayWithSize(value, Platform.DOUBLE_ARRAY_OFFSET, size); @@ -807,8 +827,7 @@ public void write(MemoryBuffer buffer, double[] value) { writeFloat64BySwapEndian(buffer, value); } } else { - fory.writeBufferObject( - buffer, + writeContext.writeBufferObject( new PrimitiveArrayBufferObject(value, Platform.DOUBLE_ARRAY_OFFSET, 8, value.length)); } } @@ -825,14 +844,15 @@ private void writeFloat64BySwapEndian(MemoryBuffer buffer, double[] value) { } @Override - public double[] copy(double[] originArray) { + public double[] copy(CopyContext copyContext, double[] originArray) { return Arrays.copyOf(originArray, originArray.length); } @Override - public double[] read(MemoryBuffer buffer) { - if (fory.isPeerOutOfBandEnabled()) { - MemoryBuffer buf = fory.readBufferObject(buffer); + public double[] read(ReadContext readContext) { + MemoryBuffer buffer = readContext.getBuffer(); + if (readContext.isPeerOutOfBandEnabled()) { + MemoryBuffer buf = readContext.readBufferObject(); int size = buf.remaining(); int numElements = size / 8; double[] values = new double[numElements]; @@ -867,12 +887,13 @@ private void readFloat64BySwapEndian(MemoryBuffer buffer, double[] values, int n } public static final class Float16ArraySerializer extends PrimitiveArraySerializer { - public Float16ArraySerializer(Fory fory) { - super(fory, Float16[].class); + public Float16ArraySerializer(TypeResolver typeResolver) { + super(typeResolver, Float16[].class); } @Override - public void write(MemoryBuffer buffer, Float16[] value) { + public void write(WriteContext writeContext, Float16[] value) { + MemoryBuffer buffer = writeContext.getBuffer(); int length = value.length; for (int i = 0; i < length; i++) { if (value[i] == null) { @@ -902,12 +923,13 @@ private void writeNonNull(MemoryBuffer buffer, Float16[] value, int length) { } @Override - public Float16[] copy(Float16[] originArray) { + public Float16[] copy(CopyContext copyContext, Float16[] originArray) { return Arrays.copyOf(originArray, originArray.length); } @Override - public Float16[] read(MemoryBuffer buffer) { + public Float16[] read(ReadContext readContext) { + MemoryBuffer buffer = readContext.getBuffer(); int size = buffer.readVarUint32Small7(); int numElements = size / 2; Float16[] values = new Float16[numElements]; @@ -928,22 +950,24 @@ public Float16[] read(MemoryBuffer buffer) { } public static final class StringArraySerializer extends Serializer { - private final StringSerializer stringSerializer; + private final Config config; private final ForyArrayAsListSerializer collectionSerializer; private final ForyArrayAsListSerializer.ArrayAsList list; - public StringArraySerializer(Fory fory) { - super(fory, String[].class); - stringSerializer = fory.getStringSerializer(); - collectionSerializer = new ForyArrayAsListSerializer(fory); + public StringArraySerializer(TypeResolver typeResolver) { + super(typeResolver.getConfig(), String[].class); + this.config = typeResolver.getConfig(); + collectionSerializer = new ForyArrayAsListSerializer(typeResolver); list = new ForyArrayAsListSerializer.ArrayAsList(0); } @Override - public void write(MemoryBuffer buffer, String[] value) { - if (!isJava) { + public void write(WriteContext writeContext, String[] value) { + MemoryBuffer buffer = writeContext.getBuffer(); + if (config.isXlang()) { int len = value.length; buffer.writeVarUint32Small7(len); + StringSerializer stringSerializer = writeContext.getStringSerializer(); for (String elem : value) { if (elem != null) { buffer.writeByte(Fory.NOT_NULL_VALUE_FLAG); @@ -964,10 +988,10 @@ public void write(MemoryBuffer buffer, String[] value) { // this method won't throw exception. int flags = collectionSerializer.writeNullabilityHeader(buffer, list); list.clearArray(); // clear for gc - StringSerializer stringSerializer = this.stringSerializer; + StringSerializer stringSerializer = writeContext.getStringSerializer(); if ((flags & CollectionFlags.HAS_NULL) != CollectionFlags.HAS_NULL) { for (String elem : value) { - stringSerializer.write(buffer, elem); + stringSerializer.write(writeContext, elem); } } else { for (String elem : value) { @@ -975,24 +999,26 @@ public void write(MemoryBuffer buffer, String[] value) { buffer.writeByte(Fory.NULL_FLAG); } else { buffer.writeByte(Fory.NOT_NULL_VALUE_FLAG); - stringSerializer.write(buffer, elem); + stringSerializer.write(writeContext, elem); } } } } @Override - public String[] copy(String[] originArray) { + public String[] copy(CopyContext copyContext, String[] originArray) { String[] newArray = new String[originArray.length]; System.arraycopy(originArray, 0, newArray, 0, originArray.length); return newArray; } @Override - public String[] read(MemoryBuffer buffer) { - if (!isJava) { + public String[] read(ReadContext readContext) { + MemoryBuffer buffer = readContext.getBuffer(); + if (config.isXlang()) { int numElements = buffer.readVarUint32Small7(); String[] value = new String[numElements]; + StringSerializer stringSerializer = readContext.getStringSerializer(); for (int i = 0; i < numElements; i++) { if (buffer.readByte() >= Fory.NOT_NULL_VALUE_FLAG) { value[i] = stringSerializer.readString(buffer); @@ -1008,7 +1034,7 @@ public String[] read(MemoryBuffer buffer) { return value; } int flags = buffer.readByte(); - StringSerializer serializer = this.stringSerializer; + StringSerializer serializer = readContext.getStringSerializer(); if ((flags & CollectionFlags.HAS_NULL) != CollectionFlags.HAS_NULL) { for (int i = 0; i < numElements; i++) { value[i] = serializer.readString(buffer); @@ -1024,38 +1050,37 @@ public String[] read(MemoryBuffer buffer) { } } - public static void registerDefaultSerializers(Fory fory) { - TypeResolver resolver = fory.getTypeResolver(); + public static void registerDefaultSerializers(TypeResolver resolver) { resolver.registerInternalSerializer( - Object[].class, new ObjectArraySerializer<>(fory, Object[].class)); + Object[].class, new ObjectArraySerializer<>(resolver, Object[].class)); resolver.registerInternalSerializer( - Class[].class, new ObjectArraySerializer<>(fory, Class[].class)); - resolver.registerInternalSerializer(byte[].class, new ByteArraySerializer(fory)); + Class[].class, new ObjectArraySerializer<>(resolver, Class[].class)); + resolver.registerInternalSerializer(byte[].class, new ByteArraySerializer(resolver)); resolver.registerInternalSerializer( - Byte[].class, new ObjectArraySerializer<>(fory, Byte[].class)); - resolver.registerInternalSerializer(char[].class, new CharArraySerializer(fory)); + Byte[].class, new ObjectArraySerializer<>(resolver, Byte[].class)); + resolver.registerInternalSerializer(char[].class, new CharArraySerializer(resolver)); resolver.registerInternalSerializer( - Character[].class, new ObjectArraySerializer<>(fory, Character[].class)); - resolver.registerInternalSerializer(short[].class, new ShortArraySerializer(fory)); + Character[].class, new ObjectArraySerializer<>(resolver, Character[].class)); + resolver.registerInternalSerializer(short[].class, new ShortArraySerializer(resolver)); resolver.registerInternalSerializer( - Short[].class, new ObjectArraySerializer<>(fory, Short[].class)); - resolver.registerInternalSerializer(int[].class, new IntArraySerializer(fory)); + Short[].class, new ObjectArraySerializer<>(resolver, Short[].class)); + resolver.registerInternalSerializer(int[].class, new IntArraySerializer(resolver)); resolver.registerInternalSerializer( - Integer[].class, new ObjectArraySerializer<>(fory, Integer[].class)); - resolver.registerInternalSerializer(long[].class, new LongArraySerializer(fory)); + Integer[].class, new ObjectArraySerializer<>(resolver, Integer[].class)); + resolver.registerInternalSerializer(long[].class, new LongArraySerializer(resolver)); resolver.registerInternalSerializer( - Long[].class, new ObjectArraySerializer<>(fory, Long[].class)); - resolver.registerInternalSerializer(float[].class, new FloatArraySerializer(fory)); + Long[].class, new ObjectArraySerializer<>(resolver, Long[].class)); + resolver.registerInternalSerializer(float[].class, new FloatArraySerializer(resolver)); resolver.registerInternalSerializer( - Float[].class, new ObjectArraySerializer<>(fory, Float[].class)); - resolver.registerInternalSerializer(double[].class, new DoubleArraySerializer(fory)); + Float[].class, new ObjectArraySerializer<>(resolver, Float[].class)); + resolver.registerInternalSerializer(double[].class, new DoubleArraySerializer(resolver)); resolver.registerInternalSerializer( - Double[].class, new ObjectArraySerializer<>(fory, Double[].class)); - resolver.registerInternalSerializer(Float16[].class, new Float16ArraySerializer(fory)); - resolver.registerInternalSerializer(boolean[].class, new BooleanArraySerializer(fory)); + Double[].class, new ObjectArraySerializer<>(resolver, Double[].class)); + resolver.registerInternalSerializer(Float16[].class, new Float16ArraySerializer(resolver)); + resolver.registerInternalSerializer(boolean[].class, new BooleanArraySerializer(resolver)); resolver.registerInternalSerializer( - Boolean[].class, new ObjectArraySerializer<>(fory, Boolean[].class)); - resolver.registerInternalSerializer(String[].class, new StringArraySerializer(fory)); + Boolean[].class, new ObjectArraySerializer<>(resolver, Boolean[].class)); + resolver.registerInternalSerializer(String[].class, new StringArraySerializer(resolver)); } // ########################## utils ########################## @@ -1077,113 +1102,115 @@ public static PrimitiveArrayBufferObject byteArrayBufferObject(byte[] array) { public abstract static class AbstractUnknownArraySerializer extends Serializer { protected final String className; + protected final TypeResolver typeResolver; private final int dims; - public AbstractUnknownArraySerializer(Fory fory, String className, Class stubClass) { - super(fory, stubClass); + public AbstractUnknownArraySerializer( + TypeResolver typeResolver, String className, Class stubClass) { + super(typeResolver.getConfig(), stubClass); + this.typeResolver = typeResolver; this.className = className; this.dims = TypeUtils.getArrayDimensions(stubClass); } @Override - public void write(MemoryBuffer buffer, Object value) { + public void write(WriteContext writeContext, Object value) { + MemoryBuffer buffer = writeContext.getBuffer(); throw new UnsupportedOperationException(); } @Override - public Object[] read(MemoryBuffer buffer) { + public Object[] read(ReadContext readContext) { + MemoryBuffer buffer = readContext.getBuffer(); switch (dims) { case 1: - return read1DArray(buffer); + return read1DArray(readContext, buffer); case 2: - return read2DArray(buffer); + return read2DArray(readContext, buffer); case 3: - return read3DArray(buffer); + return read3DArray(readContext, buffer); default: throw new UnsupportedOperationException( String.format("Unsupported array dimension %s for class %s", dims, className)); } } - protected abstract Object readInnerElement(MemoryBuffer buffer); + protected abstract Object readInnerElement(ReadContext readContext, MemoryBuffer buffer); - private Object[] read1DArray(MemoryBuffer buffer) { + private Object[] read1DArray(ReadContext readContext, MemoryBuffer buffer) { int numElements = buffer.readVarUint32Small7(); boolean isFinal = (numElements & 0b1) != 0; numElements >>>= 1; - RefResolver refResolver = fory.getRefResolver(); Object[] value = new Object[numElements]; - refResolver.reference(value); + readContext.reference(value); if (isFinal) { for (int i = 0; i < numElements; i++) { Object elem; - int nextReadRefId = refResolver.tryPreserveRefId(buffer); + int nextReadRefId = readContext.tryPreserveRefId(); if (nextReadRefId >= Fory.NOT_NULL_VALUE_FLAG) { - elem = readInnerElement(buffer); - refResolver.setReadObject(nextReadRefId, elem); + elem = readInnerElement(readContext, buffer); + readContext.setReadObject(nextReadRefId, elem); } else { - elem = refResolver.getReadObject(); + elem = readContext.getReadObject(); } value[i] = elem; } } else { for (int i = 0; i < numElements; i++) { - value[i] = fory.readRef(buffer); + value[i] = readContext.readRef(); } } return value; } - private Object[][] read2DArray(MemoryBuffer buffer) { + private Object[][] read2DArray(ReadContext readContext, MemoryBuffer buffer) { int numElements = buffer.readVarUint32Small7(); boolean isFinal = (numElements & 0b1) != 0; numElements >>>= 1; - RefResolver refResolver = fory.getRefResolver(); Object[][] value = new Object[numElements][]; - refResolver.reference(value); + readContext.reference(value); if (isFinal) { for (int i = 0; i < numElements; i++) { Object[] elem; - int nextReadRefId = refResolver.tryPreserveRefId(buffer); + int nextReadRefId = readContext.tryPreserveRefId(); if (nextReadRefId >= Fory.NOT_NULL_VALUE_FLAG) { - elem = read1DArray(buffer); - refResolver.setReadObject(nextReadRefId, elem); + elem = read1DArray(readContext, buffer); + readContext.setReadObject(nextReadRefId, elem); } else { - elem = (Object[]) refResolver.getReadObject(); + elem = (Object[]) readContext.getReadObject(); } value[i] = elem; } } else { for (int i = 0; i < numElements; i++) { - value[i] = (Object[]) fory.readRef(buffer); + value[i] = (Object[]) readContext.readRef(); } } return value; } - private Object[] read3DArray(MemoryBuffer buffer) { + private Object[] read3DArray(ReadContext readContext, MemoryBuffer buffer) { int numElements = buffer.readVarUint32Small7(); boolean isFinal = (numElements & 0b1) != 0; numElements >>>= 1; - RefResolver refResolver = fory.getRefResolver(); Object[][][] value = new Object[numElements][][]; - refResolver.reference(value); + readContext.reference(value); if (isFinal) { for (int i = 0; i < numElements; i++) { Object[][] elem; - int nextReadRefId = refResolver.tryPreserveRefId(buffer); + int nextReadRefId = readContext.tryPreserveRefId(); if (nextReadRefId >= Fory.NOT_NULL_VALUE_FLAG) { - elem = read2DArray(buffer); - refResolver.setReadObject(nextReadRefId, elem); + elem = read2DArray(readContext, buffer); + readContext.setReadObject(nextReadRefId, elem); } else { - elem = (Object[][]) refResolver.getReadObject(); + elem = (Object[][]) readContext.getReadObject(); } value[i] = elem; } } else { for (int i = 0; i < numElements; i++) { - value[i] = (Object[][]) fory.readRef(buffer); + value[i] = (Object[][]) readContext.readRef(); } } return value; @@ -1194,17 +1221,18 @@ private Object[] read3DArray(MemoryBuffer buffer) { public static final class UnknownArraySerializer extends AbstractUnknownArraySerializer { private final Serializer componentSerializer; - public UnknownArraySerializer(Fory fory, Class cls) { - this(fory, "Unknown", cls); + public UnknownArraySerializer(TypeResolver typeResolver, Class cls) { + this(typeResolver, "Unknown", cls); } - public UnknownArraySerializer(Fory fory, String className, Class cls) { - super(fory, className, cls); + public UnknownArraySerializer(TypeResolver typeResolver, String className, Class cls) { + super(typeResolver, className, cls); if (TypeUtils.getArrayComponent(cls).isEnum()) { - componentSerializer = new UnknownClassSerializers.UnknownEnumSerializer(fory); + componentSerializer = new UnknownClassSerializers.UnknownEnumSerializer(typeResolver); } else { - if (fory.getConfig().isCompatible()) { - componentSerializer = new ObjectSerializer<>(fory, UnknownClass.UnknownEmptyStruct.class); + if (typeResolver.getConfig().getCompatibleMode() == CompatibleMode.COMPATIBLE) { + componentSerializer = + new ObjectSerializer<>(typeResolver, UnknownClass.UnknownEmptyStruct.class); } else { componentSerializer = null; } @@ -1212,12 +1240,12 @@ public UnknownArraySerializer(Fory fory, String className, Class cls) { } @Override - protected Object readInnerElement(MemoryBuffer buffer) { + protected Object readInnerElement(ReadContext readContext, MemoryBuffer buffer) { if (componentSerializer == null) { throw new IllegalStateException( String.format("Class %s should serialize elements as non-morphic", className)); } - return componentSerializer.read(buffer); + return componentSerializer.read(readContext); } } } diff --git a/java/fory-core/src/main/java/org/apache/fory/serializer/BufferSerializers.java b/java/fory-core/src/main/java/org/apache/fory/serializer/BufferSerializers.java index 85219fa313..76a41a1bc0 100644 --- a/java/fory-core/src/main/java/org/apache/fory/serializer/BufferSerializers.java +++ b/java/fory-core/src/main/java/org/apache/fory/serializer/BufferSerializers.java @@ -21,9 +21,12 @@ import java.nio.ByteBuffer; import java.nio.ByteOrder; -import org.apache.fory.Fory; +import org.apache.fory.context.CopyContext; +import org.apache.fory.context.ReadContext; +import org.apache.fory.context.WriteContext; import org.apache.fory.memory.ByteBufferUtil; import org.apache.fory.memory.MemoryBuffer; +import org.apache.fory.resolver.TypeResolver; /** Serializers for buffer related classes. */ public class BufferSerializers { @@ -32,20 +35,19 @@ public class BufferSerializers { * doesn't implement {@link java.io.Serializable}, it's ok to only serialize data. Also Note that * a direct buffer may be returned if the serialized buffer is a heap buffer. */ - public static final class ByteBufferSerializer - extends Serializers.CrossLanguageCompatibleSerializer { + public static final class ByteBufferSerializer extends Serializer { - public ByteBufferSerializer(Fory fory, Class cls) { - super(fory, cls); + public ByteBufferSerializer(TypeResolver typeResolver, Class cls) { + super(typeResolver.getConfig(), cls); } @Override - public void write(MemoryBuffer buffer, ByteBuffer value) { - fory.writeBufferObject(buffer, new BufferObject.ByteBufferBufferObject(value)); + public void write(WriteContext writeContext, ByteBuffer value) { + writeContext.writeBufferObject(new BufferObject.ByteBufferBufferObject(value)); } @Override - public ByteBuffer copy(ByteBuffer value) { + public ByteBuffer copy(CopyContext copyContext, ByteBuffer value) { ByteBuffer dst = ByteBuffer.allocate(value.remaining()); dst.put(value.duplicate()); ByteBufferUtil.rewind(dst); @@ -53,8 +55,8 @@ public ByteBuffer copy(ByteBuffer value) { } @Override - public ByteBuffer read(MemoryBuffer buffer) { - MemoryBuffer newBuffer = fory.readBufferObject(buffer); + public ByteBuffer read(ReadContext readContext) { + MemoryBuffer newBuffer = readContext.readBufferObject(); int readerIndex = newBuffer.readerIndex(); int size = newBuffer.remaining(); ByteBuffer originalBuffer = newBuffer.sliceAsByteBuffer(readerIndex, size - 1); @@ -63,6 +65,11 @@ public ByteBuffer read(MemoryBuffer buffer) { isBigEndian == (byte) 1 ? ByteOrder.BIG_ENDIAN : ByteOrder.LITTLE_ENDIAN); return originalBuffer; } + + @Override + public boolean threadSafe() { + return true; + } } // TODO(chaokunyang) add support for MemoryBuffer serialization. diff --git a/java/fory-core/src/main/java/org/apache/fory/serializer/CodegenSerializer.java b/java/fory-core/src/main/java/org/apache/fory/serializer/CodegenSerializer.java index f1b51a2986..264ec8f596 100644 --- a/java/fory-core/src/main/java/org/apache/fory/serializer/CodegenSerializer.java +++ b/java/fory-core/src/main/java/org/apache/fory/serializer/CodegenSerializer.java @@ -21,14 +21,17 @@ import static org.apache.fory.util.Preconditions.checkArgument; +import org.apache.fory.context.ReadContext; +import org.apache.fory.context.WriteContext; + import java.lang.reflect.Modifier; import org.apache.fory.Fory; import org.apache.fory.builder.CodecUtils; import org.apache.fory.builder.Generated; import org.apache.fory.memory.MemoryBuffer; +import org.apache.fory.resolver.TypeResolver; /** Util for JIT Serialization. */ -@SuppressWarnings("UnstableApiUsage") public final class CodegenSerializer { public static boolean supportCodegenForJavaSerialization(Class cls) { @@ -58,6 +61,11 @@ public static Class> loadCodegenSerializer(Fory fory, Class } } + public static Class> loadCodegenSerializer( + TypeResolver typeResolver, Class cls) { + return typeResolver.getJITContext().asyncVisitFory(f -> loadCodegenSerializer(f, cls)); + } + /** * A bean serializer which initializes lazily on first call read/write method. * @@ -69,22 +77,22 @@ public static final class LazyInitBeanSerializer extends AbstractObjectSerial private Serializer serializer; private Serializer interpreterSerializer; - public LazyInitBeanSerializer(Fory fory, Class cls) { - super(fory, cls); + public LazyInitBeanSerializer(TypeResolver typeResolver, Class cls) { + super(typeResolver, cls); } @Override - public void write(MemoryBuffer buffer, T value) { - getOrCreateGeneratedSerializer().write(buffer, value); + public void write(WriteContext writeContext, T value) { + getOrCreateGeneratedSerializer().write(writeContext, value); } @Override - public T read(MemoryBuffer buffer) { - return getOrCreateGeneratedSerializer().read(buffer); + public T read(ReadContext readContext) { + return getOrCreateGeneratedSerializer().read(readContext); } public Class loadGeneratedSerializerClass() { - return CodegenSerializer.loadCodegenSerializer(fory, type); + return CodegenSerializer.loadCodegenSerializer(typeResolver, type); } public Class getGeneratedSerializerClass() { @@ -94,7 +102,7 @@ public Class getGeneratedSerializerClass() { @SuppressWarnings({"rawtypes"}) private Serializer getOrCreateGeneratedSerializer() { if (serializer == null) { - Serializer jitSerializer = fory.getTypeResolver().getSerializer(type); + Serializer jitSerializer = typeResolver.getSerializer(type); // Just be defensive for `getSerializer`/other call in Codec Builder to make // LazyInitBeanSerializer as serializer for `type`. if (jitSerializer instanceof LazyInitBeanSerializer) { @@ -102,22 +110,23 @@ private Serializer getOrCreateGeneratedSerializer() { if (interpreterSerializer != null) { return interpreterSerializer; } - fory.getTypeResolver().getTypeInfo(type).setSerializer(null); - if (fory.getConfig().isAsyncCompilationEnabled()) { + typeResolver.getTypeInfo(type).setSerializer(null); + if (config.isAsyncCompilationEnabled()) { // jit not finished, avoid recursive call current serializer. - Class sc = fory.getTypeResolver().getSerializerClass(type, false); - fory.getTypeResolver().getTypeInfo(type).setSerializer(this); - return interpreterSerializer = Serializers.newSerializer(fory, type, sc); + Class sc = typeResolver.getSerializerClass(type, false); + typeResolver.getTypeInfo(type).setSerializer(this); + return interpreterSerializer = + Serializers.newSerializer(typeResolver, type, sc); } else { - Class sc = fory.getTypeResolver().getSerializerClass(type); - fory.getTypeResolver().getTypeInfo(type).setSerializer(this); + Class sc = typeResolver.getSerializerClass(type); + typeResolver.getTypeInfo(type).setSerializer(this); checkArgument( Generated.GeneratedSerializer.class.isAssignableFrom(sc), "Expect jit serializer but got %s for class %s", sc, type); - serializer = Serializers.newSerializer(fory, type, sc); - fory.getTypeResolver().setSerializer(type, serializer); + serializer = Serializers.newSerializer(typeResolver, type, sc); + typeResolver.setSerializer(type, serializer); return serializer; } } else { diff --git a/java/fory-core/src/main/java/org/apache/fory/serializer/CopyOnlyObjectSerializer.java b/java/fory-core/src/main/java/org/apache/fory/serializer/CopyOnlyObjectSerializer.java index b90f911475..ecd48ff430 100644 --- a/java/fory-core/src/main/java/org/apache/fory/serializer/CopyOnlyObjectSerializer.java +++ b/java/fory-core/src/main/java/org/apache/fory/serializer/CopyOnlyObjectSerializer.java @@ -20,8 +20,10 @@ package org.apache.fory.serializer; import org.apache.fory.Fory; +import org.apache.fory.context.ReadContext; +import org.apache.fory.context.WriteContext; import org.apache.fory.exception.InsecureException; -import org.apache.fory.memory.MemoryBuffer; +import org.apache.fory.resolver.TypeResolver; /** * Serializer used only for copy after registration has been frozen. @@ -30,17 +32,17 @@ * reuses {@link AbstractObjectSerializer}'s field-copy implementation. */ public final class CopyOnlyObjectSerializer extends AbstractObjectSerializer { - public CopyOnlyObjectSerializer(Fory fory, Class type) { - super(fory, type); + public CopyOnlyObjectSerializer(TypeResolver typeResolver, Class type) { + super(typeResolver, type); } @Override - public void write(MemoryBuffer buffer, T value) { + public void write(WriteContext writeContext, T value) { throw insecureException(); } @Override - public T read(MemoryBuffer buffer) { + public T read(ReadContext readContext) { throw insecureException(); } diff --git a/java/fory-core/src/main/java/org/apache/fory/serializer/DeferedLazySerializer.java b/java/fory-core/src/main/java/org/apache/fory/serializer/DeferedLazySerializer.java index 2e3296ca2d..eca3e4a5f3 100644 --- a/java/fory-core/src/main/java/org/apache/fory/serializer/DeferedLazySerializer.java +++ b/java/fory-core/src/main/java/org/apache/fory/serializer/DeferedLazySerializer.java @@ -20,29 +20,33 @@ package org.apache.fory.serializer; import java.util.function.Supplier; -import org.apache.fory.Fory; import org.apache.fory.collection.Tuple2; -import org.apache.fory.memory.MemoryBuffer; +import org.apache.fory.context.CopyContext; +import org.apache.fory.context.ReadContext; +import org.apache.fory.context.WriteContext; +import org.apache.fory.resolver.TypeResolver; @SuppressWarnings({"rawtypes", "unchecked"}) public class DeferedLazySerializer extends Serializer { private final Supplier> serializerSupplier; + private final TypeResolver typeResolver; private Serializer serializer; public DeferedLazySerializer( - Fory fory, Class type, Supplier> serializerSupplier) { - super(fory, type); + TypeResolver typeResolver, Class type, Supplier> serializerSupplier) { + super(typeResolver.getConfig(), type); + this.typeResolver = typeResolver; this.serializerSupplier = serializerSupplier; } @Override - public void write(MemoryBuffer buffer, Object value) { - getSerializer().write(buffer, value); + public void write(WriteContext writeContext, Object value) { + getSerializer().write(writeContext, value); } @Override - public Object read(MemoryBuffer buffer) { - return getSerializer().read(buffer); + public Object read(ReadContext readContext) { + return getSerializer().read(readContext); } private Serializer getSerializer() { @@ -50,7 +54,7 @@ private Serializer getSerializer() { Tuple2 tuple2 = serializerSupplier.get(); if (tuple2.f0) { serializer = tuple2.f1; - fory.getTypeResolver().setSerializer(type, serializer); + typeResolver.setSerializer(type, serializer); } else { return tuple2.f1; } @@ -69,14 +73,16 @@ public Serializer resolveSerializer() { } @Override - public Object copy(Object value) { - return getSerializer().copy(value); + public Object copy(CopyContext copyContext, Object value) { + return getSerializer().copy(copyContext, value); } public static class DeferredLazyObjectSerializer extends DeferedLazySerializer { public DeferredLazyObjectSerializer( - Fory fory, Class type, Supplier> serializerSupplier) { - super(fory, type, serializerSupplier); + TypeResolver typeResolver, + Class type, + Supplier> serializerSupplier) { + super(typeResolver, type, serializerSupplier); } } } diff --git a/java/fory-core/src/main/java/org/apache/fory/serializer/EnumSerializer.java b/java/fory-core/src/main/java/org/apache/fory/serializer/EnumSerializer.java index 5556ad7b8f..5eed39e394 100644 --- a/java/fory-core/src/main/java/org/apache/fory/serializer/EnumSerializer.java +++ b/java/fory-core/src/main/java/org/apache/fory/serializer/EnumSerializer.java @@ -19,25 +19,24 @@ package org.apache.fory.serializer; -import java.util.Arrays; -import org.apache.fory.Fory; -import org.apache.fory.collection.ForyObjectMap; -import org.apache.fory.memory.MemoryBuffer; -import org.apache.fory.meta.Encoders; -import org.apache.fory.resolver.MetaStringRef; -import org.apache.fory.resolver.MetaStringResolver; +import org.apache.fory.config.Config; +import org.apache.fory.context.ReadContext; +import org.apache.fory.context.WriteContext; import org.apache.fory.util.Preconditions; +import java.util.Arrays; +import java.util.HashMap; +import java.util.Map; + @SuppressWarnings("rawtypes") public class EnumSerializer extends ImmutableSerializer { - private final MetaStringResolver metaStringResolver; + private final Config config; private final Enum[] enumConstants; - private final ForyObjectMap metaStringtoEnumRepresentation; - private final MetaStringRef[] metaStringStateArrByEnumOrdinal; + private final Map stringToEnum; - public EnumSerializer(Fory fory, Class cls) { - super(fory, cls, false); - metaStringResolver = fory.getMetaStringResolver(); + public EnumSerializer(Config config, Class cls) { + super(config, cls, false); + this.config = config; if (cls.isEnum()) { enumConstants = cls.getEnumConstants(); } else { @@ -48,50 +47,36 @@ public EnumSerializer(Fory fory, Class cls) { Preconditions.checkArgument(enclosingClass.isEnum()); enumConstants = enclosingClass.getEnumConstants(); } - - metaStringStateArrByEnumOrdinal = new MetaStringRef[enumConstants.length]; - - if (fory.getConfig().serializeEnumByName()) { - // as we know the size of enum is fixed, initialize the size of map with that value - int initialCapacity = (int) Math.ceil(enumConstants.length / 0.5f); - - metaStringtoEnumRepresentation = new ForyObjectMap<>(initialCapacity, 0.5f); - + if (config.serializeEnumByName()) { + stringToEnum = new HashMap<>(); for (Enum enumConstant : enumConstants) { - if (enumConstant != null) { - MetaStringRef msb = - metaStringResolver.getOrCreateGenericMetaStringBytes(enumConstant.name()); - metaStringtoEnumRepresentation.put(msb, enumConstant); - metaStringStateArrByEnumOrdinal[enumConstant.ordinal()] = msb; - } + stringToEnum.put(enumConstant.name(), enumConstant); } - - } else { - metaStringtoEnumRepresentation = null; + } else { + stringToEnum = null; } } @Override - public void write(MemoryBuffer buffer, Enum value) { - if (isJava && fory.getConfig().serializeEnumByName()) { - MetaStringRef metaStringState = metaStringStateArrByEnumOrdinal[value.ordinal()]; - metaStringResolver.writeMetaStringBytes(buffer, metaStringState); + public void write(WriteContext writeContext, Enum value) { + if (!config.isXlang() && config.serializeEnumByName()) { + writeContext.writeString(value.name()); } else { - buffer.writeVarUint32Small7(value.ordinal()); + writeContext.getBuffer().writeVarUint32Small7(value.ordinal()); } } @Override - public Enum read(MemoryBuffer buffer) { - if (isJava && fory.getConfig().serializeEnumByName()) { - MetaStringRef metaStringState = metaStringResolver.readMetaStringBytes(buffer); - Enum e = metaStringtoEnumRepresentation.get(metaStringState); + public Enum read(ReadContext readContext) { + if (!config.isXlang() && config.serializeEnumByName()) { + String name = readContext.readString(); + Enum e = stringToEnum.get(name); if (e != null) { return e; } - return handleUnknownEnumValue(metaStringState.decode(Encoders.GENERIC_DECODER)); + return handleUnknownEnumValue(name); } else { - int value = buffer.readVarUint32Small7(); + int value = readContext.getBuffer().readVarUint32Small7(); if (value >= enumConstants.length) { return handleUnknownEnumValue(value); } @@ -100,7 +85,7 @@ public Enum read(MemoryBuffer buffer) { } private Enum handleUnknownEnumValue(int value) { - switch (fory.getConfig().getUnknownEnumValueStrategy()) { + switch (config.getUnknownEnumValueStrategy()) { case RETURN_NULL: return null; case RETURN_FIRST_VARIANT: @@ -114,7 +99,7 @@ private Enum handleUnknownEnumValue(int value) { } private Enum handleUnknownEnumValue(String value) { - switch (fory.getConfig().getUnknownEnumValueStrategy()) { + switch (config.getUnknownEnumValueStrategy()) { case RETURN_NULL: return null; case RETURN_FIRST_VARIANT: @@ -126,4 +111,9 @@ private Enum handleUnknownEnumValue(String value) { String.format("Enum string %s not in %s", value, Arrays.toString(enumConstants))); } } + + @Override + public boolean threadSafe() { + return config.forVirtualThread(); + } } diff --git a/java/fory-core/src/main/java/org/apache/fory/serializer/ExceptionSerializers.java b/java/fory-core/src/main/java/org/apache/fory/serializer/ExceptionSerializers.java index 06039728cc..32983f537f 100644 --- a/java/fory-core/src/main/java/org/apache/fory/serializer/ExceptionSerializers.java +++ b/java/fory-core/src/main/java/org/apache/fory/serializer/ExceptionSerializers.java @@ -29,15 +29,18 @@ import java.util.Collections; import java.util.List; import java.util.Set; -import org.apache.fory.Fory; +import org.apache.fory.config.Config; import org.apache.fory.builder.LayerMarkerClassGenerator; +import org.apache.fory.context.MetaContext; +import org.apache.fory.context.ReadContext; +import org.apache.fory.context.WriteContext; import org.apache.fory.memory.MemoryBuffer; import org.apache.fory.memory.Platform; import org.apache.fory.meta.TypeDef; import org.apache.fory.reflect.ObjectCreator; import org.apache.fory.reflect.ObjectCreators; import org.apache.fory.reflect.ReflectionUtils; -import org.apache.fory.resolver.MetaContext; +import org.apache.fory.resolver.TypeResolver; import org.apache.fory.util.GraalvmSupport; import org.apache.fory.util.unsafe._JDKAccess; @@ -49,14 +52,18 @@ public final class ExceptionSerializers { private ExceptionSerializers() {} public static final class ExceptionSerializer extends Serializer { + private final Config config; + private final TypeResolver typeResolver; private final ObjectCreator objectCreator; private volatile Serializer[] slotsSerializers; private volatile boolean rebuildSlotsSerializersAtRuntime; - public ExceptionSerializer(Fory fory, Class type) { - super(fory, type); + public ExceptionSerializer(TypeResolver typeResolver, Class type) { + super(typeResolver.getConfig(), type); + this.config = typeResolver.getConfig(); + this.typeResolver = typeResolver; objectCreator = createThrowableObjectCreator(type); - slotsSerializers = buildSlotsSerializers(fory, type); + slotsSerializers = buildSlotsSerializers(typeResolver, type); // Native-image runtime must rebuild slot serializers once so field accessors and // descriptors are created against the runtime heap layout instead of reusing // any build-time initialized state. @@ -64,30 +71,32 @@ public ExceptionSerializer(Fory fory, Class type) { } @Override - public void write(MemoryBuffer buffer, T value) { + public void write(WriteContext writeContext, T value) { + MemoryBuffer buffer = writeContext.getBuffer(); Serializer[] slotsSerializers = getSlotsSerializers(); - fory.writeRef(buffer, value.getStackTrace()); - fory.writeRef(buffer, value.getCause()); - fory.writeStringRef(buffer, value.getMessage()); + writeContext.writeRef(value.getStackTrace()); + writeContext.writeRef(value.getCause()); + writeContext.writeStringRef(value.getMessage()); buffer.writeVarUint32(0); for (Serializer slotsSerializer : slotsSerializers) { - slotsSerializer.write(buffer, value); + slotsSerializer.write(writeContext, value); } } @Override - public T read(MemoryBuffer buffer) { + public T read(ReadContext readContext) { + MemoryBuffer buffer = readContext.getBuffer(); Serializer[] slotsSerializers = getSlotsSerializers(); T obj = objectCreator.newInstance(); - refResolver.reference(obj); - StackTraceElement[] stackTrace = (StackTraceElement[]) fory.readRef(buffer); - Throwable cause = (Throwable) fory.readRef(buffer); - String detailMessage = fory.readStringRef(buffer); - skipExtraFields(buffer); + readContext.reference(obj); + StackTraceElement[] stackTrace = (StackTraceElement[]) readContext.readRef(); + Throwable cause = (Throwable) readContext.readRef(); + String detailMessage = readContext.readStringRef(); + skipExtraFields(readContext, buffer); Platform.putObject(obj, ThrowableOffsets.DETAIL_MESSAGE_FIELD_OFFSET, detailMessage); Platform.putObject(obj, ThrowableOffsets.CAUSE_FIELD_OFFSET, cause == null ? obj : cause); Platform.putObject(obj, ThrowableOffsets.STACK_TRACE_FIELD_OFFSET, stackTrace); - readAndSetFields(fory, buffer, obj, slotsSerializers); + readAndSetFields(readContext, buffer, obj, slotsSerializers, config); return obj; } @@ -97,18 +106,18 @@ private Serializer[] getSlotsSerializers() { } synchronized (this) { if (rebuildSlotsSerializersAtRuntime) { - slotsSerializers = buildSlotsSerializers(fory, type); + slotsSerializers = buildSlotsSerializers(typeResolver, type); rebuildSlotsSerializersAtRuntime = false; } return slotsSerializers; } } - private void skipExtraFields(MemoryBuffer buffer) { + private void skipExtraFields(ReadContext readContext, MemoryBuffer buffer) { int numExtraFields = buffer.readVarUint32(); for (int i = 0; i < numExtraFields; i++) { - fory.readString(buffer); - fory.readRef(buffer); + readContext.readString(); + readContext.readRef(); } } } @@ -133,35 +142,37 @@ public static final class StackTraceElementSerializer extends Serializer ObjectCreator createThrowableObjectCreat return new ObjectCreators.ParentNoArgCtrObjectCreator<>(type); } - private static Serializer[] buildSlotsSerializers(Fory fory, Class type) { + private static Serializer[] buildSlotsSerializers(TypeResolver typeResolver, Class type) { + Config config = typeResolver.getConfig(); List serializers = new ArrayList<>(); int layerIndex = 0; while (!THROWABLE_SUPER_CLASSES.contains(type)) { Serializer slotsSerializer; - if (fory.getConfig().isCompatible()) { - TypeDef layerTypeDef = fory.getTypeResolver().getTypeDef(type, false); - Class layerMarkerClass = LayerMarkerClassGenerator.getOrCreate(fory, type, layerIndex); - slotsSerializer = new MetaSharedLayerSerializer(fory, type, layerTypeDef, layerMarkerClass); + if (config.isCompatible()) { + TypeDef layerTypeDef = typeResolver.getTypeDef(type, false); + Class layerMarkerClass = LayerMarkerClassGenerator.getOrCreate(type, layerIndex); + slotsSerializer = + new MetaSharedLayerSerializer(typeResolver, type, layerTypeDef, layerMarkerClass); } else { - slotsSerializer = new ObjectSerializer<>(fory, type, false); + slotsSerializer = new ObjectSerializer<>(typeResolver, type, false); } serializers.add(slotsSerializer); type = (Class) type.getSuperclass(); @@ -266,22 +279,26 @@ private static Serializer[] buildSlotsSerializers(Fory fory, Class type) } private static void readAndSetFields( - Fory fory, MemoryBuffer buffer, Object target, Serializer[] slotsSerializers) { + ReadContext readContext, + MemoryBuffer buffer, + Object target, + Serializer[] slotsSerializers, + Config config) { for (Serializer slotsSerializer : slotsSerializers) { if (slotsSerializer instanceof MetaSharedLayerSerializer) { MetaSharedLayerSerializer metaSerializer = (MetaSharedLayerSerializer) slotsSerializer; - if (fory.getConfig().isMetaShareEnabled()) { - readAndSkipLayerClassMeta(fory, buffer); + if (config.isMetaShareEnabled()) { + readAndSkipLayerClassMeta(readContext, buffer); } - metaSerializer.readAndSetFields(buffer, target); + metaSerializer.readAndSetFields(readContext, buffer, target); } else { - ((ObjectSerializer) slotsSerializer).readAndSetFields(buffer, target); + ((ObjectSerializer) slotsSerializer).readAndSetFields(readContext, buffer, target); } } } - private static void readAndSkipLayerClassMeta(Fory fory, MemoryBuffer buffer) { - MetaContext metaContext = fory.getSerializationContext().getMetaContext(); + private static void readAndSkipLayerClassMeta(ReadContext readContext, MemoryBuffer buffer) { + MetaContext metaContext = readContext.getMetaContext(); if (metaContext == null) { return; } diff --git a/java/fory-core/src/main/java/org/apache/fory/serializer/ExternalizableSerializer.java b/java/fory-core/src/main/java/org/apache/fory/serializer/ExternalizableSerializer.java index a0c3b501c3..3782dc0c46 100644 --- a/java/fory-core/src/main/java/org/apache/fory/serializer/ExternalizableSerializer.java +++ b/java/fory-core/src/main/java/org/apache/fory/serializer/ExternalizableSerializer.java @@ -21,11 +21,13 @@ import java.io.Externalizable; import java.io.IOException; -import org.apache.fory.Fory; +import org.apache.fory.context.ReadContext; +import org.apache.fory.context.WriteContext; import org.apache.fory.io.MemoryBufferObjectInput; import org.apache.fory.io.MemoryBufferObjectOutput; import org.apache.fory.memory.MemoryBuffer; import org.apache.fory.memory.Platform; +import org.apache.fory.resolver.TypeResolver; /** Serializer for class implements {@link Externalizable}. */ public class ExternalizableSerializer @@ -33,18 +35,20 @@ public class ExternalizableSerializer private final MemoryBufferObjectInput objectInput; private final MemoryBufferObjectOutput objectOutput; - public ExternalizableSerializer(Fory fory, Class cls) { - super(fory, cls); - objectInput = new MemoryBufferObjectInput(fory, null); - objectOutput = new MemoryBufferObjectOutput(fory, null); + public ExternalizableSerializer(TypeResolver typeResolver, Class cls) { + super(typeResolver, cls); + objectInput = new MemoryBufferObjectInput(config, null); + objectOutput = new MemoryBufferObjectOutput(config, null); } @Override - public void write(MemoryBuffer buffer, T value) { - if (!isJava) { + public void write(WriteContext writeContext, T value) { + MemoryBuffer buffer = writeContext.getBuffer(); + if (config.isXlang()) { throw new UnsupportedOperationException("Externalizable can only be used in java"); } objectOutput.setBuffer(buffer); + objectOutput.setWriteContext(writeContext); try { value.writeExternal(objectOutput); } catch (IOException e) { @@ -53,10 +57,12 @@ public void write(MemoryBuffer buffer, T value) { } @Override - public T read(MemoryBuffer buffer) { + public T read(ReadContext readContext) { + MemoryBuffer buffer = readContext.getBuffer(); T t = objectCreator.newInstance(); - refResolver.reference(t); + readContext.reference(t); objectInput.setBuffer(buffer); + objectInput.setReadContext(readContext); try { t.readExternal(objectInput); } catch (IOException | ClassNotFoundException e) { diff --git a/java/fory-core/src/main/java/org/apache/fory/serializer/FieldGroups.java b/java/fory-core/src/main/java/org/apache/fory/serializer/FieldGroups.java index a60a45ac2f..55dbaa239b 100644 --- a/java/fory-core/src/main/java/org/apache/fory/serializer/FieldGroups.java +++ b/java/fory-core/src/main/java/org/apache/fory/serializer/FieldGroups.java @@ -26,7 +26,6 @@ import java.util.Collection; import java.util.List; import java.util.function.Function; -import org.apache.fory.Fory; import org.apache.fory.meta.TypeExtMeta; import org.apache.fory.reflect.FieldAccessor; import org.apache.fory.reflect.TypeRef; @@ -68,23 +67,23 @@ public FieldGroups( allFields = fields; } - public static FieldGroups buildFieldsInfo(Fory fory, List fields) { + public static FieldGroups buildFieldsInfo(TypeResolver typeResolver, List fields) { List descriptors = new ArrayList<>(); for (Field field : fields) { if (!Modifier.isTransient(field.getModifiers()) && !Modifier.isStatic(field.getModifiers())) { descriptors.add(new Descriptor(field, TypeRef.of(field.getAnnotatedType()), null, null)); } } - DescriptorGrouper descriptorGrouper = buildDescriptorGrouper(fory, descriptors, false, null); - return buildFieldInfos(fory, descriptorGrouper); + DescriptorGrouper descriptorGrouper = + buildDescriptorGrouper(typeResolver, descriptors, false, null); + return buildFieldInfos(typeResolver, descriptorGrouper); } static DescriptorGrouper buildDescriptorGrouper( - Fory fory, + TypeResolver typeResolver, Collection descriptors, boolean descriptorsGroupedOrdered, Function descriptorUpdator) { - TypeResolver typeResolver = fory.getTypeResolver(); return DescriptorGrouper.createDescriptorGrouper( typeResolver::isBuildIn, descriptors, @@ -95,7 +94,7 @@ static DescriptorGrouper buildDescriptorGrouper( .sort(); } - public static FieldGroups buildFieldInfos(Fory fory, DescriptorGrouper grouper) { + public static FieldGroups buildFieldInfos(TypeResolver typeResolver, DescriptorGrouper grouper) { // When a type is both Collection/Map and final, add it to collection/map fields to keep // consistent with jit. List primitives = new ArrayList<>(grouper.getPrimitiveDescriptors()); @@ -103,7 +102,7 @@ public static FieldGroups buildFieldInfos(Fory fory, DescriptorGrouper grouper) Collection buildIn = grouper.getBuildInDescriptors(); List regularBuildIn = new ArrayList<>(buildIn.size()); for (Descriptor d : buildIn) { - if (DispatchId.getDispatchId(fory, d) == DispatchId.FLOAT16) { + if (DispatchId.getDispatchId(typeResolver, d) == DispatchId.FLOAT16) { if (d.isNullable()) { boxed.add(d); } else { @@ -117,19 +116,19 @@ public static FieldGroups buildFieldInfos(Fory fory, DescriptorGrouper grouper) new SerializationFieldInfo[primitives.size() + boxed.size() + regularBuildIn.size()]; int cnt = 0; for (Descriptor d : primitives) { - allBuildIn[cnt++] = new SerializationFieldInfo(fory, d); + allBuildIn[cnt++] = new SerializationFieldInfo(typeResolver, d); } for (Descriptor d : boxed) { - allBuildIn[cnt++] = new SerializationFieldInfo(fory, d); + allBuildIn[cnt++] = new SerializationFieldInfo(typeResolver, d); } for (Descriptor d : regularBuildIn) { - allBuildIn[cnt++] = new SerializationFieldInfo(fory, d); + allBuildIn[cnt++] = new SerializationFieldInfo(typeResolver, d); } cnt = 0; SerializationFieldInfo[] otherFields = new SerializationFieldInfo[grouper.getOtherDescriptors().size()]; for (Descriptor descriptor : grouper.getOtherDescriptors()) { - SerializationFieldInfo genericTypeField = new SerializationFieldInfo(fory, descriptor); + SerializationFieldInfo genericTypeField = new SerializationFieldInfo(typeResolver, descriptor); otherFields[cnt++] = genericTypeField; } cnt = 0; @@ -138,10 +137,10 @@ public static FieldGroups buildFieldInfos(Fory fory, DescriptorGrouper grouper) SerializationFieldInfo[] containerFields = new SerializationFieldInfo[collections.size() + maps.size()]; for (Descriptor d : collections) { - containerFields[cnt++] = new SerializationFieldInfo(fory, d); + containerFields[cnt++] = new SerializationFieldInfo(typeResolver, d); } for (Descriptor d : maps) { - containerFields[cnt++] = new SerializationFieldInfo(fory, d); + containerFields[cnt++] = new SerializationFieldInfo(typeResolver, d); } return new FieldGroups(allBuildIn, containerFields, otherFields); } @@ -168,20 +167,20 @@ public static final class SerializationFieldInfo { public final TypeInfoHolder classInfoHolder; public final TypeInfo containerTypeInfo; - SerializationFieldInfo(Fory fory, Descriptor d) { + SerializationFieldInfo(TypeResolver resolver, Descriptor d) { this.descriptor = d; this.type = descriptor.getRawType(); this.typeRef = d.getTypeRef(); - this.dispatchId = DispatchId.getDispatchId(fory, d); - TypeResolver resolver = fory.getTypeResolver(); + this.dispatchId = DispatchId.getDispatchId(resolver, d); // invoke `copy` to avoid ObjectSerializer construct clear serializer by `clearSerializer`. if (resolver.isMonomorphic(descriptor)) { - typeInfo = fory.getTypeResolver().getTypeInfo(typeRef.getRawType()); - if (!fory.isShareMeta() - && !fory.isCompatible() + typeInfo = resolver.getTypeInfo(typeRef.getRawType()); + if (!resolver.isShareMeta() + && !resolver.isCompatible() && typeInfo.getSerializer() instanceof ReplaceResolveSerializer) { // overwrite replace resolve serializer for final field - typeInfo.setSerializer(new FinalFieldReplaceResolveSerializer(fory, typeInfo.getCls())); + typeInfo.setSerializer( + new FinalFieldReplaceResolveSerializer(resolver, typeInfo.getCls())); } } else { typeInfo = null; @@ -227,7 +226,7 @@ public static final class SerializationFieldInfo { genericType = t; Field field = descriptor.getField(); if (field != null) { - TypeUtils.applyRefTrackingOverride(t, field.getAnnotatedType(), fory.trackingRef()); + TypeUtils.applyRefTrackingOverride(t, field.getAnnotatedType(), resolver.trackingRef()); } if (needsClassInfoHolder(resolver, cls)) { classInfoHolder = resolver.nilTypeInfoHolder(); @@ -235,7 +234,7 @@ public static final class SerializationFieldInfo { classInfoHolder = null; } isArray = cls.isArray(); - if (!fory.isCrossLanguage()) { + if (!resolver.isCrossLanguage()) { containerTypeInfo = null; } else { if (resolver.isMap(cls) || resolver.isCollection(cls) || resolver.isSet(cls)) { diff --git a/java/fory-core/src/main/java/org/apache/fory/serializer/FieldSkipper.java b/java/fory-core/src/main/java/org/apache/fory/serializer/FieldSkipper.java index 830bbac9f8..a659f24cdc 100644 --- a/java/fory-core/src/main/java/org/apache/fory/serializer/FieldSkipper.java +++ b/java/fory-core/src/main/java/org/apache/fory/serializer/FieldSkipper.java @@ -20,9 +20,10 @@ package org.apache.fory.serializer; import org.apache.fory.Fory; +import org.apache.fory.context.ReadContext; +import org.apache.fory.context.RefReader; import org.apache.fory.memory.MemoryBuffer; import org.apache.fory.resolver.RefMode; -import org.apache.fory.resolver.RefResolver; import org.apache.fory.resolver.TypeResolver; import org.apache.fory.serializer.FieldGroups.SerializationFieldInfo; import org.apache.fory.type.DispatchId; @@ -38,16 +39,15 @@ public class FieldSkipper { * Skip a field value in the buffer. Handles all dispatch IDs including basic types and complex * types. Whether to read a null flag is determined by fieldInfo.refMode. * - * @param fory fory runtime * @param typeResolver resolver used for type metadata read - * @param refResolver resolver used for reference tracking + * @param refReader resolver used for reference tracking * @param fieldInfo the field metadata * @param buffer the buffer to skip from */ static void skipField( - Fory fory, + ReadContext readContext, TypeResolver typeResolver, - RefResolver refResolver, + RefReader refReader, SerializationFieldInfo fieldInfo, MemoryBuffer buffer) { int dispatchId = fieldInfo.dispatchId; @@ -55,7 +55,7 @@ static void skipField( // For non-basic types, fall back to binding.readField if (!DispatchId.isBasicType(dispatchId)) { - AbstractObjectSerializer.readField(fory, typeResolver, refResolver, fieldInfo, buffer); + AbstractObjectSerializer.readField(readContext, typeResolver, refReader, fieldInfo, buffer); return; } @@ -118,7 +118,7 @@ static void skipField( buffer.readTaggedUint64(); break; case DispatchId.STRING: - fory.readString(buffer); + readContext.readString(); break; default: throw new IllegalStateException("Unexpected basic dispatchId: " + dispatchId); diff --git a/java/fory-core/src/main/java/org/apache/fory/serializer/FinalFieldReplaceResolveSerializer.java b/java/fory-core/src/main/java/org/apache/fory/serializer/FinalFieldReplaceResolveSerializer.java index 2a3a99fc7f..027b772e81 100644 --- a/java/fory-core/src/main/java/org/apache/fory/serializer/FinalFieldReplaceResolveSerializer.java +++ b/java/fory-core/src/main/java/org/apache/fory/serializer/FinalFieldReplaceResolveSerializer.java @@ -19,9 +19,11 @@ package org.apache.fory.serializer; -import org.apache.fory.Fory; import org.apache.fory.config.CompatibleMode; +import org.apache.fory.context.ReadContext; +import org.apache.fory.context.WriteContext; import org.apache.fory.memory.MemoryBuffer; +import org.apache.fory.resolver.TypeResolver; /** * Serializer for class which: - has jdk `writeReplace`/`readResolve` method defined, - is a final @@ -31,23 +33,26 @@ @SuppressWarnings({"unchecked", "rawtypes"}) public class FinalFieldReplaceResolveSerializer extends ReplaceResolveSerializer { - public FinalFieldReplaceResolveSerializer(Fory fory, Class type) { + public FinalFieldReplaceResolveSerializer(TypeResolver typeResolver, Class type) { // the serializer does not write class info // and does not set itself for the provided class // see checks in ReplaceResolveSerializer constructor - super(fory, type, true, false); + super(typeResolver, type, true, false); } @Override protected void writeObject( - MemoryBuffer buffer, Object value, MethodInfoCache jdkMethodInfoCache) { - jdkMethodInfoCache.objectSerializer.write(buffer, value); + WriteContext writeContext, + MemoryBuffer buffer, + Object value, + MethodInfoCache jdkMethodInfoCache) { + jdkMethodInfoCache.objectSerializer.write(writeContext, value); } @Override - protected Object readObject(MemoryBuffer buffer) { + protected Object readObject(ReadContext readContext, MemoryBuffer buffer) { MethodInfoCache jdkMethodInfoCache = getMethodInfoCache(type); - Object o = jdkMethodInfoCache.objectSerializer.read(buffer); + Object o = jdkMethodInfoCache.objectSerializer.read(readContext); ReplaceResolveInfo replaceResolveInfo = jdkMethodInfoCache.info; if (replaceResolveInfo.readResolveMethod == null) { return o; diff --git a/java/fory-core/src/main/java/org/apache/fory/serializer/Float16Serializer.java b/java/fory-core/src/main/java/org/apache/fory/serializer/Float16Serializer.java index e92aef336d..1d7e322adf 100644 --- a/java/fory-core/src/main/java/org/apache/fory/serializer/Float16Serializer.java +++ b/java/fory-core/src/main/java/org/apache/fory/serializer/Float16Serializer.java @@ -19,23 +19,29 @@ package org.apache.fory.serializer; -import org.apache.fory.Fory; -import org.apache.fory.memory.MemoryBuffer; +import org.apache.fory.config.Config; +import org.apache.fory.context.ReadContext; +import org.apache.fory.context.WriteContext; import org.apache.fory.type.Float16; public final class Float16Serializer extends ImmutableSerializer { - public Float16Serializer(Fory fory) { - super(fory, Float16.class); + public Float16Serializer(Config config) { + super(config, Float16.class); } @Override - public void write(MemoryBuffer buffer, Float16 value) { - buffer.writeInt16(value.toBits()); + public void write(WriteContext writeContext, Float16 value) { + writeContext.getBuffer().writeInt16(value.toBits()); } @Override - public Float16 read(MemoryBuffer buffer) { - return Float16.fromBits(buffer.readInt16()); + public Float16 read(ReadContext readContext) { + return Float16.fromBits(readContext.getBuffer().readInt16()); + } + + @Override + public boolean threadSafe() { + return true; } } diff --git a/java/fory-core/src/main/java/org/apache/fory/serializer/ForwardSerializer.java b/java/fory-core/src/main/java/org/apache/fory/serializer/ForwardSerializer.java deleted file mode 100644 index 62ecbad755..0000000000 --- a/java/fory-core/src/main/java/org/apache/fory/serializer/ForwardSerializer.java +++ /dev/null @@ -1,221 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -package org.apache.fory.serializer; - -import java.nio.ByteBuffer; -import java.util.Collections; -import java.util.IdentityHashMap; -import java.util.Set; -import java.util.function.Consumer; -import javax.annotation.concurrent.ThreadSafe; -import org.apache.fory.Fory; -import org.apache.fory.config.Language; -import org.apache.fory.memory.MemoryBuffer; -import org.apache.fory.memory.MemoryUtils; -import org.apache.fory.memory.Platform; - -/** - * A thread-safe serializer used to forward serialization to different serializer implementation. - */ -@ThreadSafe -@SuppressWarnings("unchecked") -public class ForwardSerializer { - - public abstract static class SerializerProxy { - /** Register custom serializers should be done in this method. */ - protected abstract T newSerializer(); - - protected void register(T serializer, Class clz) { - throw new UnsupportedOperationException(); - } - - protected void register(T serializer, Class clz, long id) { - throw new UnsupportedOperationException(); - } - - protected abstract byte[] serialize(T serializer, Object obj); - - protected MemoryBuffer serialize(T serializer, MemoryBuffer buffer, Object obj) { - byte[] bytes = serialize(serializer, obj); - buffer.writeBytes(bytes); - return buffer; - } - - protected ByteBuffer serialize(T serializer, ByteBuffer buffer, Object obj) { - byte[] bytes = serialize(serializer, obj); - buffer.put(bytes); - return buffer; - } - - protected abstract Object deserialize(T serializer, byte[] bytes); - - protected Object deserialize(T serializer, long address, int size) { - byte[] bytes = new byte[size]; - Platform.copyMemory(null, address, bytes, Platform.BYTE_ARRAY_OFFSET, size); - return deserialize(serializer, bytes); - } - - protected Object deserialize(T serializer, ByteBuffer byteBuffer) { - return deserialize(serializer, MemoryUtils.wrap(byteBuffer)); - } - - protected Object deserialize(T serializer, MemoryBuffer buffer) { - byte[] bytes = buffer.getRemainingBytes(); - return deserialize(serializer, bytes); - } - - protected Object copy(T serializer, Object obj) { - throw new UnsupportedOperationException(); - } - } - - public static class DefaultForyProxy extends SerializerProxy { - - private final ThreadLocal bufferLocal = - ThreadLocal.withInitial(() -> MemoryUtils.buffer(32)); - - /** Override this method to register custom serializers. */ - @Override - protected Fory newSerializer() { - return newForySerializer(); - } - - protected Fory newForySerializer() { - return Fory.builder() - .withLanguage(Language.JAVA) - .withRefTracking(true) - .requireClassRegistration(false) - .build(); - } - - @Override - protected void register(Fory fory, Class clz) { - fory.register(clz); - } - - @Override - protected void register(Fory fory, Class clz, long id) { - long unsignedId = id < 0 ? id & 0xffff_ffffL : id; - if (unsignedId < 0 || unsignedId > 0xffff_fffEL) { - throw new IllegalArgumentException("User type id must be in range [0, 0xfffffffe]"); - } - fory.register(clz, (int) unsignedId); - } - - @Override - protected byte[] serialize(Fory fory, Object obj) { - MemoryBuffer buffer = bufferLocal.get(); - buffer.writerIndex(0); - fory.serialize(buffer, obj); - return buffer.getBytes(0, buffer.writerIndex()); - } - - @Override - protected MemoryBuffer serialize(Fory fory, MemoryBuffer buffer, Object obj) { - fory.serialize(buffer, obj); - return buffer; - } - - @Override - protected Object deserialize(Fory fory, byte[] bytes) { - return fory.deserialize(bytes); - } - - @Override - protected Object deserialize(Fory fory, MemoryBuffer buffer) { - return fory.deserialize(buffer); - } - - @Override - protected Object copy(Fory fory, Object obj) { - return fory.copy(obj); - } - } - - private final SerializerProxy proxy; - private final ThreadLocal serializerLocal; - private final Set serializerSet = - Collections.newSetFromMap(Collections.synchronizedMap(new IdentityHashMap<>())); - private Consumer serializerCallback = obj -> {}; - - public ForwardSerializer(SerializerProxy proxy) { - this.proxy = proxy; - serializerLocal = - ThreadLocal.withInitial( - () -> { - Object serializer = proxy.newSerializer(); - synchronized (ForwardSerializer.this) { - serializerSet.add(serializer); - serializerCallback.accept(serializer); - } - return serializer; - }); - } - - public synchronized void register(Class clz) { - serializerSet.forEach(serializer -> proxy.register(serializer, clz)); - serializerCallback = - serializerCallback.andThen( - serializer -> { - proxy.register(serializer, clz); - }); - } - - public synchronized void register(Class clz, long id) { - serializerSet.forEach(serializer -> proxy.register(serializer, clz, id)); - serializerCallback = - serializerCallback.andThen( - serializer -> { - proxy.register(serializer, clz, id); - }); - } - - public byte[] serialize(Object obj) { - return proxy.serialize(serializerLocal.get(), obj); - } - - public MemoryBuffer serialize(MemoryBuffer buffer, Object obj) { - return proxy.serialize(serializerLocal.get(), buffer, obj); - } - - public ByteBuffer serialize(ByteBuffer buffer, Object obj) { - return proxy.serialize(serializerLocal.get(), buffer, obj); - } - - public T deserialize(byte[] bytes) { - return (T) proxy.deserialize(serializerLocal.get(), bytes); - } - - public T deserialize(long address, int size) { - return (T) proxy.deserialize(serializerLocal.get(), address, size); - } - - public T deserialize(ByteBuffer byteBuffer) { - return (T) proxy.deserialize(serializerLocal.get(), byteBuffer); - } - - public T deserialize(MemoryBuffer buffer) { - return (T) proxy.deserialize(serializerLocal.get(), buffer); - } - - public T copy(T obj) { - return (T) proxy.copy(serializerLocal.get(), obj); - } -} diff --git a/java/fory-core/src/main/java/org/apache/fory/serializer/ForyCopyableSerializer.java b/java/fory-core/src/main/java/org/apache/fory/serializer/ForyCopyableSerializer.java index d372a86889..37a489d688 100644 --- a/java/fory-core/src/main/java/org/apache/fory/serializer/ForyCopyableSerializer.java +++ b/java/fory-core/src/main/java/org/apache/fory/serializer/ForyCopyableSerializer.java @@ -21,30 +21,33 @@ import org.apache.fory.Fory; import org.apache.fory.ForyCopyable; -import org.apache.fory.memory.MemoryBuffer; +import org.apache.fory.config.Config; +import org.apache.fory.context.CopyContext; +import org.apache.fory.context.ReadContext; +import org.apache.fory.context.WriteContext; /** Fory custom copy serializer. see {@link ForyCopyable} */ public class ForyCopyableSerializer extends Serializer { private final Serializer serializer; - public ForyCopyableSerializer(Fory fory, Class type, Serializer serializer) { - super(fory, type); + public ForyCopyableSerializer(Config config, Class type, Serializer serializer) { + super(config, type); this.serializer = serializer; } @Override - public void write(MemoryBuffer buffer, T value) { - serializer.write(buffer, value); + public void write(WriteContext writeContext, T value) { + serializer.write(writeContext, value); } @Override - public T copy(T obj) { - return ((ForyCopyable) obj).copy(fory); + public T copy(CopyContext copyContext, T obj) { + return ((ForyCopyable) obj).copy(copyContext); } @Override - public T read(MemoryBuffer buffer) { - return serializer.read(buffer); + public T read(ReadContext readContext) { + return serializer.read(readContext); } } diff --git a/java/fory-core/src/main/java/org/apache/fory/serializer/ImmutableSerializer.java b/java/fory-core/src/main/java/org/apache/fory/serializer/ImmutableSerializer.java index fd9a42c346..a706c433f4 100644 --- a/java/fory-core/src/main/java/org/apache/fory/serializer/ImmutableSerializer.java +++ b/java/fory-core/src/main/java/org/apache/fory/serializer/ImmutableSerializer.java @@ -19,7 +19,7 @@ package org.apache.fory.serializer; -import org.apache.fory.Fory; +import org.apache.fory.config.Config; /** * Serializer for immutable objects. @@ -28,15 +28,16 @@ */ public abstract class ImmutableSerializer extends Serializer { - public ImmutableSerializer(Fory fory, Class type) { - super(fory, type, true); + public ImmutableSerializer(Config config, Class type) { + super(config, type, true); } - public ImmutableSerializer(Fory fory, Class type, boolean needToWriteRef) { - super(fory, type, needToWriteRef, true); + public ImmutableSerializer(Config config, Class type, boolean needToWriteRef) { + super(config, type, needToWriteRef, true); } - public ImmutableSerializer(Fory fory, Class type, boolean needToWriteRef, boolean immutable) { - super(fory, type, needToWriteRef, immutable); + public ImmutableSerializer( + Config config, Class type, boolean needToWriteRef, boolean immutable) { + super(config, type, needToWriteRef, immutable); } } diff --git a/java/fory-core/src/main/java/org/apache/fory/serializer/JavaSerializer.java b/java/fory-core/src/main/java/org/apache/fory/serializer/JavaSerializer.java index d6096f47d1..f51035dff6 100644 --- a/java/fory-core/src/main/java/org/apache/fory/serializer/JavaSerializer.java +++ b/java/fory-core/src/main/java/org/apache/fory/serializer/JavaSerializer.java @@ -29,6 +29,8 @@ import java.lang.reflect.Modifier; import java.nio.ByteBuffer; import org.apache.fory.Fory; +import org.apache.fory.context.ReadContext; +import org.apache.fory.context.WriteContext; import org.apache.fory.io.ClassLoaderObjectInputStream; import org.apache.fory.io.MemoryBufferObjectInput; import org.apache.fory.io.MemoryBufferObjectOutput; @@ -38,6 +40,7 @@ import org.apache.fory.memory.MemoryBuffer; import org.apache.fory.memory.Platform; import org.apache.fory.resolver.ClassResolver; +import org.apache.fory.resolver.TypeResolver; /** * Serializes objects using Java's built in serialization to be compatible with java serialization. @@ -53,8 +56,8 @@ public class JavaSerializer extends AbstractObjectSerializer { private final MemoryBufferObjectInput objectInput; private final MemoryBufferObjectOutput objectOutput; - public JavaSerializer(Fory fory, Class cls) { - super(fory, cls); + public JavaSerializer(TypeResolver typeResolver, Class cls) { + super(typeResolver, cls); // TODO(chgaokunyang) enable this check when ObjectSerializer is implemented. // Preconditions.checkArgument(ClassResolver.requireJavaSerialization(cls)); if (cls != SerializedLambda.class) { @@ -65,19 +68,21 @@ public JavaSerializer(Fory fory, Class cls) { Serializer.class.getName(), Externalizable.class.getName()); } - objectInput = new MemoryBufferObjectInput(fory, null); - objectOutput = new MemoryBufferObjectOutput(fory, null); + objectInput = new MemoryBufferObjectInput(config, null); + objectOutput = new MemoryBufferObjectOutput(config, null); } @Override - public void write(MemoryBuffer buffer, Object value) { + public void write(WriteContext writeContext, Object value) { + MemoryBuffer buffer = writeContext.getBuffer(); try { objectOutput.setBuffer(buffer); + objectOutput.setWriteContext(writeContext); ObjectOutputStream objectOutputStream = - (ObjectOutputStream) fory.getSerializationContext().get(objectOutput); + (ObjectOutputStream) writeContext.getContextObject(objectOutput); if (objectOutputStream == null) { objectOutputStream = new ObjectOutputStream(objectOutput); - fory.getSerializationContext().add(objectOutput, objectOutputStream); + writeContext.putContextObject(objectOutput, objectOutputStream); } objectOutputStream.writeObject(value); objectOutputStream.flush(); @@ -87,14 +92,17 @@ public void write(MemoryBuffer buffer, Object value) { } @Override - public Object read(MemoryBuffer buffer) { + public Object read(ReadContext readContext) { + MemoryBuffer buffer = readContext.getBuffer(); try { objectInput.setBuffer(buffer); + objectInput.setReadContext(readContext); ObjectInputStream objectInputStream = - (ObjectInputStream) fory.getSerializationContext().get(objectInput); + (ObjectInputStream) readContext.getContextObject(objectInput); if (objectInputStream == null) { - objectInputStream = new ClassLoaderObjectInputStream(fory.getClassLoader(), objectInput); - fory.getSerializationContext().add(objectInput, objectInputStream); + objectInputStream = + new ClassLoaderObjectInputStream(typeResolver.getClassLoader(), objectInput); + readContext.putContextObject(objectInput, objectInputStream); } return objectInputStream.readObject(); } catch (IOException | ClassNotFoundException e) { diff --git a/java/fory-core/src/main/java/org/apache/fory/serializer/JdkProxySerializer.java b/java/fory-core/src/main/java/org/apache/fory/serializer/JdkProxySerializer.java index 273a860ef2..12b1d74441 100644 --- a/java/fory-core/src/main/java/org/apache/fory/serializer/JdkProxySerializer.java +++ b/java/fory-core/src/main/java/org/apache/fory/serializer/JdkProxySerializer.java @@ -23,11 +23,13 @@ import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; -import org.apache.fory.Fory; +import org.apache.fory.context.CopyContext; +import org.apache.fory.context.ReadContext; +import org.apache.fory.context.WriteContext; import org.apache.fory.memory.MemoryBuffer; import org.apache.fory.memory.Platform; import org.apache.fory.reflect.ReflectionUtils; -import org.apache.fory.resolver.RefResolver; +import org.apache.fory.resolver.TypeResolver; import org.apache.fory.util.GraalvmSupport; import org.apache.fory.util.Preconditions; @@ -51,17 +53,11 @@ public Object invoke(Object proxy, Method method, Object[] args) throws Throwabl } private static final InvocationHandler STUB_HANDLER = new StubInvocationHandler(); + private final TypeResolver typeResolver; - private interface StubInterface { - int apply(); - } - - public static Object SUBT_PROXY = - Proxy.newProxyInstance( - Serializer.class.getClassLoader(), new Class[] {StubInterface.class}, STUB_HANDLER); - - public JdkProxySerializer(Fory fory, Class cls) { - super(fory, cls); + public JdkProxySerializer(TypeResolver typeResolver, Class cls) { + super(typeResolver.getConfig(), cls); + this.typeResolver = typeResolver; if (cls != ReplaceStub.class) { // Skip proxy class validation in GraalVM native image runtime to avoid issues with proxy // detection @@ -72,32 +68,33 @@ public JdkProxySerializer(Fory fory, Class cls) { } @Override - public void write(MemoryBuffer buffer, Object value) { - fory.writeRef(buffer, value.getClass().getInterfaces()); - fory.writeRef(buffer, Proxy.getInvocationHandler(value)); + public void write(WriteContext writeContext, Object value) { + writeContext.writeRef(value.getClass().getInterfaces()); + writeContext.writeRef(Proxy.getInvocationHandler(value)); } @Override - public Object copy(Object value) { + public Object copy(CopyContext copyContext, Object value) { Class[] interfaces = value.getClass().getInterfaces(); InvocationHandler invocationHandler = Proxy.getInvocationHandler(value); Preconditions.checkNotNull(interfaces); Preconditions.checkNotNull(invocationHandler); - Object proxy = Proxy.newProxyInstance(fory.getClassLoader(), interfaces, STUB_HANDLER); - fory.reference(value, proxy); - Platform.putObject(proxy, PROXY_HANDLER_FIELD_OFFSET, fory.copyObject(invocationHandler)); + Object proxy = + Proxy.newProxyInstance(typeResolver.getClassLoader(), interfaces, STUB_HANDLER); + copyContext.reference(value, proxy); + Platform.putObject(proxy, PROXY_HANDLER_FIELD_OFFSET, copyContext.copyObject(invocationHandler)); return proxy; } @Override - public Object read(MemoryBuffer buffer) { - final RefResolver resolver = fory.getRefResolver(); - final int refId = resolver.lastPreservedRefId(); - final Class[] interfaces = (Class[]) fory.readRef(buffer); + public Object read(ReadContext readContext) { + final int refId = readContext.lastPreservedRefId(); + final Class[] interfaces = (Class[]) readContext.readRef(); Preconditions.checkNotNull(interfaces); - Object proxy = Proxy.newProxyInstance(fory.getClassLoader(), interfaces, STUB_HANDLER); - resolver.setReadObject(refId, proxy); - InvocationHandler invocationHandler = (InvocationHandler) fory.readRef(buffer); + Object proxy = + Proxy.newProxyInstance(typeResolver.getClassLoader(), interfaces, STUB_HANDLER); + readContext.setReadObject(refId, proxy); + InvocationHandler invocationHandler = (InvocationHandler) readContext.readRef(); Preconditions.checkNotNull(invocationHandler); Platform.putObject(proxy, PROXY_HANDLER_FIELD_OFFSET, invocationHandler); return proxy; diff --git a/java/fory-core/src/main/java/org/apache/fory/serializer/LambdaSerializer.java b/java/fory-core/src/main/java/org/apache/fory/serializer/LambdaSerializer.java index 6860742fe5..2b11f22f3a 100644 --- a/java/fory-core/src/main/java/org/apache/fory/serializer/LambdaSerializer.java +++ b/java/fory-core/src/main/java/org/apache/fory/serializer/LambdaSerializer.java @@ -23,11 +23,12 @@ import java.lang.invoke.MethodHandle; import java.lang.invoke.SerializedLambda; import java.lang.reflect.Method; -import org.apache.fory.Fory; import org.apache.fory.collection.ClassValueCache; -import org.apache.fory.memory.MemoryBuffer; +import org.apache.fory.context.CopyContext; +import org.apache.fory.context.ReadContext; +import org.apache.fory.context.WriteContext; +import org.apache.fory.resolver.TypeResolver; import org.apache.fory.util.Preconditions; -import org.apache.fory.util.function.SerializableFunction; import org.apache.fory.util.unsafe._JDKAccess; /** @@ -37,23 +38,17 @@ */ @SuppressWarnings({"unchecked", "rawtypes"}) public class LambdaSerializer extends Serializer { - public static final Class STUB_LAMBDA_CLASS = stubLambdaClass(); private static final ClassValueCache writeReplaceMethodCache = ClassValueCache.newClassKeySoftCache(32); private final MethodHandle writeReplaceHandle; private final SerializedLambdaSerializer serializedLambdaSerializer; - private static Class stubLambdaClass() { - SerializableFunction function = x -> x * 2; - return function.getClass(); - } - - public LambdaSerializer(Fory fory, Class cls) { - super(fory, cls); + public LambdaSerializer(TypeResolver typeResolver, Class cls) { + super(typeResolver.getConfig(), cls); serializedLambdaSerializer = (SerializedLambdaSerializer) - fory.getTypeResolver().getSerializer(SerializedLambdaSerializer.SERIALIZED_LAMBDA); + typeResolver.getSerializer(SerializedLambdaSerializer.SERIALIZED_LAMBDA); if (cls == ReplaceStub.class) { writeReplaceHandle = null; return; @@ -74,22 +69,21 @@ public LambdaSerializer(Fory fory, Class cls) { } @Override - public void write(MemoryBuffer buffer, Object value) { - serializedLambdaSerializer.write(buffer, extractSerializedLambda(value, "serialize")); + public void write(WriteContext writeContext, Object value) { + serializedLambdaSerializer.write(writeContext, extractSerializedLambda(value, "serialize")); } @Override - public Object copy(Object value) { + public Object copy(CopyContext copyContext, Object value) { SerializedLambda serializedLambda = extractSerializedLambda(value, "copy"); return SerializedLambdaSerializer.readResolve( - serializedLambdaSerializer.copy(serializedLambda)); + serializedLambdaSerializer.copy(copyContext, serializedLambda)); } @Override - public Object read(MemoryBuffer buffer) { + public Object read(ReadContext readContext) { try { - return SerializedLambdaSerializer.readResolve( - serializedLambdaSerializer.readUnresolved(buffer)); + return SerializedLambdaSerializer.readResolve(serializedLambdaSerializer.readUnresolved(readContext)); } catch (Throwable e) { throw new RuntimeException("Can't deserialize lambda", e); } diff --git a/java/fory-core/src/main/java/org/apache/fory/serializer/LazySerializer.java b/java/fory-core/src/main/java/org/apache/fory/serializer/LazySerializer.java index 60626a9836..5912cf75b0 100644 --- a/java/fory-core/src/main/java/org/apache/fory/serializer/LazySerializer.java +++ b/java/fory-core/src/main/java/org/apache/fory/serializer/LazySerializer.java @@ -20,59 +20,67 @@ package org.apache.fory.serializer; import java.util.function.Supplier; -import org.apache.fory.Fory; -import org.apache.fory.memory.MemoryBuffer; +import org.apache.fory.config.Config; +import org.apache.fory.context.CopyContext; +import org.apache.fory.context.ReadContext; +import org.apache.fory.context.WriteContext; +import org.apache.fory.resolver.TypeResolver; @SuppressWarnings({"rawtypes", "unchecked"}) public class LazySerializer extends Serializer { + private final Config config; + private final TypeResolver typeResolver; private final Supplier serializerSupplier; private Serializer serializer; - public LazySerializer(Fory fory, Class type, Supplier serializerSupplier) { - super(fory, type); + public LazySerializer(TypeResolver typeResolver, Class type, Supplier serializerSupplier) { + super(typeResolver.getConfig(), type); + this.config = typeResolver.getConfig(); + this.typeResolver = typeResolver; this.serializerSupplier = serializerSupplier; } @Override - public void write(MemoryBuffer buffer, Object value) { + public void write(WriteContext writeContext, Object value) { if (serializer == null) { serializer = serializerSupplier.get(); - fory.getTypeResolver().setSerializer(value.getClass(), serializer); - if (!isJava) { - fory.getTypeResolver().getTypeInfo(value.getClass()).setSerializer(serializer); + typeResolver.setSerializer(value.getClass(), serializer); + if (config.isXlang()) { + typeResolver.getTypeInfo(value.getClass()).setSerializer(serializer); } } - serializer.write(buffer, value); + serializer.write(writeContext, value); } @Override - public Object read(MemoryBuffer buffer) { + public Object read(ReadContext readContext) { boolean unInit = serializer == null; if (unInit) { serializer = serializerSupplier.get(); } - Object value = serializer.read(buffer); + Object value = serializer.read(readContext); if (unInit) { - fory.getTypeResolver().setSerializer(value.getClass(), serializer); - if (!isJava) { - fory.getTypeResolver().getTypeInfo(value.getClass()).setSerializer(serializer); + typeResolver.setSerializer(value.getClass(), serializer); + if (config.isXlang()) { + typeResolver.getTypeInfo(value.getClass()).setSerializer(serializer); } } return value; } @Override - public Object copy(Object value) { + public Object copy(CopyContext copyContext, Object value) { if (serializer == null) { serializer = serializerSupplier.get(); - fory.getTypeResolver().setSerializer(value.getClass(), serializer); + typeResolver.setSerializer(value.getClass(), serializer); } - return serializer.copy(value); + return serializer.copy(copyContext, value); } public static class LazyObjectSerializer extends LazySerializer { - public LazyObjectSerializer(Fory fory, Class type, Supplier serializerSupplier) { - super(fory, type, serializerSupplier); + public LazyObjectSerializer( + TypeResolver typeResolver, Class type, Supplier serializerSupplier) { + super(typeResolver, type, serializerSupplier); } } } diff --git a/java/fory-core/src/main/java/org/apache/fory/serializer/LocaleSerializer.java b/java/fory-core/src/main/java/org/apache/fory/serializer/LocaleSerializer.java index 46e8a5bc34..d2a9352115 100644 --- a/java/fory-core/src/main/java/org/apache/fory/serializer/LocaleSerializer.java +++ b/java/fory-core/src/main/java/org/apache/fory/serializer/LocaleSerializer.java @@ -22,9 +22,10 @@ import java.util.HashMap; import java.util.Locale; import java.util.Map; -import org.apache.fory.Fory; import org.apache.fory.collection.Tuple3; -import org.apache.fory.memory.MemoryBuffer; +import org.apache.fory.config.Config; +import org.apache.fory.context.ReadContext; +import org.apache.fory.context.WriteContext; /** Local serializer for {@link Locale}. */ public final class LocaleSerializer extends ImmutableSerializer { @@ -65,20 +66,20 @@ private static void populateMap(Map, Locale> map, map.put(Tuple3.of(locale.getCountry(), locale.getLanguage(), locale.getVariant()), locale); } - public LocaleSerializer(Fory fory) { - super(fory, Locale.class); + public LocaleSerializer(Config config) { + super(config, Locale.class); } - public void write(MemoryBuffer buffer, Locale l) { - fory.writeString(buffer, l.getLanguage()); - fory.writeString(buffer, l.getCountry()); - fory.writeString(buffer, l.getVariant()); + public void write(WriteContext writeContext, Locale l) { + writeContext.writeString(l.getLanguage()); + writeContext.writeString(l.getCountry()); + writeContext.writeString(l.getVariant()); } - public Locale read(MemoryBuffer buffer) { - String language = fory.readString(buffer); - String country = fory.readString(buffer); - String variant = fory.readString(buffer); + public Locale read(ReadContext readContext) { + String language = readContext.readString(); + String country = readContext.readString(); + String variant = readContext.readString(); // Fast path for Default/US/SIMPLIFIED_CHINESE Locale defaultLocale = Locale.getDefault(); if (isSame(defaultLocale, language, country, variant)) { @@ -102,4 +103,9 @@ static boolean isSame(Locale locale, String language, String country, String var && locale.getCountry().equals(country) && locale.getVariant().equals(variant)); } + + @Override + public boolean threadSafe() { + return true; + } } diff --git a/java/fory-core/src/main/java/org/apache/fory/serializer/MetaSharedLayerSerializer.java b/java/fory-core/src/main/java/org/apache/fory/serializer/MetaSharedLayerSerializer.java index cf560222f9..7211b7f800 100644 --- a/java/fory-core/src/main/java/org/apache/fory/serializer/MetaSharedLayerSerializer.java +++ b/java/fory-core/src/main/java/org/apache/fory/serializer/MetaSharedLayerSerializer.java @@ -19,8 +19,8 @@ package org.apache.fory.serializer; -import org.apache.fory.Fory; import org.apache.fory.meta.TypeDef; +import org.apache.fory.resolver.TypeResolver; /** * Interpreter implementation for a single meta-shared class layer. Generated layer serializers @@ -30,8 +30,8 @@ public class MetaSharedLayerSerializer extends MetaSharedLayerSerializerBase { public MetaSharedLayerSerializer( - Fory fory, Class type, TypeDef layerTypeDef, Class layerMarkerClass) { - super(fory, type); + TypeResolver typeResolver, Class type, TypeDef layerTypeDef, Class layerMarkerClass) { + super(typeResolver, type); setLayerSerializerMeta(layerTypeDef, layerMarkerClass); } } diff --git a/java/fory-core/src/main/java/org/apache/fory/serializer/MetaSharedLayerSerializerBase.java b/java/fory-core/src/main/java/org/apache/fory/serializer/MetaSharedLayerSerializerBase.java index d21adde48f..35723f620a 100644 --- a/java/fory-core/src/main/java/org/apache/fory/serializer/MetaSharedLayerSerializerBase.java +++ b/java/fory-core/src/main/java/org/apache/fory/serializer/MetaSharedLayerSerializerBase.java @@ -19,13 +19,16 @@ package org.apache.fory.serializer; -import org.apache.fory.Fory; import org.apache.fory.collection.IdentityObjectIntMap; import org.apache.fory.collection.ObjectIntMap; +import org.apache.fory.context.MetaContext; +import org.apache.fory.context.ReadContext; +import org.apache.fory.context.RefReader; +import org.apache.fory.context.RefWriter; +import org.apache.fory.context.WriteContext; import org.apache.fory.memory.MemoryBuffer; import org.apache.fory.meta.TypeDef; import org.apache.fory.reflect.FieldAccessor; -import org.apache.fory.resolver.MetaContext; import org.apache.fory.resolver.TypeResolver; import org.apache.fory.serializer.FieldGroups.SerializationFieldInfo; import org.apache.fory.type.DescriptorGrouper; @@ -33,9 +36,8 @@ import org.apache.fory.util.Preconditions; /** - * Base class for meta-shared layer serializers. The default implementation uses the reflection - * field accessors built from the layer {@link TypeDef}. Generated layer serializers override only - * the hot field read/write methods and reuse the remaining reflection-backed helpers here. + * Base class for meta-shared layer serializers. The default implementation uses reflection-backed + * field access and generated layer serializers override only the hot field read/write methods. */ @SuppressWarnings({"unchecked", "rawtypes"}) public abstract class MetaSharedLayerSerializerBase extends AbstractObjectSerializer { @@ -45,8 +47,8 @@ public abstract class MetaSharedLayerSerializerBase extends AbstractObjectSer protected SerializationFieldInfo[] otherFields = new SerializationFieldInfo[0]; protected SerializationFieldInfo[] containerFields = new SerializationFieldInfo[0]; - public MetaSharedLayerSerializerBase(Fory fory, Class type) { - super(fory, type); + public MetaSharedLayerSerializerBase(TypeResolver typeResolver, Class type) { + super(typeResolver, type); } public final void setLayerSerializerMeta(TypeDef layerTypeDef, Class layerMarkerClass) { @@ -60,12 +62,11 @@ public final void setLayerSerializerMeta(TypeDef layerTypeDef, Class layerMar } this.layerTypeDef = layerTypeDef; this.layerMarkerClass = layerMarkerClass; - TypeResolver typeResolver = fory.getTypeResolver(); DescriptorGrouper descriptorGrouper = typeResolver.createDescriptorGrouper(layerTypeDef, type); - FieldGroups fieldGroups = FieldGroups.buildFieldInfos(fory, descriptorGrouper); - this.buildInFields = fieldGroups.buildInFields; - this.otherFields = fieldGroups.userTypeFields; - this.containerFields = fieldGroups.containerFields; + FieldGroups fieldGroups = FieldGroups.buildFieldInfos(typeResolver, descriptorGrouper); + buildInFields = fieldGroups.buildInFields; + otherFields = fieldGroups.userTypeFields; + containerFields = fieldGroups.containerFields; } protected final void checkLayerSerializerMeta() { @@ -73,16 +74,17 @@ protected final void checkLayerSerializerMeta() { } @Override - public void write(MemoryBuffer buffer, T value) { - if (fory.getConfig().isMetaShareEnabled()) { - writeLayerClassMeta(buffer); + public void write(WriteContext writeContext, T value) { + MemoryBuffer buffer = writeContext.getBuffer(); + if (config.isMetaShareEnabled()) { + writeLayerClassMeta(writeContext, buffer); } - writeFieldsOnly(buffer, value); + writeFieldsOnly(writeContext, buffer, value); } - public void writeLayerClassMeta(MemoryBuffer buffer) { + public void writeLayerClassMeta(WriteContext writeContext, MemoryBuffer buffer) { checkLayerSerializerMeta(); - MetaContext metaContext = fory.getSerializationContext().getMetaContext(); + MetaContext metaContext = writeContext.getMetaContext(); if (metaContext == null) { return; } @@ -97,66 +99,69 @@ public void writeLayerClassMeta(MemoryBuffer buffer) { } } - public void writeFieldsOnly(MemoryBuffer buffer, T value) { + public void writeFieldsOnly(WriteContext writeContext, MemoryBuffer buffer, T value) { checkLayerSerializerMeta(); - writeBuildInFields(buffer, value); - writeContainerFields(buffer, value); - writeOtherFields(buffer, value); + writeBuildInFields(writeContext, buffer, value); + writeContainerFields(writeContext, buffer, value); + writeOtherFields(writeContext, buffer, value); } - public void writeFieldValues(MemoryBuffer buffer, Object[] vals) { + public void writeFieldValues(WriteContext writeContext, MemoryBuffer buffer, Object[] vals) { checkLayerSerializerMeta(); + RefWriter refWriter = writeContext.getRefWriter(); int index = 0; for (SerializationFieldInfo fieldInfo : buildInFields) { AbstractObjectSerializer.writeBuildInFieldValue( - fory, typeResolver, refResolver, fieldInfo, buffer, vals[index++]); + writeContext, typeResolver, refWriter, fieldInfo, buffer, vals[index++]); } - Generics generics = fory.getGenerics(); + Generics generics = writeContext.getGenerics(); for (SerializationFieldInfo fieldInfo : containerFields) { AbstractObjectSerializer.writeContainerFieldValue( - fory, typeResolver, refResolver, generics, fieldInfo, buffer, vals[index++]); + writeContext, typeResolver, refWriter, generics, fieldInfo, buffer, vals[index++]); } for (SerializationFieldInfo fieldInfo : otherFields) { AbstractObjectSerializer.writeField( - fory, typeResolver, refResolver, fieldInfo, buffer, vals[index++]); + writeContext, typeResolver, refWriter, fieldInfo, buffer, vals[index++]); } } - public Object[] readFieldValues(MemoryBuffer buffer) { + public Object[] readFieldValues(ReadContext readContext, MemoryBuffer buffer) { checkLayerSerializerMeta(); + RefReader refReader = readContext.getRefReader(); Object[] vals = new Object[getNumFields()]; int index = 0; for (SerializationFieldInfo fieldInfo : buildInFields) { vals[index++] = AbstractObjectSerializer.readBuildInFieldValue( - fory, typeResolver, refResolver, fieldInfo, buffer); + readContext, typeResolver, refReader, fieldInfo, buffer); } - Generics generics = fory.getGenerics(); + Generics generics = readContext.getGenerics(); for (SerializationFieldInfo fieldInfo : containerFields) { vals[index++] = AbstractObjectSerializer.readContainerFieldValue( - fory, typeResolver, refResolver, generics, fieldInfo, buffer); + readContext, typeResolver, refReader, generics, fieldInfo, buffer); } for (SerializationFieldInfo fieldInfo : otherFields) { vals[index++] = - AbstractObjectSerializer.readField(fory, typeResolver, refResolver, fieldInfo, buffer); + AbstractObjectSerializer.readField(readContext, typeResolver, refReader, fieldInfo, buffer); } return vals; } @Override - public T read(MemoryBuffer buffer) { + public T read(ReadContext readContext) { checkLayerSerializerMeta(); + MemoryBuffer buffer = readContext.getBuffer(); T obj = newBean(); - refResolver.reference(obj); - return readAndSetFields(buffer, obj); + readContext.reference(obj); + return readAndSetFields(readContext, buffer, obj); } - public T readAndSetFields(MemoryBuffer buffer, T obj) { + public T readAndSetFields(ReadContext readContext, MemoryBuffer buffer, T obj) { checkLayerSerializerMeta(); - readBuildInFields(buffer, obj); - readContainerFields(buffer, obj); - readOtherFields(buffer, obj); + readBuildInFields(readContext, buffer, obj); + readContainerFields(readContext, buffer, obj); + readOtherFields(readContext, buffer, obj); return obj; } @@ -198,57 +203,62 @@ public Object[] getFieldValuesForPutFields( return vals; } - public void skipFields(MemoryBuffer buffer) { + public void skipFields(ReadContext readContext, MemoryBuffer buffer) { checkLayerSerializerMeta(); - skipBuildInFields(buffer); - skipContainerFields(buffer); - skipOtherFields(buffer); + skipBuildInFields(readContext, buffer); + skipContainerFields(readContext, buffer); + skipOtherFields(readContext, buffer); } - private void writeBuildInFields(MemoryBuffer buffer, T value) { + private void writeBuildInFields(WriteContext writeContext, MemoryBuffer buffer, T value) { + RefWriter refWriter = writeContext.getRefWriter(); for (SerializationFieldInfo fieldInfo : buildInFields) { AbstractObjectSerializer.writeBuildInField( - fory, typeResolver, refResolver, fieldInfo, buffer, value); + writeContext, typeResolver, refWriter, fieldInfo, buffer, value); } } - private void writeContainerFields(MemoryBuffer buffer, T value) { - Generics generics = fory.getGenerics(); + private void writeContainerFields(WriteContext writeContext, MemoryBuffer buffer, T value) { + RefWriter refWriter = writeContext.getRefWriter(); + Generics generics = writeContext.getGenerics(); for (SerializationFieldInfo fieldInfo : containerFields) { FieldAccessor fieldAccessor = fieldInfo.fieldAccessor; Object fieldValue = fieldAccessor.getObject(value); AbstractObjectSerializer.writeContainerFieldValue( - fory, typeResolver, refResolver, generics, fieldInfo, buffer, fieldValue); + writeContext, typeResolver, refWriter, generics, fieldInfo, buffer, fieldValue); } } - private void writeOtherFields(MemoryBuffer buffer, T value) { + private void writeOtherFields(WriteContext writeContext, MemoryBuffer buffer, T value) { + RefWriter refWriter = writeContext.getRefWriter(); for (SerializationFieldInfo fieldInfo : otherFields) { FieldAccessor fieldAccessor = fieldInfo.fieldAccessor; Object fieldValue = fieldAccessor.getObject(value); AbstractObjectSerializer.writeField( - fory, typeResolver, refResolver, fieldInfo, buffer, fieldValue); + writeContext, typeResolver, refWriter, fieldInfo, buffer, fieldValue); } } - private void readBuildInFields(MemoryBuffer buffer, T targetObject) { + private void readBuildInFields(ReadContext readContext, MemoryBuffer buffer, T targetObject) { + RefReader refReader = readContext.getRefReader(); for (SerializationFieldInfo fieldInfo : buildInFields) { FieldAccessor fieldAccessor = fieldInfo.fieldAccessor; if (fieldAccessor != null) { AbstractObjectSerializer.readBuildInFieldValue( - fory, typeResolver, refResolver, fieldInfo, buffer, targetObject); + readContext, typeResolver, refReader, fieldInfo, buffer, targetObject); } else { - FieldSkipper.skipField(fory, typeResolver, refResolver, fieldInfo, buffer); + FieldSkipper.skipField(readContext, typeResolver, refReader, fieldInfo, buffer); } } } - private void readContainerFields(MemoryBuffer buffer, T obj) { - Generics generics = fory.getGenerics(); + private void readContainerFields(ReadContext readContext, MemoryBuffer buffer, T obj) { + RefReader refReader = readContext.getRefReader(); + Generics generics = readContext.getGenerics(); for (SerializationFieldInfo fieldInfo : containerFields) { Object fieldValue = AbstractObjectSerializer.readContainerFieldValue( - fory, typeResolver, refResolver, generics, fieldInfo, buffer); + readContext, typeResolver, refReader, generics, fieldInfo, buffer); FieldAccessor fieldAccessor = fieldInfo.fieldAccessor; if (fieldAccessor != null) { fieldAccessor.putObject(obj, fieldValue); @@ -256,10 +266,11 @@ private void readContainerFields(MemoryBuffer buffer, T obj) { } } - private void readOtherFields(MemoryBuffer buffer, T obj) { + private void readOtherFields(ReadContext readContext, MemoryBuffer buffer, T obj) { + RefReader refReader = readContext.getRefReader(); for (SerializationFieldInfo fieldInfo : otherFields) { Object fieldValue = - AbstractObjectSerializer.readField(fory, typeResolver, refResolver, fieldInfo, buffer); + AbstractObjectSerializer.readField(readContext, typeResolver, refReader, fieldInfo, buffer); FieldAccessor fieldAccessor = fieldInfo.fieldAccessor; if (fieldAccessor != null) { fieldAccessor.putObject(obj, fieldValue); @@ -312,23 +323,26 @@ private void collectPutFieldValues( } } - private void skipBuildInFields(MemoryBuffer buffer) { + private void skipBuildInFields(ReadContext readContext, MemoryBuffer buffer) { + RefReader refReader = readContext.getRefReader(); for (SerializationFieldInfo fieldInfo : buildInFields) { - FieldSkipper.skipField(fory, typeResolver, refResolver, fieldInfo, buffer); + FieldSkipper.skipField(readContext, typeResolver, refReader, fieldInfo, buffer); } } - private void skipContainerFields(MemoryBuffer buffer) { - Generics generics = fory.getGenerics(); + private void skipContainerFields(ReadContext readContext, MemoryBuffer buffer) { + RefReader refReader = readContext.getRefReader(); + Generics generics = readContext.getGenerics(); for (SerializationFieldInfo fieldInfo : containerFields) { AbstractObjectSerializer.readContainerFieldValue( - fory, typeResolver, refResolver, generics, fieldInfo, buffer); + readContext, typeResolver, refReader, generics, fieldInfo, buffer); } } - private void skipOtherFields(MemoryBuffer buffer) { + private void skipOtherFields(ReadContext readContext, MemoryBuffer buffer) { + RefReader refReader = readContext.getRefReader(); for (SerializationFieldInfo fieldInfo : otherFields) { - AbstractObjectSerializer.readField(fory, typeResolver, refResolver, fieldInfo, buffer); + AbstractObjectSerializer.readField(readContext, typeResolver, refReader, fieldInfo, buffer); } } } diff --git a/java/fory-core/src/main/java/org/apache/fory/serializer/MetaSharedSerializer.java b/java/fory-core/src/main/java/org/apache/fory/serializer/MetaSharedSerializer.java index fa54ff2428..95e4b91be9 100644 --- a/java/fory-core/src/main/java/org/apache/fory/serializer/MetaSharedSerializer.java +++ b/java/fory-core/src/main/java/org/apache/fory/serializer/MetaSharedSerializer.java @@ -22,10 +22,12 @@ import java.util.Arrays; import java.util.List; import java.util.stream.Collectors; -import org.apache.fory.Fory; import org.apache.fory.builder.MetaSharedCodecBuilder; import org.apache.fory.config.CompatibleMode; import org.apache.fory.config.ForyBuilder; +import org.apache.fory.context.ReadContext; +import org.apache.fory.context.RefReader; +import org.apache.fory.context.WriteContext; import org.apache.fory.logging.Logger; import org.apache.fory.logging.LoggerFactory; import org.apache.fory.memory.MemoryBuffer; @@ -33,8 +35,7 @@ import org.apache.fory.meta.TypeDef; import org.apache.fory.reflect.FieldAccessor; import org.apache.fory.resolver.ClassResolver; -import org.apache.fory.resolver.MapRefResolver; -import org.apache.fory.resolver.RefResolver; +import org.apache.fory.resolver.TypeResolver; import org.apache.fory.serializer.FieldGroups.SerializationFieldInfo; import org.apache.fory.type.Descriptor; import org.apache.fory.type.DescriptorGrouper; @@ -75,13 +76,12 @@ public class MetaSharedSerializer extends AbstractObjectSerializer { private final boolean hasDefaultValues; private final DefaultValueUtils.DefaultValueField[] defaultValueFields; - public MetaSharedSerializer(Fory fory, Class type, TypeDef typeDef) { - super(fory, type); + public MetaSharedSerializer(TypeResolver typeResolver, Class type, TypeDef typeDef) { + super(typeResolver, type); Preconditions.checkArgument( - !fory.getConfig().checkClassVersion(), + !config.checkClassVersion(), "Class version check should be disabled when compatible mode is enabled."); - Preconditions.checkArgument( - fory.getConfig().isMetaShareEnabled(), "Meta share must be enabled."); + Preconditions.checkArgument(config.isMetaShareEnabled(), "Meta share must be enabled."); if (Utils.DEBUG_OUTPUT_ENABLED) { LOG.info("========== MetaSharedSerializer TypeDef for {} ==========", type.getName()); LOG.info("TypeDef fieldsInfo count: {}", typeDef.getFieldCount()); @@ -90,7 +90,7 @@ public MetaSharedSerializer(Fory fory, Class type, TypeDef typeDef) { } } DescriptorGrouper descriptorGrouper = - fory.getTypeResolver().createDescriptorGrouper(typeDef, type); + typeResolver.createDescriptorGrouper(typeDef, type); if (Utils.DEBUG_OUTPUT_ENABLED) { LOG.info( "========== MetaSharedSerializer sorted descriptors for {} ==========", type.getName()); @@ -101,11 +101,11 @@ public MetaSharedSerializer(Fory fory, Class type, TypeDef typeDef) { d.getTypeName(), d.isTrackingRef(), d.isNullable(), - Types.getDescriptorTypeId(fory, d)); + Types.getDescriptorTypeId(typeResolver, d)); } } // d.getField() may be null if not exists in this class when meta share enabled. - FieldGroups fieldGroups = FieldGroups.buildFieldInfos(fory, descriptorGrouper); + FieldGroups fieldGroups = FieldGroups.buildFieldInfos(typeResolver, descriptorGrouper); buildInFields = fieldGroups.buildInFields; containerFields = fieldGroups.containerFields; otherFields = fieldGroups.userTypeFields; @@ -122,12 +122,12 @@ public MetaSharedSerializer(Fory fory, Class type, TypeDef typeDef) { DefaultValueUtils.DefaultValueField[] defaultValueFields = new DefaultValueUtils.DefaultValueField[0]; DefaultValueUtils.DefaultValueSupport defaultValueSupport; - if (fory.getConfig().isScalaOptimizationEnabled()) { + if (config.isScalaOptimizationEnabled()) { defaultValueSupport = DefaultValueUtils.getScalaDefaultValueSupport(); hasDefaultValues = defaultValueSupport.hasDefaultValues(type); - defaultValueFields = - defaultValueSupport.buildDefaultValueFields( - fory, type, descriptorGrouper.getSortedDescriptors()); + defaultValueFields = + defaultValueSupport.buildDefaultValueFields( + typeResolver, type, descriptorGrouper.getSortedDescriptors()); } if (!hasDefaultValues) { DefaultValueUtils.DefaultValueSupport kotlinDefaultValueSupport = @@ -136,7 +136,7 @@ public MetaSharedSerializer(Fory fory, Class type, TypeDef typeDef) { hasDefaultValues = kotlinDefaultValueSupport.hasDefaultValues(type); defaultValueFields = kotlinDefaultValueSupport.buildDefaultValueFields( - fory, type, descriptorGrouper.getSortedDescriptors()); + typeResolver, type, descriptorGrouper.getSortedDescriptors()); } } this.hasDefaultValues = hasDefaultValues; @@ -144,14 +144,15 @@ public MetaSharedSerializer(Fory fory, Class type, TypeDef typeDef) { } @Override - public void write(MemoryBuffer buffer, T value) { + public void write(WriteContext writeContext, T value) { + MemoryBuffer buffer = writeContext.getBuffer(); if (serializer == null) { // xlang mode will register class and create serializer in advance, it won't go to here. serializer = ((ClassResolver) typeResolver) - .createSerializerSafe(type, () -> new ObjectSerializer<>(fory, type)); + .createSerializerSafe(type, () -> new ObjectSerializer<>(typeResolver, type)); } - serializer.write(buffer, value); + serializer.write(writeContext, value); } private T newInstance() { @@ -165,27 +166,22 @@ private T newInstance() { } @Override - public T read(MemoryBuffer buffer) { + public T read(ReadContext readContext) { + MemoryBuffer buffer = readContext.getBuffer(); if (isRecord) { Object[] fieldValues = new Object[buildInFields.length + otherFields.length + containerFields.length]; - readFields(buffer, fieldValues); + readFields(readContext, buffer, fieldValues); fieldValues = RecordUtils.remapping(recordInfo, fieldValues); T t = objectCreator.newInstanceWithArguments(fieldValues); Arrays.fill(recordInfo.getRecordComponents(), null); return t; } T targetObject = newInstance(); - Fory fory = this.fory; - RefResolver refResolver = this.refResolver; - if (refResolver instanceof MapRefResolver) { - MapRefResolver mapRefResolver = (MapRefResolver) refResolver; - if (mapRefResolver.hasPreservedRefId()) { - refResolver.reference(targetObject); - } - } else { - refResolver.reference(targetObject); + if (readContext.hasPreservedRefId()) { + readContext.reference(targetObject); } + RefReader refReader = readContext.getRefReader(); // read order: primitive,boxed,final,other,collection,map for (SerializationFieldInfo fieldInfo : this.buildInFields) { if (Utils.DEBUG_OUTPUT_VERBOSE) { @@ -194,24 +190,24 @@ public T read(MemoryBuffer buffer) { FieldAccessor fieldAccessor = fieldInfo.fieldAccessor; if (fieldAccessor != null) { AbstractObjectSerializer.readBuildInFieldValue( - fory, typeResolver, refResolver, fieldInfo, buffer, targetObject); + readContext, typeResolver, refReader, fieldInfo, buffer, targetObject); } else { if (fieldInfo.fieldConverter == null) { // Skip the field value from buffer since it doesn't exist in current class - FieldSkipper.skipField(fory, typeResolver, refResolver, fieldInfo, buffer); + FieldSkipper.skipField(readContext, typeResolver, refReader, fieldInfo, buffer); } else { - compatibleRead(buffer, fieldInfo, targetObject); + compatibleRead(readContext, buffer, fieldInfo, targetObject); } } } - Generics generics = fory.getGenerics(); + Generics generics = readContext.getGenerics(); for (SerializationFieldInfo fieldInfo : containerFields) { if (Utils.DEBUG_OUTPUT_VERBOSE) { printFieldDebugInfo(fieldInfo, buffer); } Object fieldValue = AbstractObjectSerializer.readContainerFieldValue( - fory, typeResolver, refResolver, generics, fieldInfo, buffer); + readContext, typeResolver, refReader, generics, fieldInfo, buffer); FieldAccessor fieldAccessor = fieldInfo.fieldAccessor; if (fieldAccessor != null) { fieldAccessor.putObject(targetObject, fieldValue); @@ -222,7 +218,7 @@ public T read(MemoryBuffer buffer) { printFieldDebugInfo(fieldInfo, buffer); } Object fieldValue = - AbstractObjectSerializer.readField(fory, typeResolver, refResolver, fieldInfo, buffer); + AbstractObjectSerializer.readField(readContext, typeResolver, refReader, fieldInfo, buffer); FieldAccessor fieldAccessor = fieldInfo.fieldAccessor; if (fieldAccessor != null) { fieldAccessor.putObject(targetObject, fieldValue); @@ -231,17 +227,17 @@ public T read(MemoryBuffer buffer) { return targetObject; } - private void compatibleRead(MemoryBuffer buffer, SerializationFieldInfo fieldInfo, Object obj) { + private void compatibleRead( + ReadContext readContext, MemoryBuffer buffer, SerializationFieldInfo fieldInfo, Object obj) { Object fieldValue = AbstractObjectSerializer.readBuildInFieldValue( - fory, typeResolver, refResolver, fieldInfo, buffer); + readContext, typeResolver, readContext.getRefReader(), fieldInfo, buffer); fieldInfo.fieldConverter.set(obj, fieldValue); } - private void readFields(MemoryBuffer buffer, Object[] fields) { + private void readFields(ReadContext readContext, MemoryBuffer buffer, Object[] fields) { int counter = 0; - Fory fory = this.fory; - RefResolver refResolver = this.refResolver; + RefReader refReader = readContext.getRefReader(); // read order: primitive,boxed,final,other,collection,map for (SerializationFieldInfo fieldInfo : this.buildInFields) { if (Utils.DEBUG_OUTPUT_ENABLED) { @@ -250,24 +246,24 @@ private void readFields(MemoryBuffer buffer, Object[] fields) { if (fieldInfo.fieldAccessor != null) { fields[counter++] = AbstractObjectSerializer.readBuildInFieldValue( - fory, typeResolver, refResolver, fieldInfo, buffer); + readContext, typeResolver, refReader, fieldInfo, buffer); } else { // Skip the field value from buffer since it doesn't exist in current class. // For records, fieldConverter can't be used since records are immutable and // constructed all at once. We just read to advance buffer position. - FieldSkipper.skipField(fory, typeResolver, refResolver, fieldInfo, buffer); + FieldSkipper.skipField(readContext, typeResolver, refReader, fieldInfo, buffer); // remapping will handle those extra fields from peers. fields[counter++] = null; } } - Generics generics = fory.getGenerics(); + Generics generics = readContext.getGenerics(); for (SerializationFieldInfo fieldInfo : containerFields) { if (Utils.DEBUG_OUTPUT_ENABLED) { printFieldDebugInfo(fieldInfo, buffer); } Object fieldValue = AbstractObjectSerializer.readContainerFieldValue( - fory, typeResolver, refResolver, generics, fieldInfo, buffer); + readContext, typeResolver, refReader, generics, fieldInfo, buffer); fields[counter++] = fieldValue; } for (SerializationFieldInfo fieldInfo : otherFields) { @@ -275,7 +271,7 @@ private void readFields(MemoryBuffer buffer, Object[] fields) { printFieldDebugInfo(fieldInfo, buffer); } Object fieldValue = - AbstractObjectSerializer.readField(fory, typeResolver, refResolver, fieldInfo, buffer); + AbstractObjectSerializer.readField(readContext, typeResolver, refReader, fieldInfo, buffer); fields[counter++] = fieldValue; } } diff --git a/java/fory-core/src/main/java/org/apache/fory/serializer/NoneSerializer.java b/java/fory-core/src/main/java/org/apache/fory/serializer/NoneSerializer.java index 9c4beb7223..5e25ac62db 100644 --- a/java/fory-core/src/main/java/org/apache/fory/serializer/NoneSerializer.java +++ b/java/fory-core/src/main/java/org/apache/fory/serializer/NoneSerializer.java @@ -19,20 +19,22 @@ package org.apache.fory.serializer; -import org.apache.fory.Fory; -import org.apache.fory.memory.MemoryBuffer; +import org.apache.fory.config.Config; +import org.apache.fory.context.ReadContext; +import org.apache.fory.context.WriteContext; @SuppressWarnings("rawtypes") public class NoneSerializer extends Serializer { - public NoneSerializer(Fory fory, Class type) { - super(fory, type); + public NoneSerializer(Config config, Class type) { + super(config, type); } @Override - public void write(MemoryBuffer buffer, Object value) {} + public void write(WriteContext writeContext, Object value) { + } @Override - public Object read(MemoryBuffer buffer) { + public Object read(ReadContext readContext) { return null; } } diff --git a/java/fory-core/src/main/java/org/apache/fory/serializer/ObjectSerializer.java b/java/fory-core/src/main/java/org/apache/fory/serializer/ObjectSerializer.java index b756054119..fdd224434f 100644 --- a/java/fory-core/src/main/java/org/apache/fory/serializer/ObjectSerializer.java +++ b/java/fory-core/src/main/java/org/apache/fory/serializer/ObjectSerializer.java @@ -21,19 +21,23 @@ import static org.apache.fory.serializer.AbstractObjectSerializer.readBuildInFieldValue; +import org.apache.fory.context.ReadContext; +import org.apache.fory.context.WriteContext; + import java.nio.charset.StandardCharsets; import java.util.Arrays; import java.util.Collection; import java.util.List; import java.util.stream.Collectors; -import org.apache.fory.Fory; +import org.apache.fory.context.RefReader; +import org.apache.fory.context.RefWriter; +import org.apache.fory.resolver.TypeResolver; import org.apache.fory.exception.ForyException; import org.apache.fory.logging.Logger; import org.apache.fory.logging.LoggerFactory; import org.apache.fory.memory.MemoryBuffer; import org.apache.fory.meta.TypeDef; import org.apache.fory.reflect.FieldAccessor; -import org.apache.fory.resolver.RefResolver; import org.apache.fory.serializer.FieldGroups.SerializationFieldInfo; import org.apache.fory.serializer.struct.Fingerprint; import org.apache.fory.type.Descriptor; @@ -68,12 +72,12 @@ public final class ObjectSerializer extends AbstractObjectSerializer { private final SerializationFieldInfo[] containerFields; private final int classVersionHash; - public ObjectSerializer(Fory fory, Class cls) { - this(fory, cls, true); + public ObjectSerializer(TypeResolver typeResolver, Class cls) { + this(typeResolver, cls, true); } - public ObjectSerializer(Fory fory, Class cls, boolean resolveParent) { - super(fory, cls); + public ObjectSerializer(TypeResolver typeResolver, Class cls, boolean resolveParent) { + super(typeResolver, cls); // avoid recursive building serializers. // Use `setSerializerIfAbsent` to avoid overwriting existing serializer for class when used // as data serializer. @@ -82,7 +86,7 @@ public ObjectSerializer(Fory fory, Class cls, boolean resolveParent) { } Collection descriptors; DescriptorGrouper grouper; - boolean shareMeta = fory.getConfig().isMetaShareEnabled(); + boolean shareMeta = config.isMetaShareEnabled(); if (shareMeta) { TypeDef typeDef = typeResolver.getTypeDef(cls, resolveParent); if (Utils.DEBUG_OUTPUT_ENABLED) { @@ -122,28 +126,28 @@ public ObjectSerializer(Fory fory, Class cls, boolean resolveParent) { } else { recordInfo = null; } - if (fory.checkClassVersion()) { - classVersionHash = computeStructHash(fory, grouper); + if (typeResolver.checkClassVersion()) { + classVersionHash = computeStructHash(typeResolver, grouper); } else { classVersionHash = 0; } - FieldGroups fieldGroups = FieldGroups.buildFieldInfos(fory, grouper); + FieldGroups fieldGroups = FieldGroups.buildFieldInfos(typeResolver, grouper); buildInFields = fieldGroups.buildInFields; otherFields = fieldGroups.userTypeFields; containerFields = fieldGroups.containerFields; } @Override - public void write(MemoryBuffer buffer, T value) { - Fory fory = this.fory; - RefResolver refResolver = this.refResolver; - if (fory.checkClassVersion()) { + public void write(WriteContext writeContext, T value) { + MemoryBuffer buffer = writeContext.getBuffer(); + if (typeResolver.checkClassVersion()) { buffer.writeInt32(classVersionHash); } // write order: primitive,boxed,final,other,collection,map - writeBuildInFields(buffer, value, fory); - writeContainerFields(buffer, value, fory, refResolver); - writeOtherFields(buffer, value); + RefWriter refWriter = writeContext.getRefWriter(); + writeBuildInFields(writeContext, buffer, value, refWriter); + writeContainerFields(writeContext, buffer, value, refWriter); + writeOtherFields(writeContext, buffer, value); } private void printWriteFieldDebugInfo(SerializationFieldInfo fieldInfo, MemoryBuffer buffer) { @@ -162,7 +166,8 @@ private void printReadFieldDebugInfo(SerializationFieldInfo fieldInfo, MemoryBuf buffer.readerIndex()); } - private void writeOtherFields(MemoryBuffer buffer, T value) { + private void writeOtherFields(WriteContext writeContext, MemoryBuffer buffer, T value) { + RefWriter refWriter = writeContext.getRefWriter(); for (SerializationFieldInfo fieldInfo : otherFields) { if (Utils.DEBUG_OUTPUT_VERBOSE) { printWriteFieldDebugInfo(fieldInfo, buffer); @@ -170,23 +175,24 @@ private void writeOtherFields(MemoryBuffer buffer, T value) { FieldAccessor fieldAccessor = fieldInfo.fieldAccessor; Object fieldValue = fieldAccessor.getObject(value); AbstractObjectSerializer.writeField( - fory, typeResolver, refResolver, fieldInfo, buffer, fieldValue); + writeContext, typeResolver, refWriter, fieldInfo, buffer, fieldValue); } } - private void writeBuildInFields(MemoryBuffer buffer, T value, Fory fory) { + private void writeBuildInFields( + WriteContext writeContext, MemoryBuffer buffer, T value, RefWriter refWriter) { for (SerializationFieldInfo fieldInfo : this.buildInFields) { if (Utils.DEBUG_OUTPUT_VERBOSE) { printWriteFieldDebugInfo(fieldInfo, buffer); } AbstractObjectSerializer.writeBuildInField( - fory, typeResolver, refResolver, fieldInfo, buffer, value); + writeContext, typeResolver, refWriter, fieldInfo, buffer, value); } } private void writeContainerFields( - MemoryBuffer buffer, T value, Fory fory, RefResolver refResolver) { - Generics generics = fory.getGenerics(); + WriteContext writeContext, MemoryBuffer buffer, T value, RefWriter refWriter) { + Generics generics = writeContext.getGenerics(); for (SerializationFieldInfo fieldInfo : containerFields) { if (Utils.DEBUG_OUTPUT_VERBOSE) { printWriteFieldDebugInfo(fieldInfo, buffer); @@ -194,27 +200,28 @@ private void writeContainerFields( FieldAccessor fieldAccessor = fieldInfo.fieldAccessor; Object fieldValue = fieldAccessor.getObject(value); writeContainerFieldValue( - fory, typeResolver, refResolver, generics, fieldInfo, buffer, fieldValue); + writeContext, typeResolver, refWriter, generics, fieldInfo, buffer, fieldValue); } } @Override - public T read(MemoryBuffer buffer) { + public T read(ReadContext readContext) { + MemoryBuffer buffer = readContext.getBuffer(); if (isRecord) { - Object[] fields = readFields(buffer); + Object[] fields = readFields(readContext, buffer); fields = RecordUtils.remapping(recordInfo, fields); T obj = objectCreator.newInstanceWithArguments(fields); Arrays.fill(recordInfo.getRecordComponents(), null); return obj; } T obj = newBean(); - refResolver.reference(obj); - return readAndSetFields(buffer, obj); + readContext.reference(obj); + return readAndSetFields(readContext, buffer, obj); } - public Object[] readFields(MemoryBuffer buffer) { - Fory fory = this.fory; - if (fory.checkClassVersion()) { + public Object[] readFields(ReadContext readContext, MemoryBuffer buffer) { + RefReader refReader = readContext.getRefReader(); + if (typeResolver.checkClassVersion()) { int hash = buffer.readInt32(); checkClassVersion(type, hash, classVersionHash); } @@ -227,30 +234,30 @@ public Object[] readFields(MemoryBuffer buffer) { printReadFieldDebugInfo(fieldInfo, buffer); } fieldValues[counter++] = - readBuildInFieldValue(fory, typeResolver, refResolver, fieldInfo, buffer); + readBuildInFieldValue(readContext, typeResolver, refReader, fieldInfo, buffer); } - Generics generics = fory.getGenerics(); + Generics generics = readContext.getGenerics(); for (SerializationFieldInfo fieldInfo : containerFields) { if (Utils.DEBUG_OUTPUT_VERBOSE) { printReadFieldDebugInfo(fieldInfo, buffer); } Object fieldValue = - readContainerFieldValue(fory, typeResolver, refResolver, generics, fieldInfo, buffer); + readContainerFieldValue(readContext, typeResolver, refReader, generics, fieldInfo, buffer); fieldValues[counter++] = fieldValue; } for (SerializationFieldInfo fieldInfo : otherFields) { if (Utils.DEBUG_OUTPUT_VERBOSE) { printReadFieldDebugInfo(fieldInfo, buffer); } - Object fieldValue = readField(fory, typeResolver, refResolver, fieldInfo, buffer); + Object fieldValue = readField(readContext, typeResolver, refReader, fieldInfo, buffer); fieldValues[counter++] = fieldValue; } return fieldValues; } - public T readAndSetFields(MemoryBuffer buffer, T obj) { - Fory fory = this.fory; - if (fory.checkClassVersion()) { + public T readAndSetFields(ReadContext readContext, MemoryBuffer buffer, T obj) { + RefReader refReader = readContext.getRefReader(); + if (typeResolver.checkClassVersion()) { int hash = buffer.readInt32(); checkClassVersion(type, hash, classVersionHash); } @@ -260,15 +267,15 @@ public T readAndSetFields(MemoryBuffer buffer, T obj) { printReadFieldDebugInfo(fieldInfo, buffer); } // a numeric type can have only three kinds: primitive, not_null_boxed, nullable_boxed - readBuildInFieldValue(fory, typeResolver, refResolver, fieldInfo, buffer, obj); + readBuildInFieldValue(readContext, typeResolver, refReader, fieldInfo, buffer, obj); } - Generics generics = fory.getGenerics(); + Generics generics = readContext.getGenerics(); for (SerializationFieldInfo fieldInfo : containerFields) { if (Utils.DEBUG_OUTPUT_VERBOSE) { printReadFieldDebugInfo(fieldInfo, buffer); } Object fieldValue = - readContainerFieldValue(fory, typeResolver, refResolver, generics, fieldInfo, buffer); + readContainerFieldValue(readContext, typeResolver, refReader, generics, fieldInfo, buffer); FieldAccessor fieldAccessor = fieldInfo.fieldAccessor; fieldAccessor.putObject(obj, fieldValue); } @@ -276,16 +283,16 @@ public T readAndSetFields(MemoryBuffer buffer, T obj) { if (Utils.DEBUG_OUTPUT_VERBOSE) { printReadFieldDebugInfo(fieldInfo, buffer); } - Object fieldValue = readField(fory, typeResolver, refResolver, fieldInfo, buffer); + Object fieldValue = readField(readContext, typeResolver, refReader, fieldInfo, buffer); FieldAccessor fieldAccessor = fieldInfo.fieldAccessor; fieldAccessor.putObject(obj, fieldValue); } return obj; } - public static int computeStructHash(Fory fory, DescriptorGrouper grouper) { + public static int computeStructHash(TypeResolver typeResolver, DescriptorGrouper grouper) { List sorted = grouper.getSortedDescriptors(); - String fingerprint = Fingerprint.computeStructFingerprint(fory, sorted); + String fingerprint = Fingerprint.computeStructFingerprint(typeResolver, sorted); byte[] bytes = fingerprint.getBytes(StandardCharsets.UTF_8); long hashLong = MurmurHash3.murmurhash3_x64_128(bytes, 0, bytes.length, 47)[0]; return (int) (hashLong & 0xffffffffL); diff --git a/java/fory-core/src/main/java/org/apache/fory/serializer/ObjectStreamSerializer.java b/java/fory-core/src/main/java/org/apache/fory/serializer/ObjectStreamSerializer.java index 0a5c9622d4..fcce094f1a 100644 --- a/java/fory-core/src/main/java/org/apache/fory/serializer/ObjectStreamSerializer.java +++ b/java/fory-core/src/main/java/org/apache/fory/serializer/ObjectStreamSerializer.java @@ -44,11 +44,14 @@ import java.util.function.Consumer; import org.apache.fory.Fory; import org.apache.fory.builder.CodecUtils; -import org.apache.fory.builder.Generated; import org.apache.fory.builder.LayerMarkerClassGenerator; import org.apache.fory.collection.LongMap; import org.apache.fory.collection.ObjectArray; import org.apache.fory.collection.ObjectIntMap; +import org.apache.fory.config.LongEncoding; +import org.apache.fory.context.MetaContext; +import org.apache.fory.context.ReadContext; +import org.apache.fory.context.WriteContext; import org.apache.fory.logging.Logger; import org.apache.fory.logging.LoggerFactory; import org.apache.fory.memory.MemoryBuffer; @@ -61,8 +64,9 @@ import org.apache.fory.reflect.ObjectCreators; import org.apache.fory.reflect.ReflectionUtils; import org.apache.fory.resolver.ClassResolver; -import org.apache.fory.resolver.MetaContext; import org.apache.fory.resolver.TypeInfo; +import org.apache.fory.resolver.TypeResolver; +import org.apache.fory.serializer.PrimitiveSerializers.LongSerializer; import org.apache.fory.type.Descriptor; import org.apache.fory.type.TypeUtils; import org.apache.fory.type.Types; @@ -92,13 +96,13 @@ public class ObjectStreamSerializer extends AbstractObjectSerializer { private final LongMap typeDefIdToTypeInfo = new LongMap<>(4, 0.4f); private static MetaSharedLayerSerializerBase newGeneratedSerializer( - Fory fory, + TypeResolver typeResolver, Class cls, Class serializerClass, TypeDef layerTypeDef, Class layerMarkerClass) { MetaSharedLayerSerializerBase serializer = - (MetaSharedLayerSerializerBase) Serializers.newSerializer(fory, cls, serializerClass); + (MetaSharedLayerSerializerBase) Serializers.newSerializer(typeResolver, cls, serializerClass); serializer.setLayerSerializerMeta(layerTypeDef, layerMarkerClass); return serializer; } @@ -121,11 +125,12 @@ private interface SlotInfo { * serializer. Also stores the returned serializer for later retrieval via {@link * #getCurrentReadSerializer()}. * - * @param fory the Fory instance + * @param typeResolver the type resolver * @param buffer the memory buffer to read TypeDef from * @return the serializer to use for reading */ - MetaSharedLayerSerializerBase getReadSerializer(Fory fory, MemoryBuffer buffer); + MetaSharedLayerSerializerBase getReadSerializer( + TypeResolver typeResolver, ReadContext readContext, MemoryBuffer buffer); /** * Get the current read serializer (last returned by {@link #getReadSerializer}). This is used @@ -175,8 +180,8 @@ private static ObjectStreamClass safeObjectStreamClassLookup(Class type) { } } - public ObjectStreamSerializer(Fory fory, Class type) { - super(fory, type, createObjectCreatorForGraalVM(type)); + public ObjectStreamSerializer(TypeResolver typeResolver, Class type) { + super(typeResolver, type, createObjectCreatorForGraalVM(type)); if (!Serializable.class.isAssignableFrom(type)) { throw new IllegalArgumentException( String.format("Class %s should implement %s.", type, Serializable.class)); @@ -190,7 +195,7 @@ public ObjectStreamSerializer(Fory fory, Class type) { Externalizable.class.getName()); } // stream serializer may be data serializer of ReplaceResolver serializer. - fory.getTypeResolver().setSerializerIfAbsent(type, this); + typeResolver.setSerializerIfAbsent(type, this); List slotsInfoList = new ArrayList<>(); Class end = type; // locate closest non-serializable superclass @@ -198,7 +203,7 @@ public ObjectStreamSerializer(Fory fory, Class type) { end = end.getSuperclass(); } while (type != end) { - slotsInfoList.add(new SlotsInfo(fory, type)); + slotsInfoList.add(new SlotsInfo(typeResolver, type)); type = type.getSuperclass(); } Collections.reverse(slotsInfoList); @@ -220,33 +225,36 @@ private static ObjectCreator createObjectCreatorForGraalVM(Class type) } @Override - public void write(MemoryBuffer buffer, Object value) { + public void write(WriteContext writeContext, Object value) { + MemoryBuffer buffer = writeContext.getBuffer(); buffer.writeInt16((short) slotsInfos.length); try { ClassResolver classResolver = (ClassResolver) typeResolver; for (SlotInfo slotsInfo : slotsInfos) { // create a classinfo to avoid null class bytes when class id is a // replacement id. - classResolver.writeClassInternal(buffer, slotsInfo.getCls()); + classResolver.writeClassInternal(writeContext, slotsInfo.getCls()); // Write layer class meta first (if meta share enabled) MetaSharedLayerSerializerBase serializer = slotsInfo.getSlotsSerializer(); - if (fory.getConfig().isMetaShareEnabled()) { - serializer.writeLayerClassMeta(buffer); + if (config.isMetaShareEnabled()) { + serializer.writeLayerClassMeta(writeContext, buffer); } StreamTypeInfo streamTypeInfo = slotsInfo.getStreamTypeInfo(); Method writeObjectMethod = streamTypeInfo.writeObjectMethod; if (writeObjectMethod == null) { // No custom writeObject - write fields directly - serializer.writeFieldsOnly(buffer, value); + serializer.writeFieldsOnly(writeContext, buffer, value); } else { ForyObjectOutputStream objectOutputStream = slotsInfo.getObjectOutputStream(); Object oldObject = objectOutputStream.targetObject; MemoryBuffer oldBuffer = objectOutputStream.buffer; + WriteContext oldWriteContext = objectOutputStream.writeContext; ForyObjectOutputStream.PutFieldImpl oldPutField = objectOutputStream.curPut; boolean fieldsWritten = objectOutputStream.fieldsWritten; try { objectOutputStream.targetObject = value; objectOutputStream.buffer = buffer; + objectOutputStream.writeContext = writeContext; objectOutputStream.curPut = null; objectOutputStream.fieldsWritten = false; if (streamTypeInfo.writeObjectFunc != null) { @@ -257,6 +265,7 @@ public void write(MemoryBuffer buffer, Object value) { } finally { objectOutputStream.targetObject = oldObject; objectOutputStream.buffer = oldBuffer; + objectOutputStream.writeContext = oldWriteContext; objectOutputStream.curPut = oldPutField; objectOutputStream.fieldsWritten = fieldsWritten; } @@ -268,9 +277,10 @@ public void write(MemoryBuffer buffer, Object value) { } @Override - public Object read(MemoryBuffer buffer) { + public Object read(ReadContext readContext) { + MemoryBuffer buffer = readContext.getBuffer(); Object obj = objectCreator.newInstance(); - fory.getRefResolver().reference(obj); + readContext.reference(obj); int numClasses = buffer.readInt16(); int slotIndex = 0; @@ -278,7 +288,7 @@ public Object read(MemoryBuffer buffer) { ClassResolver classResolver = (ClassResolver) typeResolver; TreeMap callbacks = new TreeMap<>(Collections.reverseOrder()); for (int i = 0; i < numClasses; i++) { - Class currentClass = classResolver.readClassInternal(buffer); + Class currentClass = classResolver.readClassInternal(readContext); // Find the matching local slot for sender's class SlotInfo matchedSlot = null; @@ -311,25 +321,26 @@ public Object read(MemoryBuffer buffer) { if (matchedSlot == null) { // Sender has a layer that receiver doesn't have - read TypeDef and skip the data - skipUnknownLayerData(buffer, currentClass); + skipUnknownLayerData(readContext, buffer, currentClass); continue; } // Read data for the matched layer - getReadSerializer reads TypeDef from buffer // This must be called exactly once per layer to read the TypeDef - matchedSlot.getReadSerializer(fory, buffer); + matchedSlot.getReadSerializer(typeResolver, readContext, buffer); StreamTypeInfo streamTypeInfo = matchedSlot.getStreamTypeInfo(); Method readObjectMethod = streamTypeInfo.readObjectMethod; if (readObjectMethod == null) { // For standard field serialization - use getCurrentReadSerializer() - matchedSlot.getCurrentReadSerializer().readAndSetFields(buffer, obj); + matchedSlot.getCurrentReadSerializer().readAndSetFields(readContext, buffer, obj); } else { // For custom readObject, it handles its own format ForyObjectInputStream objectInputStream = matchedSlot.getObjectInputStream(); MemoryBuffer oldBuffer = objectInputStream.buffer; Object oldObject = objectInputStream.targetObject; + ReadContext oldReadContext = objectInputStream.readContext; ForyObjectInputStream.GetFieldImpl oldGetField = objectInputStream.getField; ForyObjectInputStream.GetFieldImpl getField = (ForyObjectInputStream.GetFieldImpl) matchedSlot.getFieldPool().popOrNull(); @@ -341,6 +352,7 @@ public Object read(MemoryBuffer buffer) { objectInputStream.fieldsRead = false; objectInputStream.buffer = buffer; objectInputStream.targetObject = obj; + objectInputStream.readContext = readContext; objectInputStream.getField = getField; objectInputStream.callbacks = callbacks; if (streamTypeInfo.readObjectFunc != null) { @@ -352,6 +364,7 @@ public Object read(MemoryBuffer buffer) { objectInputStream.fieldsRead = fieldsRead; objectInputStream.buffer = oldBuffer; objectInputStream.targetObject = oldObject; + objectInputStream.readContext = oldReadContext; objectInputStream.getField = oldGetField; matchedSlot.getFieldPool().add(getField); objectInputStream.callbacks = null; @@ -390,13 +403,14 @@ public Object read(MemoryBuffer buffer) { * @param buffer the memory buffer to read from * @param senderClass the class from sender that receiver doesn't have */ - private void skipUnknownLayerData(MemoryBuffer buffer, Class senderClass) { + private void skipUnknownLayerData( + ReadContext readContext, MemoryBuffer buffer, Class senderClass) { // For layers without custom writeObject, we can skip using a serializer created from the // TypeDef. Note: For layers with custom writeObject, the sender would have that class // locally, and we'd have a matching slot. This method is only called when sender has a // layer the receiver doesn't have. - if (!fory.getConfig().isMetaShareEnabled()) { + if (!typeResolver.getConfig().isMetaShareEnabled()) { throw new UnsupportedOperationException( "Cannot skip unknown layer data without meta share enabled for class: " + senderClass.getName() @@ -404,7 +418,7 @@ private void skipUnknownLayerData(MemoryBuffer buffer, Class senderClass) { } // Read TypeInfo from buffer (maintains index alignment with readTypeInfos) - MetaContext metaContext = fory.getSerializationContext().getMetaContext(); + MetaContext metaContext = readContext.getMetaContext(); if (metaContext == null) { throw new IllegalStateException("MetaContext is null but meta share is enabled"); } @@ -424,8 +438,7 @@ private void skipUnknownLayerData(MemoryBuffer buffer, Class senderClass) { TypeDef.skipTypeDef(buffer, typeDefId); } else { // Not cached - read full TypeDef and create TypeInfo - TypeDef typeDef = - fory.getTypeResolver().cacheTypeDef(TypeDef.readTypeDef(fory, buffer, typeDefId)); + TypeDef typeDef = typeResolver.cacheTypeDef(TypeDef.readTypeDef(typeResolver, buffer, typeDefId)); typeInfo = new TypeInfo(senderClass, typeDef); typeDefIdToTypeInfo.put(typeDefId, typeInfo); } @@ -436,20 +449,20 @@ private void skipUnknownLayerData(MemoryBuffer buffer, Class senderClass) { MetaSharedLayerSerializerBase skipSerializer = (MetaSharedLayerSerializerBase) typeInfo.getSerializer(); if (skipSerializer == null) { - Class layerMarkerClass = LayerMarkerClassGenerator.getOrCreate(fory, senderClass, 0); + Class layerMarkerClass = LayerMarkerClassGenerator.getOrCreate(senderClass, 0); MetaSharedLayerSerializer newSerializer = - new MetaSharedLayerSerializer(fory, senderClass, typeInfo.getTypeDef(), layerMarkerClass); + new MetaSharedLayerSerializer(typeResolver, senderClass, typeInfo.getTypeDef(), layerMarkerClass); typeInfo.setSerializer(newSerializer); skipSerializer = newSerializer; } - skipSerializer.skipFields(buffer); + skipSerializer.skipFields(readContext, buffer); } private static void throwUnsupportedEncodingException(Class cls) throws UnsupportedEncodingException { throw new UnsupportedEncodingException( String.format( - "Use %s instead by `fory.registerSerializer(%s, new JavaSerializer(fory, %s))` or " + "Use %s instead by `fory.registerSerializer(%s, new JavaSerializer(fory.getTypeResolver(), %s))` or " + "implement a custom %s.", JavaSerializer.class, cls, cls, Serializer.class)); } @@ -458,7 +471,7 @@ private static void throwSerializationException(Class type, Exception e) { throw new RuntimeException( String.format( "Serialize object of type %s failed, " - + "Try to use %s instead by `fory.registerSerializer(%s, new JavaSerializer(fory, %s))` or " + + "Try to use %s instead by `fory.registerSerializer(%s, new JavaSerializer(fory.getTypeResolver(), %s))` or " + "implement a custom %s.", type, JavaSerializer.class, type, type, Serializer.class), e); @@ -469,19 +482,19 @@ private static void throwSerializationException(Class type, Exception e) { * This is necessary because when a new Fory instance is created at runtime in GraalVM native * images, it needs to reuse the serializers that were generated at build time. * - * @param fory the Fory instance + * @param typeResolver the type resolver * @param layerTypeDef the TypeDef for this layer * @param type the target class type */ private static void ensureFieldSerializersGenerated( - Fory fory, TypeDef layerTypeDef, Class type) { - Collection descriptors = layerTypeDef.getDescriptors(fory.getTypeResolver(), type); + TypeResolver typeResolver, TypeDef layerTypeDef, Class type) { + Collection descriptors = layerTypeDef.getDescriptors(typeResolver, type); for (Descriptor descriptor : descriptors) { Class fieldType = descriptor.getRawType(); if (fieldType != null && !fieldType.isPrimitive()) { try { // Trigger serializer generation for this field type - fory.getTypeResolver().getSerializerClass(fieldType); + typeResolver.getSerializerClass(fieldType); } catch (Exception e) { // Ignore errors - some types may not need serializers or may be handled specially ExceptionUtils.ignore(e); @@ -495,13 +508,13 @@ private static void ensureFieldSerializersGenerated( * FieldInfo objects that represent the serialization contract defined by the class, which may * differ from the actual class fields when serialPersistentFields is defined. * - * @param fory the Fory instance + * @param typeResolver the type resolver * @param objectStreamClass the ObjectStreamClass for the type * @param type the class type * @return list of FieldInfo representing the serialization fields */ private static List buildFieldInfoFromObjectStreamClass( - Fory fory, ObjectStreamClass objectStreamClass, Class type) { + TypeResolver typeResolver, ObjectStreamClass objectStreamClass, Class type) { ObjectStreamField[] streamFields = objectStreamClass.getFields(); List fieldInfos = new ArrayList<>(streamFields.length); String className = type.getName(); @@ -511,7 +524,7 @@ private static List buildFieldInfoFromObjectStreamClass( String fieldName = streamField.getName(); // Build FieldType based on the field's declared type - FieldTypes.FieldType fieldTypeInfo = buildFieldTypeFromClass(fory, fieldType); + FieldTypes.FieldType fieldTypeInfo = buildFieldTypeFromClass(typeResolver, fieldType); fieldInfos.add(new FieldInfo(className, fieldName, fieldTypeInfo)); } @@ -522,22 +535,23 @@ private static List buildFieldInfoFromObjectStreamClass( * Build a FieldType from a Class. This is a simplified version of FieldTypes.buildFieldType that * works with Class instead of Field, used for ObjectStreamField which only provides type info. */ - private static FieldTypes.FieldType buildFieldTypeFromClass(Fory fory, Class fieldType) { + private static FieldTypes.FieldType buildFieldTypeFromClass( + TypeResolver typeResolver, Class fieldType) { // For primitives, use RegisteredFieldType if (fieldType.isPrimitive()) { - int typeId = Types.getTypeId(fory, fieldType); + int typeId = Types.getTypeId(typeResolver, fieldType); return new FieldTypes.RegisteredFieldType(false, false, typeId, -1); } // For boxed primitives Class unwrapped = TypeUtils.unwrap(fieldType); if (unwrapped.isPrimitive()) { - int typeId = Types.getTypeId(fory, unwrapped); + int typeId = Types.getTypeId(typeResolver, unwrapped); return new FieldTypes.RegisteredFieldType(true, true, typeId, -1); } // For registered types - ClassResolver classResolver = (ClassResolver) fory.getTypeResolver(); + ClassResolver classResolver = (ClassResolver) typeResolver; if (classResolver.isRegisteredById(fieldType)) { int typeId = classResolver.getTypeIdForTypeDef(fieldType); int userTypeId = classResolver.getUserTypeIdForTypeDef(fieldType); @@ -625,8 +639,6 @@ protected StreamTypeInfo computeValue(Class type) { private class SlotsInfo implements SlotInfo { private final Class cls; private final StreamTypeInfo streamTypeInfo; - private final TypeDef layerTypeDef; - private final Class layerMarkerClass; // mark non-final for async-jit to update it to jit-serializer. private MetaSharedLayerSerializerBase slotsSerializer; private final ObjectIntMap fieldIndexMap; @@ -638,7 +650,7 @@ private class SlotsInfo implements SlotInfo { // Current read serializer (set by getReadSerializer, used by getCurrentReadSerializer) private MetaSharedLayerSerializerBase currentReadSerializer; - public SlotsInfo(Fory fory, Class type) { + public SlotsInfo(TypeResolver typeResolver, Class type) { this.cls = type; ObjectStreamClass objectStreamClass = safeObjectStreamClassLookup(type); streamTypeInfo = STREAM_CLASS_INFO_CACHE.get(type); @@ -649,58 +661,47 @@ public SlotsInfo(Fory fory, Class type) { TypeDef layerTypeDef; if (objectStreamClass != null) { List fieldInfos = - buildFieldInfoFromObjectStreamClass(fory, objectStreamClass, type); + buildFieldInfoFromObjectStreamClass(typeResolver, objectStreamClass, type); layerTypeDef = - NativeTypeDefEncoder.buildTypeDefWithFieldInfos( - (ClassResolver) fory.getTypeResolver(), type, fieldInfos, true); + NativeTypeDefEncoder.buildTypeDefWithFieldInfos((ClassResolver) typeResolver, type, fieldInfos, true); } else { // Fallback when ObjectStreamClass is not available (e.g., GraalVM native image) - layerTypeDef = fory.getTypeResolver().getTypeDef(type, false); + layerTypeDef = typeResolver.getTypeDef(type, false); } - this.layerTypeDef = layerTypeDef; // Generate marker class for this layer. Use 0 as layer index since each class // has its own SlotsInfo, and the (class, 0) pair is unique for each class. - this.layerMarkerClass = LayerMarkerClassGenerator.getOrCreate(fory, type, 0); + Class layerMarkerClass = LayerMarkerClassGenerator.getOrCreate(type, 0); // Create interpreter-mode serializer first - this.slotsSerializer = - new MetaSharedLayerSerializer(fory, type, this.layerTypeDef, this.layerMarkerClass); + this.slotsSerializer = new MetaSharedLayerSerializer(typeResolver, type, layerTypeDef, layerMarkerClass); // Register JIT callback to replace with JIT serializer when ready - if (fory.getConfig().isCodeGenEnabled()) { - if (GraalvmSupport.isGraalBuildtime()) { - Class generatedSlotsSerializerClass = - CodecUtils.loadOrGenMetaSharedLayerCodecClass( - cls, fory, layerTypeDef, layerMarkerClass); - int configHash = fory.getConfig().getConfigHash(); - GraalvmSupport.registerSerializerClass(generatedSlotsSerializerClass); - GraalvmSupport.registerLayerSerializerClass( - layerTypeDef.getId(), generatedSlotsSerializerClass, configHash); - } else if (GraalvmSupport.isGraalRuntime()) { - Class generatedSlotsSerializerClass = - GraalvmSupport.getLayerSerializerClass( - layerTypeDef.getId(), fory.getConfig().getConfigHash()); - if (generatedSlotsSerializerClass != null) { - replaceSlotsSerializer( - newGeneratedSerializer( - fory, cls, generatedSlotsSerializerClass, layerTypeDef, layerMarkerClass)); - } - } else { - SlotsInfo thisInfo = this; - MetaSharedLayerSerializerBase serializer = - fory.getJITContext() - .registerSerializerJITCallback( - () -> thisInfo.slotsSerializer, - thisInfo::createGeneratedLayerSerializer, - thisInfo::replaceSlotsSerializer); - replaceSlotsSerializer(serializer); - } + if (config.isCodeGenEnabled() && !GraalvmSupport.isGraalRuntime()) { + SlotsInfo thisInfo = this; + typeResolver.getJITContext() + .registerSerializerJITCallback( + () -> MetaSharedLayerSerializer.class, + () -> + typeResolver + .getJITContext() + .asyncVisitFory( + fory -> + CodecUtils.loadOrGenMetaSharedLayerCodecClass( + type, fory, layerTypeDef, layerMarkerClass)), + c -> + thisInfo.slotsSerializer = + newGeneratedSerializer( + typeResolver, + type, + (Class) c, + layerTypeDef, + layerMarkerClass)); } // In GraalVM, ensure serializers are generated for all field types at build time // so they're available when new Fory instances are created at runtime if (GraalvmSupport.isGraalBuildtime()) { - ensureFieldSerializersGenerated(fory, this.layerTypeDef, type); + ensureFieldSerializersGenerated(typeResolver, layerTypeDef, type); } // Build fieldIndexMap and putFieldTypes from serializer's field order. @@ -788,28 +789,27 @@ public Class[] getPutFieldTypes() { @Override @SuppressWarnings("unchecked") - public MetaSharedLayerSerializerBase getReadSerializer(Fory fory, MemoryBuffer buffer) { + public MetaSharedLayerSerializerBase getReadSerializer( + TypeResolver typeResolver, ReadContext readContext, MemoryBuffer buffer) { MetaSharedLayerSerializerBase result; - if (!fory.getConfig().isMetaShareEnabled()) { + if (!typeResolver.getConfig().isMetaShareEnabled()) { // Meta share not enabled - use the default slots serializer - result = getSlotsSerializer(); + result = slotsSerializer; } else { // Read TypeInfo from buffer (creates new or returns existing) - TypeInfo typeInfo = readLayerTypeInfo(fory, buffer); + TypeInfo typeInfo = readLayerTypeInfo(typeResolver, readContext, buffer); if (typeInfo == null) { - result = getSlotsSerializer(); + result = slotsSerializer; } else { // Get or create serializer from TypeInfo Serializer serializer = typeInfo.getSerializer(); if (serializer != null) { result = (MetaSharedLayerSerializerBase) serializer; - } else if (typeInfo.getTypeDef().getId() == layerTypeDef.getId()) { - result = getSlotsSerializer(); - typeInfo.setSerializer(result); } else { // Create a new serializer based on the TypeDef from stream + Class layerMarkerClass = LayerMarkerClassGenerator.getOrCreate(cls, 0); MetaSharedLayerSerializer newSerializer = - new MetaSharedLayerSerializer(fory, cls, typeInfo.getTypeDef(), layerMarkerClass); + new MetaSharedLayerSerializer(typeResolver, cls, typeInfo.getTypeDef(), layerMarkerClass); typeInfo.setSerializer(newSerializer); result = newSerializer; } @@ -825,45 +825,9 @@ public MetaSharedLayerSerializerBase getCurrentReadSerializer() { return currentReadSerializer; } - private void updateCachedLayerSerializer(MetaSharedLayerSerializerBase serializer) { - TypeInfo typeInfo = typeDefIdToTypeInfo.get(layerTypeDef.getId()); - if (typeInfo != null) { - typeInfo.setSerializer(serializer); - } - } - - /** - * Replace the serializer for this layer under the JIT lock. - * - *

    When a generated layer class is already cached, the async callback can replace the - * interpreter serializer immediately after {@code registerSerializerJITCallback} releases the - * lock. The constructor still needs to apply the returned placeholder or generated serializer - * for synchronous paths, but it must not overwrite an already-installed generated serializer - * with the stale interpreter placeholder. - */ - private void replaceSlotsSerializer(MetaSharedLayerSerializerBase serializer) { - try { - fory.getJITContext().lock(); - if (serializer instanceof Generated || !(slotsSerializer instanceof Generated)) { - slotsSerializer = serializer; - updateCachedLayerSerializer(serializer); - } - } finally { - fory.getJITContext().unlock(); - } - } - - private MetaSharedLayerSerializerBase createGeneratedLayerSerializer() { - Class serializerClass = - CodecUtils.loadOrGenMetaSharedLayerCodecClass(cls, fory, layerTypeDef, layerMarkerClass); - if (GraalvmSupport.isGraalBuildtime()) { - GraalvmSupport.registerSerializerClass(serializerClass); - } - return newGeneratedSerializer(fory, cls, serializerClass, layerTypeDef, layerMarkerClass); - } - - private TypeInfo readLayerTypeInfo(Fory fory, MemoryBuffer buffer) { - MetaContext metaContext = fory.getSerializationContext().getMetaContext(); + private TypeInfo readLayerTypeInfo( + TypeResolver typeResolver, ReadContext readContext, MemoryBuffer buffer) { + MetaContext metaContext = readContext.getMetaContext(); if (metaContext == null) { return null; } @@ -882,8 +846,7 @@ private TypeInfo readLayerTypeInfo(Fory fory, MemoryBuffer buffer) { TypeDef.skipTypeDef(buffer, typeDefId); } else { // Not cached - read full TypeDef and create TypeInfo - TypeDef typeDef = - fory.getTypeResolver().cacheTypeDef(TypeDef.readTypeDef(fory, buffer, typeDefId)); + TypeDef typeDef = typeResolver.cacheTypeDef(TypeDef.readTypeDef(typeResolver, buffer, typeDefId)); typeInfo = new TypeInfo(cls, typeDef); typeDefIdToTypeInfo.put(typeDefId, typeInfo); } @@ -906,9 +869,11 @@ public String toString() { * Java Object Serialization Output Specification */ private static class ForyObjectOutputStream extends ObjectOutputStream { - private final Fory fory; private final boolean compressInt; + private final LongEncoding longEncoding; private final SlotInfo slotsInfo; + private final TypeResolver typeResolver; + private WriteContext writeContext; private MemoryBuffer buffer; private Object targetObject; private boolean fieldsWritten; @@ -916,18 +881,19 @@ private static class ForyObjectOutputStream extends ObjectOutputStream { protected ForyObjectOutputStream(SlotInfo slotsInfo) throws IOException { super(); this.slotsInfo = slotsInfo; - this.fory = slotsInfo.getSlotsSerializer().fory; - this.compressInt = fory.compressInt(); + this.typeResolver = slotsInfo.getSlotsSerializer().typeResolver; + this.compressInt = slotsInfo.getSlotsSerializer().config.compressInt(); + this.longEncoding = slotsInfo.getSlotsSerializer().config.longEncoding(); } @Override protected final void writeObjectOverride(Object obj) throws IOException { - fory.writeRef(buffer, obj); + writeContext.writeRef(obj); } @Override public void writeUnshared(Object obj) throws IOException { - fory.writeNonRef(buffer, obj); + writeContext.writeNonRef(obj); } /** @@ -1001,7 +967,6 @@ public void put(String name, Object val) { putValue(name, val); } - @Deprecated @Override public void write(ObjectOutput out) throws IOException { Class cls = slotsInfo.getSlotsSerializer().getType(); @@ -1034,7 +999,7 @@ public void writeFields() throws IOException { throw new NotActiveException("no current PutField object"); } // Write field values using MetaShare serialization - slotsInfo.getSlotsSerializer().writeFieldValues(buffer, curPut.vals); + slotsInfo.getSlotsSerializer().writeFieldValues(writeContext, buffer, curPut.vals); Arrays.fill(curPut.vals, null); putFieldsCache.add(curPut); this.curPut = null; @@ -1057,14 +1022,14 @@ private void writePutFieldValue(MemoryBuffer buffer, Class fieldType, Object buffer.writeInt32(value == null ? 0 : (Integer) value); } } else if (fieldType == long.class) { - fory.writeInt64(buffer, value == null ? 0L : (Long) value); + LongSerializer.writeInt64(buffer, value == null ? 0L : (Long) value, longEncoding); } else if (fieldType == float.class) { buffer.writeFloat32(value == null ? 0f : (Float) value); } else if (fieldType == double.class) { buffer.writeFloat64(value == null ? 0d : (Double) value); } else { // Object reference - fory.writeRef(buffer, value); + writeContext.writeRef(value); } } @@ -1074,7 +1039,7 @@ public void defaultWriteObject() throws IOException { throw new NotActiveException("not in writeObject invocation or fields already written"); } // Write fields using MetaShare serialization (layer meta already written by write()) - slotsInfo.getSlotsSerializer().writeFieldsOnly(buffer, targetObject); + slotsInfo.getSlotsSerializer().writeFieldsOnly(writeContext, buffer, targetObject); fieldsWritten = true; } @@ -1161,7 +1126,7 @@ public void writeInt(int v) throws IOException { @Override public void writeLong(long v) throws IOException { - fory.writeInt64(buffer, v); + LongSerializer.writeInt64(buffer, v, longEncoding); } @Override @@ -1186,13 +1151,13 @@ public void writeBytes(String s) throws IOException { @Override public void writeChars(String s) throws IOException { Preconditions.checkNotNull(s); - fory.writeString(buffer, s); + writeContext.writeString(s); } @Override public void writeUTF(String s) throws IOException { Preconditions.checkNotNull(s); - fory.writeString(buffer, s); + writeContext.writeString(s); } @Override @@ -1219,9 +1184,11 @@ public void close() throws IOException {} * Java Object Serialization Input Specification */ private static class ForyObjectInputStream extends ObjectInputStream { - private final Fory fory; private final boolean compressInt; + private final LongEncoding longEncoding; private final SlotInfo slotsInfo; + private final TypeResolver typeResolver; + private ReadContext readContext; private MemoryBuffer buffer; private Object targetObject; private GetFieldImpl getField; @@ -1229,19 +1196,20 @@ private static class ForyObjectInputStream extends ObjectInputStream { private TreeMap callbacks; protected ForyObjectInputStream(SlotInfo slotsInfo) throws IOException { - this.fory = slotsInfo.getSlotsSerializer().fory; - this.compressInt = fory.compressInt(); + this.compressInt = slotsInfo.getSlotsSerializer().config.compressInt(); + this.longEncoding = slotsInfo.getSlotsSerializer().config.longEncoding(); this.slotsInfo = slotsInfo; + this.typeResolver = slotsInfo.getSlotsSerializer().typeResolver; } @Override protected Object readObjectOverride() { - return fory.readRef(buffer); + return readContext.readRef(); } @Override public Object readUnshared() { - return fory.readNonRef(buffer); + return readContext.readNonRef(); } private static final Object NO_VALUE_STUB = new Object(); @@ -1377,7 +1345,7 @@ public GetField readFields() throws IOException { throw new NotActiveException("not in readObject invocation or fields already read"); } // Read field values using MetaShare serialization - Object[] vals = slotsInfo.getCurrentReadSerializer().readFieldValues(buffer); + Object[] vals = slotsInfo.getCurrentReadSerializer().readFieldValues(readContext, buffer); System.arraycopy(vals, 0, getField.vals, 0, vals.length); fieldsRead = true; return getField; @@ -1399,14 +1367,14 @@ private Object readPutFieldValue(MemoryBuffer buffer, Class fieldType) { return buffer.readInt32(); } } else if (fieldType == long.class) { - return fory.readInt64(buffer); + return LongSerializer.readInt64(buffer, longEncoding); } else if (fieldType == float.class) { return buffer.readFloat32(); } else if (fieldType == double.class) { return buffer.readFloat64(); } else { // Object reference - return fory.readRef(buffer); + return readContext.readRef(); } } @@ -1417,7 +1385,7 @@ public void defaultReadObject() throws IOException, ClassNotFoundException { throw new NotActiveException("not in readObject invocation or fields already read"); } // Read fields using MetaShare serialization (layer meta already read by getReadSerializer()) - slotsInfo.getCurrentReadSerializer().readAndSetFields(buffer, targetObject); + slotsInfo.getCurrentReadSerializer().readAndSetFields(readContext, buffer, targetObject); fieldsRead = true; } @@ -1505,7 +1473,7 @@ public int readInt() throws IOException { @Override public long readLong() throws IOException { - return fory.readInt64(buffer); + return LongSerializer.readInt64(buffer, longEncoding); } @Override @@ -1541,7 +1509,7 @@ public String readLine() throws IOException { @Override public String readUTF() throws IOException { - return fory.readString(buffer); + return readContext.readString(); } @Override diff --git a/java/fory-core/src/main/java/org/apache/fory/serializer/OptionalSerializers.java b/java/fory-core/src/main/java/org/apache/fory/serializer/OptionalSerializers.java index eb91cf21dd..0526e87eac 100644 --- a/java/fory-core/src/main/java/org/apache/fory/serializer/OptionalSerializers.java +++ b/java/fory-core/src/main/java/org/apache/fory/serializer/OptionalSerializers.java @@ -23,8 +23,10 @@ import java.util.OptionalDouble; import java.util.OptionalInt; import java.util.OptionalLong; -import org.apache.fory.Fory; -import org.apache.fory.memory.MemoryBuffer; +import org.apache.fory.config.Config; +import org.apache.fory.context.CopyContext; +import org.apache.fory.context.ReadContext; +import org.apache.fory.context.WriteContext; import org.apache.fory.resolver.TypeResolver; /** @@ -34,107 +36,122 @@ public final class OptionalSerializers { public static final class OptionalSerializer extends Serializer { - public OptionalSerializer(Fory fory) { - super(fory, Optional.class); + public OptionalSerializer(Config config) { + super(config, Optional.class); } @Override - public void write(MemoryBuffer buffer, Optional value) { + public void write(WriteContext writeContext, Optional value) { Object nullable = value.isPresent() ? value.get() : null; - fory.writeRef(buffer, nullable); + writeContext.writeRef(nullable); } @Override - public Optional copy(Optional originOptional) { + public Optional copy(CopyContext copyContext, Optional originOptional) { if (originOptional.isPresent()) { - return Optional.ofNullable(fory.copyObject(originOptional.get())); + return Optional.ofNullable(copyContext.copyObject(originOptional.get())); } return originOptional; } @Override - public Optional read(MemoryBuffer buffer) { - return Optional.ofNullable(fory.readRef(buffer)); + public Optional read(ReadContext readContext) { + return Optional.ofNullable(readContext.readRef()); } } public static final class OptionalIntSerializer extends ImmutableSerializer { - public OptionalIntSerializer(Fory fory) { - super(fory, OptionalInt.class); + public OptionalIntSerializer(Config config) { + super(config, OptionalInt.class); } @Override - public void write(MemoryBuffer buffer, OptionalInt value) { + public void write(WriteContext writeContext, OptionalInt value) { boolean present = value.isPresent(); - buffer.writeBoolean(present); + writeContext.getBuffer().writeBoolean(present); if (present) { - buffer.writeInt32(value.getAsInt()); + writeContext.getBuffer().writeInt32(value.getAsInt()); } } @Override - public OptionalInt read(MemoryBuffer buffer) { - if (buffer.readBoolean()) { - return OptionalInt.of(buffer.readInt32()); + public OptionalInt read(ReadContext readContext) { + if (readContext.getBuffer().readBoolean()) { + return OptionalInt.of(readContext.getBuffer().readInt32()); } else { return OptionalInt.empty(); } } + + @Override + public boolean threadSafe() { + return true; + } } public static final class OptionalLongSerializer extends ImmutableSerializer { - public OptionalLongSerializer(Fory fory) { - super(fory, OptionalLong.class); + public OptionalLongSerializer(Config config) { + super(config, OptionalLong.class); } @Override - public void write(MemoryBuffer buffer, OptionalLong value) { + public void write(WriteContext writeContext, OptionalLong value) { boolean present = value.isPresent(); - buffer.writeBoolean(present); + writeContext.getBuffer().writeBoolean(present); if (present) { - buffer.writeInt64(value.getAsLong()); + writeContext.getBuffer().writeInt64(value.getAsLong()); } } @Override - public OptionalLong read(MemoryBuffer buffer) { - if (buffer.readBoolean()) { - return OptionalLong.of(buffer.readInt64()); + public OptionalLong read(ReadContext readContext) { + if (readContext.getBuffer().readBoolean()) { + return OptionalLong.of(readContext.getBuffer().readInt64()); } else { return OptionalLong.empty(); } } + + @Override + public boolean threadSafe() { + return true; + } } public static final class OptionalDoubleSerializer extends ImmutableSerializer { - public OptionalDoubleSerializer(Fory fory) { - super(fory, OptionalDouble.class); + public OptionalDoubleSerializer(Config config) { + super(config, OptionalDouble.class); } @Override - public void write(MemoryBuffer buffer, OptionalDouble value) { + public void write(WriteContext writeContext, OptionalDouble value) { boolean present = value.isPresent(); - buffer.writeBoolean(present); + writeContext.getBuffer().writeBoolean(present); if (present) { - buffer.writeFloat64(value.getAsDouble()); + writeContext.getBuffer().writeFloat64(value.getAsDouble()); } } @Override - public OptionalDouble read(MemoryBuffer buffer) { - if (buffer.readBoolean()) { - return OptionalDouble.of(buffer.readFloat64()); + public OptionalDouble read(ReadContext readContext) { + if (readContext.getBuffer().readBoolean()) { + return OptionalDouble.of(readContext.getBuffer().readFloat64()); } else { return OptionalDouble.empty(); } } + + @Override + public boolean threadSafe() { + return true; + } } - public static void registerDefaultSerializers(Fory fory) { - TypeResolver resolver = fory.getTypeResolver(); - resolver.registerInternalSerializer(Optional.class, new OptionalSerializer(fory)); - resolver.registerInternalSerializer(OptionalInt.class, new OptionalIntSerializer(fory)); - resolver.registerInternalSerializer(OptionalLong.class, new OptionalLongSerializer(fory)); - resolver.registerInternalSerializer(OptionalDouble.class, new OptionalDoubleSerializer(fory)); + public static void registerDefaultSerializers(TypeResolver resolver) { + Config config = resolver.getConfig(); + resolver.registerInternalSerializer(Optional.class, new OptionalSerializer(config)); + resolver.registerInternalSerializer(OptionalInt.class, new OptionalIntSerializer(config)); + resolver.registerInternalSerializer(OptionalLong.class, new OptionalLongSerializer(config)); + resolver.registerInternalSerializer(OptionalDouble.class, new OptionalDoubleSerializer(config)); } } diff --git a/java/fory-core/src/main/java/org/apache/fory/serializer/PrimitiveSerializers.java b/java/fory-core/src/main/java/org/apache/fory/serializer/PrimitiveSerializers.java index 8b32b626be..9c747e8050 100644 --- a/java/fory-core/src/main/java/org/apache/fory/serializer/PrimitiveSerializers.java +++ b/java/fory-core/src/main/java/org/apache/fory/serializer/PrimitiveSerializers.java @@ -21,181 +21,223 @@ import static org.apache.fory.type.TypeUtils.PRIMITIVE_LONG_TYPE; +import org.apache.fory.context.ReadContext; +import org.apache.fory.context.WriteContext; + import org.apache.fory.Fory; +import org.apache.fory.config.Config; import org.apache.fory.codegen.Expression; import org.apache.fory.codegen.Expression.Invoke; import org.apache.fory.config.LongEncoding; import org.apache.fory.memory.MemoryBuffer; import org.apache.fory.memory.Platform; import org.apache.fory.resolver.TypeResolver; -import org.apache.fory.serializer.Serializers.CrossLanguageCompatibleSerializer; import org.apache.fory.type.Float16; import org.apache.fory.util.Preconditions; /** Serializers for java primitive types. */ @SuppressWarnings({"rawtypes", "unchecked"}) public class PrimitiveSerializers { - public static final class BooleanSerializer extends CrossLanguageCompatibleSerializer { - public BooleanSerializer(Fory fory, Class cls) { - super(fory, (Class) cls, false, true); + public static final class BooleanSerializer extends ImmutableSerializer { + public BooleanSerializer(Config config, Class cls) { + super(config, (Class) cls, false); + } + + @Override + public void write(WriteContext writeContext, Boolean value) { + writeContext.getBuffer().writeBoolean(value); } @Override - public void write(MemoryBuffer buffer, Boolean value) { - buffer.writeBoolean(value); + public Boolean read(ReadContext readContext) { + return readContext.getBuffer().readBoolean(); } @Override - public Boolean read(MemoryBuffer buffer) { - return buffer.readBoolean(); + public boolean threadSafe() { + return true; } } - public static final class ByteSerializer extends CrossLanguageCompatibleSerializer { - public ByteSerializer(Fory fory, Class cls) { - super(fory, (Class) cls, false, true); + public static final class ByteSerializer extends ImmutableSerializer { + public ByteSerializer(Config config, Class cls) { + super(config, (Class) cls, false); } @Override - public void write(MemoryBuffer buffer, Byte value) { - buffer.writeByte(value); + public void write(WriteContext writeContext, Byte value) { + writeContext.getBuffer().writeByte(value); } @Override - public Byte read(MemoryBuffer buffer) { - return buffer.readByte(); + public Byte read(ReadContext readContext) { + return readContext.getBuffer().readByte(); + } + + @Override + public boolean threadSafe() { + return true; } } - public static final class Uint8Serializer extends CrossLanguageCompatibleSerializer { - public Uint8Serializer(Fory fory) { - super(fory, Integer.class); + public static final class Uint8Serializer extends ImmutableSerializer { + public Uint8Serializer(Config config) { + super(config, Integer.class); } @Override - public void write(MemoryBuffer buffer, Integer value) { + public void write(WriteContext writeContext, Integer value) { Preconditions.checkArgument(value >= 0 && value <= 255); - buffer.writeByte(value.byteValue()); + writeContext.getBuffer().writeByte(value.byteValue()); } @Override - public Integer read(MemoryBuffer buffer) { - int b = buffer.readByte(); + public Integer read(ReadContext readContext) { + int b = readContext.getBuffer().readByte(); return b >>> 24; } + + @Override + public boolean threadSafe() { + return true; + } } - public static final class Uint16Serializer extends CrossLanguageCompatibleSerializer { - public Uint16Serializer(Fory fory) { - super(fory, Integer.class); + public static final class Uint16Serializer extends ImmutableSerializer { + public Uint16Serializer(Config config) { + super(config, Integer.class); } @Override - public void write(MemoryBuffer buffer, Integer value) { + public void write(WriteContext writeContext, Integer value) { Preconditions.checkArgument(value >= 0 && value <= 65535); - buffer.writeByte(value.byteValue()); + writeContext.getBuffer().writeByte(value.byteValue()); } @Override - public Integer read(MemoryBuffer buffer) { - int b = buffer.readByte(); + public Integer read(ReadContext readContext) { + int b = readContext.getBuffer().readByte(); return b >>> 16; } + + @Override + public boolean threadSafe() { + return true; + } } public static final class CharSerializer extends ImmutableSerializer { - public CharSerializer(Fory fory, Class cls) { - super(fory, (Class) cls, false); + public CharSerializer(Config config, Class cls) { + super(config, (Class) cls, false); + } + + @Override + public void write(WriteContext writeContext, Character value) { + writeContext.getBuffer().writeChar(value); } @Override - public void write(MemoryBuffer buffer, Character value) { - buffer.writeChar(value); + public Character read(ReadContext readContext) { + return readContext.getBuffer().readChar(); } @Override - public Character read(MemoryBuffer buffer) { - return buffer.readChar(); + public boolean threadSafe() { + return true; } } - public static final class ShortSerializer extends CrossLanguageCompatibleSerializer { - public ShortSerializer(Fory fory, Class cls) { - super(fory, (Class) cls, false, true); + public static final class ShortSerializer extends ImmutableSerializer { + public ShortSerializer(Config config, Class cls) { + super(config, (Class) cls, false); } @Override - public void write(MemoryBuffer buffer, Short value) { - buffer.writeInt16(value); + public void write(WriteContext writeContext, Short value) { + writeContext.getBuffer().writeInt16(value); } @Override - public Short read(MemoryBuffer buffer) { - return buffer.readInt16(); + public Short read(ReadContext readContext) { + return readContext.getBuffer().readInt16(); + } + + @Override + public boolean threadSafe() { + return true; } } - public static final class IntSerializer extends CrossLanguageCompatibleSerializer { + public static final class IntSerializer extends ImmutableSerializer { private final boolean compressNumber; - public IntSerializer(Fory fory, Class cls) { - super(fory, (Class) cls, false, true); + public IntSerializer(Config config, Class cls) { + super(config, (Class) cls, false); // Cross-language encoding always uses varint; Java mode follows compressInt config. - compressNumber = !isJava || fory.compressInt(); + compressNumber = config.isXlang() || config.compressInt(); } @Override - public void write(MemoryBuffer buffer, Integer value) { + public void write(WriteContext writeContext, Integer value) { if (compressNumber) { - buffer.writeVarInt32(value); + writeContext.getBuffer().writeVarInt32(value); } else { - buffer.writeInt32(value); + writeContext.getBuffer().writeInt32(value); } } @Override - public Integer read(MemoryBuffer buffer) { + public Integer read(ReadContext readContext) { if (compressNumber) { - return buffer.readVarInt32(); - } else { - return buffer.readInt32(); + return readContext.getBuffer().readVarInt32(); } + return readContext.getBuffer().readInt32(); + } + + @Override + public boolean threadSafe() { + return true; } } public static final class VarUint32Serializer extends Serializer { - public VarUint32Serializer(Fory fory) { - super(fory, Integer.class); + public VarUint32Serializer(Config config) { + super(config, Integer.class); } @Override - public void write(MemoryBuffer buffer, Integer value) { + public void write(WriteContext writeContext, Integer value) { Preconditions.checkArgument(value >= 0); - buffer.writeVarUint32(value); + writeContext.getBuffer().writeVarUint32(value); + } + + @Override + public Integer read(ReadContext readContext) { + return readContext.getBuffer().readVarUint32(); } @Override - public Integer read(MemoryBuffer buffer) { - return buffer.readVarUint32(); + public boolean threadSafe() { + return true; } } - public static final class LongSerializer extends CrossLanguageCompatibleSerializer { + public static final class LongSerializer extends ImmutableSerializer { private final LongEncoding longEncoding; - public LongSerializer(Fory fory, Class cls) { - super(fory, (Class) cls, false, true); - longEncoding = isJava ? fory.longEncoding() : LongEncoding.VARINT; + public LongSerializer(Config config, Class cls) { + super(config, (Class) cls, false); + longEncoding = config.isXlang() ? LongEncoding.VARINT : config.longEncoding(); } @Override - public void write(MemoryBuffer buffer, Long value) { - writeInt64(buffer, value, longEncoding); + public void write(WriteContext writeContext, Long value) { + writeInt64(writeContext.getBuffer(), value, longEncoding); } @Override - public Long read(MemoryBuffer buffer) { - return readInt64(buffer, longEncoding); + public Long read(ReadContext readContext) { + return readInt64(readContext.getBuffer(), longEncoding); } public static Expression writeInt64( @@ -249,92 +291,119 @@ public static String readLongFunc(LongEncoding longEncoding) { throw new UnsupportedOperationException("Unsupported long encoding " + longEncoding); } } + + @Override + public boolean threadSafe() { + return true; + } } public static final class VarUint64Serializer extends Serializer { - public VarUint64Serializer(Fory fory) { - super(fory, Long.class); + public VarUint64Serializer(Config config) { + super(config, Long.class); } @Override - public void write(MemoryBuffer buffer, Long value) { + public void write(WriteContext writeContext, Long value) { Preconditions.checkArgument(value >= 0); - buffer.writeVarUint64(value); + writeContext.getBuffer().writeVarUint64(value); + } + + @Override + public Long read(ReadContext readContext) { + return readContext.getBuffer().readVarUint64(); } @Override - public Long read(MemoryBuffer buffer) { - return buffer.readVarUint64(); + public boolean threadSafe() { + return true; } } - public static final class FloatSerializer extends CrossLanguageCompatibleSerializer { - public FloatSerializer(Fory fory, Class cls) { - super(fory, (Class) cls, false, true); + public static final class FloatSerializer extends ImmutableSerializer { + public FloatSerializer(Config config, Class cls) { + super(config, (Class) cls, false); } @Override - public void write(MemoryBuffer buffer, Float value) { - buffer.writeFloat32(value); + public void write(WriteContext writeContext, Float value) { + writeContext.getBuffer().writeFloat32(value); } @Override - public Float read(MemoryBuffer buffer) { - return buffer.readFloat32(); + public Float read(ReadContext readContext) { + return readContext.getBuffer().readFloat32(); + } + + @Override + public boolean threadSafe() { + return true; } } - public static final class DoubleSerializer extends CrossLanguageCompatibleSerializer { - public DoubleSerializer(Fory fory, Class cls) { - super(fory, (Class) cls, false, true); + public static final class DoubleSerializer extends ImmutableSerializer { + public DoubleSerializer(Config config, Class cls) { + super(config, (Class) cls, false); + } + + @Override + public void write(WriteContext writeContext, Double value) { + writeContext.getBuffer().writeFloat64(value); } @Override - public void write(MemoryBuffer buffer, Double value) { - buffer.writeFloat64(value); + public Double read(ReadContext readContext) { + return readContext.getBuffer().readFloat64(); } @Override - public Double read(MemoryBuffer buffer) { - return buffer.readFloat64(); + public boolean threadSafe() { + return true; } } - public static final class Float16Serializer extends CrossLanguageCompatibleSerializer { - public Float16Serializer(Fory fory, Class cls) { - super(fory, (Class) cls, false, true); + public static final class Float16Serializer extends ImmutableSerializer { + public Float16Serializer(Config config, Class cls) { + super(config, (Class) cls, false); + } + + @Override + public void write(WriteContext writeContext, Float16 value) { + writeContext.getBuffer().writeInt16(value.toBits()); } @Override - public void write(MemoryBuffer buffer, Float16 value) { - buffer.writeInt16(value.toBits()); + public Float16 read(ReadContext readContext) { + return Float16.fromBits(readContext.getBuffer().readInt16()); } @Override - public Float16 read(MemoryBuffer buffer) { - return Float16.fromBits(buffer.readInt16()); + public boolean threadSafe() { + return true; } } - public static void registerDefaultSerializers(Fory fory) { + public static void registerDefaultSerializers(TypeResolver resolver) { // primitive types will be boxed. - TypeResolver resolver = fory.getTypeResolver(); - resolver.registerInternalSerializer(boolean.class, new BooleanSerializer(fory, boolean.class)); - resolver.registerInternalSerializer(byte.class, new ByteSerializer(fory, byte.class)); - resolver.registerInternalSerializer(short.class, new ShortSerializer(fory, short.class)); - resolver.registerInternalSerializer(char.class, new CharSerializer(fory, char.class)); - resolver.registerInternalSerializer(int.class, new IntSerializer(fory, int.class)); - resolver.registerInternalSerializer(long.class, new LongSerializer(fory, long.class)); - resolver.registerInternalSerializer(float.class, new FloatSerializer(fory, float.class)); - resolver.registerInternalSerializer(double.class, new DoubleSerializer(fory, double.class)); - resolver.registerInternalSerializer(Boolean.class, new BooleanSerializer(fory, Boolean.class)); - resolver.registerInternalSerializer(Byte.class, new ByteSerializer(fory, Byte.class)); - resolver.registerInternalSerializer(Short.class, new ShortSerializer(fory, Short.class)); - resolver.registerInternalSerializer(Character.class, new CharSerializer(fory, Character.class)); - resolver.registerInternalSerializer(Integer.class, new IntSerializer(fory, Integer.class)); - resolver.registerInternalSerializer(Long.class, new LongSerializer(fory, Long.class)); - resolver.registerInternalSerializer(Float.class, new FloatSerializer(fory, Float.class)); - resolver.registerInternalSerializer(Double.class, new DoubleSerializer(fory, Double.class)); - resolver.registerInternalSerializer(Float16.class, new Float16Serializer(fory, Float16.class)); + Config config = resolver.getConfig(); + resolver.registerInternalSerializer(boolean.class, new BooleanSerializer(config, boolean.class)); + resolver.registerInternalSerializer(byte.class, new ByteSerializer(config, byte.class)); + resolver.registerInternalSerializer(short.class, new ShortSerializer(config, short.class)); + resolver.registerInternalSerializer(char.class, new CharSerializer(config, char.class)); + resolver.registerInternalSerializer(int.class, new IntSerializer(config, int.class)); + resolver.registerInternalSerializer(long.class, new LongSerializer(config, long.class)); + resolver.registerInternalSerializer(float.class, new FloatSerializer(config, float.class)); + resolver.registerInternalSerializer(double.class, new DoubleSerializer(config, double.class)); + resolver.registerInternalSerializer(Boolean.class, new BooleanSerializer(config, Boolean.class)); + resolver.registerInternalSerializer(Byte.class, new ByteSerializer(config, Byte.class)); + resolver.registerInternalSerializer(Short.class, new ShortSerializer(config, Short.class)); + resolver.registerInternalSerializer( + Character.class, new CharSerializer(config, Character.class)); + resolver.registerInternalSerializer(Integer.class, new IntSerializer(config, Integer.class)); + resolver.registerInternalSerializer(Long.class, new LongSerializer(config, Long.class)); + resolver.registerInternalSerializer(Float.class, new FloatSerializer(config, Float.class)); + resolver.registerInternalSerializer(Double.class, new DoubleSerializer(config, Double.class)); + resolver.registerInternalSerializer( + Float16.class, new Float16Serializer(config, Float16.class)); } } diff --git a/java/fory-core/src/main/java/org/apache/fory/serializer/ReplaceResolveSerializer.java b/java/fory-core/src/main/java/org/apache/fory/serializer/ReplaceResolveSerializer.java index 228cb40baa..4935ec6fc7 100644 --- a/java/fory-core/src/main/java/org/apache/fory/serializer/ReplaceResolveSerializer.java +++ b/java/fory-core/src/main/java/org/apache/fory/serializer/ReplaceResolveSerializer.java @@ -28,14 +28,17 @@ import java.util.Map; import java.util.function.Function; import org.apache.fory.Fory; +import org.apache.fory.context.CopyContext; +import org.apache.fory.context.ReadContext; +import org.apache.fory.context.WriteContext; import org.apache.fory.logging.Logger; import org.apache.fory.logging.LoggerFactory; import org.apache.fory.memory.MemoryBuffer; import org.apache.fory.memory.Platform; import org.apache.fory.reflect.ReflectionUtils; import org.apache.fory.resolver.ClassResolver; -import org.apache.fory.resolver.RefResolver; import org.apache.fory.resolver.TypeInfo; +import org.apache.fory.resolver.TypeResolver; import org.apache.fory.util.Preconditions; import org.apache.fory.util.unsafe._JDKAccess; @@ -176,23 +179,22 @@ public void setObjectSerializer(Serializer objectSerializer) { } } - static MethodInfoCache newJDKMethodInfoCache(Class cls, Fory fory) { + static MethodInfoCache newJDKMethodInfoCache(TypeResolver typeResolver, Class cls) { ReplaceResolveInfo replaceResolveInfo = REPLACE_RESOLVE_INFO_CACHE.get(cls); MethodInfoCache methodInfoCache = new MethodInfoCache(replaceResolveInfo); + ClassResolver classResolver = (ClassResolver) typeResolver; Class serializerClass; if (Externalizable.class.isAssignableFrom(cls)) { serializerClass = ExternalizableSerializer.class; } else if (JavaSerializer.getReadObjectMethod(cls, true) == null && JavaSerializer.getWriteObjectMethod(cls, true) == null) { serializerClass = - ((ClassResolver) fory.getTypeResolver()) - .getObjectSerializerClass( - cls, - sc -> methodInfoCache.setObjectSerializer(createDataSerializer(fory, cls, sc))); + classResolver.getObjectSerializerClass( + cls, sc -> methodInfoCache.setObjectSerializer(createDataSerializer(typeResolver, cls, sc))); } else { - serializerClass = fory.getDefaultJDKStreamSerializerType(); + serializerClass = typeResolver.getDefaultJDKStreamSerializerType(); } - methodInfoCache.setObjectSerializer(createDataSerializer(fory, cls, serializerClass)); + methodInfoCache.setObjectSerializer(createDataSerializer(typeResolver, cls, serializerClass)); return methodInfoCache; } @@ -203,29 +205,29 @@ static MethodInfoCache newJDKMethodInfoCache(Class cls, Fory fory) { * @see #readObject */ private static Serializer createDataSerializer( - Fory fory, Class cls, Class sc) { - ClassResolver classResolver = (ClassResolver) fory.getTypeResolver(); + TypeResolver typeResolver, Class cls, Class sc) { + ClassResolver classResolver = (ClassResolver) typeResolver; Serializer prev = classResolver.getSerializer(cls, false); - Serializer serializer = Serializers.newSerializer(fory, cls, sc); + Serializer serializer = Serializers.newSerializer(typeResolver, cls, sc); classResolver.resetSerializer(cls, prev); return serializer; } - protected final RefResolver refResolver; + protected final TypeResolver typeResolver; protected final ClassResolver classResolver; protected final MethodInfoCache jdkMethodInfoWriteCache; protected final TypeInfo writeTypeInfo; protected final Map, MethodInfoCache> classTypeInfoHolderMap = new HashMap<>(); - public ReplaceResolveSerializer(Fory fory, Class type) { - this(fory, type, false, true); + public ReplaceResolveSerializer(TypeResolver typeResolver, Class type) { + this(typeResolver, type, false, true); } public ReplaceResolveSerializer( - Fory fory, Class type, boolean isFinalField, boolean setSerializer) { - super(fory, type); - refResolver = fory.getRefResolver(); - classResolver = (ClassResolver) fory.getTypeResolver(); + TypeResolver typeResolver, Class type, boolean isFinalField, boolean setSerializer) { + super(typeResolver.getConfig(), type); + this.typeResolver = typeResolver; + classResolver = (ClassResolver) typeResolver; if (setSerializer) { // `setSerializer` before `newJDKMethodInfoCache` since it query classinfo from // `classResolver`, @@ -235,7 +237,7 @@ public ReplaceResolveSerializer( classResolver.setSerializerIfAbsent(type, this); } if (type != ReplaceStub.class) { - jdkMethodInfoWriteCache = newJDKMethodInfoCache(type, fory); + jdkMethodInfoWriteCache = newJDKMethodInfoCache(typeResolver, type); classTypeInfoHolderMap.put(type, jdkMethodInfoWriteCache); if (isFinalField) { writeTypeInfo = null; @@ -251,7 +253,8 @@ public ReplaceResolveSerializer( } @Override - public void write(MemoryBuffer buffer, Object value) { + public void write(WriteContext writeContext, Object value) { + MemoryBuffer buffer = writeContext.getBuffer(); MethodInfoCache jdkMethodInfoCache = this.jdkMethodInfoWriteCache; ReplaceResolveInfo replaceResolveInfo = jdkMethodInfoCache.info; Method writeReplaceMethod = replaceResolveInfo.writeReplaceMethod; @@ -264,80 +267,83 @@ public void write(MemoryBuffer buffer, Object value) { // which is not a problem in almost every case but inconsistent with JDK serialization. if (value == null || value.getClass() != type) { buffer.writeByte(REPLACED_NEW_TYPE); - if (!refResolver.writeRefOrNull(buffer, value)) { + if (!writeContext.writeRefOrNull(value)) { // replace original object reference id with new object reference id // for later object graph serialization. // written `REF_VALUE_FLAG`/`NOT_NULL_VALUE_FLAG` id outside this method call will be // ignored. - refResolver.replaceRef(original, value); - fory.writeNonRef(buffer, value); + writeContext.replaceRef(original, value); + writeContext.writeNonRef(value); } } else { if (value != original) { buffer.writeByte(REPLACED_SAME_TYPE); - if (!refResolver.writeRefOrNull(buffer, value)) { + if (!writeContext.writeRefOrNull(value)) { // replace original object reference id with new object reference id // for later object graph serialization, // written `REF_VALUE_FLAG`/`NOT_NULL_VALUE_FLAG` id outside this method call will be // ignored. - refResolver.replaceRef(original, value); - writeObject(buffer, value, jdkMethodInfoCache); + writeContext.replaceRef(original, value); + writeObject(writeContext, buffer, value, jdkMethodInfoCache); } } else { buffer.writeByte(ORIGINAL); - writeObject(buffer, value, jdkMethodInfoCache); + writeObject(writeContext, buffer, value, jdkMethodInfoCache); } } } else { buffer.writeByte(ORIGINAL); - writeObject(buffer, value, jdkMethodInfoCache); + writeObject(writeContext, buffer, value, jdkMethodInfoCache); } } protected void writeObject( - MemoryBuffer buffer, Object value, MethodInfoCache jdkMethodInfoCache) { - classResolver.writeClassInternal(buffer, writeTypeInfo); - jdkMethodInfoCache.objectSerializer.write(buffer, value); + WriteContext writeContext, + MemoryBuffer buffer, + Object value, + MethodInfoCache jdkMethodInfoCache) { + classResolver.writeClassInternal(writeContext, writeTypeInfo); + jdkMethodInfoCache.objectSerializer.write(writeContext, value); } @Override - public Object read(MemoryBuffer buffer) { + public Object read(ReadContext readContext) { + MemoryBuffer buffer = readContext.getBuffer(); byte flag = buffer.readByte(); - RefResolver refResolver = this.refResolver; if (flag == REPLACED_NEW_TYPE) { - int outerRefId = refResolver.lastPreservedRefId(); - int nextReadRefId = refResolver.tryPreserveRefId(buffer); + int outerRefId = readContext.lastPreservedRefId(); + int nextReadRefId = readContext.tryPreserveRefId(); if (nextReadRefId >= Fory.NOT_NULL_VALUE_FLAG) { // ref value or not-null value - Object o = fory.readData(buffer, classResolver.readTypeInfo(buffer)); - refResolver.setReadObject(nextReadRefId, o); - refResolver.setReadObject(outerRefId, o); + Object o = readContext.readData(classResolver.readTypeInfo(readContext)); + readContext.setReadObject(nextReadRefId, o); + readContext.setReadObject(outerRefId, o); return o; } else { - return refResolver.getReadObject(); + return readContext.getReadObject(); } } else if (flag == REPLACED_SAME_TYPE) { - int outerRefId = refResolver.lastPreservedRefId(); - int nextReadRefId = refResolver.tryPreserveRefId(buffer); + int outerRefId = readContext.lastPreservedRefId(); + int nextReadRefId = readContext.tryPreserveRefId(); if (nextReadRefId >= Fory.NOT_NULL_VALUE_FLAG) { // ref value or not-null value - Object o = readObject(buffer); - refResolver.setReadObject(nextReadRefId, o); - refResolver.setReadObject(outerRefId, o); + Object o = readObject(readContext, buffer); + readContext.setReadObject(nextReadRefId, o); + readContext.setReadObject(outerRefId, o); return o; } else { - return refResolver.getReadObject(); + return readContext.getReadObject(); } } else { Preconditions.checkArgument(flag == ORIGINAL); - return readObject(buffer); + return readObject(readContext, buffer); } } - protected Object readObject(MemoryBuffer buffer) { - Class cls = classResolver.readClassInternal(buffer); + protected Object readObject(ReadContext readContext, MemoryBuffer buffer) { + Class cls = classResolver.readClassInternal(readContext); MethodInfoCache jdkMethodInfoCache = getMethodInfoCache(cls); - Object o = jdkMethodInfoCache.objectSerializer.read(buffer); + Object o = jdkMethodInfoCache.objectSerializer.read(readContext); ReplaceResolveInfo replaceResolveInfo = jdkMethodInfoCache.info; if (replaceResolveInfo.readResolveMethod == null) { return o; @@ -346,18 +352,18 @@ protected Object readObject(MemoryBuffer buffer) { } @Override - public Object copy(Object originObj) { + public Object copy(CopyContext copyContext, Object originObj) { ReplaceResolveInfo replaceResolveInfo = jdkMethodInfoWriteCache.info; if (replaceResolveInfo.writeReplaceMethod == null) { - return jdkMethodInfoWriteCache.objectSerializer.copy(originObj); + return jdkMethodInfoWriteCache.objectSerializer.copy(copyContext, originObj); } Object newObj = originObj; newObj = replaceResolveInfo.writeReplace(newObj); if (needToCopyRef) { - fory.reference(originObj, newObj); + copyContext.reference(originObj, newObj); } MethodInfoCache jdkMethodInfoCache = getMethodInfoCache(newObj.getClass()); - newObj = jdkMethodInfoCache.objectSerializer.copy(newObj); + newObj = jdkMethodInfoCache.objectSerializer.copy(copyContext, newObj); replaceResolveInfo = jdkMethodInfoCache.info; if (replaceResolveInfo.readResolveMethod != null) { newObj = replaceResolveInfo.readResolve(newObj); @@ -368,7 +374,7 @@ public Object copy(Object originObj) { protected MethodInfoCache getMethodInfoCache(Class cls) { MethodInfoCache jdkMethodInfoCache = classTypeInfoHolderMap.get(cls); if (jdkMethodInfoCache == null) { - jdkMethodInfoCache = newJDKMethodInfoCache(cls, fory); + jdkMethodInfoCache = newJDKMethodInfoCache(typeResolver, cls); classTypeInfoHolderMap.put(cls, jdkMethodInfoCache); } return jdkMethodInfoCache; diff --git a/java/fory-core/src/main/java/org/apache/fory/serializer/SerializedLambdaSerializer.java b/java/fory-core/src/main/java/org/apache/fory/serializer/SerializedLambdaSerializer.java index af5772a184..d3f6d58ce4 100644 --- a/java/fory-core/src/main/java/org/apache/fory/serializer/SerializedLambdaSerializer.java +++ b/java/fory-core/src/main/java/org/apache/fory/serializer/SerializedLambdaSerializer.java @@ -22,9 +22,12 @@ import java.lang.invoke.MethodHandle; import java.lang.invoke.SerializedLambda; import java.lang.reflect.Method; -import org.apache.fory.Fory; +import org.apache.fory.context.CopyContext; +import org.apache.fory.context.ReadContext; +import org.apache.fory.context.WriteContext; import org.apache.fory.exception.ForyException; import org.apache.fory.memory.MemoryBuffer; +import org.apache.fory.resolver.TypeResolver; import org.apache.fory.util.Preconditions; import org.apache.fory.util.unsafe._JDKAccess; @@ -37,6 +40,7 @@ public class SerializedLambdaSerializer extends Serializer { static final Class SERIALIZED_LAMBDA = SerializedLambda.class; private static final MethodHandle READ_RESOLVE_HANDLE; + private final TypeResolver typeResolver; static { try { @@ -49,37 +53,39 @@ public class SerializedLambdaSerializer extends Serializer { } } - public SerializedLambdaSerializer(Fory fory, Class cls) { - super(fory, cls); + public SerializedLambdaSerializer(TypeResolver typeResolver, Class cls) { + super(typeResolver.getConfig(), cls); + this.typeResolver = typeResolver; Preconditions.checkArgument(cls == SERIALIZED_LAMBDA); } @Override - public void write(MemoryBuffer buffer, Object value) { + public void write(WriteContext writeContext, Object value) { + MemoryBuffer buffer = writeContext.getBuffer(); SerializedLambda serializedLambda = (SerializedLambda) value; - fory.writeStringRef(buffer, serializedLambda.getCapturingClass()); - fory.writeStringRef(buffer, serializedLambda.getFunctionalInterfaceClass()); - fory.writeStringRef(buffer, serializedLambda.getFunctionalInterfaceMethodName()); - fory.writeStringRef(buffer, serializedLambda.getFunctionalInterfaceMethodSignature()); - fory.writeStringRef(buffer, serializedLambda.getImplClass()); - fory.writeStringRef(buffer, serializedLambda.getImplMethodName()); - fory.writeStringRef(buffer, serializedLambda.getImplMethodSignature()); + writeContext.writeStringRef(serializedLambda.getCapturingClass()); + writeContext.writeStringRef(serializedLambda.getFunctionalInterfaceClass()); + writeContext.writeStringRef(serializedLambda.getFunctionalInterfaceMethodName()); + writeContext.writeStringRef(serializedLambda.getFunctionalInterfaceMethodSignature()); + writeContext.writeStringRef(serializedLambda.getImplClass()); + writeContext.writeStringRef(serializedLambda.getImplMethodName()); + writeContext.writeStringRef(serializedLambda.getImplMethodSignature()); buffer.writeVarInt32(serializedLambda.getImplMethodKind()); - fory.writeStringRef(buffer, serializedLambda.getInstantiatedMethodType()); + writeContext.writeStringRef(serializedLambda.getInstantiatedMethodType()); int capturedArgCount = serializedLambda.getCapturedArgCount(); buffer.writeVarUint32Small7(capturedArgCount); for (int i = 0; i < capturedArgCount; i++) { - fory.writeRef(buffer, serializedLambda.getCapturedArg(i)); + writeContext.writeRef(serializedLambda.getCapturedArg(i)); } } @Override - public Object copy(Object value) { + public Object copy(CopyContext copyContext, Object value) { SerializedLambda serializedLambda = (SerializedLambda) value; int capturedArgCount = serializedLambda.getCapturedArgCount(); Object[] capturedArgs = new Object[capturedArgCount]; for (int i = 0; i < capturedArgCount; i++) { - capturedArgs[i] = fory.copyObject(serializedLambda.getCapturedArg(i)); + capturedArgs[i] = copyContext.copyObject(serializedLambda.getCapturedArg(i)); } return newSerializedLambda( serializedLambda.getCapturingClass(), @@ -95,24 +101,25 @@ public Object copy(Object value) { } @Override - public Object read(MemoryBuffer buffer) { - return readResolve(readUnresolved(buffer)); + public Object read(ReadContext readContext) { + return readResolve(readUnresolved(readContext)); } - Object readUnresolved(MemoryBuffer buffer) { - String capturingClass = fory.readStringRef(buffer); - String functionalInterfaceClass = fory.readStringRef(buffer); - String functionalInterfaceMethodName = fory.readStringRef(buffer); - String functionalInterfaceMethodSignature = fory.readStringRef(buffer); - String implClass = fory.readStringRef(buffer); - String implMethodName = fory.readStringRef(buffer); - String implMethodSignature = fory.readStringRef(buffer); + Object readUnresolved(ReadContext readContext) { + MemoryBuffer buffer = readContext.getBuffer(); + String capturingClass = readContext.readStringRef(); + String functionalInterfaceClass = readContext.readStringRef(); + String functionalInterfaceMethodName = readContext.readStringRef(); + String functionalInterfaceMethodSignature = readContext.readStringRef(); + String implClass = readContext.readStringRef(); + String implMethodName = readContext.readStringRef(); + String implMethodSignature = readContext.readStringRef(); int implMethodKind = buffer.readVarInt32(); - String instantiatedMethodType = fory.readStringRef(buffer); + String instantiatedMethodType = readContext.readStringRef(); int capturedArgCount = buffer.readVarUint32Small7(); Object[] capturedArgs = new Object[capturedArgCount]; for (int i = 0; i < capturedArgCount; i++) { - capturedArgs[i] = fory.readRef(buffer); + capturedArgs[i] = readContext.readRef(); } return newSerializedLambda( capturingClass, @@ -162,7 +169,7 @@ private SerializedLambda newSerializedLambda( private Class loadCapturingClass(String className) { String binaryClassName = className.replace('/', '.'); try { - return Class.forName(binaryClassName, false, fory.getClassLoader()); + return Class.forName(binaryClassName, false, typeResolver.getClassLoader()); } catch (ClassNotFoundException e) { try { return Class.forName( diff --git a/java/fory-core/src/main/java/org/apache/fory/serializer/Serializer.java b/java/fory-core/src/main/java/org/apache/fory/serializer/Serializer.java index 436e95ccbb..42dda1aa8e 100644 --- a/java/fory-core/src/main/java/org/apache/fory/serializer/Serializer.java +++ b/java/fory-core/src/main/java/org/apache/fory/serializer/Serializer.java @@ -21,9 +21,12 @@ import javax.annotation.concurrent.NotThreadSafe; import org.apache.fory.Fory; +import org.apache.fory.config.Config; +import org.apache.fory.context.CopyContext; +import org.apache.fory.context.ReadContext; +import org.apache.fory.context.WriteContext; import org.apache.fory.memory.MemoryBuffer; import org.apache.fory.resolver.RefMode; -import org.apache.fory.resolver.RefResolver; import org.apache.fory.type.TypeUtils; /** @@ -35,10 +38,7 @@ @NotThreadSafe @SuppressWarnings("unchecked") public abstract class Serializer { - protected final Fory fory; - protected final RefResolver refResolver; protected final Class type; - protected final boolean isJava; protected final boolean needToWriteRef; /** @@ -49,25 +49,26 @@ public abstract class Serializer { protected final boolean immutable; - public Serializer(Fory fory, Class type) { + public Serializer(Config config, Class type) { this( - fory, + config, type, - fory.trackingRef() && !TypeUtils.isBoxed(TypeUtils.wrap(type)), + config.trackingRef() && !TypeUtils.isBoxed(TypeUtils.wrap(type)), TypeUtils.isPrimitive(type) || TypeUtils.isBoxed(type)); } - public Serializer(Fory fory, Class type, boolean immutable) { - this(fory, type, fory.trackingRef() && !TypeUtils.isBoxed(TypeUtils.wrap(type)), immutable); + public Serializer(Config config, Class type, boolean immutable) { + this( + config, + type, + config.trackingRef() && !TypeUtils.isBoxed(TypeUtils.wrap(type)), + immutable); } - public Serializer(Fory fory, Class type, boolean needToWriteRef, boolean immutable) { - this.fory = fory; - this.refResolver = fory.getRefResolver(); + public Serializer(Config config, Class type, boolean needToWriteRef, boolean immutable) { this.type = type; - this.isJava = !fory.isCrossLanguage(); this.needToWriteRef = needToWriteRef; - this.needToCopyRef = fory.copyTrackingRef() && !immutable; + this.needToCopyRef = config.copyRef() && !immutable; this.immutable = immutable; } @@ -76,10 +77,11 @@ public Serializer(Fory fory, Class type, boolean needToWriteRef, boolean immu * the passed value can be null. Note that this method don't write type info, this method is * mostly be used in cases the context has already knows the value type when deserialization. */ - public void write(MemoryBuffer buffer, RefMode refMode, T value) { + public void write(WriteContext writeContext, RefMode refMode, T value) { + MemoryBuffer buffer = writeContext.getBuffer(); // noinspection Duplicates if (refMode == RefMode.TRACKING) { - if (refResolver.writeRefOrNull(buffer, value)) { + if (writeContext.writeRefOrNull(value)) { return; } } else if (refMode == RefMode.NULL_ONLY) { @@ -90,38 +92,39 @@ public void write(MemoryBuffer buffer, RefMode refMode, T value) { buffer.writeByte(Fory.NOT_NULL_VALUE_FLAG); } } - write(buffer, value); + write(writeContext, value); } /** * Write value to buffer, this method do not write ref/null flags and the passed value must not be * null. */ - public abstract void write(MemoryBuffer buffer, T value); + public abstract void write(WriteContext writeContext, T value); /** * Read value from buffer, this method may read ref/null flags based passed {@code refMode}, and * the read value can be null. Note that this method don't read type info, this method is mostly * be used in cases the context has already knows the value type for deserialization. */ - public T read(MemoryBuffer buffer, RefMode refMode) { + public T read(ReadContext readContext, RefMode refMode) { + MemoryBuffer buffer = readContext.getBuffer(); if (refMode == RefMode.TRACKING) { T obj; - int nextReadRefId = refResolver.tryPreserveRefId(buffer); + int nextReadRefId = readContext.tryPreserveRefId(); if (nextReadRefId >= Fory.NOT_NULL_VALUE_FLAG) { - obj = read(buffer); - refResolver.setReadObject(nextReadRefId, obj); + obj = read(readContext); + readContext.setReadObject(nextReadRefId, obj); return obj; } else { - return (T) refResolver.getReadObject(); + return (T) readContext.getReadObject(); } } else if (refMode != RefMode.NULL_ONLY || buffer.readByte() != Fory.NULL_FLAG) { if (needToWriteRef) { - // in normal case, the read implementation may invoke `refResolver.reference` to + // in normal case, the read implementation may invoke `readContext.reference` to // support circular reference, so we still need this `-1` - refResolver.preserveRefId(-1); + readContext.preserveRefId(-1); } - return read(buffer); + return read(readContext); } return null; } @@ -129,9 +132,9 @@ public T read(MemoryBuffer buffer, RefMode refMode) { /** * Read value from buffer, this method wont read ref/null flags and the read value won't be null. */ - public abstract T read(MemoryBuffer buffer); + public abstract T read(ReadContext readContext); - public T copy(T value) { + public T copy(CopyContext copyContext, T value) { if (isImmutable()) { return value; } @@ -154,4 +157,8 @@ public Class getType() { public boolean isImmutable() { return immutable; } + + public boolean threadSafe() { + return false; + } } diff --git a/java/fory-core/src/main/java/org/apache/fory/serializer/SerializerFactory.java b/java/fory-core/src/main/java/org/apache/fory/serializer/SerializerFactory.java index a5b2ad66e9..afc9701c78 100644 --- a/java/fory-core/src/main/java/org/apache/fory/serializer/SerializerFactory.java +++ b/java/fory-core/src/main/java/org/apache/fory/serializer/SerializerFactory.java @@ -19,10 +19,10 @@ package org.apache.fory.serializer; -import org.apache.fory.Fory; +import org.apache.fory.resolver.TypeResolver; /** Serializer factory for customizing serializer creation. */ public interface SerializerFactory { - Serializer createSerializer(Fory fory, Class cls); + Serializer createSerializer(TypeResolver typeResolver, Class cls); } diff --git a/java/fory-core/src/main/java/org/apache/fory/serializer/Serializers.java b/java/fory-core/src/main/java/org/apache/fory/serializer/Serializers.java index ccf9d30e7a..2a6a479b40 100644 --- a/java/fory-core/src/main/java/org/apache/fory/serializer/Serializers.java +++ b/java/fory-core/src/main/java/org/apache/fory/serializer/Serializers.java @@ -21,12 +21,15 @@ import static org.apache.fory.util.function.Functions.makeGetterFunction; +import org.apache.fory.context.CopyContext; +import org.apache.fory.context.ReadContext; +import org.apache.fory.context.WriteContext; + import com.google.common.cache.Cache; import com.google.common.cache.CacheBuilder; import java.lang.invoke.MethodHandle; import java.lang.invoke.MethodHandles; import java.lang.invoke.MethodType; -import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.math.BigDecimal; import java.math.BigInteger; @@ -44,13 +47,24 @@ import java.util.regex.Pattern; import org.apache.fory.Fory; import org.apache.fory.collection.Tuple2; +import org.apache.fory.config.Config; import org.apache.fory.memory.MemoryBuffer; import org.apache.fory.memory.Platform; import org.apache.fory.meta.TypeDef; import org.apache.fory.reflect.ReflectionUtils; import org.apache.fory.resolver.ClassResolver; +import org.apache.fory.resolver.SharedRegistry; import org.apache.fory.resolver.TypeInfo; import org.apache.fory.resolver.TypeResolver; +import org.apache.fory.serializer.CodegenSerializer.LazyInitBeanSerializer; +import org.apache.fory.serializer.collection.ChildContainerSerializers; +import org.apache.fory.serializer.collection.CollectionSerializer; +import org.apache.fory.serializer.collection.CollectionSerializers; +import org.apache.fory.serializer.collection.MapSerializer; +import org.apache.fory.serializer.collection.MapSerializers; +import org.apache.fory.serializer.scala.SingletonCollectionSerializer; +import org.apache.fory.serializer.scala.SingletonMapSerializer; +import org.apache.fory.serializer.scala.SingletonObjectSerializer; import org.apache.fory.util.ExceptionUtils; import org.apache.fory.util.GraalvmSupport; import org.apache.fory.util.StringUtils; @@ -70,107 +84,244 @@ public class Serializers { } } - private static final MethodType SIG1 = MethodType.methodType(void.class, Fory.class, Class.class); - private static final MethodType SIG2 = MethodType.methodType(void.class, Fory.class); - private static final MethodType SIG3 = MethodType.methodType(void.class, Class.class); - private static final MethodType SIG4 = MethodType.methodType(void.class); + private static final MethodType SIG1 = + MethodType.methodType(void.class, TypeResolver.class, Class.class); + private static final MethodType SIG2 = MethodType.methodType(void.class, TypeResolver.class); + private static final MethodType SIG3 = + MethodType.methodType(void.class, Config.class, Class.class); + private static final MethodType SIG4 = MethodType.methodType(void.class, Config.class); + private static final MethodType SIG5 = MethodType.methodType(void.class, Class.class); + private static final MethodType SIG6 = MethodType.methodType(void.class); /** - * Serializer subclass must have a constructor which take parameters of type {@link Fory} and - * {@link Class}, or {@link Fory} or {@link Class} or no-arg constructor. + * Serializer subclass must have a constructor which take parameters of type {@link TypeResolver} + * and {@link Class}, or {@link TypeResolver}, or {@link Config} and {@link Class}, or {@link + * Config}, or {@link Class}, or no-arg constructor. */ public static Serializer newSerializer( Fory fory, Class type, Class serializerClass) { - TypeResolver typeResolver = fory.getTypeResolver(); - TypeInfo typeInfo = typeResolver.getTypeInfo(type, false); - Serializer serializer = typeInfo == null ? null : typeInfo.getSerializer(); - try { - if (serializerClass == ObjectSerializer.class) { - return new ObjectSerializer(fory, type); + return newSerializer(fory.getTypeResolver(), type, serializerClass); + } + + /** + * Serializer subclass must have a constructor which take parameters of type {@link TypeResolver} + * and {@link Class}, or {@link TypeResolver}, or {@link Config} and {@link Class}, or {@link + * Config}, or {@link Class}, or no-arg constructor. + */ + public static Serializer newSerializer( + TypeResolver typeResolver, Class type, Class serializerClass) { + TypeInfo typeInfo = typeResolver.getTypeInfo(type, false); + Serializer serializer = typeInfo == null ? null : typeInfo.getSerializer(); + try { + return typeResolver.getSharedRegistry().getOrCreateSerializer( + type, + serializerClass, + new SharedRegistry.SerializerBuilder>() { + @Override + public Serializer build() { + return buildSerializer(typeResolver, type, serializerClass); + } + }); + } catch (Throwable t) { + // Some serializer may set itself in constructor as serializer, but the + // constructor failed later. For example, some final type field doesn't + // support serialization. + typeResolver.resetSerializer(type, serializer); + if (t instanceof java.lang.reflect.InvocationTargetException && t.getCause() != null) { + Platform.throwException(t.getCause()); } - if (serializerClass == MetaSharedSerializer.class) { - TypeDef typeDef = typeResolver.getTypeDef(type, true); - return new MetaSharedSerializer(fory, type, typeDef); + Platform.throwException(t); + } + throw new IllegalStateException("unreachable"); + } + + private static Serializer buildSerializer( + TypeResolver typeResolver, Class type, Class serializerClass) { + try { + Config config = typeResolver.getConfig(); + Serializer serializer = buildBuiltinSerializer(typeResolver, config, type, serializerClass); + if (serializer != null) { + return serializer; } Tuple2 ctrInfo = CTR_MAP.getIfPresent(serializerClass); if (ctrInfo != null) { MethodType sig = ctrInfo.f0; MethodHandle handle = ctrInfo.f1; if (sig.equals(SIG1)) { - return (Serializer) handle.invoke(fory, type); + return (Serializer) handle.invoke(typeResolver, type); } else if (sig.equals(SIG2)) { - return (Serializer) handle.invoke(fory); + return (Serializer) handle.invoke(typeResolver); } else if (sig.equals(SIG3)) { + return (Serializer) handle.invoke(config, type); + } else if (sig.equals(SIG4)) { + return (Serializer) handle.invoke(config); + } else if (sig.equals(SIG5)) { return (Serializer) handle.invoke(type); } else { return (Serializer) handle.invoke(); } - } else { - return createSerializer(fory, type, serializerClass); - } - } catch (InvocationTargetException e) { - typeResolver.resetSerializer(type, serializer); - if (e.getCause() != null) { - Platform.throwException(e.getCause()); - } else { - Platform.throwException(e); } + return createSerializer(typeResolver, type, serializerClass); } catch (Throwable t) { - // Some serializer may set itself in constructor as serializer, but the - // constructor failed later. For example, some final type field doesn't - // support serialization. - typeResolver.resetSerializer(type, serializer); Platform.throwException(t); + throw new IllegalStateException("unreachable"); } - throw new IllegalStateException("unreachable"); } - private static Serializer createSerializer( - Fory fory, Class type, Class serializerClass) throws Throwable { - MethodHandles.Lookup lookup = _JDKAccess._trustedLookup(serializerClass); - try { - MethodHandle ctr = lookup.findConstructor(serializerClass, SIG1); - CTR_MAP.put(serializerClass, Tuple2.of(SIG1, ctr)); - return (Serializer) ctr.invoke(fory, type); - } catch (NoSuchMethodException e) { - ExceptionUtils.ignore(e); + private static Serializer buildBuiltinSerializer( + TypeResolver typeResolver, + Config config, + Class type, + Class serializerClass) { + if (serializerClass == ObjectSerializer.class) { + return new ObjectSerializer(typeResolver, type); } - try { - MethodHandle ctr = lookup.findConstructor(serializerClass, SIG2); - CTR_MAP.put(serializerClass, Tuple2.of(SIG2, ctr)); - return (Serializer) ctr.invoke(fory); - } catch (NoSuchMethodException e) { - ExceptionUtils.ignore(e); + if (serializerClass == ArraySerializers.ObjectArraySerializer.class) { + return new ArraySerializers.ObjectArraySerializer(typeResolver, type); } + if (serializerClass == ObjectStreamSerializer.class) { + return new ObjectStreamSerializer(typeResolver, type); + } + if (serializerClass == ExceptionSerializers.ExceptionSerializer.class) { + return new ExceptionSerializers.ExceptionSerializer(typeResolver, type); + } + if (serializerClass == ExceptionSerializers.StackTraceElementSerializer.class) { + return (Serializer) new ExceptionSerializers.StackTraceElementSerializer(config); + } + if (serializerClass == MetaSharedSerializer.class) { + TypeDef typeDef = typeResolver.getTypeDef(type, true); + return new MetaSharedSerializer(typeResolver, type, typeDef); + } + if (serializerClass == EnumSerializer.class) { + return (Serializer) new EnumSerializer(config, type); + } + if (serializerClass == LambdaSerializer.class) { + return new LambdaSerializer(typeResolver, type); + } + if (serializerClass == JdkProxySerializer.class) { + return new JdkProxySerializer(typeResolver, type); + } + if (serializerClass == ReplaceResolveSerializer.class) { + return new ReplaceResolveSerializer(typeResolver, type); + } + if (serializerClass == ExternalizableSerializer.class) { + return new ExternalizableSerializer(typeResolver, type); + } + if (serializerClass == LazyInitBeanSerializer.class) { + return new LazyInitBeanSerializer(typeResolver, type); + } + if (serializerClass == TimeSerializers.CalendarSerializer.class) { + return (Serializer) new TimeSerializers.CalendarSerializer(config, type); + } + if (serializerClass == TimeSerializers.ZoneIdSerializer.class) { + return (Serializer) new TimeSerializers.ZoneIdSerializer(config, type); + } + if (serializerClass == TimeSerializers.TimeZoneSerializer.class) { + return (Serializer) new TimeSerializers.TimeZoneSerializer(config, type); + } + if (serializerClass == BufferSerializers.ByteBufferSerializer.class) { + return (Serializer) new BufferSerializers.ByteBufferSerializer(typeResolver, type); + } + if (serializerClass == CharsetSerializer.class) { + return new CharsetSerializer(config, type); + } + if (serializerClass == CollectionSerializers.EnumSetSerializer.class) { + return (Serializer) new CollectionSerializers.EnumSetSerializer(typeResolver, type); + } + if (serializerClass == CollectionSerializer.class) { + return new CollectionSerializer(typeResolver, type); + } + if (serializerClass == CollectionSerializers.DefaultJavaCollectionSerializer.class) { + return new CollectionSerializers.DefaultJavaCollectionSerializer(typeResolver, type); + } + if (serializerClass == CollectionSerializers.JDKCompatibleCollectionSerializer.class) { + return new CollectionSerializers.JDKCompatibleCollectionSerializer(typeResolver, type); + } + if (serializerClass == MapSerializer.class) { + return new MapSerializer(typeResolver, type); + } + if (serializerClass == MapSerializers.DefaultJavaMapSerializer.class) { + return new MapSerializers.DefaultJavaMapSerializer(typeResolver, type); + } + if (serializerClass == MapSerializers.JDKCompatibleMapSerializer.class) { + return new MapSerializers.JDKCompatibleMapSerializer(typeResolver, type); + } + if (serializerClass == ChildContainerSerializers.ChildCollectionSerializer.class) { + return new ChildContainerSerializers.ChildCollectionSerializer(typeResolver, type); + } + if (serializerClass == ChildContainerSerializers.ChildArrayListSerializer.class) { + return new ChildContainerSerializers.ChildArrayListSerializer(typeResolver, type); + } + if (serializerClass == ChildContainerSerializers.ChildMapSerializer.class) { + return new ChildContainerSerializers.ChildMapSerializer(typeResolver, type); + } + if (serializerClass == SingletonCollectionSerializer.class) { + return new SingletonCollectionSerializer(typeResolver, type); + } + if (serializerClass == SingletonMapSerializer.class) { + return new SingletonMapSerializer(typeResolver, type); + } + if (serializerClass == SingletonObjectSerializer.class) { + return new SingletonObjectSerializer(typeResolver, type); + } + return null; + } + + private static Serializer createSerializer( + TypeResolver typeResolver, Class type, Class serializerClass) { try { - MethodHandle ctr = lookup.findConstructor(serializerClass, SIG3); - CTR_MAP.put(serializerClass, Tuple2.of(SIG3, ctr)); - return (Serializer) ctr.invoke(type); - } catch (NoSuchMethodException e) { + MethodHandles.Lookup lookup = _JDKAccess._trustedLookup(serializerClass); + Config config = typeResolver.getConfig(); + try { + MethodHandle ctr = lookup.findConstructor(serializerClass, SIG1); + CTR_MAP.put(serializerClass, Tuple2.of(SIG1, ctr)); + return (Serializer) ctr.invoke(typeResolver, type); + } catch (NoSuchMethodException e) { + ExceptionUtils.ignore(e); + } + try { + MethodHandle ctr = lookup.findConstructor(serializerClass, SIG2); + CTR_MAP.put(serializerClass, Tuple2.of(SIG2, ctr)); + return (Serializer) ctr.invoke(typeResolver); + } catch (NoSuchMethodException e) { + ExceptionUtils.ignore(e); + } + try { + MethodHandle ctr = lookup.findConstructor(serializerClass, SIG3); + CTR_MAP.put(serializerClass, Tuple2.of(SIG3, ctr)); + return (Serializer) ctr.invoke(config, type); + } catch (NoSuchMethodException e) { + ExceptionUtils.ignore(e); + } + try { + MethodHandle ctr = lookup.findConstructor(serializerClass, SIG4); + CTR_MAP.put(serializerClass, Tuple2.of(SIG4, ctr)); + return (Serializer) ctr.invoke(config); + } catch (NoSuchMethodException e) { + ExceptionUtils.ignore(e); + } + try { + MethodHandle ctr = lookup.findConstructor(serializerClass, SIG5); + CTR_MAP.put(serializerClass, Tuple2.of(SIG5, ctr)); + return (Serializer) ctr.invoke(type); + } catch (NoSuchMethodException e) { + ExceptionUtils.ignore(e); + } MethodHandle ctr = ReflectionUtils.getCtrHandle(serializerClass); - CTR_MAP.put(serializerClass, Tuple2.of(SIG4, ctr)); + CTR_MAP.put(serializerClass, Tuple2.of(SIG6, ctr)); return (Serializer) ctr.invoke(); + } catch (Throwable t) { + Platform.throwException(t); + throw new IllegalStateException("unreachable"); } } - public static void write(MemoryBuffer buffer, Serializer serializer, T obj) { - serializer.write(buffer, obj); - } - - public static T read(MemoryBuffer buffer, Serializer serializer) { - return serializer.read(buffer); + public static void write(WriteContext writeContext, Serializer serializer, T obj) { + serializer.write(writeContext, obj); } - public abstract static class CrossLanguageCompatibleSerializer extends Serializer { - - public CrossLanguageCompatibleSerializer(Fory fory, Class cls) { - super(fory, cls); - } - - public CrossLanguageCompatibleSerializer( - Fory fory, Class cls, boolean needToWriteRef, boolean immutable) { - super(fory, cls, needToWriteRef, immutable); - } + public static T read(ReadContext readContext, Serializer serializer) { + return serializer.read(readContext); } private static final ToIntFunction GET_CODER; @@ -190,39 +341,41 @@ public CrossLanguageCompatibleSerializer( public abstract static class AbstractStringBuilderSerializer extends Serializer { - protected final StringSerializer stringSerializer; + private final Config config; - public AbstractStringBuilderSerializer(Fory fory, Class type) { - super(fory, type); - stringSerializer = fory.getStringSerializer(); + public AbstractStringBuilderSerializer(Config config, Class type) { + super(config, type); + this.config = config; } @Override - public void write(MemoryBuffer buffer, T value) { - if (isJava) { - if (GET_CODER != null) { - int coder = GET_CODER.applyAsInt(value); - byte[] v = (byte[]) GET_VALUE.apply(value); - int bytesLen = value.length(); - if (coder != 0) { - if (coder != 1) { - throw new UnsupportedOperationException("Unsupported coder " + coder); - } - bytesLen <<= 1; - } - long header = ((long) bytesLen << 2) | coder; - buffer.writeVarUint64(header); - buffer.writeBytes(v, 0, bytesLen); - } else { - char[] v = (char[]) GET_VALUE.apply(value); - if (StringUtils.isLatin(v)) { - stringSerializer.writeCharsLatin1(buffer, v, value.length()); - } else { - stringSerializer.writeCharsUTF16(buffer, v, value.length()); + public void write(WriteContext writeContext, T value) { + MemoryBuffer buffer = writeContext.getBuffer(); + StringSerializer stringSerializer = writeContext.getStringSerializer(); + if (config.isXlang()) { + stringSerializer.writeString(buffer, value.toString()); + return; + } + if (GET_CODER != null) { + int coder = GET_CODER.applyAsInt(value); + byte[] v = (byte[]) GET_VALUE.apply(value); + int bytesLen = value.length(); + if (coder != 0) { + if (coder != 1) { + throw new UnsupportedOperationException("Unsupported coder " + coder); } + bytesLen <<= 1; } + long header = ((long) bytesLen << 2) | coder; + buffer.writeVarUint64(header); + buffer.writeBytes(v, 0, bytesLen); } else { - stringSerializer.writeString(buffer, value.toString()); + char[] v = (char[]) GET_VALUE.apply(value); + if (StringUtils.isLatin(v)) { + stringSerializer.writeCharsLatin1(buffer, v, value.length()); + } else { + stringSerializer.writeCharsUTF16(buffer, v, value.length()); + } } } } @@ -230,54 +383,47 @@ public void write(MemoryBuffer buffer, T value) { public static final class StringBuilderSerializer extends AbstractStringBuilderSerializer { - public StringBuilderSerializer(Fory fory) { - super(fory, StringBuilder.class); + public StringBuilderSerializer(Config config) { + super(config, StringBuilder.class); } @Override - public StringBuilder copy(StringBuilder origin) { + public StringBuilder copy(CopyContext copyContext, StringBuilder origin) { return new StringBuilder(origin); } @Override - public StringBuilder read(MemoryBuffer buffer) { - if (isJava) { - return new StringBuilder(stringSerializer.readString(buffer)); - } else { - return new StringBuilder(stringSerializer.readString(buffer)); - } + public StringBuilder read(ReadContext readContext) { + return new StringBuilder(readContext.readString()); } } public static final class StringBufferSerializer extends AbstractStringBuilderSerializer { - public StringBufferSerializer(Fory fory) { - super(fory, StringBuffer.class); + public StringBufferSerializer(Config config) { + super(config, StringBuffer.class); } @Override - public StringBuffer copy(StringBuffer origin) { + public StringBuffer copy(CopyContext copyContext, StringBuffer origin) { return new StringBuffer(origin); } @Override - public StringBuffer read(MemoryBuffer buffer) { - if (isJava) { - return new StringBuffer(stringSerializer.readString(buffer)); - } else { - return new StringBuffer(stringSerializer.readString(buffer)); - } + public StringBuffer read(ReadContext readContext) { + return new StringBuffer(readContext.readString()); } } public static final class BigDecimalSerializer extends ImmutableSerializer { - public BigDecimalSerializer(Fory fory) { - super(fory, BigDecimal.class); + public BigDecimalSerializer(Config config) { + super(config, BigDecimal.class); } @Override - public void write(MemoryBuffer buffer, BigDecimal value) { + public void write(WriteContext writeContext, BigDecimal value) { + MemoryBuffer buffer = writeContext.getBuffer(); final byte[] bytes = value.unscaledValue().toByteArray(); buffer.writeVarUint32Small7(value.scale()); buffer.writeVarUint32Small7(value.precision()); @@ -286,7 +432,8 @@ public void write(MemoryBuffer buffer, BigDecimal value) { } @Override - public BigDecimal read(MemoryBuffer buffer) { + public BigDecimal read(ReadContext readContext) { + MemoryBuffer buffer = readContext.getBuffer(); int scale = buffer.readVarUint32Small7(); int precision = buffer.readVarUint32Small7(); int len = buffer.readVarUint32Small7(); @@ -294,215 +441,281 @@ public BigDecimal read(MemoryBuffer buffer) { final BigInteger bigInteger = new BigInteger(bytes); return new BigDecimal(bigInteger, scale, new MathContext(precision)); } + + @Override + public boolean threadSafe() { + return true; + } } public static final class BigIntegerSerializer extends ImmutableSerializer { - public BigIntegerSerializer(Fory fory) { - super(fory, BigInteger.class); + public BigIntegerSerializer(Config config) { + super(config, BigInteger.class); } @Override - public void write(MemoryBuffer buffer, BigInteger value) { + public void write(WriteContext writeContext, BigInteger value) { + MemoryBuffer buffer = writeContext.getBuffer(); final byte[] bytes = value.toByteArray(); buffer.writeVarUint32Small7(bytes.length); buffer.writeBytes(bytes); } @Override - public BigInteger read(MemoryBuffer buffer) { + public BigInteger read(ReadContext readContext) { + MemoryBuffer buffer = readContext.getBuffer(); int len = buffer.readVarUint32Small7(); byte[] bytes = buffer.readBytes(len); return new BigInteger(bytes); } + + @Override + public boolean threadSafe() { + return true; + } } public static final class AtomicBooleanSerializer extends Serializer { - public AtomicBooleanSerializer(Fory fory) { - super(fory, AtomicBoolean.class); + public AtomicBooleanSerializer(Config config) { + super(config, AtomicBoolean.class); } @Override - public void write(MemoryBuffer buffer, AtomicBoolean value) { - buffer.writeBoolean(value.get()); + public void write(WriteContext writeContext, AtomicBoolean value) { + writeContext.getBuffer().writeBoolean(value.get()); } @Override - public AtomicBoolean copy(AtomicBoolean origin) { + public AtomicBoolean copy(CopyContext copyContext, AtomicBoolean origin) { return new AtomicBoolean(origin.get()); } @Override - public AtomicBoolean read(MemoryBuffer buffer) { - return new AtomicBoolean(buffer.readBoolean()); + public AtomicBoolean read(ReadContext readContext) { + return new AtomicBoolean(readContext.getBuffer().readBoolean()); + } + + @Override + public boolean threadSafe() { + return true; } } public static final class AtomicIntegerSerializer extends Serializer { - public AtomicIntegerSerializer(Fory fory) { - super(fory, AtomicInteger.class); + public AtomicIntegerSerializer(Config config) { + super(config, AtomicInteger.class); } @Override - public void write(MemoryBuffer buffer, AtomicInteger value) { - buffer.writeInt32(value.get()); + public void write(WriteContext writeContext, AtomicInteger value) { + writeContext.getBuffer().writeInt32(value.get()); } @Override - public AtomicInteger copy(AtomicInteger origin) { + public AtomicInteger copy(CopyContext copyContext, AtomicInteger origin) { return new AtomicInteger(origin.get()); } @Override - public AtomicInteger read(MemoryBuffer buffer) { - return new AtomicInteger(buffer.readInt32()); + public AtomicInteger read(ReadContext readContext) { + return new AtomicInteger(readContext.getBuffer().readInt32()); + } + + @Override + public boolean threadSafe() { + return true; } } public static final class AtomicLongSerializer extends Serializer { - public AtomicLongSerializer(Fory fory) { - super(fory, AtomicLong.class); + public AtomicLongSerializer(Config config) { + super(config, AtomicLong.class); } @Override - public void write(MemoryBuffer buffer, AtomicLong value) { - buffer.writeInt64(value.get()); + public void write(WriteContext writeContext, AtomicLong value) { + writeContext.getBuffer().writeInt64(value.get()); } @Override - public AtomicLong copy(AtomicLong origin) { + public AtomicLong copy(CopyContext copyContext, AtomicLong origin) { return new AtomicLong(origin.get()); } @Override - public AtomicLong read(MemoryBuffer buffer) { - return new AtomicLong(buffer.readInt64()); + public AtomicLong read(ReadContext readContext) { + return new AtomicLong(readContext.getBuffer().readInt64()); + } + + @Override + public boolean threadSafe() { + return true; } } public static final class AtomicReferenceSerializer extends Serializer { - public AtomicReferenceSerializer(Fory fory) { - super(fory, AtomicReference.class); + public AtomicReferenceSerializer(Config config) { + super(config, AtomicReference.class); } @Override - public void write(MemoryBuffer buffer, AtomicReference value) { - fory.writeRef(buffer, value.get()); + public void write(WriteContext writeContext, AtomicReference value) { + writeContext.writeRef(value.get()); } @Override - public AtomicReference copy(AtomicReference origin) { - return new AtomicReference(fory.copyObject(origin.get())); + public AtomicReference copy( + CopyContext copyContext, AtomicReference origin) { + return new AtomicReference(copyContext.copyObject(origin.get())); } @Override - public AtomicReference read(MemoryBuffer buffer) { - return new AtomicReference(fory.readRef(buffer)); + public AtomicReference read(ReadContext readContext) { + return new AtomicReference(readContext.readRef()); + } + + @Override + public boolean threadSafe() { + return true; } } public static final class CurrencySerializer extends ImmutableSerializer { - public CurrencySerializer(Fory fory) { - super(fory, Currency.class); + public CurrencySerializer(Config config) { + super(config, Currency.class); + } + + @Override + public void write(WriteContext writeContext, Currency object) { + writeContext.writeString(object.getCurrencyCode()); } @Override - public void write(MemoryBuffer buffer, Currency object) { - fory.writeString(buffer, object.getCurrencyCode()); + public Currency read(ReadContext readContext) { + return Currency.getInstance(readContext.readString()); } @Override - public Currency read(MemoryBuffer buffer) { - String currencyCode = fory.readString(buffer); - return Currency.getInstance(currencyCode); + public boolean threadSafe() { + return true; } } /** Serializer for {@link Charset}. */ public static final class CharsetSerializer extends ImmutableSerializer { - public CharsetSerializer(Fory fory, Class type) { - super(fory, type); + public CharsetSerializer(Config config, Class type) { + super(config, type); + } + + public void write(WriteContext writeContext, T object) { + writeContext.writeString(object.name()); } - public void write(MemoryBuffer buffer, T object) { - fory.writeString(buffer, object.name()); + public T read(ReadContext readContext) { + return (T) Charset.forName(readContext.readString()); } - public T read(MemoryBuffer buffer) { - return (T) Charset.forName(fory.readString(buffer)); + @Override + public boolean threadSafe() { + return true; } } public static final class URISerializer extends ImmutableSerializer { - public URISerializer(Fory fory) { - super(fory, URI.class); + public URISerializer(Config config) { + super(config, URI.class); + } + + @Override + public void write(WriteContext writeContext, final URI uri) { + writeContext.writeString(uri.toString()); } @Override - public void write(MemoryBuffer buffer, final URI uri) { - fory.writeString(buffer, uri.toString()); + public URI read(ReadContext readContext) { + return URI.create(readContext.readString()); } @Override - public URI read(MemoryBuffer buffer) { - return URI.create(fory.readString(buffer)); + public boolean threadSafe() { + return true; } } public static final class RegexSerializer extends ImmutableSerializer { - public RegexSerializer(Fory fory) { - super(fory, Pattern.class); + public RegexSerializer(Config config) { + super(config, Pattern.class); } @Override - public void write(MemoryBuffer buffer, Pattern pattern) { - fory.writeString(buffer, pattern.pattern()); + public void write(WriteContext writeContext, Pattern pattern) { + MemoryBuffer buffer = writeContext.getBuffer(); + writeContext.writeString(pattern.pattern()); buffer.writeInt32(pattern.flags()); } @Override - public Pattern read(MemoryBuffer buffer) { - String regex = fory.readString(buffer); + public Pattern read(ReadContext readContext) { + MemoryBuffer buffer = readContext.getBuffer(); + String regex = readContext.readString(); int flags = buffer.readInt32(); return Pattern.compile(regex, flags); } + + @Override + public boolean threadSafe() { + return true; + } } public static final class UUIDSerializer extends ImmutableSerializer { - public UUIDSerializer(Fory fory) { - super(fory, UUID.class); + public UUIDSerializer(Config config) { + super(config, UUID.class); } @Override - public void write(MemoryBuffer buffer, final UUID uuid) { + public void write(WriteContext writeContext, final UUID uuid) { + MemoryBuffer buffer = writeContext.getBuffer(); buffer.writeInt64(uuid.getMostSignificantBits()); buffer.writeInt64(uuid.getLeastSignificantBits()); } @Override - public UUID read(MemoryBuffer buffer) { + public UUID read(ReadContext readContext) { + MemoryBuffer buffer = readContext.getBuffer(); return new UUID(buffer.readInt64(), buffer.readInt64()); } + + @Override + public boolean threadSafe() { + return true; + } } public static final class ClassSerializer extends ImmutableSerializer { - public ClassSerializer(Fory fory) { - super(fory, Class.class); + public ClassSerializer(Config config) { + super(config, Class.class); + } + + @Override + public void write(WriteContext writeContext, Class value) { + ((ClassResolver) writeContext.getTypeResolver()).writeClassInternal(writeContext, value); } @Override - public void write(MemoryBuffer buffer, Class value) { - ((ClassResolver) fory.getTypeResolver()).writeClassInternal(buffer, value); + public Class read(ReadContext readContext) { + return ((ClassResolver) readContext.getTypeResolver()).readClassInternal(readContext); } @Override - public Class read(MemoryBuffer buffer) { - return ((ClassResolver) fory.getTypeResolver()).readClassInternal(buffer); + public boolean threadSafe() { + return true; } } @@ -515,34 +728,40 @@ public Class read(MemoryBuffer buffer) { // Use a separate serializer to avoid codegen for empty object. public static final class EmptyObjectSerializer extends ImmutableSerializer { - public EmptyObjectSerializer(Fory fory) { - super(fory, Object.class); + public EmptyObjectSerializer(Config config) { + super(config, Object.class); } @Override - public void write(MemoryBuffer buffer, Object value) {} + public void write(WriteContext writeContext, Object value) {} @Override - public Object read(MemoryBuffer buffer) { + public Object read(ReadContext readContext) { return new Object(); } + + @Override + public boolean threadSafe() { + return true; + } } - public static void registerDefaultSerializers(Fory fory) { - TypeResolver resolver = fory.getTypeResolver(); - resolver.registerInternalSerializer(Class.class, new ClassSerializer(fory)); - resolver.registerInternalSerializer(StringBuilder.class, new StringBuilderSerializer(fory)); - resolver.registerInternalSerializer(StringBuffer.class, new StringBufferSerializer(fory)); - resolver.registerInternalSerializer(BigInteger.class, new BigIntegerSerializer(fory)); - resolver.registerInternalSerializer(BigDecimal.class, new BigDecimalSerializer(fory)); - resolver.registerInternalSerializer(AtomicBoolean.class, new AtomicBooleanSerializer(fory)); - resolver.registerInternalSerializer(AtomicInteger.class, new AtomicIntegerSerializer(fory)); - resolver.registerInternalSerializer(AtomicLong.class, new AtomicLongSerializer(fory)); - resolver.registerInternalSerializer(AtomicReference.class, new AtomicReferenceSerializer(fory)); - resolver.registerInternalSerializer(Currency.class, new CurrencySerializer(fory)); - resolver.registerInternalSerializer(URI.class, new URISerializer(fory)); - resolver.registerInternalSerializer(Pattern.class, new RegexSerializer(fory)); - resolver.registerInternalSerializer(UUID.class, new UUIDSerializer(fory)); - resolver.registerInternalSerializer(Object.class, new EmptyObjectSerializer(fory)); + public static void registerDefaultSerializers(TypeResolver resolver) { + Config config = resolver.getConfig(); + resolver.registerInternalSerializer(Class.class, new ClassSerializer(config)); + resolver.registerInternalSerializer(StringBuilder.class, new StringBuilderSerializer(config)); + resolver.registerInternalSerializer(StringBuffer.class, new StringBufferSerializer(config)); + resolver.registerInternalSerializer(BigInteger.class, new BigIntegerSerializer(config)); + resolver.registerInternalSerializer(BigDecimal.class, new BigDecimalSerializer(config)); + resolver.registerInternalSerializer(AtomicBoolean.class, new AtomicBooleanSerializer(config)); + resolver.registerInternalSerializer(AtomicInteger.class, new AtomicIntegerSerializer(config)); + resolver.registerInternalSerializer(AtomicLong.class, new AtomicLongSerializer(config)); + resolver.registerInternalSerializer( + AtomicReference.class, new AtomicReferenceSerializer(config)); + resolver.registerInternalSerializer(Currency.class, new CurrencySerializer(config)); + resolver.registerInternalSerializer(URI.class, new URISerializer(config)); + resolver.registerInternalSerializer(Pattern.class, new RegexSerializer(config)); + resolver.registerInternalSerializer(UUID.class, new UUIDSerializer(config)); + resolver.registerInternalSerializer(Object.class, new EmptyObjectSerializer(config)); } } diff --git a/java/fory-core/src/main/java/org/apache/fory/serializer/StringSerializer.java b/java/fory-core/src/main/java/org/apache/fory/serializer/StringSerializer.java index 331d4d349a..6ae5ca54ed 100644 --- a/java/fory-core/src/main/java/org/apache/fory/serializer/StringSerializer.java +++ b/java/fory-core/src/main/java/org/apache/fory/serializer/StringSerializer.java @@ -23,6 +23,9 @@ import static org.apache.fory.util.StringUtils.MULTI_CHARS_NON_ASCII_MASK; import static org.apache.fory.util.StringUtils.MULTI_CHARS_NON_LATIN_MASK; +import org.apache.fory.context.ReadContext; +import org.apache.fory.context.WriteContext; + import java.lang.invoke.CallSite; import java.lang.invoke.LambdaMetafactory; import java.lang.invoke.MethodHandle; @@ -33,8 +36,8 @@ import java.util.Arrays; import java.util.function.BiFunction; import java.util.function.Function; -import org.apache.fory.Fory; import org.apache.fory.annotation.CodegenInvoke; +import org.apache.fory.config.Config; import org.apache.fory.codegen.Expression; import org.apache.fory.codegen.Expression.Invoke; import org.apache.fory.codegen.Expression.StaticInvoke; @@ -131,24 +134,29 @@ private static class Offset { private int smoothCharArrayLength = DEFAULT_BUFFER_SIZE; private byte[] byteArray2 = EMPTY_BYTES_STUB; - public StringSerializer(Fory fory) { - super(fory, String.class, fory.trackingRef() && !fory.isStringRefIgnored()); - compressString = fory.compressString(); - xlang = fory.isCrossLanguage(); + public StringSerializer(Config config) { + super(config, String.class, config.trackingRef() && !config.isStringRefIgnored()); + compressString = config.compressString(); + xlang = config.isXlang(); if (xlang) { Preconditions.checkArgument(compressString, "compress string muse be enabled for xlang mode"); } - writeNumUtf16BytesForUtf8Encoding = fory.getConfig().writeNumUtf16BytesForUtf8Encoding(); + writeNumUtf16BytesForUtf8Encoding = config.writeNumUtf16BytesForUtf8Encoding(); + } + + @Override + public void write(WriteContext writeContext, String value) { + writeString(writeContext.getBuffer(), value); } @Override - public void write(MemoryBuffer buffer, String value) { - writeString(buffer, value); + public String read(ReadContext readContext) { + return readString(readContext.getBuffer()); } @Override - public String read(MemoryBuffer buffer) { - return readString(buffer); + public boolean threadSafe() { + return false; } public Expression writeStringExpr(Expression strSerializer, Expression buffer, Expression str) { diff --git a/java/fory-core/src/main/java/org/apache/fory/serializer/TimeSerializers.java b/java/fory-core/src/main/java/org/apache/fory/serializer/TimeSerializers.java index bbed8517b6..64be530149 100644 --- a/java/fory-core/src/main/java/org/apache/fory/serializer/TimeSerializers.java +++ b/java/fory-core/src/main/java/org/apache/fory/serializer/TimeSerializers.java @@ -39,7 +39,10 @@ import java.util.Date; import java.util.GregorianCalendar; import java.util.TimeZone; -import org.apache.fory.Fory; +import org.apache.fory.config.Config; +import org.apache.fory.context.CopyContext; +import org.apache.fory.context.ReadContext; +import org.apache.fory.context.WriteContext; import org.apache.fory.memory.MemoryBuffer; import org.apache.fory.resolver.TypeResolver; import org.apache.fory.util.DateTimeUtils; @@ -47,56 +50,62 @@ /** Serializers for all time related types. */ public class TimeSerializers { public abstract static class TimeSerializer extends Serializer { + protected final Config config; - public TimeSerializer(Fory fory, Class type) { - super(fory, type, !fory.getConfig().isTimeRefIgnored(), false); + public TimeSerializer(Config config, Class type) { + super(config, type, !config.isTimeRefIgnored(), false); + this.config = config; } - public TimeSerializer(Fory fory, Class type, boolean needToWriteRef) { - super(fory, type, needToWriteRef, false); + public TimeSerializer(Config config, Class type, boolean needToWriteRef) { + super(config, type, needToWriteRef, false); + this.config = config; } } public abstract static class ImmutableTimeSerializer extends ImmutableSerializer { + protected final Config config; - public ImmutableTimeSerializer(Fory fory, Class type) { - super(fory, type, !fory.getConfig().isTimeRefIgnored()); + public ImmutableTimeSerializer(Config config, Class type) { + super(config, type, !config.isTimeRefIgnored()); + this.config = config; } - public ImmutableTimeSerializer(Fory fory, Class type, boolean needToWriteRef) { - super(fory, type, needToWriteRef); + public ImmutableTimeSerializer(Config config, Class type, boolean needToWriteRef) { + super(config, type, needToWriteRef); + this.config = config; } } public abstract static class BaseDateSerializer extends TimeSerializer { - public BaseDateSerializer(Fory fory, Class type) { - super(fory, type); + public BaseDateSerializer(Config config, Class type) { + super(config, type); } - public BaseDateSerializer(Fory fory, Class type, boolean needToWriteRef) { - super(fory, type, needToWriteRef); + public BaseDateSerializer(Config config, Class type, boolean needToWriteRef) { + super(config, type, needToWriteRef); } @Override - public void write(MemoryBuffer buffer, T value) { - buffer.writeInt64(value.getTime()); + public void write(WriteContext writeContext, T value) { + writeContext.getBuffer().writeInt64(value.getTime()); } @Override - public T read(MemoryBuffer buffer) { - return newInstance(buffer.readInt64()); + public T read(ReadContext readContext) { + return newInstance(readContext.getBuffer().readInt64()); } protected abstract T newInstance(long time); } public static final class DateSerializer extends BaseDateSerializer { - public DateSerializer(Fory fory) { - super(fory, Date.class); + public DateSerializer(Config config) { + super(config, Date.class); } - public DateSerializer(Fory fory, boolean needToWriteRef) { - super(fory, Date.class, needToWriteRef); + public DateSerializer(Config config, boolean needToWriteRef) { + super(config, Date.class, needToWriteRef); } @Override @@ -105,18 +114,18 @@ protected Date newInstance(long time) { } @Override - public Date copy(Date value) { + public Date copy(CopyContext copyContext, Date value) { return newInstance(value.getTime()); } } public static final class SqlDateSerializer extends BaseDateSerializer { - public SqlDateSerializer(Fory fory) { - super(fory, java.sql.Date.class); + public SqlDateSerializer(Config config) { + super(config, java.sql.Date.class); } - public SqlDateSerializer(Fory fory, boolean needToWriteRef) { - super(fory, java.sql.Date.class, needToWriteRef); + public SqlDateSerializer(Config config, boolean needToWriteRef) { + super(config, java.sql.Date.class, needToWriteRef); } @Override @@ -125,19 +134,19 @@ protected java.sql.Date newInstance(long time) { } @Override - public java.sql.Date copy(java.sql.Date value) { + public java.sql.Date copy(CopyContext copyContext, java.sql.Date value) { return newInstance(value.getTime()); } } public static final class SqlTimeSerializer extends BaseDateSerializer