42
42
43
43
import com .oracle .graal .python .PythonLanguage ;
44
44
import com .oracle .graal .python .builtins .PythonBuiltinClassType ;
45
+ import com .oracle .graal .python .builtins .modules .ExternalFunctionNodesFactory .CreateArgsTupleNodeGen ;
46
+ import com .oracle .graal .python .builtins .modules .ExternalFunctionNodesFactory .MaterializePrimitiveNodeGen ;
45
47
import com .oracle .graal .python .builtins .modules .ExternalFunctionNodesFactory .ReleaseNativeWrapperNodeGen ;
48
+ import com .oracle .graal .python .builtins .modules .ExternalFunctionNodesFactory .TraverseNativeWrapperNodeGen ;
46
49
import com .oracle .graal .python .builtins .modules .PythonCextBuiltins .CheckFunctionResultNode ;
47
50
import com .oracle .graal .python .builtins .objects .PNone ;
51
+ import com .oracle .graal .python .builtins .objects .PythonAbstractObject ;
48
52
import com .oracle .graal .python .builtins .objects .cext .CApiGuards ;
49
53
import com .oracle .graal .python .builtins .objects .cext .CExtNodes ;
50
54
import com .oracle .graal .python .builtins .objects .cext .CExtNodes .ConvertArgsToSulongNode ;
53
57
import com .oracle .graal .python .builtins .objects .cext .CExtNodesFactory .ToJavaStealingNodeGen ;
54
58
import com .oracle .graal .python .builtins .objects .cext .common .CExtCommonNodes .ConvertPIntToPrimitiveNode ;
55
59
import com .oracle .graal .python .builtins .objects .cext .common .CExtCommonNodesFactory .ConvertPIntToPrimitiveNodeGen ;
60
+ import com .oracle .graal .python .builtins .objects .cext .DynamicObjectNativeWrapper ;
61
+ import com .oracle .graal .python .builtins .objects .cext .PythonNativeWrapper ;
62
+ import com .oracle .graal .python .builtins .objects .cext .PythonNativeWrapperLibrary ;
63
+ import com .oracle .graal .python .builtins .objects .common .SequenceStorageNodes .GetElementType ;
64
+ import com .oracle .graal .python .builtins .objects .common .SequenceStorageNodes .ToArrayNode ;
65
+ import com .oracle .graal .python .builtins .objects .floats .PFloat ;
56
66
import com .oracle .graal .python .builtins .objects .function .PArguments ;
57
67
import com .oracle .graal .python .builtins .objects .function .PKeyword ;
58
68
import com .oracle .graal .python .builtins .objects .function .Signature ;
69
+ import com .oracle .graal .python .builtins .objects .ints .PInt ;
70
+ import com .oracle .graal .python .builtins .objects .str .PString ;
71
+ import com .oracle .graal .python .builtins .objects .tuple .PTuple ;
59
72
import com .oracle .graal .python .nodes .ErrorMessages ;
60
73
import com .oracle .graal .python .nodes .IndirectCallNode ;
74
+ import com .oracle .graal .python .nodes .PGuards ;
61
75
import com .oracle .graal .python .nodes .PNodeWithContext ;
62
76
import com .oracle .graal .python .nodes .PRaiseNode ;
63
77
import com .oracle .graal .python .nodes .PRootNode ;
67
81
import com .oracle .graal .python .nodes .argument .ReadVarKeywordsNode ;
68
82
import com .oracle .graal .python .nodes .call .special .CallVarargsMethodNode ;
69
83
import com .oracle .graal .python .nodes .interop .PForeignToPTypeNode ;
84
+ import com .oracle .graal .python .nodes .truffle .PythonTypes ;
70
85
import com .oracle .graal .python .runtime .ExecutionContext .CalleeContext ;
71
86
import com .oracle .graal .python .runtime .ExecutionContext .ForeignCallContext ;
72
87
import com .oracle .graal .python .runtime .PythonContext ;
73
88
import com .oracle .graal .python .runtime .object .PythonObjectFactory ;
89
+ import com .oracle .graal .python .runtime .sequence .storage .SequenceStorage ;
90
+ import com .oracle .graal .python .runtime .sequence .storage .SequenceStorage .ListStorageType ;
74
91
import com .oracle .graal .python .util .PythonUtils ;
75
92
import com .oracle .truffle .api .Assumption ;
76
93
import com .oracle .truffle .api .CompilerDirectives ;
79
96
import com .oracle .truffle .api .Truffle ;
80
97
import com .oracle .truffle .api .TruffleLanguage .ContextReference ;
81
98
import com .oracle .truffle .api .dsl .Cached ;
99
+ import com .oracle .truffle .api .dsl .Fallback ;
100
+ import com .oracle .truffle .api .dsl .ImportStatic ;
82
101
import com .oracle .truffle .api .dsl .Specialization ;
102
+ import com .oracle .truffle .api .dsl .TypeSystemReference ;
83
103
import com .oracle .truffle .api .frame .VirtualFrame ;
84
104
import com .oracle .truffle .api .interop .ArityException ;
85
105
import com .oracle .truffle .api .interop .InteropLibrary ;
86
106
import com .oracle .truffle .api .interop .UnsupportedMessageException ;
87
107
import com .oracle .truffle .api .interop .UnsupportedTypeException ;
108
+ import com .oracle .truffle .api .library .CachedLibrary ;
88
109
import com .oracle .truffle .api .nodes .ExplodeLoop ;
110
+ import com .oracle .truffle .api .nodes .ExplodeLoop .LoopExplosionKind ;
89
111
import com .oracle .truffle .api .nodes .Node ;
90
112
import com .oracle .truffle .api .nodes .NodeCost ;
91
113
import com .oracle .truffle .api .nodes .UnexpectedResultException ;
@@ -304,27 +326,33 @@ abstract static class ReleaseNativeWrapperNode extends Node {
304
326
static void doCachedLength (Object [] nativeArguments ,
305
327
@ Cached ("nativeArguments.length" ) int cachedLength ,
306
328
@ Cached (value = "createClassProfiles(cachedLength)" , dimensions = 1 ) ValueProfile [] classProfiles ,
329
+ @ CachedLibrary (limit = "nativeArguments.length" ) PythonNativeWrapperLibrary lib ,
330
+ @ Cached ("createTraverseNodes(cachedLength)" ) TraverseNativeWrapperNode [] traverseNativeWrapperNodes ,
307
331
@ Cached SubRefCntNode subRefCntNode ) {
308
332
309
333
for (int i = 0 ; i < cachedLength ; i ++) {
310
- doCheck (classProfiles [i ].profile (nativeArguments [i ]), subRefCntNode );
334
+ doCheck (classProfiles [i ].profile (nativeArguments [i ]), subRefCntNode , lib , traverseNativeWrapperNodes [ i ] );
311
335
}
312
336
}
313
337
314
338
@ Specialization (replaces = "doCachedLength" )
315
339
static void doGeneric (Object [] nativeArguments ,
316
340
@ Cached ("createClassProfile()" ) ValueProfile classProfile ,
341
+ @ CachedLibrary (limit = "3" ) PythonNativeWrapperLibrary lib ,
342
+ @ Cached TraverseNativeWrapperNode traverseNativeWrapperNode ,
317
343
@ Cached SubRefCntNode freeNode ) {
318
344
319
345
for (int i = 0 ; i < nativeArguments .length ; i ++) {
320
- doCheck (classProfile .profile (nativeArguments [i ]), freeNode );
346
+ doCheck (classProfile .profile (nativeArguments [i ]), freeNode , lib , traverseNativeWrapperNode );
321
347
}
322
348
}
323
349
324
- private static void doCheck (Object argument , SubRefCntNode refCntNode ) {
350
+ private static void doCheck (Object argument , SubRefCntNode refCntNode , PythonNativeWrapperLibrary lib , TraverseNativeWrapperNode traverseNativeWrapperNode ) {
325
351
if (CApiGuards .isNativeWrapper (argument )) {
326
352
// in the cached case, refCntNode acts as a branch profile
327
- refCntNode .dec (argument );
353
+ if (refCntNode .dec (argument ) == 0 ) {
354
+ traverseNativeWrapperNode .execute (lib .getDelegate ((PythonNativeWrapper ) argument ));
355
+ }
328
356
}
329
357
}
330
358
@@ -335,6 +363,54 @@ static ValueProfile[] createClassProfiles(int length) {
335
363
}
336
364
return classProfiles ;
337
365
}
366
+
367
+ static TraverseNativeWrapperNode [] createTraverseNodes (int length ) {
368
+ TraverseNativeWrapperNode [] nodes = new TraverseNativeWrapperNode [length ];
369
+ for (int i = 0 ; i < nodes .length ; i ++) {
370
+ nodes [i ] = TraverseNativeWrapperNodeGen .create ();
371
+ }
372
+ return nodes ;
373
+ }
374
+ }
375
+
376
+ /**
377
+ * Traverses the items of a tuple and applies {@link ReleaseNativeWrapperNode} on the items if
378
+ * the tuple is up to be released.
379
+ */
380
+ abstract static class TraverseNativeWrapperNode extends Node {
381
+
382
+ public abstract void execute (Object containerObject );
383
+
384
+ @ Specialization
385
+ static void doTuple (PTuple tuple ,
386
+ @ Cached GetElementType getElementTypeNode ,
387
+ @ Cached ToArrayNode toArrayNode ,
388
+ @ Cached ReleaseNativeWrapperNode releaseNativeWrapperNode ) {
389
+
390
+ SequenceStorage sequenceStorage = tuple .getSequenceStorage ();
391
+ ListStorageType storageType = getElementTypeNode .execute (sequenceStorage );
392
+ if (storageType == ListStorageType .Generic || storageType == ListStorageType .List || storageType == ListStorageType .Tuple ) {
393
+ Object [] values = toArrayNode .execute (sequenceStorage );
394
+ Object [] wrappers = new Object [values .length ];
395
+ for (int i = 0 ; i < values .length ; i ++) {
396
+ Object value = values [i ];
397
+ if (value instanceof PythonAbstractObject ) {
398
+ DynamicObjectNativeWrapper nativeWrapper = ((PythonAbstractObject ) value ).getNativeWrapper ();
399
+ // only traverse if refCnt != 0; this will break the cycle
400
+ if (nativeWrapper != null ) {
401
+ wrappers [i ] = nativeWrapper .getRefCount () != 0 ? nativeWrapper : null ;
402
+ }
403
+ }
404
+ }
405
+ releaseNativeWrapperNode .execute (wrappers );
406
+ }
407
+ }
408
+
409
+ @ Fallback
410
+ static void doOther (@ SuppressWarnings ("unused" ) Object other ) {
411
+ // do nothing
412
+ }
413
+
338
414
}
339
415
340
416
abstract static class MethodDescriptorRoot extends PRootNode {
@@ -443,6 +519,7 @@ public static final class MethKeywordsRoot extends MethodDescriptorRoot {
443
519
@ Child private PythonObjectFactory factory ;
444
520
@ Child private ReadVarArgsNode readVarargsNode ;
445
521
@ Child private ReadVarKeywordsNode readKwargsNode ;
522
+ @ Child private CreateArgsTupleNode createArgsTupleNode ;
446
523
447
524
public MethKeywordsRoot (PythonLanguage language , String name , Object callable ) {
448
525
super (language , name , callable );
@@ -453,14 +530,15 @@ public MethKeywordsRoot(PythonLanguage language, String name, Object callable, C
453
530
this .factory = PythonObjectFactory .create ();
454
531
this .readVarargsNode = ReadVarArgsNode .create (1 , true );
455
532
this .readKwargsNode = ReadVarKeywordsNode .create (new String [0 ]);
533
+ this .createArgsTupleNode = CreateArgsTupleNodeGen .create ();
456
534
}
457
535
458
536
@ Override
459
537
protected Object [] prepareCArguments (VirtualFrame frame ) {
460
538
Object self = readSelfNode .execute (frame );
461
539
Object [] args = readVarargsNode .executeObjectArray (frame );
462
540
PKeyword [] kwargs = readKwargsNode .executePKeyword (frame );
463
- return new Object []{self , factory . createTuple ( args ), factory .createDict (kwargs )};
541
+ return new Object []{self , createArgsTupleNode . execute ( factory , args ), factory .createDict (kwargs )};
464
542
}
465
543
466
544
@ Override
@@ -473,6 +551,7 @@ public static final class MethVarargsRoot extends MethodDescriptorRoot {
473
551
private static final Signature SIGNATURE = new Signature (-1 , false , 1 , false , new String []{"self" }, new String [0 ]);
474
552
@ Child private PythonObjectFactory factory ;
475
553
@ Child private ReadVarArgsNode readVarargsNode ;
554
+ @ Child private CreateArgsTupleNode createArgsTupleNode ;
476
555
477
556
public MethVarargsRoot (PythonLanguage language , String name , Object callable ) {
478
557
super (language , name , callable );
@@ -482,13 +561,14 @@ public MethVarargsRoot(PythonLanguage language, String name, Object callable, Co
482
561
super (language , name , callable , convertArgsToSulongNode );
483
562
this .factory = PythonObjectFactory .create ();
484
563
this .readVarargsNode = ReadVarArgsNode .create (1 , true );
564
+ this .createArgsTupleNode = CreateArgsTupleNodeGen .create ();
485
565
}
486
566
487
567
@ Override
488
568
protected Object [] prepareCArguments (VirtualFrame frame ) {
489
569
Object self = readSelfNode .execute (frame );
490
570
Object [] args = readVarargsNode .executeObjectArray (frame );
491
- return new Object []{self , factory . createTuple ( args )};
571
+ return new Object []{self , createArgsTupleNode . execute ( factory , args )};
492
572
}
493
573
494
574
@ Override
@@ -934,4 +1014,95 @@ public Signature getSignature() {
934
1014
return SIGNATURE ;
935
1015
}
936
1016
}
1017
+
1018
+ /**
1019
+ * We need to inflate all primitives in order to avoid memory leaks. Explanation: Primitives
1020
+ * would currently be wrapped into a PrimitiveNativeWrapper. If any of those will receive a
1021
+ * toNative message, the managed code will be the only owner of those wrappers. But we will
1022
+ * never be able to reach the wrapper from the arguments if they are just primitive. So, we
1023
+ * inflate the primitives and we can then traverse the tuple and reach the wrappers of its
1024
+ * arguments after the call returned.
1025
+ */
1026
+ abstract static class CreateArgsTupleNode extends Node {
1027
+ public abstract PTuple execute (PythonObjectFactory factory , Object [] args );
1028
+
1029
+ @ Specialization (guards = {"args.length == cachedLen" , "cachedLen <= 16" })
1030
+ @ ExplodeLoop (kind = LoopExplosionKind .FULL_UNROLL )
1031
+ static PTuple doCachedLen (PythonObjectFactory factory , Object [] args ,
1032
+ @ Cached ("args.length" ) int cachedLen ,
1033
+ @ Cached ("createMaterializeNodes(args.length)" ) MaterializePrimitiveNode [] materializePrimitiveNodes ) {
1034
+
1035
+ for (int i = 0 ; i < cachedLen ; i ++) {
1036
+ args [i ] = materializePrimitiveNodes [i ].execute (factory , args [i ]);
1037
+ }
1038
+ return factory .createTuple (args );
1039
+ }
1040
+
1041
+ @ Specialization (replaces = "doCachedLen" )
1042
+ static PTuple doGeneric (PythonObjectFactory factory , Object [] args ,
1043
+ @ Cached MaterializePrimitiveNode materializePrimitiveNode ) {
1044
+
1045
+ for (int i = 0 ; i < args .length ; i ++) {
1046
+ args [i ] = materializePrimitiveNode .execute (factory , args [i ]);
1047
+ }
1048
+ return factory .createTuple (args );
1049
+ }
1050
+
1051
+ static MaterializePrimitiveNode [] createMaterializeNodes (int length ) {
1052
+ MaterializePrimitiveNode [] materializePrimitiveNodes = new MaterializePrimitiveNode [length ];
1053
+ for (int i = 0 ; i < length ; i ++) {
1054
+ materializePrimitiveNodes [i ] = MaterializePrimitiveNodeGen .create ();
1055
+ }
1056
+ return materializePrimitiveNodes ;
1057
+ }
1058
+ }
1059
+
1060
+ /**
1061
+ * Special helper nodes that materializes any primitive that would leak the wrapper if the
1062
+ * reference is owned by managed code only.
1063
+ */
1064
+ @ ImportStatic (CApiGuards .class )
1065
+ @ TypeSystemReference (PythonTypes .class )
1066
+ abstract static class MaterializePrimitiveNode extends Node {
1067
+
1068
+ public abstract Object execute (PythonObjectFactory factory , Object object );
1069
+
1070
+ // NOTE: Booleans don't need to be materialized because they are singletons.
1071
+
1072
+ @ Specialization (guards = "!isSmallInteger(i)" )
1073
+ static PInt doInteger (PythonObjectFactory factory , int i ) {
1074
+ return factory .createInt (i );
1075
+ }
1076
+
1077
+ @ Specialization (guards = "!isSmallLong(l)" , replaces = "doInteger" )
1078
+ static PInt doLong (PythonObjectFactory factory , long l ) {
1079
+ return factory .createInt (l );
1080
+ }
1081
+
1082
+ @ Specialization
1083
+ static PFloat doDouble (PythonObjectFactory factory , double d ) {
1084
+ return factory .createFloat (d );
1085
+ }
1086
+
1087
+ @ Specialization
1088
+ static PString doString (PythonObjectFactory factory , String s ) {
1089
+ return factory .createString (s );
1090
+ }
1091
+
1092
+ @ Specialization (guards = "!needsMaterialization(object)" )
1093
+ static Object doObject (@ SuppressWarnings ("unused" ) PythonObjectFactory factory , Object object ) {
1094
+ return object ;
1095
+ }
1096
+
1097
+ static boolean needsMaterialization (Object object ) {
1098
+ if (object instanceof Integer ) {
1099
+ return !CApiGuards .isSmallInteger ((Integer ) object );
1100
+ }
1101
+ if (object instanceof Long ) {
1102
+ return !CApiGuards .isSmallLong ((Long ) object );
1103
+ }
1104
+ return PGuards .isDouble (object ) || object instanceof String ;
1105
+ }
1106
+ }
1107
+
937
1108
}
0 commit comments