23
23
import ai .timefold .jpyinterpreter .implementors .JavaEqualsImplementor ;
24
24
import ai .timefold .jpyinterpreter .implementors .JavaHashCodeImplementor ;
25
25
import ai .timefold .jpyinterpreter .implementors .JavaInterfaceImplementor ;
26
+ import ai .timefold .jpyinterpreter .implementors .PythonConstantsImplementor ;
26
27
import ai .timefold .jpyinterpreter .opcodes .AbstractOpcode ;
27
28
import ai .timefold .jpyinterpreter .opcodes .Opcode ;
28
29
import ai .timefold .jpyinterpreter .opcodes .SelfOpcodeWithoutSource ;
@@ -205,14 +206,14 @@ public static PythonLikeType translatePythonClass(PythonCompiledClass pythonComp
205
206
attributeNameToTypeMap .put (attributeName , type );
206
207
FieldVisitor fieldVisitor ;
207
208
String javaFieldTypeDescriptor ;
209
+ String signature = null ;
208
210
boolean isJavaType ;
209
211
if (type .getJavaTypeInternalName ().equals (Type .getInternalName (JavaObjectWrapper .class ))) {
210
212
javaFieldTypeDescriptor = Type .getDescriptor (type .getJavaObjectWrapperType ());
211
213
fieldVisitor = classWriter .visitField (Modifier .PUBLIC , getJavaFieldName (attributeName ), javaFieldTypeDescriptor ,
212
214
null , null );
213
215
isJavaType = true ;
214
216
} else {
215
- String signature = null ;
216
217
if (typeHint .genericArgs () != null ) {
217
218
var signatureWriter = new SignatureWriter ();
218
219
visitSignature (typeHint , signatureWriter );
@@ -223,10 +224,12 @@ public static PythonLikeType translatePythonClass(PythonCompiledClass pythonComp
223
224
signature , null );
224
225
isJavaType = false ;
225
226
}
226
- for (var annotation : typeHint .annotationList ()) {
227
- annotation .addAnnotationTo (fieldVisitor );
228
- }
229
227
fieldVisitor .visitEnd ();
228
+ createJavaGetterSetter (classWriter , preparedClassInfo ,
229
+ attributeName ,
230
+ Type .getType (javaFieldTypeDescriptor ),
231
+ signature ,
232
+ typeHint );
230
233
FieldDescriptor fieldDescriptor =
231
234
new FieldDescriptor (attributeName , getJavaFieldName (attributeName ), internalClassName ,
232
235
javaFieldTypeDescriptor , type , true , isJavaType );
@@ -761,6 +764,85 @@ private static PythonLikeFunction createConstructor(String classInternalName,
761
764
}
762
765
}
763
766
767
+ private static void createJavaGetterSetter (ClassWriter classWriter ,
768
+ PreparedClassInfo preparedClassInfo ,
769
+ String attributeName , Type attributeType ,
770
+ String signature ,
771
+ TypeHint typeHint ) {
772
+ createJavaGetter (classWriter , preparedClassInfo , attributeName , attributeType , signature , typeHint );
773
+ createJavaSetter (classWriter , preparedClassInfo , attributeName , attributeType , signature , typeHint );
774
+ }
775
+
776
+ private static void createJavaGetter (ClassWriter classWriter , PreparedClassInfo preparedClassInfo , String attributeName ,
777
+ Type attributeType , String signature , TypeHint typeHint ) {
778
+ var getterName = "get" + attributeName .substring (0 , 1 ).toUpperCase () + attributeName .substring (1 );
779
+ if (signature != null ) {
780
+ signature = "()" + signature ;
781
+ }
782
+ var getterVisitor = classWriter .visitMethod (Modifier .PUBLIC , getterName , Type .getMethodDescriptor (attributeType ),
783
+ signature , null );
784
+ var maxStack = 1 ;
785
+
786
+ for (var annotation : typeHint .annotationList ()) {
787
+ annotation .addAnnotationTo (getterVisitor );
788
+ }
789
+
790
+ getterVisitor .visitCode ();
791
+ getterVisitor .visitVarInsn (Opcodes .ALOAD , 0 );
792
+ getterVisitor .visitFieldInsn (Opcodes .GETFIELD , preparedClassInfo .classInternalName ,
793
+ attributeName , attributeType .getDescriptor ());
794
+ if (typeHint .type ().isInstance (PythonNone .INSTANCE )) {
795
+ maxStack = 3 ;
796
+ getterVisitor .visitInsn (Opcodes .DUP );
797
+ PythonConstantsImplementor .loadNone (getterVisitor );
798
+ Label returnLabel = new Label ();
799
+ getterVisitor .visitJumpInsn (Opcodes .IF_ACMPNE , returnLabel );
800
+ // field is None, so we want Java to see it as null
801
+ getterVisitor .visitInsn (Opcodes .POP );
802
+ getterVisitor .visitInsn (Opcodes .ACONST_NULL );
803
+ getterVisitor .visitLabel (returnLabel );
804
+ // If branch is taken, stack is field
805
+ // If branch is not taken, stack is null
806
+ }
807
+ getterVisitor .visitInsn (Opcodes .ARETURN );
808
+ getterVisitor .visitMaxs (maxStack , 0 );
809
+ getterVisitor .visitEnd ();
810
+ }
811
+
812
+ private static void createJavaSetter (ClassWriter classWriter , PreparedClassInfo preparedClassInfo , String attributeName ,
813
+ Type attributeType , String signature , TypeHint typeHint ) {
814
+ var setterName = "set" + attributeName .substring (0 , 1 ).toUpperCase () + attributeName .substring (1 );
815
+ if (signature != null ) {
816
+ signature = "(" + signature + ")V" ;
817
+ }
818
+ var setterVisitor = classWriter .visitMethod (Modifier .PUBLIC , setterName , Type .getMethodDescriptor (Type .VOID_TYPE ,
819
+ attributeType ),
820
+ signature , null );
821
+ var maxStack = 2 ;
822
+ setterVisitor .visitCode ();
823
+ setterVisitor .visitVarInsn (Opcodes .ALOAD , 0 );
824
+ setterVisitor .visitVarInsn (Opcodes .ALOAD , 1 );
825
+ if (typeHint .type ().isInstance (PythonNone .INSTANCE )) {
826
+ maxStack = 4 ;
827
+ // We want to replace null with None
828
+ setterVisitor .visitInsn (Opcodes .DUP );
829
+ setterVisitor .visitInsn (Opcodes .ACONST_NULL );
830
+ Label setFieldLabel = new Label ();
831
+ setterVisitor .visitJumpInsn (Opcodes .IF_ACMPNE , setFieldLabel );
832
+ // set value is null, so we want Python to see it as None
833
+ setterVisitor .visitInsn (Opcodes .POP );
834
+ PythonConstantsImplementor .loadNone (setterVisitor );
835
+ setterVisitor .visitLabel (setFieldLabel );
836
+ // If branch is taken, stack is (non-null instance)
837
+ // If branch is not taken, stack is None
838
+ }
839
+ setterVisitor .visitFieldInsn (Opcodes .PUTFIELD , preparedClassInfo .classInternalName ,
840
+ attributeName , attributeType .getDescriptor ());
841
+ setterVisitor .visitInsn (Opcodes .RETURN );
842
+ setterVisitor .visitMaxs (maxStack , 0 );
843
+ setterVisitor .visitEnd ();
844
+ }
845
+
764
846
private static void addAnnotationsToMethod (PythonCompiledFunction function , MethodVisitor methodVisitor ) {
765
847
var returnTypeHint = function .typeAnnotations .get ("return" );
766
848
if (returnTypeHint != null ) {
@@ -956,15 +1038,9 @@ public static void createGetAttribute(ClassWriter classWriter, String classInter
956
1038
methodVisitor .visitVarInsn (Opcodes .ALOAD , 0 );
957
1039
var type = fieldToType .get (field );
958
1040
if (type .getJavaTypeInternalName ().equals (Type .getInternalName (JavaObjectWrapper .class ))) {
959
- Class <?> fieldType = type .getJavaObjectWrapperType ();
960
1041
methodVisitor .visitFieldInsn (Opcodes .GETFIELD , classInternalName , getJavaFieldName (field ),
961
- Type .getDescriptor (fieldType ));
962
- methodVisitor .visitTypeInsn (Opcodes .NEW , Type .getInternalName (JavaObjectWrapper .class ));
963
- methodVisitor .visitInsn (Opcodes .DUP_X1 );
964
- methodVisitor .visitInsn (Opcodes .DUP_X1 );
965
- methodVisitor .visitInsn (Opcodes .POP );
966
- methodVisitor .visitMethodInsn (Opcodes .INVOKESPECIAL , Type .getInternalName (JavaObjectWrapper .class ),
967
- "<init>" , Type .getMethodDescriptor (Type .VOID_TYPE , Type .getType (Object .class )), false );
1042
+ Type .getDescriptor (type .getJavaObjectWrapperType ()));
1043
+ getWrappedJavaObject (methodVisitor );
968
1044
} else {
969
1045
methodVisitor .visitFieldInsn (Opcodes .GETFIELD , classInternalName , getJavaFieldName (field ),
970
1046
'L' + type .getJavaTypeInternalName () + ';' );
@@ -984,6 +1060,15 @@ public static void createGetAttribute(ClassWriter classWriter, String classInter
984
1060
methodVisitor .visitEnd ();
985
1061
}
986
1062
1063
+ private static void getWrappedJavaObject (MethodVisitor methodVisitor ) {
1064
+ methodVisitor .visitTypeInsn (Opcodes .NEW , Type .getInternalName (JavaObjectWrapper .class ));
1065
+ methodVisitor .visitInsn (Opcodes .DUP_X1 );
1066
+ methodVisitor .visitInsn (Opcodes .DUP_X1 );
1067
+ methodVisitor .visitInsn (Opcodes .POP );
1068
+ methodVisitor .visitMethodInsn (Opcodes .INVOKESPECIAL , Type .getInternalName (JavaObjectWrapper .class ),
1069
+ "<init>" , Type .getMethodDescriptor (Type .VOID_TYPE , Type .getType (Object .class )), false );
1070
+ }
1071
+
987
1072
public static void createSetAttribute (ClassWriter classWriter , String classInternalName , String superInternalName ,
988
1073
Collection <String > instanceAttributes ,
989
1074
Map <String , PythonLikeType > fieldToType ) {
@@ -1008,10 +1093,7 @@ public static void createSetAttribute(ClassWriter classWriter, String classInter
1008
1093
String typeDescriptor = type .getJavaTypeDescriptor ();
1009
1094
if (type .getJavaTypeInternalName ().equals (Type .getInternalName (JavaObjectWrapper .class ))) {
1010
1095
// Need to unwrap the object
1011
- methodVisitor .visitTypeInsn (Opcodes .CHECKCAST , Type .getInternalName (JavaObjectWrapper .class ));
1012
- methodVisitor .visitMethodInsn (Opcodes .INVOKEVIRTUAL , Type .getInternalName (JavaObjectWrapper .class ),
1013
- "getWrappedObject" , Type .getMethodDescriptor (Type .getType (Object .class )), false );
1014
- methodVisitor .visitTypeInsn (Opcodes .CHECKCAST , Type .getType (type .getJavaObjectWrapperType ()).getInternalName ());
1096
+ getUnwrappedJavaObject (methodVisitor , type );
1015
1097
typeDescriptor = Type .getDescriptor (type .getJavaObjectWrapperType ());
1016
1098
} else {
1017
1099
methodVisitor .visitTypeInsn (Opcodes .CHECKCAST , type .getJavaTypeInternalName ());
@@ -1035,6 +1117,13 @@ public static void createSetAttribute(ClassWriter classWriter, String classInter
1035
1117
methodVisitor .visitEnd ();
1036
1118
}
1037
1119
1120
+ private static void getUnwrappedJavaObject (MethodVisitor methodVisitor , PythonLikeType type ) {
1121
+ methodVisitor .visitTypeInsn (Opcodes .CHECKCAST , Type .getInternalName (JavaObjectWrapper .class ));
1122
+ methodVisitor .visitMethodInsn (Opcodes .INVOKEVIRTUAL , Type .getInternalName (JavaObjectWrapper .class ),
1123
+ "getWrappedObject" , Type .getMethodDescriptor (Type .getType (Object .class )), false );
1124
+ methodVisitor .visitTypeInsn (Opcodes .CHECKCAST , Type .getType (type .getJavaObjectWrapperType ()).getInternalName ());
1125
+ }
1126
+
1038
1127
public static void createDeleteAttribute (ClassWriter classWriter , String classInternalName , String superInternalName ,
1039
1128
Collection <String > instanceAttributes ,
1040
1129
Map <String , PythonLikeType > fieldToType ) {
0 commit comments