7878import com .oracle .graal .python .runtime .PosixSupportLibrary ;
7979import com .oracle .graal .python .runtime .PosixSupportLibrary .PosixException ;
8080import com .oracle .truffle .api .CompilerDirectives ;
81+ import com .oracle .truffle .api .dsl .Bind ;
8182import com .oracle .truffle .api .dsl .Cached ;
8283import com .oracle .truffle .api .dsl .GenerateNodeFactory ;
8384import com .oracle .truffle .api .dsl .NodeFactory ;
8485import com .oracle .truffle .api .dsl .Specialization ;
86+ import com .oracle .truffle .api .dsl .Cached .Shared ;
8587import com .oracle .truffle .api .frame .VirtualFrame ;
8688import com .oracle .truffle .api .library .CachedLibrary ;
8789import com .oracle .truffle .api .profiles .ConditionProfile ;
@@ -237,7 +239,7 @@ static Object stat(VirtualFrame frame, PDirEntry self, boolean followSymlinks,
237239 }
238240 }
239241
240- abstract static class StatHelperNode extends PythonBuiltinBaseNode {
242+ abstract static class StatHelperSimpleNode extends PythonBuiltinBaseNode {
241243
242244 abstract PTuple execute (VirtualFrame frame , PDirEntry self , boolean followSymlinks , boolean catchNoent );
243245
@@ -253,44 +255,60 @@ static PTuple cachedLStat(PDirEntry self, boolean followSymlinks, boolean catchN
253255 return self .lstatCache ;
254256 }
255257
256- @ Specialization (guards = "self.getStatCache(followSymlinks) == null" )
257- PTuple stat (VirtualFrame frame , PDirEntry self , boolean followSymlinks , boolean catchNoent ,
258+ @ Specialization (guards = {"followSymlinks" , "self.statCache == null" , "isSymlink" }, limit = "1" )
259+ PTuple uncachedStatWithSymlink (VirtualFrame frame , PDirEntry self , boolean followSymlinks , boolean catchNoent ,
260+ @ SuppressWarnings ("unused" ) @ Cached IsSymlinkNode isSymlinkNode ,
261+ @ SuppressWarnings ("unused" ) @ Bind ("isSymlinkNode.executeBoolean(frame, self)" ) boolean isSymlink ,
258262 @ CachedLibrary ("getPosixSupport()" ) PosixSupportLibrary posixLib ,
259- @ Cached IsSymlinkNode isSymlinkNode ,
260- @ Cached StatHelperNode recursiveNode ,
261- @ Cached CachedPosixPathNode cachedPosixPathNode ,
262- @ Cached ConditionProfile positiveLongProfile ,
263- @ Cached ConditionProfile noSymlinkProfile ) {
264- PTuple res ;
263+ @ Shared ("cachedPosixPathNode" ) @ Cached CachedPosixPathNode cachedPosixPathNode ,
264+ @ Shared ("positiveLongProfile" ) @ Cached ConditionProfile positiveLongProfile ) {
265265 // There are two caches - one for `follow_symlinks=True` and the other for
266266 // 'follow_symlinks=False`. They are different only when the dir entry is a symlink.
267- // If it is not, they need to be the same, so we must make sure that fstatat() gets
268- // called only once.
269- if (noSymlinkProfile .profile (followSymlinks && !isSymlinkNode .execute (frame , self ))) {
270- // The entry is not a symlink, so both stat caches need to have the
271- // same value. Also, the `follow_symlinks=False` cache might already be filled
272- // in. (In fact, the call to isSymlinkNode in the condition may fill it.)
273- // So we call ourselves recursively to either use or fill that cache first, and
274- // the `follow_symlinks=True` cache will be filled below.
275- res = recursiveNode .execute (frame , self , false , catchNoent );
276- } else {
277- int dirFd = self .scandirPath instanceof PosixFd ? ((PosixFd ) self .scandirPath ).fd : AT_FDCWD .value ;
278- PosixPath posixPath = cachedPosixPathNode .execute (frame , self );
279- try {
280- long [] rawStat = posixLib .fstatat (getPosixSupport (), dirFd , posixPath .value , followSymlinks );
281- res = PosixModuleBuiltins .createStatResult (factory (), positiveLongProfile , rawStat );
282- } catch (PosixException e ) {
283- if (catchNoent && e .getErrorCode () == OSErrorEnum .ENOENT .getNumber ()) {
284- return null ;
285- }
286- throw raiseOSErrorFromPosixException (frame , e , posixPath .originalObject );
267+ return uncachedLStatWithSymlink (frame , self , followSymlinks , catchNoent , posixLib , cachedPosixPathNode , positiveLongProfile );
268+ }
269+
270+ @ Specialization (guards = {"!followSymlinks" , "self.lstatCache == null" })
271+ PTuple uncachedLStatWithSymlink (VirtualFrame frame , PDirEntry self , boolean followSymlinks , boolean catchNoent ,
272+ @ CachedLibrary ("getPosixSupport()" ) PosixSupportLibrary posixLib ,
273+ @ Shared ("cachedPosixPathNode" ) @ Cached CachedPosixPathNode cachedPosixPathNode ,
274+ @ Shared ("positiveLongProfile" ) @ Cached ConditionProfile positiveLongProfile ) {
275+ PTuple res ;
276+ int dirFd = self .scandirPath instanceof PosixFd ? ((PosixFd ) self .scandirPath ).fd : AT_FDCWD .value ;
277+ PosixPath posixPath = cachedPosixPathNode .execute (frame , self );
278+ try {
279+ long [] rawStat = posixLib .fstatat (getPosixSupport (), dirFd , posixPath .value , followSymlinks );
280+ res = PosixModuleBuiltins .createStatResult (factory (), positiveLongProfile , rawStat );
281+ } catch (PosixException e ) {
282+ if (catchNoent && e .getErrorCode () == OSErrorEnum .ENOENT .getNumber ()) {
283+ return null ;
287284 }
285+ throw raiseOSErrorFromPosixException (frame , e , posixPath .originalObject );
288286 }
289287 self .setStatCache (followSymlinks , res );
290288 return res ;
291289 }
292290 }
293291
292+ abstract static class StatHelperNode extends StatHelperSimpleNode {
293+ @ Specialization (guards = {"followSymlinks" , "self.statCache == null" , "!isSymlink" })
294+ static PTuple uncachedStatWithSymlink (VirtualFrame frame , PDirEntry self , boolean followSymlinks , boolean catchNoent ,
295+ @ SuppressWarnings ("unused" ) @ Cached IsSymlinkNode isSymlinkNode ,
296+ @ SuppressWarnings ("unused" ) @ Bind ("isSymlinkNode.executeBoolean(frame, self)" ) boolean isSymlink ,
297+ @ Cached StatHelperSimpleNode recursiveNode ) {
298+ // There are two caches - one for `follow_symlinks=True` and the other for
299+ // 'follow_symlinks=False`. They are different only when the dir entry is a symlink.
300+ // If it is not, they need to be the same, so we must make sure that fstatat() gets
301+ // called only once. The entry is not a symlink, so both stat caches need to have the
302+ // same value. Also, the `follow_symlinks=False` cache might already be filled
303+ // in. (In fact, the call to isSymlinkNode in the condition may fill it.)
304+ // So we call ourselves recursively to either use or fill that cache first, and
305+ // the `follow_symlinks=True` cache will be filled below.
306+ PTuple res = recursiveNode .execute (frame , self , false , catchNoent );
307+ self .setStatCache (followSymlinks , res );
308+ return res ;
309+ }
310+ }
311+
294312 abstract static class TestModeNode extends PythonBuiltinBaseNode {
295313
296314 private final long expectedMode ;
@@ -355,7 +373,7 @@ static TestModeNode createDir() {
355373 @ GenerateNodeFactory
356374 abstract static class IsSymlinkNode extends PythonUnaryBuiltinNode {
357375
358- abstract boolean execute (VirtualFrame frame , PDirEntry self );
376+ abstract boolean executeBoolean (VirtualFrame frame , PDirEntry self );
359377
360378 @ Specialization
361379 static boolean isSymlink (VirtualFrame frame , PDirEntry self ,
0 commit comments