wip
This commit is contained in:
@@ -54,7 +54,7 @@ class ClassAnalyzerVisitor extends ClassVisitor {
|
||||
var type = Type.getMethodType(descriptor);
|
||||
var returnClassName = type.getReturnType().getClassName();
|
||||
var parameterClassNames = Arrays.stream(type.getArgumentTypes()).map(Type::getClassName).toArray(String[]::new);
|
||||
return new MethodAnalyzerVisitor(Opcodes.ASM9, className, name, returnClassName, parameterClassNames, entityAccess -> {
|
||||
return new MethodAnalyzerVisitor(Opcodes.ASM9, className, name, access, returnClassName, parameterClassNames, entityAccess -> {
|
||||
declaredConstructor.addAll(entityAccess.getDeclaredConstructors());
|
||||
declaredGetters.addAll(entityAccess.getDeclaredGetters());
|
||||
declaredSetters.addAll(entityAccess.getDeclaredSetters());
|
||||
|
||||
@@ -6,6 +6,7 @@ import jef.asm.access.model.ConstructorParameterDescription;
|
||||
import jef.asm.access.model.GetterDescription;
|
||||
import jef.asm.access.model.SetterDescription;
|
||||
import jef.asm.access.model.SuperConstructorParameterDescription;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
import org.objectweb.asm.Handle;
|
||||
import org.objectweb.asm.Label;
|
||||
@@ -14,6 +15,7 @@ import org.objectweb.asm.Opcodes;
|
||||
import org.objectweb.asm.Type;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.Stack;
|
||||
@@ -22,14 +24,15 @@ import java.util.function.Consumer;
|
||||
@Getter
|
||||
public class MethodAnalyzerVisitor extends MethodVisitor {//TODO verify param count
|
||||
private final Consumer<ClassDescription> callback;
|
||||
private final String methodClassName;
|
||||
private final String declaringClassName;
|
||||
private final String methodName;
|
||||
private final int access;
|
||||
private final String returnClassName;
|
||||
private final String[] parameterClassNames;
|
||||
private final String[] parameterClassNames;
|
||||
|
||||
private List<ConstructorParameterDescription> constructorParameterDescriptions = new ArrayList<>();
|
||||
private List<SuperConstructorParameterDescription> superConstructorParameterDescriptions = new ArrayList<>();
|
||||
private Stack<Integer> varIndexStack = new Stack<>();
|
||||
private Stack<Var> varStack = new Stack<>();
|
||||
private String fieldDeclaringClassName = null;
|
||||
private String fieldName = null;
|
||||
|
||||
@@ -69,12 +72,19 @@ public class MethodAnalyzerVisitor extends MethodVisitor {//TODO verify param co
|
||||
INVALID
|
||||
}
|
||||
|
||||
protected MethodAnalyzerVisitor(int api, String methodClassName, String methodName, String returnClassName, String[] parameterClassNames, Consumer<ClassDescription> callback) {
|
||||
protected MethodAnalyzerVisitor(int api, String declaringClassName, String methodName, int access, String returnClassName, String[] parameterClassNames, Consumer<ClassDescription> callback) {
|
||||
super(api);
|
||||
this.methodClassName = methodClassName;
|
||||
this.declaringClassName = declaringClassName;
|
||||
this.methodName = methodName;
|
||||
this.access = access;
|
||||
this.returnClassName = returnClassName;
|
||||
this.parameterClassNames = parameterClassNames;
|
||||
if (hasThis()) {
|
||||
this.parameterClassNames = new String[parameterClassNames.length + 1];
|
||||
System.arraycopy(parameterClassNames, 0, this.parameterClassNames, 1, parameterClassNames.length);
|
||||
this.parameterClassNames[0] = declaringClassName;
|
||||
} else {
|
||||
this.parameterClassNames = parameterClassNames;
|
||||
}
|
||||
this.callback = callback;
|
||||
}
|
||||
|
||||
@@ -122,7 +132,11 @@ public class MethodAnalyzerVisitor extends MethodVisitor {//TODO verify param co
|
||||
@Override
|
||||
public void visitVarInsn(int opcode, int varIndex) {
|
||||
if (Set.of(Opcodes.ILOAD, Opcodes.LLOAD, Opcodes.FLOAD, Opcodes.DLOAD, Opcodes.ALOAD).contains(opcode)) {
|
||||
varIndexStack.push(varIndex);
|
||||
System.out.println(methodName + ": load_" + varIndex + ": " + (varIndex < parameterClassNames.length ? parameterClassNames[varIndex] : null));
|
||||
if (varIndex == 5) {
|
||||
int x = 0;
|
||||
}
|
||||
varStack.push(new Var(varIndex < parameterClassNames.length ? parameterClassNames[varIndex] : null, varIndex));
|
||||
}
|
||||
if (opcode == Opcodes.ALOAD && varIndex == 0) {
|
||||
if (constructorStep == ConstructorSteps.CODE) {
|
||||
@@ -160,6 +174,10 @@ public class MethodAnalyzerVisitor extends MethodVisitor {//TODO verify param co
|
||||
super.visitVarInsn(opcode, varIndex);
|
||||
}
|
||||
|
||||
private boolean hasThis() {
|
||||
return (access & Opcodes.ACC_STATIC) == 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitTypeInsn(int opcode, String type) {
|
||||
constructorStep = ConstructorSteps.INVALID;
|
||||
@@ -170,15 +188,15 @@ public class MethodAnalyzerVisitor extends MethodVisitor {//TODO verify param co
|
||||
|
||||
@Override
|
||||
public void visitFieldInsn(int opcode, String owner, String name, String descriptor) {
|
||||
var lastLoadedVarIndex = -1;
|
||||
Var lastLoadedVar = null;
|
||||
if (opcode == Opcodes.PUTFIELD) {
|
||||
lastLoadedVarIndex = varIndexStack.pop();
|
||||
varIndexStack.pop();
|
||||
lastLoadedVar = varStack.pop();
|
||||
varStack.pop();
|
||||
constructorStep = constructorStep == ConstructorSteps.VAR_LOAD ? ConstructorSteps.PUT_FIELD : ConstructorSteps.INVALID;
|
||||
getterStep = GetterSteps.INVALID;
|
||||
setterStep = setterStep == SetterSteps.VAR_LOAD ? SetterSteps.PUT_FIELD : SetterSteps.INVALID;
|
||||
} else if (opcode == Opcodes.GETFIELD) {
|
||||
lastLoadedVarIndex = varIndexStack.pop();
|
||||
lastLoadedVar = varStack.pop();
|
||||
constructorStep = ConstructorSteps.INVALID;
|
||||
getterStep = getterStep == GetterSteps.ALOAD_0 ? GetterSteps.GET_FIELD : GetterSteps.INVALID;
|
||||
setterStep = SetterSteps.INVALID;
|
||||
@@ -189,21 +207,21 @@ public class MethodAnalyzerVisitor extends MethodVisitor {//TODO verify param co
|
||||
}
|
||||
this.fieldDeclaringClassName = owner.replace("/", ".");
|
||||
this.fieldName = name;
|
||||
this.constructorParameterDescriptions.add(new ConstructorParameterDescription(lastLoadedVarIndex - 1, this.fieldDeclaringClassName, this.fieldName)); //lastLoadedVarIndex - 1 because arg 0 of an instance function is always this
|
||||
this.constructorParameterDescriptions.add(new ConstructorParameterDescription(lastLoadedVar.getIndex() - 1, lastLoadedVar.getClassName(), this.fieldDeclaringClassName, this.fieldName)); //lastLoadedVar - 1 because arg 0 of an instance function is always this
|
||||
super.visitFieldInsn(opcode, owner, name, descriptor);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitMethodInsn(int opcode, String owner, String name, String descriptor, boolean isInterface) {
|
||||
var argIndexes = new ArrayList<Integer>();
|
||||
var args = new ArrayList<Var>();
|
||||
for (var i = 0; i < Type.getArgumentTypes(descriptor).length; i++) {
|
||||
argIndexes.add(0, varIndexStack.pop());
|
||||
args.add(0, varStack.pop());
|
||||
}
|
||||
varIndexStack.pop(); // also pop ALOAD0
|
||||
varStack.pop(); // also pop ALOAD0
|
||||
if (opcode == Opcodes.INVOKESPECIAL && name.equals("<init>") //TODO does it have to be called '<init>' or is this just convention
|
||||
&& Set.of(ConstructorSteps.PRE_SUPER_INIT_ALOAD_0, ConstructorSteps.PRE_SUPER_INIT_VAR_LOAD).contains(constructorStep)) {
|
||||
for (int i = 0; i < argIndexes.size(); i++) {
|
||||
superConstructorParameterDescriptions.add(new SuperConstructorParameterDescription(argIndexes.get(i), i));
|
||||
&& Set.of(ConstructorSteps.PRE_SUPER_INIT_ALOAD_0, ConstructorSteps.PRE_SUPER_INIT_VAR_LOAD).contains(constructorStep)) {
|
||||
for (int i = 0; i < args.size(); i++) {
|
||||
superConstructorParameterDescriptions.add(new SuperConstructorParameterDescription(args.get(i).getIndex(), i));
|
||||
}
|
||||
constructorStep = ConstructorSteps.SUPER_INIT;
|
||||
} else {
|
||||
@@ -232,9 +250,6 @@ public class MethodAnalyzerVisitor extends MethodVisitor {//TODO verify param co
|
||||
|
||||
@Override
|
||||
public void visitLabel(Label label) {
|
||||
// constructorStep = ConstructorSteps.INVALID;
|
||||
// getterStep = GetterSteps.INVALID;
|
||||
// setterStep = SetterSteps.INVALID;
|
||||
super.visitLabel(label);
|
||||
}
|
||||
|
||||
@@ -300,9 +315,16 @@ public class MethodAnalyzerVisitor extends MethodVisitor {//TODO verify param co
|
||||
public void visitEnd() {
|
||||
super.visitEnd();
|
||||
List<ConstructorDescription> constructors = constructorStep == ConstructorSteps.RETURN ? List.of(new ConstructorDescription(constructorParameterDescriptions, superConstructorParameterDescriptions)) : List.of();
|
||||
List<GetterDescription> getters = getterStep == GetterSteps.RETURN ? List.of(new GetterDescription(methodClassName, methodName, returnClassName, parameterClassNames, fieldDeclaringClassName, fieldName)) : List.of();
|
||||
List<SetterDescription> setters = setterStep == SetterSteps.RETURN ? List.of(new SetterDescription(methodClassName, methodName, returnClassName, parameterClassNames, fieldDeclaringClassName, fieldName)) : List.of();
|
||||
List<GetterDescription> getters = getterStep == GetterSteps.RETURN ? List.of(new GetterDescription(declaringClassName, methodName, returnClassName, Arrays.stream(parameterClassNames).skip(hasThis() ? 1 : 0).toArray(String[]::new), fieldDeclaringClassName, fieldName)) : List.of();
|
||||
List<SetterDescription> setters = setterStep == SetterSteps.RETURN ? List.of(new SetterDescription(declaringClassName, methodName, returnClassName, Arrays.stream(parameterClassNames).skip(hasThis() ? 1 : 0).toArray(String[]::new), fieldDeclaringClassName, fieldName)) : List.of();
|
||||
var access = new ClassDescription(null, null, null, constructors, getters, setters);
|
||||
callback.accept(access);
|
||||
}
|
||||
|
||||
@AllArgsConstructor
|
||||
@Getter
|
||||
private static class Var {
|
||||
private final String className;
|
||||
private final int index;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@ import lombok.ToString;
|
||||
@ToString
|
||||
public class ConstructorParameterDescription {
|
||||
private final int parameterIndex;
|
||||
private final String parameterClassName;
|
||||
private final String declaringClassName;
|
||||
private final String fieldName;
|
||||
}
|
||||
|
||||
@@ -15,7 +15,7 @@ public @interface Generated {
|
||||
enum Type {
|
||||
NONE,//no value is generated
|
||||
IDENTITY,//value is generated on insert
|
||||
// COMPUTED,//value is generated on insert and update
|
||||
COMPUTED,//value is generated on insert and update
|
||||
;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ import jef.asm.access.model.ClassDescription;
|
||||
import jef.asm.access.model.GetterDescription;
|
||||
import jef.asm.access.model.SetterDescription;
|
||||
import jef.model.DbField;
|
||||
import org.objectweb.asm.ClassVisitor;
|
||||
import org.objectweb.asm.Label;
|
||||
import org.objectweb.asm.MethodVisitor;
|
||||
import org.objectweb.asm.Opcodes;
|
||||
@@ -18,6 +19,7 @@ import java.sql.SQLException;
|
||||
import java.sql.Types;
|
||||
import java.util.Arrays;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@@ -56,24 +58,15 @@ public abstract class ByteCodeHelper {
|
||||
"(" + Arrays.stream(getter.getMethodParameterClassNames())
|
||||
.map(ByteCodeHelper::getDescriptorForClassName)
|
||||
.collect(Collectors.joining())
|
||||
+ ")" + getDescriptorForClassName(getter.getMethodReturnClassName()),
|
||||
+ ")" + getDescriptorForClassName(getter.getMethodReturnClassName()),
|
||||
false);
|
||||
}
|
||||
|
||||
private static void visitGetViaReflection(MethodVisitor mv, Field field, int varIndexEntity, int varIndexField) {
|
||||
// var field = EntityClass.class.getDeclaredField("fieldname");
|
||||
mv.visitLdcInsn(Type.getObjectType(field.getDeclaringClass().getName()));
|
||||
mv.visitLdcInsn(field.getName());
|
||||
mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, T_CLASS.getInternalName(), "getDeclaredField", Type.getMethodDescriptor(T_FIELD, T_STRING), false);
|
||||
mv.visitVarInsn(Opcodes.ASTORE, varIndexField);
|
||||
private static void visitGetViaReflection(MethodVisitor mv, Field field, int varIndexEntity, String className, Runnable defineFieldCallback) {
|
||||
defineFieldCallback.run();
|
||||
|
||||
// field.setAccessible(true);
|
||||
mv.visitVarInsn(Opcodes.ALOAD, varIndexField);
|
||||
mv.visitInsn(Opcodes.ICONST_1);
|
||||
mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, T_FIELD.getInternalName(), "setAccessible", Type.getMethodDescriptor(T_VOID, T_BOOL), false);
|
||||
|
||||
// field.get*(entity);
|
||||
mv.visitVarInsn(Opcodes.ALOAD, varIndexField);
|
||||
// field.get*(entity, value);
|
||||
mv.visitFieldInsn(Opcodes.GETSTATIC, className.replace(".", "/"), makeReflectedFieldName(field), Type.getDescriptor(field.getType()));
|
||||
mv.visitVarInsn(Opcodes.ALOAD, varIndexEntity);
|
||||
var clazz = field.getType();
|
||||
if (clazz == boolean.class) {
|
||||
@@ -97,7 +90,7 @@ public abstract class ByteCodeHelper {
|
||||
}
|
||||
}
|
||||
|
||||
static void visitGetField(MethodVisitor mv, Field field, int varIndexEntity, int varIndexField, ClassDescription classDescription) {
|
||||
static void visitGetField(MethodVisitor mv, Field field, int varIndexEntity, String className, Runnable defineFieldCallback, ClassDescription classDescription) {
|
||||
//try direct member access
|
||||
if (Modifier.isPublic(field.getModifiers())) {//TODO better check if publicly accessible
|
||||
visitGetFieldDirectAccess(mv, field, varIndexEntity);
|
||||
@@ -107,7 +100,7 @@ public abstract class ByteCodeHelper {
|
||||
//try finding getter
|
||||
Optional<GetterDescription> getter = classDescription.getDeclaredGetters().stream()
|
||||
.filter(e -> e.getFieldDeclaringClassName().equals(field.getDeclaringClass().getName())
|
||||
&& e.getFieldName().equals(field.getName()))
|
||||
&& e.getFieldName().equals(field.getName()))
|
||||
.findFirst();
|
||||
if (getter.isPresent()) {
|
||||
visitGetter(mv, getter.get(), varIndexEntity);
|
||||
@@ -115,7 +108,7 @@ public abstract class ByteCodeHelper {
|
||||
}
|
||||
|
||||
//use reflection
|
||||
visitGetViaReflection(mv, field, varIndexEntity, varIndexField);
|
||||
visitGetViaReflection(mv, field, varIndexEntity, className, defineFieldCallback);
|
||||
}
|
||||
//endregion
|
||||
|
||||
@@ -135,24 +128,15 @@ public abstract class ByteCodeHelper {
|
||||
"(" + Arrays.stream(setter.getMethodParameterClassNames())
|
||||
.map(ByteCodeHelper::getDescriptorForClassName)
|
||||
.collect(Collectors.joining())
|
||||
+ ")" + getDescriptorForClassName(setter.getMethodReturnClassName()),
|
||||
+ ")" + getDescriptorForClassName(setter.getMethodReturnClassName()),
|
||||
false);
|
||||
}
|
||||
|
||||
private static void visitSetViaReflection(MethodVisitor mv, Field field, Consumer<MethodVisitor> value, int varIndexEntity, int varIndexField) {
|
||||
// var field = EntityClass.class.getDeclaredField("fieldname");
|
||||
mv.visitLdcInsn(Type.getObjectType(field.getDeclaringClass().getName()));
|
||||
mv.visitLdcInsn(field.getName());
|
||||
mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, T_CLASS.getInternalName(), "getDeclaredField", Type.getMethodDescriptor(T_FIELD, T_STRING), false);
|
||||
mv.visitVarInsn(Opcodes.ASTORE, varIndexField);
|
||||
|
||||
// field.setAccessible(true);
|
||||
mv.visitVarInsn(Opcodes.ALOAD, varIndexField);
|
||||
mv.visitInsn(Opcodes.ICONST_1);
|
||||
mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, T_FIELD.getInternalName(), "setAccessible", Type.getMethodDescriptor(T_VOID, T_BOOL), false);
|
||||
private static void visitSetViaReflection(MethodVisitor mv, Field field, Consumer<MethodVisitor> value, int varIndexEntity, String className, Runnable defineFieldCallback) {
|
||||
defineFieldCallback.run();
|
||||
|
||||
// field.set*(entity, value);
|
||||
mv.visitVarInsn(Opcodes.ALOAD, varIndexField);
|
||||
mv.visitFieldInsn(Opcodes.GETSTATIC, className.replace(".", "/"), makeReflectedFieldName(field), Type.getDescriptor(field.getType()));
|
||||
mv.visitVarInsn(Opcodes.ALOAD, varIndexEntity);
|
||||
value.accept(mv);
|
||||
var clazz = field.getType();
|
||||
@@ -177,7 +161,7 @@ public abstract class ByteCodeHelper {
|
||||
}
|
||||
}
|
||||
|
||||
static void visitSetField(MethodVisitor mv, Field field, Consumer<MethodVisitor> value, int varIndexEntity, int varIndexField, ClassDescription classDescription) {
|
||||
static void visitSetField(MethodVisitor mv, Field field, Consumer<MethodVisitor> value, int varIndexEntity, String className, Runnable defineFieldCallback, ClassDescription classDescription) {
|
||||
//try direct member access
|
||||
if (Modifier.isPublic(field.getModifiers()) && !Modifier.isFinal(field.getModifiers())) {//TODO better check if publicly accessible
|
||||
visitSetFieldDirectAccess(mv, field, value, varIndexEntity);
|
||||
@@ -187,7 +171,7 @@ public abstract class ByteCodeHelper {
|
||||
//try finding getter
|
||||
Optional<SetterDescription> setter = classDescription.getDeclaredSetters().stream()
|
||||
.filter(e -> e.getFieldDeclaringClassName().equals(field.getDeclaringClass().getName())
|
||||
&& e.getFieldName().equals(field.getName()))
|
||||
&& e.getFieldName().equals(field.getName()))
|
||||
.findFirst();
|
||||
if (setter.isPresent()) {
|
||||
visitSetter(mv, setter.get(), value, varIndexEntity);
|
||||
@@ -195,7 +179,7 @@ public abstract class ByteCodeHelper {
|
||||
}
|
||||
|
||||
//use reflection
|
||||
visitSetViaReflection(mv, field, value, varIndexEntity, varIndexField);
|
||||
visitSetViaReflection(mv, field, value, varIndexEntity, className, defineFieldCallback);
|
||||
}
|
||||
//endregion
|
||||
|
||||
@@ -383,6 +367,17 @@ public abstract class ByteCodeHelper {
|
||||
mv.visitInsn(Opcodes.ATHROW);
|
||||
}
|
||||
|
||||
public static void visitPushDefault(MethodVisitor mv, Class<?> type) {
|
||||
if (Set.of(void.class, boolean.class, byte.class, char.class, short.class, int.class, float.class).contains(type)) {
|
||||
mv.visitInsn(Opcodes.ICONST_0);
|
||||
} else if (Set.of(double.class, long.class).contains(type)) {
|
||||
mv.visitInsn(Opcodes.ICONST_0);
|
||||
mv.visitInsn(Opcodes.ICONST_0);
|
||||
} else {
|
||||
mv.visitInsn(Opcodes.ACONST_NULL);
|
||||
}
|
||||
}
|
||||
|
||||
static String getDescriptorForClassName(String className) {
|
||||
return switch (className) {
|
||||
case "void" -> "V";
|
||||
@@ -399,6 +394,33 @@ public abstract class ByteCodeHelper {
|
||||
}
|
||||
//endregion
|
||||
|
||||
//region field reflection
|
||||
public static void visitReflectedFieldDefinition(ClassVisitor visitor, Field field, boolean synthetic) {
|
||||
visitor.visitField(Opcodes.ACC_PRIVATE | Opcodes.ACC_STATIC | (synthetic ? Opcodes.ACC_SYNTHETIC : 0),
|
||||
makeReflectedFieldName(field), T_FIELD.getDescriptor(), null, null);
|
||||
}
|
||||
|
||||
public static void visitReflectedFieldInit(MethodVisitor mv, Field field, String className) {
|
||||
className = className.replace(".", "/");
|
||||
var fieldname = makeReflectedFieldName(field);
|
||||
|
||||
// field = EntityClass.class.getDeclaredField("fieldname");
|
||||
mv.visitLdcInsn(Type.getObjectType(field.getDeclaringClass().getName()));
|
||||
mv.visitLdcInsn(field.getName());
|
||||
mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, T_CLASS.getInternalName(), "getDeclaredField", Type.getMethodDescriptor(T_FIELD, T_STRING), false);
|
||||
mv.visitFieldInsn(Opcodes.PUTSTATIC, className, fieldname, T_FIELD.getDescriptor());
|
||||
|
||||
// field.setAccessible(true);
|
||||
mv.visitFieldInsn(Opcodes.GETSTATIC, className, fieldname, Type.getDescriptor(field.getType()));
|
||||
mv.visitInsn(Opcodes.ICONST_1);
|
||||
mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, T_FIELD.getInternalName(), "setAccessible", Type.getMethodDescriptor(T_VOID, T_BOOL), false);
|
||||
}
|
||||
|
||||
private static String makeReflectedFieldName(Field field) {
|
||||
return field.getDeclaringClass().getName().replace(".", "_") + "_" + field.getName();
|
||||
}
|
||||
//endregion
|
||||
|
||||
//region var access
|
||||
// private void visitLoadVariable(MethodVisitor mv, String className, int varIndex) {
|
||||
// switch (className) {
|
||||
|
||||
@@ -24,40 +24,53 @@ public abstract class MysqlEntityDbInterface<T extends SerializableObject> {
|
||||
}
|
||||
|
||||
protected abstract PreparedStatement queryAdd(Connection connection) throws SQLException;
|
||||
// protected abstract PreparedStatement queryAdd(Connection connection) throws SQLException {
|
||||
// return connection.prepareStatement("query", Statement.RETURN_GENERATED_KEYS);
|
||||
// }
|
||||
|
||||
// protected abstract void bindParamsAdd(PreparedStatement stmt, T entity) throws SQLException;
|
||||
protected void bindParamsAdd(PreparedStatement stmt, T entity) throws SQLException {
|
||||
stmt.setInt(1, 1);
|
||||
stmt.setInt(2, 2);
|
||||
var s = "string";
|
||||
if (s != null) {
|
||||
stmt.setString(3, "string");
|
||||
} else {
|
||||
stmt.setNull(3, Types.INTEGER);
|
||||
}
|
||||
stmt.setInt(4, 4);
|
||||
}
|
||||
protected abstract void bindParamsAdd(PreparedStatement stmt, T entity) throws SQLException;
|
||||
|
||||
protected void readBackGeneratedValuesAdd(PreparedStatement stmt, T entity) throws SQLException {
|
||||
try (var res = stmt.getGeneratedKeys()) {
|
||||
readBackGeneratedValuesAdd(res, entity);
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
protected void readBackGeneratedValuesAdd(ResultSet res, T entity) throws Exception {
|
||||
var f = Exception.class.getDeclaredField("f");
|
||||
f.setAccessible(true);
|
||||
f.setInt(null, 12);
|
||||
protected abstract void readBackGeneratedValuesAdd(ResultSet res, T entity) throws SQLException;
|
||||
|
||||
public void update(Connection connection, T entity) throws SQLException {
|
||||
try (var stmt = queryUpdate(connection)) {
|
||||
bindParamsUpdate(stmt, entity);
|
||||
|
||||
var updated = stmt.executeUpdate();
|
||||
if (updated != 1) {
|
||||
throw new SQLException("Failed to update entity");
|
||||
}
|
||||
|
||||
readBackGeneratedValuesUpdate(stmt, entity);
|
||||
}
|
||||
}
|
||||
|
||||
// public void update(Connection connection, T model);
|
||||
//
|
||||
// public void delete(Connection connection, T model);
|
||||
//
|
||||
// public T read(ResultSet res);
|
||||
protected abstract PreparedStatement queryUpdate(Connection connection) throws SQLException;
|
||||
|
||||
protected abstract void bindParamsUpdate(PreparedStatement stmt, T entity) throws SQLException;
|
||||
|
||||
protected void readBackGeneratedValuesUpdate(PreparedStatement stmt, T entity) throws SQLException {
|
||||
try (var res = stmt.getGeneratedKeys()) {
|
||||
readBackGeneratedValuesUpdate(res, entity);
|
||||
}
|
||||
}
|
||||
|
||||
protected abstract void readBackGeneratedValuesUpdate(ResultSet res, T entity) throws SQLException;
|
||||
|
||||
public void delete(Connection connection, T entity) throws SQLException {
|
||||
try (var stmt = queryUpdate(connection)) {
|
||||
bindParamsDelete(stmt, entity);
|
||||
|
||||
var deleted = stmt.executeUpdate();
|
||||
if (deleted != 1) {
|
||||
throw new SQLException("Failed to delete entity");
|
||||
}
|
||||
}
|
||||
}
|
||||
protected abstract void bindParamsDelete(PreparedStatement stmt, T entity) throws SQLException;
|
||||
|
||||
public abstract T read(ResultSet res);
|
||||
}
|
||||
|
||||
@@ -17,8 +17,13 @@ import org.objectweb.asm.Opcodes;
|
||||
import org.objectweb.asm.Type;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.sql.Statement;
|
||||
import java.util.AbstractMap;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Comparator;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@@ -34,6 +39,7 @@ public class MysqlEntityDbInterfaceGenerator<T extends SerializableObject> {
|
||||
|
||||
//runtime
|
||||
private ClassDescription classDescription;
|
||||
private final Set<Field> fieldsToReflect = new HashSet<>();
|
||||
|
||||
//results
|
||||
@Getter
|
||||
@@ -45,7 +51,7 @@ public class MysqlEntityDbInterfaceGenerator<T extends SerializableObject> {
|
||||
this.context = context;
|
||||
this.entity = entity;
|
||||
writer = new ClassWriter(Opcodes.ASM9);
|
||||
visitor = new ClassVisitor(Opcodes.ASM9, writer) {//TODO definitely broken here
|
||||
visitor = new ClassVisitor(Opcodes.ASM9, writer) {
|
||||
};
|
||||
}
|
||||
|
||||
@@ -57,15 +63,30 @@ public class MysqlEntityDbInterfaceGenerator<T extends SerializableObject> {
|
||||
className = entity.getType().getSimpleName() + "" + MysqlEntityDbInterface.class.getSimpleName();
|
||||
|
||||
defineClass();
|
||||
defineConstructor();
|
||||
// defineAddFunction();
|
||||
|
||||
//add
|
||||
defineQueryAddFunction();
|
||||
defineBindParamsAddFunction();
|
||||
defineReadBackGeneratedValuesAddPreparedStatement();
|
||||
defineReadBackGeneratedValuesAddResultSet();
|
||||
// defineUpdateFunction();
|
||||
// defineDeleteFunction();
|
||||
// defineReadFunction();
|
||||
|
||||
//update
|
||||
defineQueryUpdateFunction();
|
||||
defineBindParamsUpdateFunction();
|
||||
defineReadBackGeneratedValuesUpdatePreparedStatement();
|
||||
defineReadBackGeneratedValuesUpdateResultSet();
|
||||
|
||||
//delete
|
||||
defineQueryDeleteFunction();
|
||||
defineBindParamsDeleteFunction();
|
||||
|
||||
//read
|
||||
defineReadFunction();
|
||||
|
||||
//other
|
||||
defineReflectedFields();
|
||||
defineClassConstructor();
|
||||
defineConstructor();
|
||||
|
||||
visitor.visitEnd();
|
||||
|
||||
@@ -74,10 +95,7 @@ public class MysqlEntityDbInterfaceGenerator<T extends SerializableObject> {
|
||||
|
||||
protected void defineClass() {
|
||||
var classSimpleName = className.substring(Math.max(className.lastIndexOf("."), className.lastIndexOf("$")) + 1);
|
||||
visitor.visit(Opcodes.V17, Opcodes.ACC_PUBLIC | Opcodes.ACC_SUPER | SYNTHETIC_FLAG,
|
||||
classSimpleName,
|
||||
Type.getDescriptor(MysqlEntityDbInterface.class).replace(";", "<" + ByteCodeHelper.getDescriptorForClassName(entity.getTypeName()) + ">;"),
|
||||
Type.getInternalName(MysqlEntityDbInterface.class), new String[]{});
|
||||
visitor.visit(Opcodes.V17, Opcodes.ACC_PUBLIC | Opcodes.ACC_SUPER | SYNTHETIC_FLAG, classSimpleName, Type.getDescriptor(MysqlEntityDbInterface.class).replace(";", "<" + ByteCodeHelper.getDescriptorForClassName(entity.getTypeName()) + ">;"), Type.getInternalName(MysqlEntityDbInterface.class), new String[]{});
|
||||
// visitor.visitNestHost(Type.getInternalName(context));
|
||||
// visitor.visitInnerClass(className.replace(".", "/"), Type.getInternalName(context), classSimpleName, Opcodes.ACC_PUBLIC | Opcodes.ACC_STATIC | Opcodes.ACC_SUPER);
|
||||
}
|
||||
@@ -93,84 +111,39 @@ public class MysqlEntityDbInterfaceGenerator<T extends SerializableObject> {
|
||||
mv.visitEnd();
|
||||
}
|
||||
|
||||
private ColumnsAndValueBinders prepareColumnsAndValueBindersAdd() {
|
||||
var columns = new ArrayList<String>();
|
||||
var valueBinders = new ArrayList<ValueBinder>();
|
||||
|
||||
for (DbField<?> field : entity.getFields()) {
|
||||
if (!field.isDatabaseField()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
Field f = field.getField();
|
||||
if (f == null) {
|
||||
if (field.isDatabaseField()) {//field defined but not present in entity class -> currently nothing to insert -> null
|
||||
columns.add(field.getName());
|
||||
valueBinders.add((mv, varIndexEntity, varIndexStmt, varIndexField, index) -> {
|
||||
ByteCodeHelper.visitIntInsn(mv, index);
|
||||
|
||||
//stmt.setNull(i, Types.INTEGER);
|
||||
ByteCodeHelper.visitPreparedStatementSetNull(mv, field, varIndexStmt, index);
|
||||
});
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
columns.add(field.getName());
|
||||
valueBinders.add((mv, varIndexEntity, varIndexStmt, varIndexField, index) -> {
|
||||
if (field.isNotNull()) {
|
||||
//stmt.set*(i, value);
|
||||
ByteCodeHelper.visitPreparedStatementSetAny(mv, field, varIndexStmt, index, (mv2) -> ByteCodeHelper.visitGetField(mv2, field.getField(), varIndexEntity, varIndexField, classDescription));
|
||||
} else {
|
||||
// getFieldValue(entity);
|
||||
ByteCodeHelper.visitGetField(mv, f, varIndexEntity, varIndexField, classDescription);
|
||||
|
||||
// if (value == null)
|
||||
// stmt.setNull(i, Types.*);
|
||||
// } else {
|
||||
// stmt.set*(i, value);
|
||||
// }
|
||||
var labelAfter = new Label();
|
||||
var labelElse = new Label();
|
||||
mv.visitJumpInsn(Opcodes.IFNULL, labelElse);
|
||||
ByteCodeHelper.visitPreparedStatementSetAny(mv, field, varIndexStmt, index, (mv2) -> ByteCodeHelper.visitGetField(mv2, field.getField(), varIndexEntity, varIndexField, classDescription));
|
||||
mv.visitJumpInsn(Opcodes.GOTO, labelAfter);
|
||||
mv.visitLabel(labelElse);
|
||||
ByteCodeHelper.visitPreparedStatementSetNull(mv, field, varIndexStmt, index);
|
||||
mv.visitLabel(labelAfter);
|
||||
// mv.visitFrame(Opcodes.F_SAME, 0, new Object[0], 0, new Object[0]);
|
||||
mv.visitFrame(Opcodes.F_FULL, 3, new Object[]{
|
||||
ByteCodeHelper.T_MYSQL_ENTITY_DB_INTERFACE.getInternalName(), ByteCodeHelper.T_PREPARED_STATEMENT.getInternalName(), entity.getTypeName().replace(".", "/")
|
||||
}, 0, new Object[0]);
|
||||
}
|
||||
});
|
||||
private void defineReflectedFields() {
|
||||
for (Field field : fieldsToReflect) {
|
||||
ByteCodeHelper.visitReflectedFieldDefinition(visitor, field, SET_SYNTHETIC_FLAG);
|
||||
}
|
||||
return new ColumnsAndValueBinders(columns, valueBinders);
|
||||
}
|
||||
|
||||
private interface ValueBinder {
|
||||
void bind(MethodVisitor mv, int varIndexEntity, int varIndexStmt, int varIndexField, int index);
|
||||
}
|
||||
|
||||
@AllArgsConstructor
|
||||
@Getter
|
||||
private static class ColumnsAndValueBinders {
|
||||
private final List<String> columns;
|
||||
private final List<ValueBinder> valueBinders;
|
||||
//<clinit>
|
||||
private void defineClassConstructor() {
|
||||
var mv = visitor.visitMethod(Opcodes.ACC_STATIC | SYNTHETIC_FLAG, "<clinit>", "()V", null, new String[0]);
|
||||
mv.visitCode();
|
||||
|
||||
//init reflected fields
|
||||
for (Field field : fieldsToReflect) {
|
||||
ByteCodeHelper.visitReflectedFieldInit(mv, field, className);
|
||||
}
|
||||
|
||||
mv.visitInsn(Opcodes.RETURN);
|
||||
mv.visitMaxs(2, 0);
|
||||
mv.visitEnd();
|
||||
}
|
||||
|
||||
//region add
|
||||
private void defineQueryAddFunction() {
|
||||
var mv = visitor.visitMethod(Opcodes.ACC_PROTECTED | SYNTHETIC_FLAG, "queryAdd", Type.getMethodDescriptor(ByteCodeHelper.T_PREPARED_STATEMENT, ByteCodeHelper.T_CONNECTION),
|
||||
null, new String[]{ByteCodeHelper.T_SQLEXCEPTION.getInternalName()});
|
||||
var mv = visitor.visitMethod(Opcodes.ACC_PROTECTED | SYNTHETIC_FLAG, "queryAdd", Type.getMethodDescriptor(ByteCodeHelper.T_PREPARED_STATEMENT, ByteCodeHelper.T_CONNECTION), null, new String[]{ByteCodeHelper.T_SQLEXCEPTION.getInternalName()});
|
||||
mv.visitAnnotation(ByteCodeHelper.T_OVERRIDE.getDescriptor(), true).visitEnd();
|
||||
|
||||
var varIndexThis = 0;
|
||||
var varIndexConnection = 1;
|
||||
|
||||
var columnsAndValueBinders = prepareColumnsAndValueBindersAdd();
|
||||
var columnsAndValueBindersSet = prepareColumnsAndValueBindersSet();
|
||||
|
||||
var columnsString = columnsAndValueBinders.getColumns().stream().map(e -> "`" + e + "`").collect(Collectors.joining(","));
|
||||
var placeholdersString = columnsAndValueBinders.getColumns().stream().map(e -> "?").collect(Collectors.joining(","));
|
||||
var columnsString = columnsAndValueBindersSet.getColumns().stream().map(e -> "`" + e + "`").collect(Collectors.joining(","));
|
||||
var placeholdersString = columnsAndValueBindersSet.getColumns().stream().map(e -> "?").collect(Collectors.joining(","));
|
||||
var query = "INSERT INTO `" + entity.getName() + "` (" + columnsString + ") VALUES (" + placeholdersString + ")";
|
||||
|
||||
// variable scopes
|
||||
@@ -189,8 +162,8 @@ public class MysqlEntityDbInterfaceGenerator<T extends SerializableObject> {
|
||||
//return connection.prepareStatement("query", Statement.RETURN_GENERATED_KEYS);
|
||||
mv.visitVarInsn(Opcodes.ALOAD, 1);
|
||||
mv.visitLdcInsn(query);
|
||||
if (entity.getPrimaryKey() != null) {
|
||||
mv.visitInsn(Opcodes.ICONST_1); //Statement.RETURN_GENERATED_KEYS == 1
|
||||
if (entity.getFields().stream().anyMatch(e -> e.getGenerated() != Generated.Type.NONE)) {
|
||||
ByteCodeHelper.visitIntInsn(mv, Statement.RETURN_GENERATED_KEYS);
|
||||
} else {
|
||||
mv.visitInsn(Opcodes.ICONST_0); //no flags
|
||||
}
|
||||
@@ -211,17 +184,15 @@ public class MysqlEntityDbInterfaceGenerator<T extends SerializableObject> {
|
||||
}
|
||||
|
||||
private void defineBindParamsAddFunction() {
|
||||
var mv = visitor.visitMethod(Opcodes.ACC_PROTECTED | SYNTHETIC_FLAG, "bindParamsAdd", Type.getMethodDescriptor(ByteCodeHelper.T_VOID, ByteCodeHelper.T_PREPARED_STATEMENT, Type.getType(ByteCodeHelper.getDescriptorForClassName(entity.getTypeName()))),
|
||||
null, new String[]{ByteCodeHelper.T_SQLEXCEPTION.getInternalName()});
|
||||
var mv = visitor.visitMethod(Opcodes.ACC_PROTECTED | SYNTHETIC_FLAG, "bindParamsAdd", Type.getMethodDescriptor(ByteCodeHelper.T_VOID, ByteCodeHelper.T_PREPARED_STATEMENT, Type.getType(ByteCodeHelper.getDescriptorForClassName(entity.getTypeName()))), null, new String[]{ByteCodeHelper.T_SQLEXCEPTION.getInternalName()});
|
||||
mv.visitAnnotation(ByteCodeHelper.T_OVERRIDE.getDescriptor(), true).visitEnd();
|
||||
|
||||
var varIndexThis = 0;
|
||||
var varIndexStmt = 1;
|
||||
var varIndexEntity = 2;
|
||||
var varIndexField = 3;
|
||||
|
||||
//create lists for columns and value binders
|
||||
var columnsAndValueBinders = prepareColumnsAndValueBindersAdd();
|
||||
var columnsAndValueBindersSet = prepareColumnsAndValueBindersSet();
|
||||
|
||||
// variable scopes
|
||||
var labelStartThis = new Label();
|
||||
@@ -230,8 +201,6 @@ public class MysqlEntityDbInterfaceGenerator<T extends SerializableObject> {
|
||||
var labelEndStmt = new Label();
|
||||
var labelStartEntity = new Label();
|
||||
var labelEndEntity = new Label();
|
||||
var labelStartField = new Label();
|
||||
var labelEndField = new Label();
|
||||
|
||||
// begin
|
||||
mv.visitCode();
|
||||
@@ -240,12 +209,11 @@ public class MysqlEntityDbInterfaceGenerator<T extends SerializableObject> {
|
||||
mv.visitLabel(labelStartThis);
|
||||
mv.visitLabel(labelStartStmt);
|
||||
mv.visitLabel(labelStartEntity);
|
||||
mv.visitLabel(labelStartField);
|
||||
|
||||
//stmt.set* bind calls
|
||||
var i = 1;
|
||||
for (var valueBinder : columnsAndValueBinders.getValueBinders()) {
|
||||
valueBinder.bind(mv, varIndexEntity, varIndexStmt, varIndexField, i++);
|
||||
for (var valueBinder : columnsAndValueBindersSet.getValueBinders()) {
|
||||
valueBinder.bind(mv, varIndexEntity, varIndexStmt, i++);
|
||||
}
|
||||
|
||||
//return
|
||||
@@ -255,55 +223,503 @@ public class MysqlEntityDbInterfaceGenerator<T extends SerializableObject> {
|
||||
mv.visitLabel(labelEndThis);
|
||||
mv.visitLabel(labelEndStmt);
|
||||
mv.visitLabel(labelEndEntity);
|
||||
mv.visitLabel(labelEndField);
|
||||
|
||||
// debug symbols
|
||||
mv.visitLocalVariable("this", ByteCodeHelper.getDescriptorForClassName(className), null, labelStartThis, labelEndThis, varIndexThis);
|
||||
mv.visitLocalVariable("stmt", ByteCodeHelper.T_PREPARED_STATEMENT.getDescriptor(), null, labelStartStmt, labelEndStmt, varIndexStmt);
|
||||
mv.visitLocalVariable("entity", ByteCodeHelper.getDescriptorForClassName(entity.getTypeName()), null, labelStartEntity, labelEndEntity, varIndexEntity);
|
||||
mv.visitLocalVariable("field", ByteCodeHelper.T_FIELD.getDescriptor(), null, labelStartField, labelEndField, varIndexField);
|
||||
|
||||
mv.visitMaxs(3, 4);
|
||||
mv.visitMaxs(3, 3);
|
||||
|
||||
mv.visitEnd();
|
||||
}
|
||||
|
||||
private void defineReadBackGeneratedValuesAddPreparedStatement() {
|
||||
if (entity.getFields().stream().anyMatch(e -> e.getGenerated() != Generated.Type.NONE)) {
|
||||
if (entity.getFields().stream().anyMatch(e -> e.getGenerated() == Generated.Type.IDENTITY)) {
|
||||
return;
|
||||
}
|
||||
|
||||
//stub method if no generated values
|
||||
var mv = visitor.visitMethod(Opcodes.ACC_PROTECTED | SYNTHETIC_FLAG, "readBackGeneratedValuesAdd", Type.getMethodDescriptor(ByteCodeHelper.T_VOID, ByteCodeHelper.T_PREPARED_STATEMENT, Type.getType(ByteCodeHelper.getDescriptorForClassName(entity.getTypeName()))),
|
||||
null, new String[]{ByteCodeHelper.T_SQLEXCEPTION.getInternalName()});
|
||||
var mv = visitor.visitMethod(Opcodes.ACC_PROTECTED | SYNTHETIC_FLAG, "readBackGeneratedValuesAdd", Type.getMethodDescriptor(ByteCodeHelper.T_VOID, ByteCodeHelper.T_PREPARED_STATEMENT, Type.getType(ByteCodeHelper.getDescriptorForClassName(entity.getTypeName()))), null, new String[]{ByteCodeHelper.T_SQLEXCEPTION.getInternalName()});
|
||||
mv.visitAnnotation(ByteCodeHelper.T_OVERRIDE.getDescriptor(), true).visitEnd();
|
||||
|
||||
var label = new Label();
|
||||
var labelStart = new Label();
|
||||
var labelEnd = new Label();
|
||||
|
||||
mv.visitLabel(labelStart);
|
||||
|
||||
mv.visitCode();
|
||||
mv.visitInsn(Opcodes.RETURN);
|
||||
|
||||
mv.visitLabel(labelEnd);
|
||||
|
||||
// debug symbols
|
||||
mv.visitLocalVariable("this", ByteCodeHelper.getDescriptorForClassName(className), null, label, label, 0);
|
||||
mv.visitLocalVariable("stmt", ByteCodeHelper.T_PREPARED_STATEMENT.getDescriptor(), null, label, label, 1);
|
||||
mv.visitLocalVariable("entity", ByteCodeHelper.getDescriptorForClassName(entity.getTypeName()), null, label, label, 2);
|
||||
mv.visitLocalVariable("this", ByteCodeHelper.getDescriptorForClassName(className), null, labelStart, labelEnd, 0);
|
||||
mv.visitLocalVariable("stmt", ByteCodeHelper.T_PREPARED_STATEMENT.getDescriptor(), null, labelStart, labelEnd, 1);
|
||||
mv.visitLocalVariable("entity", ByteCodeHelper.getDescriptorForClassName(entity.getTypeName()), null, labelStart, labelEnd, 2);
|
||||
|
||||
mv.visitMaxs(0, 3);//TODO stack size
|
||||
mv.visitMaxs(0, 3);
|
||||
mv.visitEnd();
|
||||
}
|
||||
|
||||
private void defineReadBackGeneratedValuesAddResultSet() {
|
||||
if (entity.getFields().stream().noneMatch(e -> e.getGenerated() != Generated.Type.NONE)) {
|
||||
var fieldsToRead = entity.getFields().stream().filter(e -> e.getGenerated() != Generated.Type.NONE).toList();
|
||||
if (fieldsToRead.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
var mv = visitor.visitMethod(Opcodes.ACC_PROTECTED | SYNTHETIC_FLAG, "readBackGeneratedValuesAdd", Type.getMethodDescriptor(ByteCodeHelper.T_VOID, ByteCodeHelper.T_RESULT_SET, Type.getType(ByteCodeHelper.getDescriptorForClassName(entity.getTypeName()))),
|
||||
null, new String[]{ByteCodeHelper.T_SQLEXCEPTION.getInternalName()});
|
||||
var mv = visitor.visitMethod(Opcodes.ACC_PROTECTED | SYNTHETIC_FLAG, "readBackGeneratedValuesAdd", Type.getMethodDescriptor(ByteCodeHelper.T_VOID, ByteCodeHelper.T_RESULT_SET, Type.getType(ByteCodeHelper.getDescriptorForClassName(entity.getTypeName()))), null, new String[]{ByteCodeHelper.T_SQLEXCEPTION.getInternalName()});
|
||||
mv.visitAnnotation(ByteCodeHelper.T_OVERRIDE.getDescriptor(), true).visitEnd();
|
||||
|
||||
visitReadFieldsIntoEntityFunctionBody(mv, fieldsToRead, true);
|
||||
}
|
||||
//endregion
|
||||
|
||||
//region update
|
||||
private void defineQueryUpdateFunction() {
|
||||
var mv = visitor.visitMethod(Opcodes.ACC_PROTECTED | SYNTHETIC_FLAG, "queryUpdate", Type.getMethodDescriptor(ByteCodeHelper.T_PREPARED_STATEMENT, ByteCodeHelper.T_CONNECTION), null, new String[]{ByteCodeHelper.T_SQLEXCEPTION.getInternalName()});
|
||||
mv.visitAnnotation(ByteCodeHelper.T_OVERRIDE.getDescriptor(), true).visitEnd();
|
||||
|
||||
var varIndexThis = 0;
|
||||
var varIndexConnection = 1;
|
||||
|
||||
var columnsAndValueBindersSet = prepareColumnsAndValueBindersSet();
|
||||
var columnsAndValueBindersWhere = prepareColumnsAndValueBindersWhere();
|
||||
|
||||
var updatesString = columnsAndValueBindersSet.getColumns().stream().map(e -> "`" + e + "` = ?").collect(Collectors.joining(", "));
|
||||
var whereString = columnsAndValueBindersWhere.getColumns().stream().map(e -> "`" + e + "` = ?").collect(Collectors.joining(" AND "));
|
||||
|
||||
var query = "UPDATE `" + entity.getName() + "` SET " + updatesString + " WHERE " + whereString;
|
||||
|
||||
// variable scopes
|
||||
var labelStartThis = new Label();
|
||||
var labelEndThis = new Label();
|
||||
var labelStartConnection = new Label();
|
||||
var labelEndConnection = new Label();
|
||||
|
||||
// begin
|
||||
mv.visitCode();
|
||||
|
||||
// variable scopes
|
||||
mv.visitLabel(labelStartThis);
|
||||
mv.visitLabel(labelStartConnection);
|
||||
|
||||
//return connection.prepareStatement("query", Statement.RETURN_GENERATED_KEYS);
|
||||
mv.visitVarInsn(Opcodes.ALOAD, 1);
|
||||
mv.visitLdcInsn(query);
|
||||
if (entity.getFields().stream().anyMatch(e -> e.getGenerated() == Generated.Type.COMPUTED)) {
|
||||
ByteCodeHelper.visitIntInsn(mv, Statement.RETURN_GENERATED_KEYS);
|
||||
} else {
|
||||
mv.visitInsn(Opcodes.ICONST_0); //no flags
|
||||
}
|
||||
mv.visitMethodInsn(Opcodes.INVOKEINTERFACE, ByteCodeHelper.T_CONNECTION.getInternalName(), "prepareStatement", Type.getMethodDescriptor(ByteCodeHelper.T_PREPARED_STATEMENT, ByteCodeHelper.T_STRING, ByteCodeHelper.T_INT), true);
|
||||
mv.visitInsn(Opcodes.ARETURN);
|
||||
|
||||
// variable scopes
|
||||
mv.visitLabel(labelEndConnection);
|
||||
mv.visitLabel(labelEndThis);
|
||||
|
||||
// debug symbols
|
||||
mv.visitLocalVariable("this", ByteCodeHelper.getDescriptorForClassName(className), null, labelStartConnection, labelEndConnection, varIndexThis);
|
||||
mv.visitLocalVariable("connection", ByteCodeHelper.T_CONNECTION.getDescriptor(), null, labelStartThis, labelEndThis, varIndexConnection);
|
||||
|
||||
mv.visitMaxs(3, 2);
|
||||
|
||||
mv.visitEnd();
|
||||
}
|
||||
|
||||
private void defineBindParamsUpdateFunction() {
|
||||
var mv = visitor.visitMethod(Opcodes.ACC_PROTECTED | SYNTHETIC_FLAG, "bindParamsUpdate", Type.getMethodDescriptor(ByteCodeHelper.T_VOID, ByteCodeHelper.T_PREPARED_STATEMENT, Type.getType(ByteCodeHelper.getDescriptorForClassName(entity.getTypeName()))), null, new String[]{ByteCodeHelper.T_SQLEXCEPTION.getInternalName()});
|
||||
mv.visitAnnotation(ByteCodeHelper.T_OVERRIDE.getDescriptor(), true).visitEnd();
|
||||
|
||||
var varIndexThis = 0;
|
||||
var varIndexStmt = 1;
|
||||
var varIndexEntity = 2;
|
||||
|
||||
//create lists for columns and value binders
|
||||
var columnsAndValueBindersSet = prepareColumnsAndValueBindersSet();
|
||||
var columnsAndValueBindersWhere = prepareColumnsAndValueBindersWhere();
|
||||
|
||||
// variable scopes
|
||||
var labelStartThis = new Label();
|
||||
var labelEndThis = new Label();
|
||||
var labelStartStmt = new Label();
|
||||
var labelEndStmt = new Label();
|
||||
var labelStartEntity = new Label();
|
||||
var labelEndEntity = new Label();
|
||||
|
||||
// begin
|
||||
mv.visitCode();
|
||||
|
||||
// variable scopes
|
||||
mv.visitLabel(labelStartThis);
|
||||
mv.visitLabel(labelStartStmt);
|
||||
mv.visitLabel(labelStartEntity);
|
||||
|
||||
//stmt.set* bind calls
|
||||
var i = 1;
|
||||
for (var valueBinder : columnsAndValueBindersSet.getValueBinders()) {
|
||||
valueBinder.bind(mv, varIndexEntity, varIndexStmt, i++);
|
||||
}
|
||||
for (var valueBinder : columnsAndValueBindersWhere.getValueBinders()) {
|
||||
valueBinder.bind(mv, varIndexEntity, varIndexStmt, i++);
|
||||
}
|
||||
|
||||
//return
|
||||
mv.visitInsn(Opcodes.RETURN);
|
||||
|
||||
// variable scopes
|
||||
mv.visitLabel(labelEndThis);
|
||||
mv.visitLabel(labelEndStmt);
|
||||
mv.visitLabel(labelEndEntity);
|
||||
|
||||
// debug symbols
|
||||
mv.visitLocalVariable("this", ByteCodeHelper.getDescriptorForClassName(className), null, labelStartThis, labelEndThis, varIndexThis);
|
||||
mv.visitLocalVariable("stmt", ByteCodeHelper.T_PREPARED_STATEMENT.getDescriptor(), null, labelStartStmt, labelEndStmt, varIndexStmt);
|
||||
mv.visitLocalVariable("entity", ByteCodeHelper.getDescriptorForClassName(entity.getTypeName()), null, labelStartEntity, labelEndEntity, varIndexEntity);
|
||||
|
||||
mv.visitMaxs(3, 3);
|
||||
|
||||
mv.visitEnd();
|
||||
}
|
||||
|
||||
private void defineReadBackGeneratedValuesUpdatePreparedStatement() {
|
||||
if (entity.getFields().stream().anyMatch(e -> e.getGenerated() == Generated.Type.COMPUTED)) {
|
||||
return;
|
||||
}
|
||||
|
||||
//stub method if no generated values
|
||||
var mv = visitor.visitMethod(Opcodes.ACC_PROTECTED | SYNTHETIC_FLAG, "readBackGeneratedValuesUpdate", Type.getMethodDescriptor(ByteCodeHelper.T_VOID, ByteCodeHelper.T_PREPARED_STATEMENT, Type.getType(ByteCodeHelper.getDescriptorForClassName(entity.getTypeName()))), null, new String[]{ByteCodeHelper.T_SQLEXCEPTION.getInternalName()});
|
||||
mv.visitAnnotation(ByteCodeHelper.T_OVERRIDE.getDescriptor(), true).visitEnd();
|
||||
|
||||
var labelStart = new Label();
|
||||
var labelEnd = new Label();
|
||||
|
||||
mv.visitLabel(labelStart);
|
||||
|
||||
mv.visitCode();
|
||||
mv.visitInsn(Opcodes.RETURN);
|
||||
|
||||
mv.visitLabel(labelEnd);
|
||||
|
||||
// debug symbols
|
||||
mv.visitLocalVariable("this", ByteCodeHelper.getDescriptorForClassName(className), null, labelStart, labelEnd, 0);
|
||||
mv.visitLocalVariable("stmt", ByteCodeHelper.T_PREPARED_STATEMENT.getDescriptor(), null, labelStart, labelEnd, 1);
|
||||
mv.visitLocalVariable("entity", ByteCodeHelper.getDescriptorForClassName(entity.getTypeName()), null, labelStart, labelEnd, 2);
|
||||
|
||||
mv.visitMaxs(0, 3);
|
||||
mv.visitEnd();
|
||||
}
|
||||
|
||||
private void defineReadBackGeneratedValuesUpdateResultSet() {
|
||||
var fieldsToRead = entity.getFields().stream().filter(e -> e.getGenerated() == Generated.Type.COMPUTED).toList();
|
||||
if (fieldsToRead.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
var mv = visitor.visitMethod(Opcodes.ACC_PROTECTED | SYNTHETIC_FLAG, "readBackGeneratedValuesUpdate", Type.getMethodDescriptor(ByteCodeHelper.T_VOID, ByteCodeHelper.T_RESULT_SET, Type.getType(ByteCodeHelper.getDescriptorForClassName(entity.getTypeName()))), null, new String[]{ByteCodeHelper.T_SQLEXCEPTION.getInternalName()});
|
||||
mv.visitAnnotation(ByteCodeHelper.T_OVERRIDE.getDescriptor(), true).visitEnd();
|
||||
|
||||
visitReadFieldsIntoEntityFunctionBody(mv, fieldsToRead, true);
|
||||
}
|
||||
//endregion
|
||||
|
||||
//region delete
|
||||
private void defineQueryDeleteFunction() {
|
||||
var mv = visitor.visitMethod(Opcodes.ACC_PROTECTED | SYNTHETIC_FLAG, "queryDelete", Type.getMethodDescriptor(ByteCodeHelper.T_PREPARED_STATEMENT, ByteCodeHelper.T_CONNECTION), null, new String[]{ByteCodeHelper.T_SQLEXCEPTION.getInternalName()});
|
||||
mv.visitAnnotation(ByteCodeHelper.T_OVERRIDE.getDescriptor(), true).visitEnd();
|
||||
|
||||
var varIndexThis = 0;
|
||||
var varIndexConnection = 1;
|
||||
|
||||
var columnsAndValueBindersWhere = prepareColumnsAndValueBindersWhere();
|
||||
|
||||
var whereString = columnsAndValueBindersWhere.getColumns().stream().map(e -> "`" + e + "` = ?").collect(Collectors.joining(" AND "));
|
||||
|
||||
var query = "DELETE FROM `" + entity.getName() + "` WHERE " + whereString;
|
||||
|
||||
// variable scopes
|
||||
var labelStartThis = new Label();
|
||||
var labelEndThis = new Label();
|
||||
var labelStartConnection = new Label();
|
||||
var labelEndConnection = new Label();
|
||||
|
||||
// begin
|
||||
mv.visitCode();
|
||||
|
||||
// variable scopes
|
||||
mv.visitLabel(labelStartThis);
|
||||
mv.visitLabel(labelStartConnection);
|
||||
|
||||
//return connection.prepareStatement("query", Statement.RETURN_GENERATED_KEYS);
|
||||
mv.visitVarInsn(Opcodes.ALOAD, 1);
|
||||
mv.visitLdcInsn(query);
|
||||
if (entity.getFields().stream().anyMatch(e -> e.getGenerated() != Generated.Type.NONE)) {
|
||||
ByteCodeHelper.visitIntInsn(mv, Statement.RETURN_GENERATED_KEYS);
|
||||
} else {
|
||||
mv.visitInsn(Opcodes.ICONST_0); //no flags
|
||||
}
|
||||
mv.visitMethodInsn(Opcodes.INVOKEINTERFACE, ByteCodeHelper.T_CONNECTION.getInternalName(), "prepareStatement", Type.getMethodDescriptor(ByteCodeHelper.T_PREPARED_STATEMENT, ByteCodeHelper.T_STRING, ByteCodeHelper.T_INT), true);
|
||||
mv.visitInsn(Opcodes.ARETURN);
|
||||
|
||||
// variable scopes
|
||||
mv.visitLabel(labelEndConnection);
|
||||
mv.visitLabel(labelEndThis);
|
||||
|
||||
// debug symbols
|
||||
mv.visitLocalVariable("this", ByteCodeHelper.getDescriptorForClassName(className), null, labelStartConnection, labelEndConnection, varIndexThis);
|
||||
mv.visitLocalVariable("connection", ByteCodeHelper.T_CONNECTION.getDescriptor(), null, labelStartThis, labelEndThis, varIndexConnection);
|
||||
|
||||
mv.visitMaxs(3, 2);
|
||||
|
||||
mv.visitEnd();
|
||||
}
|
||||
|
||||
private void defineBindParamsDeleteFunction() {
|
||||
var mv = visitor.visitMethod(Opcodes.ACC_PROTECTED | SYNTHETIC_FLAG, "bindParamsDelete", Type.getMethodDescriptor(ByteCodeHelper.T_VOID, ByteCodeHelper.T_PREPARED_STATEMENT, Type.getType(ByteCodeHelper.getDescriptorForClassName(entity.getTypeName()))), null, new String[]{ByteCodeHelper.T_SQLEXCEPTION.getInternalName()});
|
||||
mv.visitAnnotation(ByteCodeHelper.T_OVERRIDE.getDescriptor(), true).visitEnd();
|
||||
|
||||
var varIndexThis = 0;
|
||||
var varIndexStmt = 1;
|
||||
var varIndexEntity = 2;
|
||||
|
||||
//create lists for columns and value binders
|
||||
var columnsAndValueBindersWhere = prepareColumnsAndValueBindersWhere();
|
||||
|
||||
// variable scopes
|
||||
var labelStartThis = new Label();
|
||||
var labelEndThis = new Label();
|
||||
var labelStartStmt = new Label();
|
||||
var labelEndStmt = new Label();
|
||||
var labelStartEntity = new Label();
|
||||
var labelEndEntity = new Label();
|
||||
|
||||
// begin
|
||||
mv.visitCode();
|
||||
|
||||
// variable scopes
|
||||
mv.visitLabel(labelStartThis);
|
||||
mv.visitLabel(labelStartStmt);
|
||||
mv.visitLabel(labelStartEntity);
|
||||
|
||||
//stmt.set* bind calls
|
||||
var i = 1;
|
||||
for (var valueBinder : columnsAndValueBindersWhere.getValueBinders()) {
|
||||
valueBinder.bind(mv, varIndexEntity, varIndexStmt, i++);
|
||||
}
|
||||
|
||||
//return
|
||||
mv.visitInsn(Opcodes.RETURN);
|
||||
|
||||
// variable scopes
|
||||
mv.visitLabel(labelEndThis);
|
||||
mv.visitLabel(labelEndStmt);
|
||||
mv.visitLabel(labelEndEntity);
|
||||
|
||||
// debug symbols
|
||||
mv.visitLocalVariable("this", ByteCodeHelper.getDescriptorForClassName(className), null, labelStartThis, labelEndThis, varIndexThis);
|
||||
mv.visitLocalVariable("stmt", ByteCodeHelper.T_PREPARED_STATEMENT.getDescriptor(), null, labelStartStmt, labelEndStmt, varIndexStmt);
|
||||
mv.visitLocalVariable("entity", ByteCodeHelper.getDescriptorForClassName(entity.getTypeName()), null, labelStartEntity, labelEndEntity, varIndexEntity);
|
||||
|
||||
mv.visitMaxs(3, 3);
|
||||
|
||||
mv.visitEnd();
|
||||
}
|
||||
//endregion
|
||||
|
||||
//region read
|
||||
private void defineReadFunction() {
|
||||
var fields = entity.getFields().stream().filter(e -> e.isDatabaseField()).toList();
|
||||
var bestConstructor = classDescription.getDeclaredConstructors().stream().filter(e -> e.getSuperConstructorParameterDescriptions().size() == 0).map(ctor -> {
|
||||
var matchingParamCount = ctor.getConstructorParameterDescriptions().stream()
|
||||
.filter(param -> fields.stream().anyMatch(field -> field.getField().getDeclaringClass().getName().equals(param.getDeclaringClassName())
|
||||
&& field.getField().getName().equals(param.getFieldName())))
|
||||
.count();
|
||||
return new AbstractMap.SimpleEntry<>(ctor, matchingParamCount);
|
||||
}).sorted(Comparator.comparingLong(e -> -e.getValue())).findFirst();
|
||||
if (!bestConstructor.isPresent()) {
|
||||
throw new IllegalStateException("No suitable constructor found!");
|
||||
}
|
||||
|
||||
var orderedFields = bestConstructor.orElseThrow().getKey().getConstructorParameterDescriptions().stream()
|
||||
.sorted(Comparator.comparingInt(e -> e.getParameterIndex()))
|
||||
.map(param -> new AbstractMap.SimpleEntry<>(param, fields.stream()
|
||||
.filter(field -> field.getField().getDeclaringClass().getName().equals(param.getDeclaringClassName())
|
||||
&& field.getField().getName().equals(param.getFieldName()))
|
||||
.findFirst().orElse(null)))
|
||||
.toList();
|
||||
|
||||
//begin
|
||||
var mv = visitor.visitMethod(Opcodes.ACC_PROTECTED | SYNTHETIC_FLAG, "read", Type.getMethodDescriptor(Type.getType(ByteCodeHelper.getDescriptorForClassName(entity.getTypeName())), ByteCodeHelper.T_RESULT_SET), null, new String[]{ByteCodeHelper.T_SQLEXCEPTION.getInternalName()});
|
||||
mv.visitAnnotation(ByteCodeHelper.T_OVERRIDE.getDescriptor(), true).visitEnd();
|
||||
|
||||
var varIndexThis = 0;
|
||||
var varIndexRes = 1;
|
||||
|
||||
// variable scopes
|
||||
var labelStartThis = new Label();
|
||||
var labelEndThis = new Label();
|
||||
var labelStartRes = new Label();
|
||||
var labelEndRes = new Label();
|
||||
|
||||
// begin
|
||||
mv.visitCode();
|
||||
|
||||
// variable scopes
|
||||
mv.visitLabel(labelStartThis);
|
||||
mv.visitLabel(labelStartRes);
|
||||
|
||||
// push params
|
||||
mv.visitLdcInsn(Type.getObjectType(entity.getTypeName().replace(".", "/")));
|
||||
for (var paramAndField : orderedFields) {
|
||||
if (paramAndField.getValue() == null) {
|
||||
var fieldType = entity.getFields().stream()
|
||||
.filter(field -> field.getField().getDeclaringClass().getName().equals(paramAndField.getKey().getDeclaringClassName())
|
||||
&& field.getField().getName().equals(paramAndField.getKey().getFieldName()))
|
||||
.findFirst().orElseThrow()
|
||||
.getField().getType();
|
||||
// PUSH null, 0, or false
|
||||
ByteCodeHelper.visitPushDefault(mv, fieldType);
|
||||
} else {
|
||||
// PUSH res.get*(1);
|
||||
ByteCodeHelper.visitResultSetGetAny(mv, paramAndField.getValue(), varIndexRes);
|
||||
}
|
||||
}
|
||||
|
||||
//new Entity(...)
|
||||
mv.visitMethodInsn(Opcodes.INVOKESPECIAL, entity.getTypeName().replace(".", "/"), "<init>",
|
||||
Type.getMethodDescriptor(ByteCodeHelper.T_VOID, orderedFields.stream()
|
||||
.map(paramAndField -> Type.getType(ByteCodeHelper.getDescriptorForClassName(paramAndField.getKey().getParameterClassName())))
|
||||
.toArray(Type[]::new)), false);
|
||||
|
||||
//return
|
||||
mv.visitInsn(Opcodes.ARETURN);
|
||||
|
||||
// variable scopes
|
||||
mv.visitLabel(labelEndThis);
|
||||
mv.visitLabel(labelEndRes);
|
||||
|
||||
// debug symbols
|
||||
mv.visitLocalVariable("this", ByteCodeHelper.getDescriptorForClassName(className), null, labelStartThis, labelEndThis, varIndexThis);
|
||||
mv.visitLocalVariable("res", ByteCodeHelper.T_RESULT_SET.getDescriptor(), null, labelStartRes, labelEndRes, varIndexRes);
|
||||
|
||||
mv.visitMaxs(orderedFields.size() + 1, 2);
|
||||
|
||||
mv.visitEnd();
|
||||
}
|
||||
//endregion
|
||||
|
||||
//region util
|
||||
private ColumnsAndValueBinders prepareColumnsAndValueBindersSet() {
|
||||
var columns = new ArrayList<String>();
|
||||
var valueBinders = new ArrayList<ValueBinder>();
|
||||
|
||||
for (DbField<?> field : entity.getFields()) {
|
||||
if (!field.isDatabaseField()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
Field f = field.getField();
|
||||
if (f == null) {
|
||||
if (field.isDatabaseField()) {//field defined but not present in entity class -> currently nothing to insert -> null
|
||||
columns.add(field.getName());
|
||||
valueBinders.add((mv, varIndexEntity, varIndexStmt, index) -> {
|
||||
ByteCodeHelper.visitIntInsn(mv, index);
|
||||
|
||||
//stmt.setNull(i, Types.INTEGER);
|
||||
ByteCodeHelper.visitPreparedStatementSetNull(mv, field, varIndexStmt, index);
|
||||
});
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
columns.add(field.getName());
|
||||
valueBinders.add((mv, varIndexEntity, varIndexStmt, index) -> {
|
||||
if (field.isNotNull()) {
|
||||
//stmt.set*(i, value);
|
||||
ByteCodeHelper.visitPreparedStatementSetAny(mv, field, varIndexStmt, index, (mv2) -> ByteCodeHelper.visitGetField(mv2, field.getField(), varIndexEntity, className, () -> fieldsToReflect.add(field.getField()), classDescription));
|
||||
} else {
|
||||
// getFieldValue(entity);
|
||||
ByteCodeHelper.visitGetField(mv, f, varIndexEntity, className, () -> fieldsToReflect.add(field.getField()), classDescription);
|
||||
|
||||
// if (value == null)
|
||||
// stmt.setNull(i, Types.*);
|
||||
// } else {
|
||||
// stmt.set*(i, value);
|
||||
// }
|
||||
var labelAfter = new Label();
|
||||
var labelElse = new Label();
|
||||
mv.visitJumpInsn(Opcodes.IFNULL, labelElse);
|
||||
ByteCodeHelper.visitPreparedStatementSetAny(mv, field, varIndexStmt, index, (mv2) -> ByteCodeHelper.visitGetField(mv2, field.getField(), varIndexEntity, className, () -> fieldsToReflect.add(field.getField()), classDescription));
|
||||
mv.visitJumpInsn(Opcodes.GOTO, labelAfter);
|
||||
mv.visitLabel(labelElse);
|
||||
ByteCodeHelper.visitPreparedStatementSetNull(mv, field, varIndexStmt, index);
|
||||
mv.visitLabel(labelAfter);
|
||||
// mv.visitFrame(Opcodes.F_SAME, 0, new Object[0], 0, new Object[0]);
|
||||
mv.visitFrame(Opcodes.F_FULL, 3, new Object[]{ByteCodeHelper.T_MYSQL_ENTITY_DB_INTERFACE.getInternalName(), ByteCodeHelper.T_PREPARED_STATEMENT.getInternalName(), entity.getTypeName().replace(".", "/")}, 0, new Object[0]);
|
||||
}
|
||||
});
|
||||
}
|
||||
return new ColumnsAndValueBinders(columns, valueBinders);
|
||||
}
|
||||
|
||||
private ColumnsAndValueBinders prepareColumnsAndValueBindersWhere() {
|
||||
var columns = new ArrayList<String>();
|
||||
var valueBinders = new ArrayList<ValueBinder>();
|
||||
|
||||
for (DbField<?> field : entity.getPrimaryKey().getFields()) {
|
||||
if (!field.isDatabaseField()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
Field f = field.getField();
|
||||
if (f == null) {
|
||||
if (field.isDatabaseField()) {//field defined but not present in entity class -> currently nothing to insert -> null
|
||||
columns.add(field.getName());
|
||||
valueBinders.add((mv, varIndexEntity, varIndexStmt, index) -> {
|
||||
ByteCodeHelper.visitIntInsn(mv, index);
|
||||
|
||||
//stmt.setNull(i, Types.INTEGER);
|
||||
ByteCodeHelper.visitPreparedStatementSetNull(mv, field, varIndexStmt, index);
|
||||
});
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
columns.add(field.getName());
|
||||
valueBinders.add((mv, varIndexEntity, varIndexStmt, index) -> {
|
||||
if (field.isNotNull()) {
|
||||
//stmt.set*(i, value);
|
||||
ByteCodeHelper.visitPreparedStatementSetAny(mv, field, varIndexStmt, index, (mv2) -> ByteCodeHelper.visitGetField(mv2, field.getField(), varIndexEntity, className, () -> fieldsToReflect.add(field.getField()), classDescription));
|
||||
} else {
|
||||
// getFieldValue(entity);
|
||||
ByteCodeHelper.visitGetField(mv, f, varIndexEntity, className, () -> fieldsToReflect.add(field.getField()), classDescription);
|
||||
|
||||
// if (value == null)
|
||||
// stmt.setNull(i, Types.*);
|
||||
// } else {
|
||||
// stmt.set*(i, value);
|
||||
// }
|
||||
var labelAfter = new Label();
|
||||
var labelElse = new Label();
|
||||
mv.visitJumpInsn(Opcodes.IFNULL, labelElse);
|
||||
ByteCodeHelper.visitPreparedStatementSetAny(mv, field, varIndexStmt, index, (mv2) -> ByteCodeHelper.visitGetField(mv2, field.getField(), varIndexEntity, className, () -> fieldsToReflect.add(field.getField()), classDescription));
|
||||
mv.visitJumpInsn(Opcodes.GOTO, labelAfter);
|
||||
mv.visitLabel(labelElse);
|
||||
ByteCodeHelper.visitPreparedStatementSetNull(mv, field, varIndexStmt, index);
|
||||
mv.visitLabel(labelAfter);
|
||||
// mv.visitFrame(Opcodes.F_SAME, 0, new Object[0], 0, new Object[0]);
|
||||
mv.visitFrame(Opcodes.F_FULL, 3, new Object[]{ByteCodeHelper.T_MYSQL_ENTITY_DB_INTERFACE.getInternalName(), ByteCodeHelper.T_PREPARED_STATEMENT.getInternalName(), entity.getTypeName().replace(".", "/")}, 0, new Object[0]);
|
||||
}
|
||||
});
|
||||
}
|
||||
return new ColumnsAndValueBinders(columns, valueBinders);
|
||||
}
|
||||
|
||||
private void visitReadFieldsIntoEntityFunctionBody(MethodVisitor mv, List<DbField<?>> fields, boolean moveNext) {
|
||||
var varIndexThis = 0;
|
||||
var varIndexRes = 1;
|
||||
var varIndexEntity = 2;
|
||||
var varIndexField = 3;
|
||||
|
||||
// variable scopes
|
||||
var labelStartThis = new Label();
|
||||
@@ -312,8 +728,6 @@ public class MysqlEntityDbInterfaceGenerator<T extends SerializableObject> {
|
||||
var labelEndRes = new Label();
|
||||
var labelStartEntity = new Label();
|
||||
var labelEndEntity = new Label();
|
||||
var labelStartField = new Label();
|
||||
var labelEndField = new Label();
|
||||
|
||||
// begin
|
||||
mv.visitCode();
|
||||
@@ -322,30 +736,27 @@ public class MysqlEntityDbInterfaceGenerator<T extends SerializableObject> {
|
||||
mv.visitLabel(labelStartThis);
|
||||
mv.visitLabel(labelStartRes);
|
||||
mv.visitLabel(labelStartEntity);
|
||||
mv.visitLabel(labelStartField);
|
||||
|
||||
// if (!res.next()) {
|
||||
// throw new SQLException("Missing generated keys for entity but are required");
|
||||
// }
|
||||
var labelResHasNext = new Label();
|
||||
mv.visitVarInsn(Opcodes.ALOAD, varIndexRes);
|
||||
mv.visitMethodInsn(Opcodes.INVOKEINTERFACE, ByteCodeHelper.T_RESULT_SET.getInternalName(), "next", Type.getMethodDescriptor(ByteCodeHelper.T_BOOL), true);
|
||||
mv.visitJumpInsn(Opcodes.IFNE, labelResHasNext);
|
||||
ByteCodeHelper.visitThrowSQLException(mv, "Missing generated keys for entity but are required");
|
||||
mv.visitLabel(labelResHasNext);
|
||||
|
||||
for (var field : entity.getFields()) {
|
||||
if (field.getGenerated() == Generated.Type.NONE) {
|
||||
continue;
|
||||
}
|
||||
if (moveNext) {
|
||||
// if (!res.next()) {
|
||||
// throw new SQLException("Missing generated keys for entity but are required");
|
||||
// }
|
||||
var labelResHasNext = new Label();
|
||||
mv.visitVarInsn(Opcodes.ALOAD, varIndexRes);
|
||||
mv.visitMethodInsn(Opcodes.INVOKEINTERFACE, ByteCodeHelper.T_RESULT_SET.getInternalName(), "next", Type.getMethodDescriptor(ByteCodeHelper.T_BOOL), true);
|
||||
mv.visitJumpInsn(Opcodes.IFNE, labelResHasNext);
|
||||
ByteCodeHelper.visitThrowSQLException(mv, "Missing generated keys for entity but are required");
|
||||
mv.visitLabel(labelResHasNext);
|
||||
}
|
||||
|
||||
for (var field : fields) {
|
||||
// var value = res.get*(1);
|
||||
Consumer<MethodVisitor> resGetValue = (mv2) -> {
|
||||
ByteCodeHelper.visitResultSetGetAny(mv2, field, varIndexRes);
|
||||
};
|
||||
|
||||
// *SET*(entity, field, value);
|
||||
ByteCodeHelper.visitSetField(mv, field.getField(), resGetValue, varIndexEntity, varIndexField, classDescription);
|
||||
ByteCodeHelper.visitSetField(mv, field.getField(), resGetValue, varIndexEntity, className, () -> fieldsToReflect.add(field.getField()), classDescription);
|
||||
}
|
||||
|
||||
//return
|
||||
@@ -355,26 +766,26 @@ public class MysqlEntityDbInterfaceGenerator<T extends SerializableObject> {
|
||||
mv.visitLabel(labelEndThis);
|
||||
mv.visitLabel(labelEndRes);
|
||||
mv.visitLabel(labelEndEntity);
|
||||
mv.visitLabel(labelEndField);
|
||||
|
||||
// debug symbols
|
||||
mv.visitLocalVariable("this", ByteCodeHelper.getDescriptorForClassName(className), null, labelStartThis, labelEndThis, varIndexThis);
|
||||
mv.visitLocalVariable("res", ByteCodeHelper.T_RESULT_SET.getDescriptor(), null, labelStartRes, labelEndRes, varIndexRes);
|
||||
mv.visitLocalVariable("entity", ByteCodeHelper.getDescriptorForClassName(entity.getTypeName()), null, labelStartEntity, labelEndEntity, varIndexEntity);
|
||||
mv.visitLocalVariable("field", ByteCodeHelper.T_FIELD.getDescriptor(), null, labelStartField, labelEndField, varIndexField);
|
||||
|
||||
mv.visitMaxs(3, 4);
|
||||
mv.visitMaxs(3, 3);
|
||||
|
||||
mv.visitEnd();
|
||||
}
|
||||
|
||||
private void defineUpdateFunction() {
|
||||
private interface ValueBinder {
|
||||
void bind(MethodVisitor mv, int varIndexEntity, int varIndexStmt, int index);
|
||||
}
|
||||
|
||||
private void defineDeleteFunction() {
|
||||
@AllArgsConstructor
|
||||
@Getter
|
||||
private static class ColumnsAndValueBinders {
|
||||
private final List<String> columns;
|
||||
private final List<ValueBinder> valueBinders;
|
||||
}
|
||||
|
||||
private void defineReadFunction() {
|
||||
}
|
||||
|
||||
//endregion
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user