Skip to content

Commit e81bc35

Browse files
committed
[GR-64163] Fix __new__ lookup for custom metatypes.
PullRequest: graalpython/4534
2 parents fd270a2 + 735353a commit e81bc35

2 files changed

Lines changed: 68 additions & 8 deletions

File tree

  • graalpython

graalpython/com.oracle.graal.python.test/src/tests/test_class.py

Lines changed: 49 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# Copyright (c) 2020, 2025, Oracle and/or its affiliates. All rights reserved.
1+
# Copyright (c) 2020, 2026, Oracle and/or its affiliates. All rights reserved.
22
# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
33
#
44
# The Universal Permissive License (UPL), Version 1.0
@@ -117,10 +117,54 @@ class notAMeta(metaclass=Meta):
117117
pass
118118
notAMeta2 = type("notAMeta2", (notAMeta,), {})
119119

120-
# the below assertions should pass, but this is such an unusual case that we
121-
# ignore this.
122-
# assert notAMeta.metatype is NewDescriptor
123-
# assert notAMeta2.metatype is NewDescriptor
120+
assert notAMeta.metatype is NewDescriptor
121+
assert notAMeta2.metatype is NewDescriptor
122+
123+
124+
def test_meta_new_default_metatype():
125+
class Meta(type):
126+
def __new__(*args, **kwargs):
127+
cls = type.__new__(*args, **kwargs)
128+
cls.metatype = "meta"
129+
return cls
130+
131+
class uses_meta(metaclass=Meta):
132+
pass
133+
134+
uses_meta2 = type("uses_meta2", (uses_meta,), {})
135+
136+
assert uses_meta.metatype == "meta"
137+
assert uses_meta2.metatype == "meta"
138+
139+
140+
def test_meta_meta_new_custom_getattribute():
141+
meta_new_calls = []
142+
143+
class MetaMeta(type):
144+
def __getattribute__(self, name):
145+
if name == "__new__":
146+
def new(*args, **kwargs):
147+
cls = type.__new__(*args, **kwargs)
148+
cls.metatype = "getattribute"
149+
return cls
150+
return new
151+
return super().__getattribute__(name)
152+
153+
class Meta(type, metaclass=MetaMeta):
154+
def __new__(*args, **kwargs):
155+
meta_new_calls.append(True)
156+
cls = type.__new__(*args, **kwargs)
157+
cls.metatype = "meta"
158+
return cls
159+
160+
class uses_getattribute(metaclass=Meta):
161+
pass
162+
163+
uses_getattribute2 = type("uses_getattribute2", (uses_getattribute,), {})
164+
165+
assert uses_getattribute.metatype == "getattribute"
166+
assert uses_getattribute2.metatype == "getattribute"
167+
assert meta_new_calls == []
124168

125169

126170
def test_subclasses_collection():

graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/type/slots/TpSlotVarargs.java

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,7 @@
8282
import com.oracle.graal.python.builtins.objects.type.slots.TpSlotVarargsFactory.CallSlotTpCallNodeGen;
8383
import com.oracle.graal.python.builtins.objects.type.slots.TpSlotVarargsFactory.CallSlotTpInitNodeGen;
8484
import com.oracle.graal.python.builtins.objects.type.slots.TpSlotVarargsFactory.CallSlotTpNewNodeGen;
85+
import com.oracle.graal.python.lib.PyObjectGetAttr;
8586
import com.oracle.graal.python.nodes.ErrorMessages;
8687
import com.oracle.graal.python.nodes.PGuards;
8788
import com.oracle.graal.python.nodes.PRaiseNode;
@@ -97,6 +98,7 @@
9798
import com.oracle.graal.python.nodes.function.builtins.PythonTernaryBuiltinNode;
9899
import com.oracle.graal.python.nodes.function.builtins.PythonUnaryBuiltinNode;
99100
import com.oracle.graal.python.nodes.function.builtins.PythonVarargsBuiltinNode;
101+
import com.oracle.graal.python.nodes.object.GetClassNode;
100102
import com.oracle.graal.python.runtime.IndirectCallData.BoundaryCallData;
101103
import com.oracle.graal.python.runtime.PythonContext;
102104
import com.oracle.graal.python.runtime.PythonContext.GetThreadStateNode;
@@ -110,6 +112,7 @@
110112
import com.oracle.truffle.api.Truffle;
111113
import com.oracle.truffle.api.dsl.Bind;
112114
import com.oracle.truffle.api.dsl.Cached;
115+
import com.oracle.truffle.api.dsl.Cached.Exclusive;
113116
import com.oracle.truffle.api.dsl.Fallback;
114117
import com.oracle.truffle.api.dsl.GenerateCached;
115118
import com.oracle.truffle.api.dsl.GenerateInline;
@@ -484,14 +487,27 @@ public static Object executeUncached(TpSlot slot, Object self, Object[] args, PK
484487
return CallSlotTpNewNodeGen.getUncached().execute(null, null, slot, self, args, keywords);
485488
}
486489

487-
@Specialization
488-
static Object callPython(VirtualFrame frame, Node inliningTarget, TpSlotPythonSingle slot, Object self, Object[] args, PKeyword[] keywords,
490+
@Specialization(guards = "hasDefaultMetatype(inliningTarget, self, getClassNode)", limit = "1")
491+
static Object callPythonFast(VirtualFrame frame, Node inliningTarget, TpSlotPythonSingle slot, Object self, Object[] args, PKeyword[] keywords,
492+
@Cached GetClassNode getClassNode,
489493
@Cached BindNewMethodNode bindNew,
490-
@Cached(inline = false) CallNode callNode) {
494+
@Exclusive @Cached(inline = false) CallNode callNode) {
491495
Object callable = bindNew.execute(frame, inliningTarget, slot.getCallable(), self);
492496
return callNode.execute(frame, callable, PythonUtils.prependArgument(self, args), keywords);
493497
}
494498

499+
@Specialization(replaces = "callPythonFast")
500+
static Object callPython(VirtualFrame frame, Node inliningTarget, @SuppressWarnings("unused") TpSlotPythonSingle slot, Object self, Object[] args, PKeyword[] keywords,
501+
@Cached PyObjectGetAttr getNew,
502+
@Exclusive @Cached(inline = false) CallNode callNode) {
503+
Object callable = getNew.execute(frame, inliningTarget, self, T___NEW__);
504+
return callNode.execute(frame, callable, PythonUtils.prependArgument(self, args), keywords);
505+
}
506+
507+
static boolean hasDefaultMetatype(Node inliningTarget, Object self, GetClassNode getClassNode) {
508+
return getClassNode.execute(inliningTarget, self) == PythonBuiltinClassType.PythonClass;
509+
}
510+
495511
@Specialization
496512
@InliningCutoff
497513
static Object callNative(VirtualFrame frame, TpSlotCExtNative slot, Object self, Object[] args, PKeyword[] keywords,

0 commit comments

Comments
 (0)