Skip to content

Commit

Permalink
Merge branch 'odnoklassniki:master' into json_updates
Browse files Browse the repository at this point in the history
  • Loading branch information
avrecko authored Feb 19, 2023
2 parents 3bacf0d + 4bd740b commit 65ea62c
Show file tree
Hide file tree
Showing 11 changed files with 238 additions and 35 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
1.6.1
* openssl3 support
* BigDecimal deserialization

1.6.0
* java.time serialization
* Arena Allocator
Expand Down
2 changes: 1 addition & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
<groupId>ru.odnoklassniki</groupId>
<artifactId>one-nio</artifactId>
<packaging>jar</packaging>
<version>1.6.0</version>
<version>1.6.1</version>
<name>one-nio</name>
<url>https://github.com/odnoklassniki/one-nio</url>
<description>Unconventional Java I/O library</description>
Expand Down
3 changes: 2 additions & 1 deletion src/one/nio/net/native/ssl.c
Original file line number Diff line number Diff line change
Expand Up @@ -449,7 +449,8 @@ static jbyteArray X509_cert_to_jbyteArray(JNIEnv* env, X509* cert) {

JNIEXPORT void JNICALL
Java_one_nio_net_NativeSslContext_init(JNIEnv* env, jclass cls) {
if (dlopen("libssl.so", RTLD_LAZY | RTLD_GLOBAL) == NULL &&
if (dlopen("libssl.so.3", RTLD_LAZY | RTLD_GLOBAL) == NULL &&
dlopen("libssl.so", RTLD_LAZY | RTLD_GLOBAL) == NULL &&
dlopen("libssl.so.1.0.0", RTLD_LAZY | RTLD_GLOBAL) == NULL &&
dlopen("libssl.so.10", RTLD_LAZY | RTLD_GLOBAL) == NULL) {
throw_by_name(env, "java/lang/UnsupportedOperationException", "Failed to load libssl.so");
Expand Down
5 changes: 2 additions & 3 deletions src/one/nio/serial/GeneratedSerializer.java
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@

package one.nio.serial;

import one.nio.gen.BytecodeGenerator;
import one.nio.serial.gen.Delegate;
import one.nio.serial.gen.DelegateGenerator;
import one.nio.serial.gen.StubGenerator;
Expand Down Expand Up @@ -55,7 +54,7 @@ public class GeneratedSerializer extends Serializer {
this.defaultFields = new FieldDescriptor[0];

checkFieldTypes();
this.delegate = BytecodeGenerator.INSTANCE.instantiate(code(), Delegate.class);
this.delegate = DelegateGenerator.instantiate(cls, fds, code());
}

@Override
Expand Down Expand Up @@ -93,7 +92,7 @@ public void readExternal(ObjectInput in) throws IOException, ClassNotFoundExcept
this.defaultFields = assignDefaultFields(ownFields);

checkFieldTypes();
this.delegate = BytecodeGenerator.INSTANCE.instantiate(code(), Delegate.class);
this.delegate = DelegateGenerator.instantiate(cls, fds, code());
}

@Override
Expand Down
3 changes: 3 additions & 0 deletions src/one/nio/serial/Repository.java
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
import java.io.IOException;
import java.io.Serializable;
import java.lang.reflect.Method;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.net.*;
import java.nio.file.Files;
Expand Down Expand Up @@ -62,6 +63,7 @@ public class Repository {
public static final int INLINE = 4;
public static final int FIELD_SERIALIZATION = 8;
public static final int SYNTHETIC_FIELDS = 16;
public static final int PROVIDE_GET_FIELD = 32;

public static final int ARRAY_STUBS = 1;
public static final int COLLECTION_STUBS = 2;
Expand Down Expand Up @@ -150,6 +152,7 @@ public class Repository {
setOptions(StringBuilder.class, SKIP_CUSTOM_SERIALIZATION);
setOptions(StringBuffer.class, SKIP_CUSTOM_SERIALIZATION);
setOptions(BigInteger.class, SKIP_CUSTOM_SERIALIZATION);
setOptions(BigDecimal.class, PROVIDE_GET_FIELD);

// At some moment InetAddress fields were moved to an auxilary holder class.
// This resolves backward compatibility problem by inlining holder fields during serialization.
Expand Down
66 changes: 54 additions & 12 deletions src/one/nio/serial/gen/DelegateGenerator.java
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
Expand Down Expand Up @@ -82,32 +83,63 @@ private static void defineBootstrapClass(Method m, String classData) throws Refl
m.invoke(null, null, null, code, 0, code.length, null, null);
}

public static Delegate instantiate(Class cls, FieldDescriptor[] fds, byte[] code) {
Map<String, Field> fieldsMap = null;
if (Repository.hasOptions(cls, Repository.PROVIDE_GET_FIELD)) {
fieldsMap = new HashMap<>(fds.length, 1);
for (FieldDescriptor fd : fds) {
Field field = fd.ownField();
if (field != null) {
fieldsMap.put(field.getName(), field);
JavaInternals.setAccessible(field);
}
}
}
try {
return (Delegate) BytecodeGenerator.INSTANCE.defineClass(code)
.getDeclaredConstructor(Map.class)
.newInstance(fieldsMap);
} catch (Exception e) {
throw new IllegalArgumentException("Cannot instantiate class", e);
}
}

public static Delegate instantiate(Class cls, FieldDescriptor[] fds, FieldDescriptor[] defaultFields) {
return instantiate(cls, fds, generate(cls, fds, defaultFields));
}

public static byte[] generate(Class cls, FieldDescriptor[] fds, FieldDescriptor[] defaultFields) {
String className = "sun/reflect/Delegate" + index.getAndIncrement() + '_' + cls.getSimpleName();

ClassWriter cv = new ClassWriter(ClassWriter.COMPUTE_MAXS | ClassWriter.COMPUTE_FRAMES);
cv.visit(V1_6, ACC_PUBLIC | ACC_FINAL, className, null, MAGIC_CLASS,
new String[]{"one/nio/serial/gen/Delegate"});

generateConstructor(cv);
generateConstructor(cv, className);
generateCalcSize(cv, cls, fds);
generateWrite(cv, cls, fds);
generateRead(cv, cls, fds, defaultFields);
generateRead(cv, cls, fds, defaultFields, className);
generateSkip(cv, fds);
generateToJson(cv, cls, fds);
generateFromJson(cv, cls, fds, defaultFields);
generateFromJson(cv, cls, fds, defaultFields, className);

cv.visitEnd();
return cv.toByteArray();
}

private static void generateConstructor(ClassVisitor cv) {
MethodVisitor mv = cv.visitMethod(ACC_PUBLIC, "<init>", "()V", null, null);
private static void generateConstructor(ClassVisitor cv, String className) {
MethodVisitor mv = cv.visitMethod(ACC_PUBLIC, "<init>", "(Ljava/util/Map;)V", null, null);
cv.visitField(ACC_PRIVATE | ACC_FINAL, "fields", "Ljava/util/Map;", null, null).visitEnd();

mv.visitCode();

mv.visitVarInsn(ALOAD, 0);
mv.visitMethodInsn(INVOKESPECIAL, MAGIC_CLASS, "<init>", "()V", false);

mv.visitVarInsn(ALOAD, 0);
mv.visitVarInsn(ALOAD, 1);
mv.visitFieldInsn(PUTFIELD, className, "fields", "Ljava/util/Map;");

mv.visitInsn(RETURN);
mv.visitMaxs(0, 0);
mv.visitEnd();
Expand Down Expand Up @@ -204,7 +236,7 @@ private static void emitWriteObject(Class cls, MethodVisitor mv) {
}
}

private static void generateRead(ClassVisitor cv, Class cls, FieldDescriptor[] fds, FieldDescriptor[] defaultFields) {
private static void generateRead(ClassVisitor cv, Class cls, FieldDescriptor[] fds, FieldDescriptor[] defaultFields, String className) {
MethodVisitor mv = cv.visitMethod(ACC_PUBLIC | ACC_FINAL, "read", "(Lone/nio/serial/DataStream;)Ljava/lang/Object;",
null, new String[]{"java/io/IOException", "java/lang/ClassNotFoundException"});
mv.visitCode();
Expand Down Expand Up @@ -253,20 +285,30 @@ private static void generateRead(ClassVisitor cv, Class cls, FieldDescriptor[] f
if (isRecord) {
generateCreateRecord(mv, cls, fds, defaultFields);
}

emitReadObject(cls, mv);
emitReadObject(cls, mv, className);

mv.visitInsn(ARETURN);
mv.visitMaxs(0, 0);
mv.visitEnd();
}

private static void emitReadObject(Class cls, MethodVisitor mv) {
private static void emitReadObject(Class cls, MethodVisitor mv, String className) {
MethodType methodType = MethodType.methodType(void.class, ObjectInputStream.class);
MethodHandleInfo m = MethodHandlesReflection.findInstanceMethod(cls, "readObject", methodType);
if (m != null && !Repository.hasOptions(m.getDeclaringClass(), Repository.SKIP_READ_OBJECT)) {
mv.visitInsn(DUP);
mv.visitFieldInsn(GETSTATIC, "one/nio/serial/gen/NullObjectInputStream", "INSTANCE", "Lone/nio/serial/gen/NullObjectInputStream;");
if (!Repository.hasOptions(m.getDeclaringClass(), Repository.PROVIDE_GET_FIELD)) {
mv.visitFieldInsn(GETSTATIC, "one/nio/serial/gen/NullObjectInputStream", "INSTANCE", "Lone/nio/serial/gen/NullObjectInputStream;");
} else {
mv.visitInsn(DUP);
mv.visitTypeInsn(NEW, "one/nio/serial/gen/GetFieldInputStream");
mv.visitInsn(DUP_X1);
mv.visitInsn(SWAP);
mv.visitVarInsn(ALOAD, 0);
mv.visitFieldInsn(GETFIELD, className, "fields", "Ljava/util/Map;");
mv.visitMethodInsn(INVOKESPECIAL, "one/nio/serial/gen/GetFieldInputStream", "<init>", "(Ljava/lang/Object;Ljava/util/Map;)V", false);
}
emitInvoke(mv, m);
}
}
Expand Down Expand Up @@ -373,7 +415,7 @@ private static void generateToJson(ClassVisitor cv, Class cls, FieldDescriptor[]
mv.visitEnd();
}

private static void generateFromJson(ClassVisitor cv, Class cls, FieldDescriptor[] fds, FieldDescriptor[] defaultFields) {
private static void generateFromJson(ClassVisitor cv, Class cls, FieldDescriptor[] fds, FieldDescriptor[] defaultFields, String className) {
MethodVisitor mv = cv.visitMethod(ACC_PUBLIC | ACC_FINAL, "fromJson", "(Lone/nio/serial/JsonReader;)Ljava/lang/Object;",
null, new String[]{"java/io/IOException", "java/lang/ClassNotFoundException"});
mv.visitCode();
Expand Down Expand Up @@ -510,7 +552,7 @@ private static void generateFromJson(ClassVisitor cv, Class cls, FieldDescriptor
generateCreateRecord(mv, cls, fds, defaultFields);
}

emitReadObject(cls, mv);
emitReadObject(cls, mv, className);

mv.visitInsn(ARETURN);
mv.visitMaxs(0, 0);
Expand Down
149 changes: 149 additions & 0 deletions src/one/nio/serial/gen/GetFieldInputStream.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,149 @@
/*
* Copyright 2022 Odnoklassniki Ltd, Mail.Ru Group
*
* Licensed 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 one.nio.serial.gen;

import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectStreamClass;
import java.lang.reflect.Field;
import java.util.Map;

class GetFieldInputStream extends NullObjectInputStream {

private final Map<String, Field> fields;
private final Object source;

GetFieldInputStream(Object source, Map<String, Field> fields) throws IOException, SecurityException {
this.fields = fields;
this.source = source;
}

@Override
public GetField readFields() {
return new ObjectGetField(fields, source);
}

private static class ObjectGetField extends ObjectInputStream.GetField {
private final Object object;
private final Map<String, Field> fields;

private ObjectGetField(Map<String, Field> fields, Object object) {
this.object = object;
this.fields = fields;
}

@Override
public ObjectStreamClass getObjectStreamClass() {
throw new UnsupportedOperationException();
}

@Override
public boolean defaulted(String name) {
return !fields.containsKey(name);
}

@Override
public boolean get(String name, boolean val) throws IOException {
try {
Field field = fields.get(name);
return field != null ? field.getBoolean(object) : val;
} catch (IllegalAccessException e) {
throw new IOException(e);
}
}

@Override
public byte get(String name, byte val) throws IOException {
try {
Field field = fields.get(name);
return field != null ? field.getByte(object) : val;
} catch (IllegalAccessException e) {
throw new IOException(e);
}
}

@Override
public char get(String name, char val) throws IOException {
try {
Field field = fields.get(name);
return field != null ? field.getChar(object) : val;
} catch (IllegalAccessException e) {
throw new IOException(e);
}
}

@Override
public short get(String name, short val) throws IOException {
try {
Field field = fields.get(name);
return field != null ? field.getShort(object) : val;
} catch (IllegalAccessException e) {
throw new IOException(e);
}
}

@Override
public int get(String name, int val) throws IOException {
try {
Field field = fields.get(name);
return field != null ? field.getInt(object) : val;
} catch (IllegalAccessException e) {
throw new IOException(e);
}
}

@Override
public long get(String name, long val) throws IOException {
try {
Field field = fields.get(name);
return field != null ? field.getLong(object) : val;
} catch (IllegalAccessException e) {
throw new IOException(e);
}
}

@Override
public float get(String name, float val) throws IOException {
try {
Field field = fields.get(name);
return field != null ? field.getFloat(object) : val;
} catch (IllegalAccessException e) {
throw new IOException(e);
}
}

@Override
public double get(String name, double val) throws IOException {
try {
Field field = fields.get(name);
return field != null ? field.getDouble(object) : val;
} catch (IllegalAccessException e) {
throw new IOException(e);
}
}

@Override
public Object get(String name, Object val) throws IOException {
try {
Field field = fields.get(name);
return field != null ? field.get(object) : val;
} catch (IllegalAccessException e) {
throw new IOException(e);
}
}
}
}
3 changes: 1 addition & 2 deletions src/one/nio/serial/gen/NullObjectInputStream.java
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,7 @@ public class NullObjectInputStream extends ObjectInputStream {
}
}

private NullObjectInputStream() throws IOException {
// Singleton
protected NullObjectInputStream() throws IOException {
}

@Override
Expand Down
5 changes: 1 addition & 4 deletions test/one/nio/serial/ConversionTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@

package one.nio.serial;

import one.nio.gen.BytecodeGenerator;
import one.nio.serial.gen.Delegate;
import one.nio.serial.gen.DelegateGenerator;
import org.junit.Test;
Expand All @@ -32,13 +31,11 @@ public class ConversionTest implements Serializable {

@Test
public void testFieldConversion() throws Exception {
byte[] code = DelegateGenerator.generate(ConversionTest.class, new FieldDescriptor[]{
Delegate delegate = DelegateGenerator.instantiate(ConversionTest.class, new FieldDescriptor[]{
fd("intField", BigInteger.class),
fd("longField", BigInteger.class)
}, new FieldDescriptor[0]);

Delegate delegate = BytecodeGenerator.INSTANCE.instantiate(code, Delegate.class);

byte[] data = new byte[100];
delegate.write(new ConversionTest(), new DataStream(data));
ConversionTest clone = (ConversionTest) delegate.read(new DataStream(data));
Expand Down
Loading

0 comments on commit 65ea62c

Please sign in to comment.