Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
54 changes: 49 additions & 5 deletions graalpython/com.oracle.graal.python.test/src/tests/test_class.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Copyright (c) 2020, 2025, Oracle and/or its affiliates. All rights reserved.
# Copyright (c) 2020, 2026, Oracle and/or its affiliates. All rights reserved.
# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
#
# The Universal Permissive License (UPL), Version 1.0
Expand Down Expand Up @@ -117,10 +117,54 @@ class notAMeta(metaclass=Meta):
pass
notAMeta2 = type("notAMeta2", (notAMeta,), {})

# the below assertions should pass, but this is such an unusual case that we
# ignore this.
# assert notAMeta.metatype is NewDescriptor
# assert notAMeta2.metatype is NewDescriptor
assert notAMeta.metatype is NewDescriptor
assert notAMeta2.metatype is NewDescriptor


def test_meta_new_default_metatype():
class Meta(type):
def __new__(*args, **kwargs):
cls = type.__new__(*args, **kwargs)
cls.metatype = "meta"
return cls

class uses_meta(metaclass=Meta):
pass

uses_meta2 = type("uses_meta2", (uses_meta,), {})

assert uses_meta.metatype == "meta"
assert uses_meta2.metatype == "meta"


def test_meta_meta_new_custom_getattribute():
meta_new_calls = []

class MetaMeta(type):
def __getattribute__(self, name):
if name == "__new__":
def new(*args, **kwargs):
cls = type.__new__(*args, **kwargs)
cls.metatype = "getattribute"
return cls
return new
return super().__getattribute__(name)

class Meta(type, metaclass=MetaMeta):
def __new__(*args, **kwargs):
meta_new_calls.append(True)
cls = type.__new__(*args, **kwargs)
cls.metatype = "meta"
return cls

class uses_getattribute(metaclass=Meta):
pass

uses_getattribute2 = type("uses_getattribute2", (uses_getattribute,), {})

assert uses_getattribute.metatype == "getattribute"
assert uses_getattribute2.metatype == "getattribute"
assert meta_new_calls == []


def test_subclasses_collection():
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@
import com.oracle.graal.python.builtins.objects.type.slots.TpSlotVarargsFactory.CallSlotTpCallNodeGen;
import com.oracle.graal.python.builtins.objects.type.slots.TpSlotVarargsFactory.CallSlotTpInitNodeGen;
import com.oracle.graal.python.builtins.objects.type.slots.TpSlotVarargsFactory.CallSlotTpNewNodeGen;
import com.oracle.graal.python.lib.PyObjectGetAttr;
import com.oracle.graal.python.nodes.ErrorMessages;
import com.oracle.graal.python.nodes.PGuards;
import com.oracle.graal.python.nodes.PRaiseNode;
Expand All @@ -97,6 +98,7 @@
import com.oracle.graal.python.nodes.function.builtins.PythonTernaryBuiltinNode;
import com.oracle.graal.python.nodes.function.builtins.PythonUnaryBuiltinNode;
import com.oracle.graal.python.nodes.function.builtins.PythonVarargsBuiltinNode;
import com.oracle.graal.python.nodes.object.GetClassNode;
import com.oracle.graal.python.runtime.IndirectCallData.BoundaryCallData;
import com.oracle.graal.python.runtime.PythonContext;
import com.oracle.graal.python.runtime.PythonContext.GetThreadStateNode;
Expand All @@ -110,6 +112,7 @@
import com.oracle.truffle.api.Truffle;
import com.oracle.truffle.api.dsl.Bind;
import com.oracle.truffle.api.dsl.Cached;
import com.oracle.truffle.api.dsl.Cached.Exclusive;
import com.oracle.truffle.api.dsl.Fallback;
import com.oracle.truffle.api.dsl.GenerateCached;
import com.oracle.truffle.api.dsl.GenerateInline;
Expand Down Expand Up @@ -484,14 +487,27 @@ public static Object executeUncached(TpSlot slot, Object self, Object[] args, PK
return CallSlotTpNewNodeGen.getUncached().execute(null, null, slot, self, args, keywords);
}

@Specialization
static Object callPython(VirtualFrame frame, Node inliningTarget, TpSlotPythonSingle slot, Object self, Object[] args, PKeyword[] keywords,
@Specialization(guards = "hasDefaultMetatype(inliningTarget, self, getClassNode)", limit = "1")
static Object callPythonFast(VirtualFrame frame, Node inliningTarget, TpSlotPythonSingle slot, Object self, Object[] args, PKeyword[] keywords,
@Cached GetClassNode getClassNode,
@Cached BindNewMethodNode bindNew,
@Cached(inline = false) CallNode callNode) {
@Exclusive @Cached(inline = false) CallNode callNode) {
Object callable = bindNew.execute(frame, inliningTarget, slot.getCallable(), self);
return callNode.execute(frame, callable, PythonUtils.prependArgument(self, args), keywords);
}

@Specialization(replaces = "callPythonFast")
static Object callPython(VirtualFrame frame, Node inliningTarget, @SuppressWarnings("unused") TpSlotPythonSingle slot, Object self, Object[] args, PKeyword[] keywords,
@Cached PyObjectGetAttr getNew,
@Exclusive @Cached(inline = false) CallNode callNode) {
Object callable = getNew.execute(frame, inliningTarget, self, T___NEW__);
return callNode.execute(frame, callable, PythonUtils.prependArgument(self, args), keywords);
}

static boolean hasDefaultMetatype(Node inliningTarget, Object self, GetClassNode getClassNode) {
return getClassNode.execute(inliningTarget, self) == PythonBuiltinClassType.PythonClass;
}

@Specialization
@InliningCutoff
static Object callNative(VirtualFrame frame, TpSlotCExtNative slot, Object self, Object[] args, PKeyword[] keywords,
Expand Down
Loading