diff --git a/graalpython/com.oracle.graal.python.test/src/tests/test_weakref.py b/graalpython/com.oracle.graal.python.test/src/tests/test_weakref.py index 2c23ff2714..6dc783e438 100644 --- a/graalpython/com.oracle.graal.python.test/src/tests/test_weakref.py +++ b/graalpython/com.oracle.graal.python.test/src/tests/test_weakref.py @@ -38,6 +38,16 @@ # SOFTWARE. import weakref + +def assert_raises(err, fn, *args, **kwargs): + raised = False + try: + fn(*args, **kwargs) + except err: + raised = True + assert raised + + MAX_WAIT_COUNT = 500 import sys @@ -322,3 +332,58 @@ def __call__(self): proxy = weakref.proxy(a) assert callable(proxy) is True assert proxy() == 42 + + +def test_weakref_with_callback_object_type_support(): + def callback(ref): + pass + + assert_raises(TypeError, weakref.ref, {}, callback) + assert_raises(TypeError, weakref.proxy, {}, callback) + assert_raises(TypeError, weakref.ref, [1, 2], callback) + assert_raises(TypeError, weakref.proxy, [1, 2], callback) + assert_raises(TypeError, weakref.ref, 42, callback) + assert_raises(TypeError, weakref.proxy, 42, callback) + assert_raises(TypeError, weakref.ref, None, callback) + assert_raises(TypeError, weakref.proxy, None, callback) + + +def test_weakref_builtin_types_with_weaklist(): + import _queue, _struct, array, collections, contextvars, mmap, typing + + def callback(ref): + pass + + d = collections.deque() + assert weakref.ref(d)() is d + assert weakref.ref(d, callback)() is d + a = array.array('b') + assert weakref.ref(a)() is a + assert weakref.ref(a, callback)() is a + with mmap.mmap(-1, 1) as m: + assert weakref.ref(m)() is m + assert weakref.ref(m, callback)() is m + c = contextvars.copy_context() + assert weakref.ref(c)() is c + assert weakref.ref(c, callback)() is c + q = _queue.SimpleQueue() + assert weakref.ref(q)() is q + assert weakref.ref(q, callback)() is q + s = _struct.Struct('b') + assert weakref.ref(s)() is s + assert weakref.ref(s, callback)() is s + t = typing.TypeVar('T') + assert weakref.ref(t)() is t + assert weakref.ref(t, callback)() is t + ts = typing.TypeVarTuple('Ts') + assert weakref.ref(ts)() is ts + assert weakref.ref(ts, callback)() is ts + p = typing.ParamSpec('P') + assert weakref.ref(p)() is p + assert weakref.ref(p, callback)() is p + pa = p.args + assert weakref.ref(pa)() is pa + assert weakref.ref(pa, callback)() is pa + pk = p.kwargs + assert weakref.ref(pk)() is pk + assert weakref.ref(pk, callback)() is pk diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/WeakRefModuleBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/WeakRefModuleBuiltins.java index 1b432d0974..0a46022805 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/WeakRefModuleBuiltins.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/WeakRefModuleBuiltins.java @@ -112,7 +112,8 @@ public static int getBuiltinTypeWeaklistoffset(PythonBuiltinClassType cls) { -> 40; case PMethod, // method PFileIO, // _io.FileIO - PTee // itertools._tee + PTee, // itertools._tee + ContextVarsContext // _contextvars.Context -> 32; case PFunction -> 80; // function case PIOBase, // _io._IOBase @@ -122,9 +123,14 @@ public static int getBuiltinTypeWeaklistoffset(PythonBuiltinClassType cls) { PLock // _thread.LockType -> 24; case PBytesIO, // _io.BytesIO - PPartial // functools.partial + PPartial, // functools.partial + PArray, // array.array + PStruct // _struct.Struct -> 48; - case PPattern -> 64; // _sre.Pattern + case PPattern, // _sre.Pattern + PSimpleQueue // _queue.SimpleQueue + -> 64; + case PMMap -> 72; // mmap.mmap case PStringIO -> 112; // _io.StringIO case PBufferedReader, // _io.BufferedReader PBufferedWriter, // _io.BufferedWriter @@ -134,6 +140,13 @@ public static int getBuiltinTypeWeaklistoffset(PythonBuiltinClassType cls) { case PickleBuffer -> 96; // _pickle.PickleBuffer case PTextIOWrapper -> 176; // _io.TextIOWrapper case POrderedDict -> 104; + case PDeque -> 208; // collections.deque + case PTypeVar, // typing.TypeVar + PTypeVarTuple, // typing.TypeVarTuple + PParamSpec, // typing.ParamSpec + PParamSpecArgs, // typing.ParamSpecArgs + PParamSpecKwargs // typing.ParamSpecKwargs + -> -32; default -> 0; // @formatter:on }; diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/referencetype/ReferenceTypeBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/referencetype/ReferenceTypeBuiltins.java index 03494c1aed..a4381b8213 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/referencetype/ReferenceTypeBuiltins.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/referencetype/ReferenceTypeBuiltins.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 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 @@ -160,9 +160,13 @@ static PReferenceType refType(Object cls, Object object, @SuppressWarnings("unus @Specialization(guards = {"!isNativeObject(object)", "!isPNone(callback)"}) static PReferenceType refTypeWithCallback(Object cls, Object object, Object callback, @Bind Node inliningTarget, + @Cached.Exclusive @Cached GetClassNode getClassNode, @Cached.Exclusive @Cached HiddenAttr.ReadNode readQueueNode, @Bind PythonLanguage language, @Shared @Cached TypeNodes.GetInstanceShape getInstanceShape) { + if (getClassNode.execute(inliningTarget, object) instanceof PythonBuiltinClassType type && type.getWeaklistoffset() == 0) { + throw PRaiseNode.raiseStatic(inliningTarget, TypeError, ErrorMessages.CANNOT_CREATE_WEAK_REFERENCE_TO, object); + } return PFactory.createReferenceType(language, cls, getInstanceShape.execute(cls), object, callback, getWeakReferenceQueue(inliningTarget, readQueueNode)); }