1
1
package ai .timefold .jpyinterpreter .implementors ;
2
2
3
3
import java .lang .reflect .Field ;
4
+ import java .math .BigDecimal ;
4
5
import java .math .BigInteger ;
5
6
import java .util .IdentityHashMap ;
6
7
import java .util .Iterator ;
31
32
import ai .timefold .jpyinterpreter .types .collections .PythonLikeTuple ;
32
33
import ai .timefold .jpyinterpreter .types .errors .TypeError ;
33
34
import ai .timefold .jpyinterpreter .types .numeric .PythonBoolean ;
35
+ import ai .timefold .jpyinterpreter .types .numeric .PythonDecimal ;
34
36
import ai .timefold .jpyinterpreter .types .numeric .PythonFloat ;
35
37
import ai .timefold .jpyinterpreter .types .numeric .PythonInteger ;
36
38
import ai .timefold .jpyinterpreter .types .numeric .PythonNumber ;
@@ -65,76 +67,78 @@ public static PythonLikeObject wrapJavaObject(Object object, Map<Object, PythonL
65
67
return existingObject ;
66
68
}
67
69
68
- if (object instanceof OpaqueJavaReference ) {
69
- return (( OpaqueJavaReference ) object ) .proxy ();
70
+ if (object instanceof OpaqueJavaReference opaqueJavaReference ) {
71
+ return opaqueJavaReference .proxy ();
70
72
}
71
73
72
- if (object instanceof PythonLikeObject ) {
74
+ if (object instanceof PythonLikeObject instance ) {
73
75
// Object already a PythonLikeObject; need to do nothing
74
- return ( PythonLikeObject ) object ;
76
+ return instance ;
75
77
}
76
78
77
79
if (object instanceof Byte || object instanceof Short || object instanceof Integer || object instanceof Long ) {
78
80
return PythonInteger .valueOf (((Number ) object ).longValue ());
79
81
}
80
82
81
- if (object instanceof BigInteger ) {
82
- return PythonInteger .valueOf ((BigInteger ) object );
83
+ if (object instanceof BigInteger integer ) {
84
+ return PythonInteger .valueOf (integer );
85
+ }
86
+
87
+ if (object instanceof BigDecimal decimal ) {
88
+ return new PythonDecimal (decimal );
83
89
}
84
90
85
91
if (object instanceof Float || object instanceof Double ) {
86
92
return PythonFloat .valueOf (((Number ) object ).doubleValue ());
87
93
}
88
94
89
- if (object instanceof Boolean ) {
90
- return PythonBoolean .valueOf (( Boolean ) object );
95
+ if (object instanceof Boolean booleanValue ) {
96
+ return PythonBoolean .valueOf (booleanValue );
91
97
}
92
98
93
- if (object instanceof String ) {
94
- return PythonString .valueOf (( String ) object );
99
+ if (object instanceof String string ) {
100
+ return PythonString .valueOf (string );
95
101
}
96
102
97
- if (object instanceof Iterator ) {
98
- return new DelegatePythonIterator <>(( Iterator ) object );
103
+ if (object instanceof Iterator <?> iterator ) {
104
+ return new DelegatePythonIterator <>(iterator );
99
105
}
100
106
101
- if (object instanceof List ) {
102
- PythonLikeList out = new PythonLikeList ();
107
+ if (object instanceof List <?> list ) {
108
+ PythonLikeList <?> out = new PythonLikeList <> ();
103
109
createdObjectMap .put (object , out );
104
- for (Object item : ( List ) object ) {
110
+ for (Object item : list ) {
105
111
out .add (wrapJavaObject (item ));
106
112
}
107
113
return out ;
108
114
}
109
115
110
- if (object instanceof Set ) {
111
- PythonLikeSet out = new PythonLikeSet ();
116
+ if (object instanceof Set <?> set ) {
117
+ PythonLikeSet <?> out = new PythonLikeSet <> ();
112
118
createdObjectMap .put (object , out );
113
- for (Object item : ( Set ) object ) {
119
+ for (Object item : set ) {
114
120
out .add (wrapJavaObject (item ));
115
121
}
116
122
return out ;
117
123
}
118
124
119
- if (object instanceof Map ) {
120
- PythonLikeDict out = new PythonLikeDict ();
125
+ if (object instanceof Map <?, ?> map ) {
126
+ PythonLikeDict <?, ?> out = new PythonLikeDict <> ();
121
127
createdObjectMap .put (object , out );
122
- Set < Map . Entry <?, ?>> entrySet = (( Map ) object ) .entrySet ();
123
- for (Map . Entry <?, ?> entry : entrySet ) {
128
+ var entrySet = map .entrySet ();
129
+ for (var entry : entrySet ) {
124
130
out .put (wrapJavaObject (entry .getKey ()), wrapJavaObject (entry .getValue ()));
125
131
}
126
132
return out ;
127
133
}
128
134
129
- if (object instanceof Class ) {
130
- Class <?> maybeFunctionClass = (Class <?>) object ;
131
- if (Set .of (maybeFunctionClass .getInterfaces ()).contains (PythonLikeFunction .class )) {
132
- return new PythonCode ((Class <? extends PythonLikeFunction >) maybeFunctionClass );
133
- }
135
+ if (object instanceof Class <?> maybeFunctionClass &&
136
+ Set .of (maybeFunctionClass .getInterfaces ()).contains (PythonLikeFunction .class )) {
137
+ return new PythonCode ((Class <? extends PythonLikeFunction >) maybeFunctionClass );
134
138
}
135
139
136
- if (object instanceof OpaquePythonReference ) {
137
- return new PythonObjectWrapper (( OpaquePythonReference ) object );
140
+ if (object instanceof OpaquePythonReference opaquePythonReference ) {
141
+ return new PythonObjectWrapper (opaquePythonReference );
138
142
}
139
143
140
144
// Default: return a JavaObjectWrapper
@@ -161,6 +165,10 @@ public static PythonLikeType getPythonLikeType(Class<?> javaClass) {
161
165
return BuiltinTypes .INT_TYPE ;
162
166
}
163
167
168
+ if (BigDecimal .class .equals (javaClass ) || PythonDecimal .class .equals (javaClass )) {
169
+ return BuiltinTypes .DECIMAL_TYPE ;
170
+ }
171
+
164
172
if (float .class .equals (javaClass ) || double .class .equals (javaClass ) ||
165
173
Float .class .equals (javaClass ) || Double .class .equals (javaClass ) ||
166
174
PythonFloat .class .equals (javaClass )) {
@@ -254,8 +262,7 @@ public static <T> T convertPythonObjectToJavaType(Class<? extends T> type, Pytho
254
262
return null ;
255
263
}
256
264
257
- if (object instanceof JavaObjectWrapper ) {
258
- JavaObjectWrapper wrappedObject = (JavaObjectWrapper ) object ;
265
+ if (object instanceof JavaObjectWrapper wrappedObject ) {
259
266
Object javaObject = wrappedObject .getWrappedObject ();
260
267
if (!type .isAssignableFrom (javaObject .getClass ())) {
261
268
throw new TypeError ("Cannot convert from (" + getPythonLikeType (javaObject .getClass ()) + ") to ("
@@ -266,14 +273,13 @@ public static <T> T convertPythonObjectToJavaType(Class<? extends T> type, Pytho
266
273
267
274
if (type .equals (byte .class ) || type .equals (short .class ) || type .equals (int .class ) || type .equals (long .class ) ||
268
275
type .equals (float .class ) || type .equals (double .class ) || Number .class .isAssignableFrom (type )) {
269
- if (!(object instanceof PythonNumber )) {
276
+ if (!(object instanceof PythonNumber pythonNumber )) {
270
277
throw new TypeError ("Cannot convert from (" + getPythonLikeType (object .getClass ()) + ") to ("
271
278
+ getPythonLikeType (type ) + ")." );
272
279
}
273
- PythonNumber pythonNumber = (PythonNumber ) object ;
274
280
Number value = pythonNumber .getValue ();
275
281
276
- if (type .equals (BigInteger .class )) {
282
+ if (type .equals (BigInteger .class ) || type . equals ( BigDecimal . class ) ) {
277
283
return (T ) value ;
278
284
}
279
285
@@ -303,11 +309,10 @@ public static <T> T convertPythonObjectToJavaType(Class<? extends T> type, Pytho
303
309
}
304
310
305
311
if (type .equals (boolean .class ) || type .equals (Boolean .class )) {
306
- if (!(object instanceof PythonBoolean )) {
312
+ if (!(object instanceof PythonBoolean pythonBoolean )) {
307
313
throw new TypeError ("Cannot convert from (" + getPythonLikeType (object .getClass ()) + ") to ("
308
314
+ getPythonLikeType (type ) + ")." );
309
315
}
310
- PythonBoolean pythonBoolean = (PythonBoolean ) object ;
311
316
return (T ) (Boolean ) pythonBoolean .getBooleanValue ();
312
317
}
313
318
@@ -335,6 +340,53 @@ public static void loadName(MethodVisitor methodVisitor, String name) {
335
340
false );
336
341
}
337
342
343
+ private record ReturnValueOpDescriptor (
344
+ String wrapperClassName ,
345
+ String methodName ,
346
+ String methodDescriptor ,
347
+ int opcode ,
348
+ boolean noConversionNeeded ) {
349
+ public static ReturnValueOpDescriptor noConversion () {
350
+ return new ReturnValueOpDescriptor ("" , "" , "" ,
351
+ Opcodes .ARETURN , true );
352
+ }
353
+
354
+ public static ReturnValueOpDescriptor forNumeric (String methodName ,
355
+ String methodDescriptor ,
356
+ int opcode ) {
357
+ return new ReturnValueOpDescriptor (Type .getInternalName (Number .class ), methodName , methodDescriptor , opcode ,
358
+ false );
359
+ }
360
+ }
361
+
362
+ private static final Map <Type , ReturnValueOpDescriptor > numericReturnValueOpDescriptorMap = Map .of (
363
+ Type .BYTE_TYPE , ReturnValueOpDescriptor .forNumeric (
364
+ "byteValue" ,
365
+ Type .getMethodDescriptor (Type .BYTE_TYPE ),
366
+ Opcodes .IRETURN ),
367
+ Type .SHORT_TYPE , ReturnValueOpDescriptor .forNumeric (
368
+ "shortValue" ,
369
+ Type .getMethodDescriptor (Type .SHORT_TYPE ),
370
+ Opcodes .IRETURN ),
371
+ Type .INT_TYPE , ReturnValueOpDescriptor .forNumeric (
372
+ "intValue" ,
373
+ Type .getMethodDescriptor (Type .INT_TYPE ),
374
+ Opcodes .IRETURN ),
375
+ Type .LONG_TYPE , ReturnValueOpDescriptor .forNumeric (
376
+ "longValue" ,
377
+ Type .getMethodDescriptor (Type .LONG_TYPE ),
378
+ Opcodes .LRETURN ),
379
+ Type .FLOAT_TYPE , ReturnValueOpDescriptor .forNumeric (
380
+ "floatValue" ,
381
+ Type .getMethodDescriptor (Type .FLOAT_TYPE ),
382
+ Opcodes .FRETURN ),
383
+ Type .DOUBLE_TYPE , ReturnValueOpDescriptor .forNumeric (
384
+ "doubleValue" ,
385
+ Type .getMethodDescriptor (Type .DOUBLE_TYPE ),
386
+ Opcodes .DRETURN ),
387
+ Type .getType (BigInteger .class ), ReturnValueOpDescriptor .noConversion (),
388
+ Type .getType (BigDecimal .class ), ReturnValueOpDescriptor .noConversion ());
389
+
338
390
/**
339
391
* If {@code method} return type is not void, convert TOS into its Java equivalent and return it.
340
392
* If {@code method} return type is void, immediately return.
@@ -344,67 +396,36 @@ public static void loadName(MethodVisitor methodVisitor, String name) {
344
396
public static void returnValue (MethodVisitor methodVisitor , MethodDescriptor method , StackMetadata stackMetadata ) {
345
397
Type returnAsmType = method .getReturnType ();
346
398
399
+ if (Type .CHAR_TYPE .equals (returnAsmType )) {
400
+ throw new IllegalStateException ("Unhandled case for primitive type (char)." );
401
+ }
402
+
347
403
if (Type .VOID_TYPE .equals (returnAsmType )) {
348
404
methodVisitor .visitInsn (Opcodes .RETURN );
349
405
return ;
350
406
}
351
407
352
- if (Type .BYTE_TYPE .equals (returnAsmType ) ||
353
- Type .CHAR_TYPE .equals (returnAsmType ) ||
354
- Type .SHORT_TYPE .equals (returnAsmType ) ||
355
- Type .INT_TYPE .equals (returnAsmType ) ||
356
- Type .LONG_TYPE .equals (returnAsmType ) ||
357
- Type .FLOAT_TYPE .equals (returnAsmType ) ||
358
- Type .DOUBLE_TYPE .equals (returnAsmType )) {
408
+ if (numericReturnValueOpDescriptorMap .containsKey (returnAsmType )) {
409
+ var returnValueOpDescriptor = numericReturnValueOpDescriptorMap .get (returnAsmType );
359
410
methodVisitor .visitTypeInsn (Opcodes .CHECKCAST , Type .getInternalName (PythonNumber .class ));
360
411
methodVisitor .visitMethodInsn (Opcodes .INVOKEINTERFACE ,
361
412
Type .getInternalName (PythonNumber .class ),
362
413
"getValue" ,
363
414
Type .getMethodDescriptor (Type .getType (Number .class )),
364
415
true );
365
- String wrapperClassName = null ;
366
- String methodName = null ;
367
- String methodDescriptor = null ;
368
- int returnOpcode = 0 ;
369
-
370
- if (Type .BYTE_TYPE .equals (returnAsmType )) {
371
- wrapperClassName = Type .getInternalName (Number .class );
372
- methodName = "byteValue" ;
373
- methodDescriptor = Type .getMethodDescriptor (Type .BYTE_TYPE );
374
- returnOpcode = Opcodes .IRETURN ;
375
- } else if (Type .CHAR_TYPE .equals (returnAsmType )) {
376
- throw new IllegalStateException ("Unhandled case for primitive type (char)." );
377
- // returnOpcode = Opcodes.IRETURN;
378
- } else if (Type .SHORT_TYPE .equals (returnAsmType )) {
379
- wrapperClassName = Type .getInternalName (Number .class );
380
- methodName = "shortValue" ;
381
- methodDescriptor = Type .getMethodDescriptor (Type .SHORT_TYPE );
382
- returnOpcode = Opcodes .IRETURN ;
383
- } else if (Type .INT_TYPE .equals (returnAsmType )) {
384
- wrapperClassName = Type .getInternalName (Number .class );
385
- methodName = "intValue" ;
386
- methodDescriptor = Type .getMethodDescriptor (Type .INT_TYPE );
387
- returnOpcode = Opcodes .IRETURN ;
388
- } else if (Type .FLOAT_TYPE .equals (returnAsmType )) {
389
- wrapperClassName = Type .getInternalName (Number .class );
390
- methodName = "floatValue" ;
391
- methodDescriptor = Type .getMethodDescriptor (Type .FLOAT_TYPE );
392
- returnOpcode = Opcodes .FRETURN ;
393
- } else if (Type .LONG_TYPE .equals (returnAsmType )) {
394
- wrapperClassName = Type .getInternalName (Number .class );
395
- methodName = "longValue" ;
396
- methodDescriptor = Type .getMethodDescriptor (Type .LONG_TYPE );
397
- returnOpcode = Opcodes .LRETURN ;
398
- } else if (Type .DOUBLE_TYPE .equals (returnAsmType )) {
399
- wrapperClassName = Type .getInternalName (Number .class );
400
- methodName = "doubleValue" ;
401
- methodDescriptor = Type .getMethodDescriptor (Type .DOUBLE_TYPE );
402
- returnOpcode = Opcodes .DRETURN ;
416
+
417
+ if (returnValueOpDescriptor .noConversionNeeded ) {
418
+ methodVisitor .visitTypeInsn (Opcodes .CHECKCAST , returnAsmType .getInternalName ());
419
+ methodVisitor .visitInsn (Opcodes .ARETURN );
420
+ return ;
403
421
}
422
+
404
423
methodVisitor .visitMethodInsn (Opcodes .INVOKEVIRTUAL ,
405
- wrapperClassName , methodName , methodDescriptor ,
424
+ returnValueOpDescriptor .wrapperClassName ,
425
+ returnValueOpDescriptor .methodName ,
426
+ returnValueOpDescriptor .methodDescriptor ,
406
427
false );
407
- methodVisitor .visitInsn (returnOpcode );
428
+ methodVisitor .visitInsn (returnValueOpDescriptor . opcode );
408
429
return ;
409
430
}
410
431
0 commit comments