From 15db2dc22d5557b5d94ff7a200a995265cc77166 Mon Sep 17 00:00:00 2001 From: Tim Felgentreff Date: Wed, 3 Jun 2026 11:45:20 +0200 Subject: [PATCH 1/3] [GR-76230] Keep function kwdefaults dict live --- .../src/tests/test_functions.py | 63 ++++++++++++++++++- .../objects/function/FunctionBuiltins.java | 15 +++-- .../builtins/objects/function/PFunction.java | 57 +++++++++++++++-- .../objects/method/MethodBuiltins.java | 11 ++-- .../python/nodes/builtins/FunctionNodes.java | 61 ------------------ 5 files changed, 130 insertions(+), 77 deletions(-) diff --git a/graalpython/com.oracle.graal.python.test/src/tests/test_functions.py b/graalpython/com.oracle.graal.python.test/src/tests/test_functions.py index 407ca7c8f4..2223225e30 100644 --- a/graalpython/com.oracle.graal.python.test/src/tests/test_functions.py +++ b/graalpython/com.oracle.graal.python.test/src/tests/test_functions.py @@ -1,4 +1,4 @@ -# 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 @@ -192,6 +192,65 @@ def foo(*args, x=1): assert_raises(TypeError, foo) +def test_function_kwdefaults_dict_is_live(): + def foo(*, x=1): + return x + + kwdefaults = foo.__kwdefaults__ + assert kwdefaults is foo.__kwdefaults__ + kwdefaults["x"] = 2 + assert foo() == 2 + + assigned = {"x": 3} + foo.__kwdefaults__ = assigned + assert foo.__kwdefaults__ is assigned + assigned["x"] = 4 + assert foo() == 4 + + def assign_invalid_kwdefaults(): + foo.__kwdefaults__ = {1: "not a keyword"} + + try: + assign_invalid_kwdefaults() + except TypeError as e: + assert "keyword names must be str" in str(e) + else: + assert False + assert foo.__kwdefaults__ is assigned + + assigned[1] = "not a keyword" + assert foo.__kwdefaults__ == {"x": 4, 1: "not a keyword"} + assert foo() == 4 + + +def test_mangled_kwonly_kwdefaults_dict_is_live(): + class argmap: + def make_wrapper(self): + def func(*args, __wrapper=None, **kwargs): + return __wrapper + + assert "_argmap__wrapper" in func.__kwdefaults__ + func.__kwdefaults__["_argmap__wrapper"] = func + return func + + func = argmap().make_wrapper() + assert "_argmap__wrapper" in func.__code__.co_varnames + assert "__wrapper" not in func.__code__.co_varnames + assert func() is func + + +def test_method_kwdefaults_dict_is_live(): + class C: + def method(self, *, x=1): + return x + + obj = C() + kwdefaults = obj.method.__kwdefaults__ + assert kwdefaults is C.method.__kwdefaults__ + kwdefaults["x"] = 2 + assert obj.method() == 2 + + def test_code_change(): def foo(): return "foo" @@ -236,7 +295,7 @@ def assign_code(x, y): assert_raises(ValueError, assign_code, foo, foobar_code) bazbar.__code__ = foobar_code assert bazbar() == (2,3) - + def test_function_dict_writeable(): def foo(): pass diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/function/FunctionBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/function/FunctionBuiltins.java index f6a62ca9f6..424b093f6c 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/function/FunctionBuiltins.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/function/FunctionBuiltins.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, 2025, Oracle and/or its affiliates. + * Copyright (c) 2017, 2026, Oracle and/or its affiliates. * Copyright (c) 2014, Regents of the University of California * * All rights reserved. @@ -64,6 +64,7 @@ import com.oracle.graal.python.builtins.objects.common.HashingStorageNodes.HashingStorageIteratorKey; import com.oracle.graal.python.builtins.objects.common.HashingStorageNodes.HashingStorageIteratorNext; import com.oracle.graal.python.builtins.objects.common.HashingStorageNodes.HashingStorageIteratorValue; +import com.oracle.graal.python.builtins.objects.common.KeywordsStorage; import com.oracle.graal.python.builtins.objects.common.SequenceNodes.GetObjectArrayNode; import com.oracle.graal.python.builtins.objects.dict.PDict; import com.oracle.graal.python.builtins.objects.getsetdescriptor.DescriptorDeleteMarker; @@ -295,13 +296,13 @@ public abstract static class GetKeywordDefaultsNode extends PythonBinaryBuiltinN @Specialization(guards = "isNoValue(arg)") static Object get(PFunction self, @SuppressWarnings("unused") PNone arg, @Bind PythonLanguage language) { - PKeyword[] kwdefaults = self.getKwDefaults(); - return (kwdefaults.length > 0) ? PFactory.createDict(language, kwdefaults) : PNone.NONE; + return self.getKwDefaultsDict(language); } @Specialization(guards = "!isNoValue(arg)") - static Object set(PFunction self, @SuppressWarnings("unused") PNone arg) { - self.setKwDefaults(PKeyword.EMPTY_KEYWORDS); + static Object set(PFunction self, @SuppressWarnings("unused") PNone arg, + @Bind PythonLanguage language) { + self.setKwDefaultsDict(PFactory.createDict(language, PKeyword.EMPTY_KEYWORDS)); return PNone.NONE; } @@ -320,7 +321,9 @@ Object set(PFunction self, PDict arg) { } keywords.add(new PKeyword((TruffleString) key, HashingStorageIteratorValue.executeUncached(storage, it))); } - self.setKwDefaults(keywords.isEmpty() ? PKeyword.EMPTY_KEYWORDS : keywords.toArray(new PKeyword[keywords.size()])); + PKeyword[] kwdefaults = keywords.isEmpty() ? PKeyword.EMPTY_KEYWORDS : keywords.toArray(new PKeyword[keywords.size()]); + arg.setDictStorage(new KeywordsStorage(kwdefaults)); + self.setKwDefaultsDict(arg); return PNone.NONE; } } diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/function/PFunction.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/function/PFunction.java index 48007923fa..bac8850e64 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/function/PFunction.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/function/PFunction.java @@ -25,16 +25,28 @@ */ package com.oracle.graal.python.builtins.objects.function; +import java.util.ArrayList; + import com.oracle.graal.python.PythonLanguage; import com.oracle.graal.python.builtins.PythonBuiltinClassType; import com.oracle.graal.python.builtins.objects.PNone; import com.oracle.graal.python.builtins.objects.cell.PCell; import com.oracle.graal.python.builtins.objects.code.PCode; +import com.oracle.graal.python.builtins.objects.common.HashingStorage; +import com.oracle.graal.python.builtins.objects.common.HashingStorageNodes.HashingStorageGetIterator; +import com.oracle.graal.python.builtins.objects.common.HashingStorageNodes.HashingStorageIterator; +import com.oracle.graal.python.builtins.objects.common.HashingStorageNodes.HashingStorageIteratorKey; +import com.oracle.graal.python.builtins.objects.common.HashingStorageNodes.HashingStorageIteratorNext; +import com.oracle.graal.python.builtins.objects.common.HashingStorageNodes.HashingStorageIteratorValue; +import com.oracle.graal.python.builtins.objects.common.KeywordsStorage; +import com.oracle.graal.python.builtins.objects.dict.PDict; import com.oracle.graal.python.builtins.objects.object.PythonObject; +import com.oracle.graal.python.builtins.objects.str.PString; import com.oracle.graal.python.compiler.CodeUnit; import com.oracle.graal.python.lib.PyUnicodeCheckNode; import com.oracle.graal.python.nodes.PRootNode; import com.oracle.graal.python.runtime.GilNode; +import com.oracle.graal.python.runtime.object.PFactory; import com.oracle.graal.python.util.PythonUtils; import com.oracle.truffle.api.Assumption; import com.oracle.truffle.api.CompilerAsserts; @@ -68,6 +80,7 @@ public final class PFunction extends PythonObject { private Object[] defaultValues; @CompilationFinal(dimensions = 1) private PKeyword[] finalKwDefaultValues; private PKeyword[] kwDefaultValues; + private PDict kwDefaultDict; private Object doc; public PFunction(PythonLanguage lang, TruffleString name, TruffleString qualname, PCode code, PythonObject globals, PCell[] closure) { @@ -202,17 +215,53 @@ public void setDefaults(Object[] defaults) { public PKeyword[] getKwDefaults() { if (CompilerDirectives.inCompiledCode() && CompilerDirectives.isPartialEvaluationConstant(this)) { if (codeStableAssumption.isValid()) { + assert kwDefaultDict == null; return finalKwDefaultValues; } } + if (kwDefaultDict != null) { + HashingStorage storage = kwDefaultDict.getDictStorage(); + if (CompilerDirectives.injectBranchProbability(CompilerDirectives.LIKELY_PROBABILITY, storage instanceof KeywordsStorage)) { + return ((KeywordsStorage) storage).getStore(); + } + return kwDefaultsFromStorage(storage); + } return kwDefaultValues; } - @TruffleBoundary - public void setKwDefaults(PKeyword[] defaults) { + public Object getKwDefaultsDict(PythonLanguage language) { + if (kwDefaultDict == null) { + PKeyword[] kwdefaults = getKwDefaults(); + if (kwdefaults.length == 0) { + return PNone.NONE; + } + setKwDefaultsDict(PFactory.createDict(language, kwdefaults)); + } + return kwDefaultDict; + } + + public void setKwDefaultsDict(PDict defaults) { + assert defaults.getDictStorage() instanceof KeywordsStorage; this.codeStableAssumption.invalidate("kw defaults changed for function " + getName()); - this.finalDefaultValues = null; // avoid leak, and make code that wrongly uses it crash - this.kwDefaultValues = defaults; + this.finalKwDefaultValues = null; // avoid leak, and make code that wrongly uses it crash + this.kwDefaultValues = PKeyword.EMPTY_KEYWORDS; + this.kwDefaultDict = defaults; + } + + @TruffleBoundary + private static PKeyword[] kwDefaultsFromStorage(HashingStorage storage) { + ArrayList keywords = new ArrayList<>(); + HashingStorageIterator it = HashingStorageGetIterator.executeUncached(storage); + while (HashingStorageIteratorNext.executeUncached(storage, it)) { + Object key = HashingStorageIteratorKey.executeUncached(storage, it); + if (key instanceof PString) { + key = ((PString) key).getValueUncached(); + } + if (key instanceof TruffleString) { + keywords.add(new PKeyword((TruffleString) key, HashingStorageIteratorValue.executeUncached(storage, it))); + } + } + return keywords.isEmpty() ? PKeyword.EMPTY_KEYWORDS : keywords.toArray(new PKeyword[keywords.size()]); } @TruffleBoundary diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/method/MethodBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/method/MethodBuiltins.java index 6321bb0304..3788334ea9 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/method/MethodBuiltins.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/method/MethodBuiltins.java @@ -66,7 +66,6 @@ import com.oracle.graal.python.nodes.PRaiseNode; import com.oracle.graal.python.nodes.attributes.LookupAttributeInMRONode; import com.oracle.graal.python.nodes.builtins.FunctionNodes.GetDefaultsNode; -import com.oracle.graal.python.nodes.builtins.FunctionNodes.GetKeywordDefaultsNode; import com.oracle.graal.python.nodes.function.PythonBuiltinBaseNode; import com.oracle.graal.python.nodes.function.PythonBuiltinNode; import com.oracle.graal.python.nodes.function.builtins.PythonTernaryBuiltinNode; @@ -234,9 +233,13 @@ static Object defaults(PMethod self, public abstract static class GetMethodKwdefaultsNode extends PythonUnaryBuiltinNode { @Specialization static Object kwDefaults(PMethod self, - @Bind Node inliningTarget, - @Cached GetKeywordDefaultsNode getKeywordDefaultsNode) { - PKeyword[] kwdefaults = getKeywordDefaultsNode.execute(inliningTarget, self); + @Bind Node inliningTarget) { + Object fun = self.getFunction(); + if (fun instanceof PFunction function) { + return function.getKwDefaultsDict(PythonLanguage.get(inliningTarget)); + } + assert fun instanceof PBuiltinFunction; + PKeyword[] kwdefaults = ((PBuiltinFunction) fun).getKwDefaults(); return (kwdefaults.length > 0) ? PFactory.createDict(PythonLanguage.get(inliningTarget), kwdefaults) : PNone.NONE; } } diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/builtins/FunctionNodes.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/builtins/FunctionNodes.java index 3fc04f24cc..247565b44c 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/builtins/FunctionNodes.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/builtins/FunctionNodes.java @@ -44,7 +44,6 @@ import com.oracle.graal.python.builtins.objects.code.PCode; import com.oracle.graal.python.builtins.objects.function.PBuiltinFunction; import com.oracle.graal.python.builtins.objects.function.PFunction; -import com.oracle.graal.python.builtins.objects.function.PKeyword; import com.oracle.graal.python.builtins.objects.function.Signature; import com.oracle.graal.python.builtins.objects.method.PBuiltinMethod; import com.oracle.graal.python.builtins.objects.method.PMethod; @@ -126,66 +125,6 @@ static Object[] doBuiltinMethod(PBuiltinMethod builtinMethod) { } - @ImportStatic(PGuards.class) - @GenerateUncached - @GenerateInline - @GenerateCached(false) - public abstract static class GetKeywordDefaultsNode extends PNodeWithContext { - - public abstract PKeyword[] execute(Node inliningTarget, Object function); - - public abstract PKeyword[] execute(Node inliningTarget, PMethodBase method); - - /** - * Fast-path if method is a partial evaluation constant. - */ - public static PKeyword[] getMethodKeywords(PMethodBase method) { - CompilerAsserts.partialEvaluationConstant(method); - return getFunctionKeywords(method.getFunction()); - } - - /** - * Fast-path if the function is a partial evaluation constant. - */ - public static PKeyword[] getFunctionKeywords(Object fun) { - CompilerAsserts.partialEvaluationConstant(fun); - if (fun instanceof PFunction f) { - return f.getKwDefaults(); - } else if (fun instanceof PBuiltinFunction f) { - return f.getKwDefaults(); - } - throw CompilerDirectives.shouldNotReachHere(); - } - - @Specialization - static PKeyword[] doFunction(PFunction function) { - return function.getKwDefaults(); - } - - @Specialization - static PKeyword[] doBuiltinFunction(PBuiltinFunction builtinFunction) { - return builtinFunction.getKwDefaults(); - } - - @Specialization(guards = "isPFunction(function)") - static PKeyword[] doMethod(@SuppressWarnings("unused") PMethod method, - @Bind("method.getFunction()") Object function) { - return ((PFunction) function).getKwDefaults(); - } - - @Specialization(guards = "isPBuiltinFunction(method.getFunction())") - static PKeyword[] doMethodBuiltin(@SuppressWarnings("unused") PMethod method, - @Bind("method.getFunction()") Object function) { - return ((PBuiltinFunction) function).getKwDefaults(); - } - - @Specialization - static PKeyword[] doBuiltinMethod(PBuiltinMethod builtinMethod) { - return builtinMethod.getBuiltinFunction().getKwDefaults(); - } - - } - @GenerateUncached @GenerateInline @GenerateCached(false) From 6d222e6678f8ab5f4635f8b7f34e22d4956666fc Mon Sep 17 00:00:00 2001 From: Tim Felgentreff Date: Wed, 3 Jun 2026 17:05:02 +0200 Subject: [PATCH 2/3] [GR-76230] Match CPython kwdefaults dict assignment --- .../src/tests/test_functions.py | 18 +++++++----------- .../objects/function/FunctionBuiltins.java | 11 +++++++---- .../builtins/objects/function/PFunction.java | 1 - .../graal/python/nodes/ErrorMessages.java | 1 - 4 files changed, 14 insertions(+), 17 deletions(-) diff --git a/graalpython/com.oracle.graal.python.test/src/tests/test_functions.py b/graalpython/com.oracle.graal.python.test/src/tests/test_functions.py index 2223225e30..4f4d6b6a32 100644 --- a/graalpython/com.oracle.graal.python.test/src/tests/test_functions.py +++ b/graalpython/com.oracle.graal.python.test/src/tests/test_functions.py @@ -207,21 +207,17 @@ def foo(*, x=1): assigned["x"] = 4 assert foo() == 4 - def assign_invalid_kwdefaults(): - foo.__kwdefaults__ = {1: "not a keyword"} - - try: - assign_invalid_kwdefaults() - except TypeError as e: - assert "keyword names must be str" in str(e) - else: - assert False - assert foo.__kwdefaults__ is assigned - assigned[1] = "not a keyword" assert foo.__kwdefaults__ == {"x": 4, 1: "not a keyword"} assert foo() == 4 + non_string_kwdefaults = {1: "not a keyword"} + foo.__kwdefaults__ = non_string_kwdefaults + assert foo.__kwdefaults__ is non_string_kwdefaults + assert_raises(TypeError, foo) + non_string_kwdefaults["x"] = 5 + assert foo() == 5 + def test_mangled_kwonly_kwdefaults_dict_is_live(): class argmap: diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/function/FunctionBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/function/FunctionBuiltins.java index 424b093f6c..6b5af3e289 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/function/FunctionBuiltins.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/function/FunctionBuiltins.java @@ -64,6 +64,7 @@ import com.oracle.graal.python.builtins.objects.common.HashingStorageNodes.HashingStorageIteratorKey; import com.oracle.graal.python.builtins.objects.common.HashingStorageNodes.HashingStorageIteratorNext; import com.oracle.graal.python.builtins.objects.common.HashingStorageNodes.HashingStorageIteratorValue; +import com.oracle.graal.python.builtins.objects.common.HashingStorageNodes.HashingStorageLen; import com.oracle.graal.python.builtins.objects.common.KeywordsStorage; import com.oracle.graal.python.builtins.objects.common.SequenceNodes.GetObjectArrayNode; import com.oracle.graal.python.builtins.objects.dict.PDict; @@ -316,13 +317,15 @@ Object set(PFunction self, PDict arg) { Object key = assertNoJavaString(HashingStorageIteratorKey.executeUncached(storage, it)); if (key instanceof PString) { key = ((PString) key).getValueUncached(); - } else if (!(key instanceof TruffleString)) { - throw PRaiseNode.raiseStatic(this, PythonBuiltinClassType.TypeError, ErrorMessages.KEYWORD_NAMES_MUST_BE_STR_GOT_P, key); } - keywords.add(new PKeyword((TruffleString) key, HashingStorageIteratorValue.executeUncached(storage, it))); + if (key instanceof TruffleString) { + keywords.add(new PKeyword((TruffleString) key, HashingStorageIteratorValue.executeUncached(storage, it))); + } } PKeyword[] kwdefaults = keywords.isEmpty() ? PKeyword.EMPTY_KEYWORDS : keywords.toArray(new PKeyword[keywords.size()]); - arg.setDictStorage(new KeywordsStorage(kwdefaults)); + if (kwdefaults.length == HashingStorageLen.executeUncached(storage)) { + arg.setDictStorage(new KeywordsStorage(kwdefaults)); + } self.setKwDefaultsDict(arg); return PNone.NONE; } diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/function/PFunction.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/function/PFunction.java index bac8850e64..7dea94d971 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/function/PFunction.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/function/PFunction.java @@ -241,7 +241,6 @@ public Object getKwDefaultsDict(PythonLanguage language) { } public void setKwDefaultsDict(PDict defaults) { - assert defaults.getDictStorage() instanceof KeywordsStorage; this.codeStableAssumption.invalidate("kw defaults changed for function " + getName()); this.finalKwDefaultValues = null; // avoid leak, and make code that wrongly uses it crash this.kwDefaultValues = PKeyword.EMPTY_KEYWORDS; diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/ErrorMessages.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/ErrorMessages.java index d10917d28f..ac63e28669 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/ErrorMessages.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/ErrorMessages.java @@ -443,7 +443,6 @@ public abstract class ErrorMessages { public static final TruffleString ITERATION_VALUE_MUST_BE_GREATER_THAN_ZERO = tsLiteral("iteration value must be greater than 0."); public static final TruffleString ITERATION_VALUE_IS_TOO_GREAT = tsLiteral("iteration value is too great."); public static final TruffleString KEY_LENGTH_MUST_BE_GREATER_THAN_ZERO = tsLiteral("key length must be greater than 0."); - public static final TruffleString KEYWORD_NAMES_MUST_BE_STR_GOT_P = tsLiteral("keyword names must be str, get %p"); public static final TruffleString KEYWORDS_S_MUST_BE_STRINGS = tsLiteral("keywords must be strings"); public static final TruffleString KLASS_ARG_IS_NOT_HOST_OBJ = tsLiteral("instanceof second argument '%p' is not a Java class"); public static final TruffleString LAZY_INITIALIZATION_FAILED = tsLiteral("lazy initialization of type %N failed"); From ceb4b64ba7f6e18a44e48ffbaeb329b446099dbf Mon Sep 17 00:00:00 2001 From: Tim Felgentreff Date: Thu, 4 Jun 2026 10:05:17 +0200 Subject: [PATCH 3/3] Move setting func.__kwdefaults__ behind boundary --- .../oracle/graal/python/builtins/objects/function/PFunction.java | 1 + 1 file changed, 1 insertion(+) diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/function/PFunction.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/function/PFunction.java index 7dea94d971..b95a1bbc37 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/function/PFunction.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/function/PFunction.java @@ -240,6 +240,7 @@ public Object getKwDefaultsDict(PythonLanguage language) { return kwDefaultDict; } + @TruffleBoundary public void setKwDefaultsDict(PDict defaults) { this.codeStableAssumption.invalidate("kw defaults changed for function " + getName()); this.finalKwDefaultValues = null; // avoid leak, and make code that wrongly uses it crash