11/*
2- * Copyright (c) 2020, 2025 , Oracle and/or its affiliates. All rights reserved.
2+ * Copyright (c) 2020, 2026 , Oracle and/or its affiliates. All rights reserved.
33 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
44 *
55 * The Universal Permissive License (UPL), Version 1.0
6161import com .oracle .graal .python .builtins .PythonBuiltinClassType ;
6262import com .oracle .graal .python .builtins .PythonBuiltins ;
6363import com .oracle .graal .python .builtins .objects .PNone ;
64+ import com .oracle .graal .python .builtins .objects .function .PBuiltinFunction ;
6465import com .oracle .graal .python .builtins .objects .str .StringUtils .SimpleTruffleStringFormatNode ;
6566import com .oracle .graal .python .builtins .objects .type .TpSlots ;
6667import com .oracle .graal .python .builtins .objects .type .TypeNodes ;
68+ import com .oracle .graal .python .builtins .objects .type .slots .TpSlotDescrGet .DescrGetBuiltinNode ;
6769import com .oracle .graal .python .lib .PyObjectGetAttr ;
6870import com .oracle .graal .python .lib .PyObjectLookupAttr ;
6971import com .oracle .graal .python .lib .PyObjectStrAsTruffleStringNode ;
72+ import com .oracle .graal .python .nodes .ErrorMessages ;
73+ import com .oracle .graal .python .nodes .PGuards ;
74+ import com .oracle .graal .python .nodes .PRaiseNode ;
75+ import com .oracle .graal .python .nodes .classes .IsSubtypeNode ;
7076import com .oracle .graal .python .nodes .function .PythonBuiltinBaseNode ;
7177import com .oracle .graal .python .nodes .function .builtins .PythonUnaryBuiltinNode ;
78+ import com .oracle .graal .python .nodes .object .GetClassNode ;
79+ import com .oracle .graal .python .runtime .exception .PException ;
80+ import com .oracle .truffle .api .CompilerDirectives .TruffleBoundary ;
81+ import com .oracle .truffle .api .HostCompilerDirectives .InliningCutoff ;
7282import com .oracle .truffle .api .dsl .Bind ;
7383import com .oracle .truffle .api .dsl .Cached ;
84+ import com .oracle .truffle .api .dsl .Cached .Exclusive ;
7485import com .oracle .truffle .api .dsl .GenerateNodeFactory ;
86+ import com .oracle .truffle .api .dsl .GenerateUncached ;
7587import com .oracle .truffle .api .dsl .NodeFactory ;
88+ import com .oracle .truffle .api .dsl .ReportPolymorphism ;
7689import com .oracle .truffle .api .dsl .Specialization ;
7790import com .oracle .truffle .api .frame .VirtualFrame ;
7891import com .oracle .truffle .api .nodes .Node ;
92+ import com .oracle .truffle .api .profiles .InlinedBranchProfile ;
7993import com .oracle .truffle .api .strings .TruffleString ;
8094
8195@ CoreFunctions (extendClasses = PythonBuiltinClassType .PBuiltinClassMethod )
@@ -88,6 +102,97 @@ protected List<? extends NodeFactory<? extends PythonBuiltinBaseNode>> getNodeFa
88102 return BuiltinClassmethodBuiltinsFactory .getFactories ();
89103 }
90104
105+ @ Slot (SlotKind .tp_descr_get )
106+ @ ReportPolymorphism
107+ @ GenerateUncached
108+ @ GenerateNodeFactory
109+ abstract static class GetNode extends DescrGetBuiltinNode {
110+ // If self.getCallable() is null, let the next @Specialization handle that
111+ @ Specialization (guards = {"isSingleContext()" , "isNoValue(type) == typeIsNoValue" , "cachedSelf == self" , "cachedCallable != null" }, limit = "3" )
112+ static Object getCached (@ SuppressWarnings ("unused" ) PDecoratedMethod self , Object obj , Object type ,
113+ @ Bind Node inliningTarget ,
114+ @ SuppressWarnings ("unused" ) @ Cached (value = "self" , weak = true ) PDecoratedMethod cachedSelf ,
115+ @ Cached (value = "self.getCallable()" , weak = true ) Object cachedCallable ,
116+ @ Cached ("isNoValue(type)" ) boolean typeIsNoValue ,
117+ @ Exclusive @ Cached GetClassNode getClass ,
118+ @ Exclusive @ Cached IsSubtypeNode isSubtypeNode ,
119+ @ Exclusive @ Cached InlinedBranchProfile errorProfile ,
120+ @ Exclusive @ Cached ClassmethodCommonBuiltins .MakeMethodNode makeMethod ) {
121+ Object actualType = getType (inliningTarget , errorProfile , getClass , cachedCallable , typeIsNoValue , obj , type );
122+ return doGet (inliningTarget , isSubtypeNode , errorProfile , makeMethod , actualType , cachedCallable );
123+ }
124+
125+ @ InliningCutoff
126+ @ Specialization (replaces = "getCached" )
127+ static Object get (PDecoratedMethod self , Object obj , Object type ,
128+ @ Bind Node inliningTarget ,
129+ @ Exclusive @ Cached GetClassNode getClass ,
130+ @ Exclusive @ Cached IsSubtypeNode isSubtypeNode ,
131+ @ Exclusive @ Cached InlinedBranchProfile errorProfile ,
132+ @ Exclusive @ Cached ClassmethodCommonBuiltins .MakeMethodNode makeMethod ,
133+ @ Exclusive @ Cached PRaiseNode raiseNode ) {
134+ Object callable = ClassmethodCommonBuiltins .getCallable (inliningTarget , self , raiseNode );
135+ Object actualType = getType (inliningTarget , errorProfile , getClass , callable , PGuards .isNoValue (type ), obj , type );
136+ return doGet (inliningTarget , isSubtypeNode , errorProfile , makeMethod , actualType , callable );
137+ }
138+
139+ private static Object getType (Node inliningTarget , InlinedBranchProfile errorProfile , GetClassNode getClassNode ,
140+ Object callable , boolean typeIsNoValue , Object obj , Object type ) {
141+ if (typeIsNoValue ) {
142+ if (PGuards .isNoValue (obj )) {
143+ errorProfile .enter (inliningTarget );
144+ throw raiseNeedsEitherObjOrType (inliningTarget , callable );
145+ }
146+ return getClassNode .execute (inliningTarget , obj );
147+ }
148+ return type ;
149+ }
150+
151+ @ TruffleBoundary
152+ private static PException raiseNeedsEitherObjOrType (Node inliningTarget , Object callable ) {
153+ if (callable instanceof PBuiltinFunction pbf ) {
154+ throw PRaiseNode .raiseStatic (inliningTarget , PythonBuiltinClassType .TypeError ,
155+ ErrorMessages .DESCRIPTOR_S_FOR_TYPE_S_NEEDS_EITHER_OBJ_OR_TYPE , pbf .getName (),
156+ TypeNodes .GetNameNode .executeUncached (pbf .getEnclosingType ()));
157+ } else {
158+ throw PRaiseNode .raiseStatic (inliningTarget , PythonBuiltinClassType .TypeError );
159+ }
160+ }
161+
162+ private static Object doGet (Node inliningTarget , IsSubtypeNode isSubtypeNode , InlinedBranchProfile errorProfile ,
163+ ClassmethodCommonBuiltins .MakeMethodNode makeMethod , Object type , Object callable ) {
164+ if (!PGuards .isPythonClass (type )) {
165+ errorProfile .enter (inliningTarget );
166+ throw raiseNeedsType (inliningTarget , callable , type );
167+ }
168+ // Not clear if we can get any other callable than PBuiltinFunction...
169+ if (callable instanceof PBuiltinFunction builtinFunction ) {
170+ Object descriptorType = builtinFunction .getEnclosingType ();
171+ if (!isSubtypeNode .execute (type , descriptorType )) {
172+ errorProfile .enter (inliningTarget );
173+ throw raiseRequiresSubtype (inliningTarget , builtinFunction , descriptorType , type );
174+ }
175+ }
176+ return makeMethod .execute (inliningTarget , type , callable );
177+ }
178+
179+ @ TruffleBoundary
180+ private static RuntimeException raiseNeedsType (Node inliningTarget , Object callable , Object type ) {
181+ if (callable instanceof PBuiltinFunction pbf ) {
182+ throw PRaiseNode .raiseStatic (inliningTarget , PythonBuiltinClassType .TypeError , ErrorMessages .DESCRIPTOR_S_FOR_TYPE_S_NEEDS_TYPE_NOT_P_AS_ARG_2 ,
183+ pbf .getName (), TypeNodes .GetNameNode .executeUncached (pbf .getEnclosingType ()), type );
184+ } else {
185+ throw PRaiseNode .raiseStatic (inliningTarget , PythonBuiltinClassType .TypeError );
186+ }
187+ }
188+
189+ @ TruffleBoundary
190+ private static RuntimeException raiseRequiresSubtype (Node inliningTarget , PBuiltinFunction builtinFunction , Object descriptorType , Object type ) {
191+ throw PRaiseNode .raiseStatic (inliningTarget , PythonBuiltinClassType .TypeError , ErrorMessages .DESCRIPTOR_S_REQUIRES_SUBTYPE_OF_S_BUT_RECEIVED_S ,
192+ builtinFunction .getName (), TypeNodes .GetNameNode .executeUncached (descriptorType ), TypeNodes .GetNameNode .executeUncached (type ));
193+ }
194+ }
195+
91196 @ Builtin (name = J___NAME__ , maxNumOfPositionalArgs = 1 , isGetter = true )
92197 @ GenerateNodeFactory
93198 abstract static class NameNode extends PythonUnaryBuiltinNode {
0 commit comments