4242
4343import com .oracle .graal .python .PythonLanguage ;
4444import 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 ;
4547import com .oracle .graal .python .builtins .modules .ExternalFunctionNodesFactory .ReleaseNativeWrapperNodeGen ;
48+ import com .oracle .graal .python .builtins .modules .ExternalFunctionNodesFactory .TraverseNativeWrapperNodeGen ;
4649import com .oracle .graal .python .builtins .modules .PythonCextBuiltins .CheckFunctionResultNode ;
4750import com .oracle .graal .python .builtins .objects .PNone ;
51+ import com .oracle .graal .python .builtins .objects .PythonAbstractObject ;
4852import com .oracle .graal .python .builtins .objects .cext .CApiGuards ;
4953import com .oracle .graal .python .builtins .objects .cext .CExtNodes ;
5054import com .oracle .graal .python .builtins .objects .cext .CExtNodes .ConvertArgsToSulongNode ;
5357import com .oracle .graal .python .builtins .objects .cext .CExtNodesFactory .ToJavaStealingNodeGen ;
5458import com .oracle .graal .python .builtins .objects .cext .common .CExtCommonNodes .ConvertPIntToPrimitiveNode ;
5559import 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 ;
5666import com .oracle .graal .python .builtins .objects .function .PArguments ;
5767import com .oracle .graal .python .builtins .objects .function .PKeyword ;
5868import 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 ;
5972import com .oracle .graal .python .nodes .ErrorMessages ;
6073import com .oracle .graal .python .nodes .IndirectCallNode ;
74+ import com .oracle .graal .python .nodes .PGuards ;
6175import com .oracle .graal .python .nodes .PNodeWithContext ;
6276import com .oracle .graal .python .nodes .PRaiseNode ;
6377import com .oracle .graal .python .nodes .PRootNode ;
6781import com .oracle .graal .python .nodes .argument .ReadVarKeywordsNode ;
6882import com .oracle .graal .python .nodes .call .special .CallVarargsMethodNode ;
6983import com .oracle .graal .python .nodes .interop .PForeignToPTypeNode ;
84+ import com .oracle .graal .python .nodes .truffle .PythonTypes ;
7085import com .oracle .graal .python .runtime .ExecutionContext .CalleeContext ;
7186import com .oracle .graal .python .runtime .ExecutionContext .ForeignCallContext ;
7287import com .oracle .graal .python .runtime .PythonContext ;
7388import 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 ;
7491import com .oracle .graal .python .util .PythonUtils ;
7592import com .oracle .truffle .api .Assumption ;
7693import com .oracle .truffle .api .CompilerDirectives ;
7996import com .oracle .truffle .api .Truffle ;
8097import com .oracle .truffle .api .TruffleLanguage .ContextReference ;
8198import com .oracle .truffle .api .dsl .Cached ;
99+ import com .oracle .truffle .api .dsl .Fallback ;
100+ import com .oracle .truffle .api .dsl .ImportStatic ;
82101import com .oracle .truffle .api .dsl .Specialization ;
102+ import com .oracle .truffle .api .dsl .TypeSystemReference ;
83103import com .oracle .truffle .api .frame .VirtualFrame ;
84104import com .oracle .truffle .api .interop .ArityException ;
85105import com .oracle .truffle .api .interop .InteropLibrary ;
86106import com .oracle .truffle .api .interop .UnsupportedMessageException ;
87107import com .oracle .truffle .api .interop .UnsupportedTypeException ;
108+ import com .oracle .truffle .api .library .CachedLibrary ;
88109import com .oracle .truffle .api .nodes .ExplodeLoop ;
110+ import com .oracle .truffle .api .nodes .ExplodeLoop .LoopExplosionKind ;
89111import com .oracle .truffle .api .nodes .Node ;
90112import com .oracle .truffle .api .nodes .NodeCost ;
91113import com .oracle .truffle .api .nodes .UnexpectedResultException ;
@@ -304,27 +326,33 @@ abstract static class ReleaseNativeWrapperNode extends Node {
304326 static void doCachedLength (Object [] nativeArguments ,
305327 @ Cached ("nativeArguments.length" ) int cachedLength ,
306328 @ Cached (value = "createClassProfiles(cachedLength)" , dimensions = 1 ) ValueProfile [] classProfiles ,
329+ @ CachedLibrary (limit = "nativeArguments.length" ) PythonNativeWrapperLibrary lib ,
330+ @ Cached ("createTraverseNodes(cachedLength)" ) TraverseNativeWrapperNode [] traverseNativeWrapperNodes ,
307331 @ Cached SubRefCntNode subRefCntNode ) {
308332
309333 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 ] );
311335 }
312336 }
313337
314338 @ Specialization (replaces = "doCachedLength" )
315339 static void doGeneric (Object [] nativeArguments ,
316340 @ Cached ("createClassProfile()" ) ValueProfile classProfile ,
341+ @ CachedLibrary (limit = "3" ) PythonNativeWrapperLibrary lib ,
342+ @ Cached TraverseNativeWrapperNode traverseNativeWrapperNode ,
317343 @ Cached SubRefCntNode freeNode ) {
318344
319345 for (int i = 0 ; i < nativeArguments .length ; i ++) {
320- doCheck (classProfile .profile (nativeArguments [i ]), freeNode );
346+ doCheck (classProfile .profile (nativeArguments [i ]), freeNode , lib , traverseNativeWrapperNode );
321347 }
322348 }
323349
324- private static void doCheck (Object argument , SubRefCntNode refCntNode ) {
350+ private static void doCheck (Object argument , SubRefCntNode refCntNode , PythonNativeWrapperLibrary lib , TraverseNativeWrapperNode traverseNativeWrapperNode ) {
325351 if (CApiGuards .isNativeWrapper (argument )) {
326352 // 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+ }
328356 }
329357 }
330358
@@ -335,6 +363,54 @@ static ValueProfile[] createClassProfiles(int length) {
335363 }
336364 return classProfiles ;
337365 }
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+
338414 }
339415
340416 abstract static class MethodDescriptorRoot extends PRootNode {
@@ -443,6 +519,7 @@ public static final class MethKeywordsRoot extends MethodDescriptorRoot {
443519 @ Child private PythonObjectFactory factory ;
444520 @ Child private ReadVarArgsNode readVarargsNode ;
445521 @ Child private ReadVarKeywordsNode readKwargsNode ;
522+ @ Child private CreateArgsTupleNode createArgsTupleNode ;
446523
447524 public MethKeywordsRoot (PythonLanguage language , String name , Object callable ) {
448525 super (language , name , callable );
@@ -453,14 +530,15 @@ public MethKeywordsRoot(PythonLanguage language, String name, Object callable, C
453530 this .factory = PythonObjectFactory .create ();
454531 this .readVarargsNode = ReadVarArgsNode .create (1 , true );
455532 this .readKwargsNode = ReadVarKeywordsNode .create (new String [0 ]);
533+ this .createArgsTupleNode = CreateArgsTupleNodeGen .create ();
456534 }
457535
458536 @ Override
459537 protected Object [] prepareCArguments (VirtualFrame frame ) {
460538 Object self = readSelfNode .execute (frame );
461539 Object [] args = readVarargsNode .executeObjectArray (frame );
462540 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 )};
464542 }
465543
466544 @ Override
@@ -473,6 +551,7 @@ public static final class MethVarargsRoot extends MethodDescriptorRoot {
473551 private static final Signature SIGNATURE = new Signature (-1 , false , 1 , false , new String []{"self" }, new String [0 ]);
474552 @ Child private PythonObjectFactory factory ;
475553 @ Child private ReadVarArgsNode readVarargsNode ;
554+ @ Child private CreateArgsTupleNode createArgsTupleNode ;
476555
477556 public MethVarargsRoot (PythonLanguage language , String name , Object callable ) {
478557 super (language , name , callable );
@@ -482,13 +561,14 @@ public MethVarargsRoot(PythonLanguage language, String name, Object callable, Co
482561 super (language , name , callable , convertArgsToSulongNode );
483562 this .factory = PythonObjectFactory .create ();
484563 this .readVarargsNode = ReadVarArgsNode .create (1 , true );
564+ this .createArgsTupleNode = CreateArgsTupleNodeGen .create ();
485565 }
486566
487567 @ Override
488568 protected Object [] prepareCArguments (VirtualFrame frame ) {
489569 Object self = readSelfNode .execute (frame );
490570 Object [] args = readVarargsNode .executeObjectArray (frame );
491- return new Object []{self , factory . createTuple ( args )};
571+ return new Object []{self , createArgsTupleNode . execute ( factory , args )};
492572 }
493573
494574 @ Override
@@ -934,4 +1014,95 @@ public Signature getSignature() {
9341014 return SIGNATURE ;
9351015 }
9361016 }
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+
9371108}
0 commit comments