Skip to content

Commit a7fdd74

Browse files
committed
[GR-46187] Make delete/read/write nodes uncacheable and usable in bytecode interpreter and operations DSL.
PullRequest: graalpython/2782
2 parents 0902ee0 + 15e9662 commit a7fdd74

File tree

8 files changed

+357
-213
lines changed

8 files changed

+357
-213
lines changed
Lines changed: 151 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,151 @@
1+
/*
2+
* Copyright (c) 2023, 2023, Oracle and/or its affiliates. All rights reserved.
3+
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4+
*
5+
* The Universal Permissive License (UPL), Version 1.0
6+
*
7+
* Subject to the condition set forth below, permission is hereby granted to any
8+
* person obtaining a copy of this software, associated documentation and/or
9+
* data (collectively the "Software"), free of charge and under any and all
10+
* copyright rights in the Software, and any and all patent rights owned or
11+
* freely licensable by each licensor hereunder covering either (i) the
12+
* unmodified Software as contributed to or provided by such licensor, or (ii)
13+
* the Larger Works (as defined below), to deal in both
14+
*
15+
* (a) the Software, and
16+
*
17+
* (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if
18+
* one is included with the Software each a "Larger Work" to which the Software
19+
* is contributed by such licensors),
20+
*
21+
* without restriction, including without limitation the rights to copy, create
22+
* derivative works of, display, perform, and distribute the Software and make,
23+
* use, sell, offer for sale, import, export, have made, and have sold the
24+
* Software and the Larger Work(s), and to sublicense the foregoing rights on
25+
* either these or other terms.
26+
*
27+
* This license is subject to the following condition:
28+
*
29+
* The above copyright notice and either this complete permission notice or at a
30+
* minimum a reference to the UPL must be included in all copies or substantial
31+
* portions of the Software.
32+
*
33+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
34+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
35+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
36+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
37+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
38+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
39+
* SOFTWARE.
40+
*/
41+
package com.oracle.graal.python.lib;
42+
43+
import static com.oracle.graal.python.builtins.objects.PNone.NO_VALUE;
44+
import static com.oracle.graal.python.nodes.SpecialMethodNames.T___FORMAT__;
45+
import static com.oracle.graal.python.nodes.StringLiterals.T_EMPTY_STRING;
46+
import static com.oracle.graal.python.runtime.exception.PythonErrorType.TypeError;
47+
48+
import com.oracle.graal.python.builtins.PythonBuiltinClassType;
49+
import com.oracle.graal.python.builtins.objects.PNone;
50+
import com.oracle.graal.python.builtins.objects.type.SpecialMethodSlot;
51+
import com.oracle.graal.python.nodes.ErrorMessages;
52+
import com.oracle.graal.python.nodes.PGuards;
53+
import com.oracle.graal.python.nodes.PNodeWithContext;
54+
import com.oracle.graal.python.nodes.PRaiseNode;
55+
import com.oracle.graal.python.nodes.attributes.LookupCallableSlotInMRONode;
56+
import com.oracle.graal.python.nodes.call.special.CallBinaryMethodNode;
57+
import com.oracle.graal.python.nodes.call.special.LookupAndCallBinaryNode;
58+
import com.oracle.graal.python.nodes.object.GetClassNode;
59+
import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
60+
import com.oracle.truffle.api.dsl.Cached;
61+
import com.oracle.truffle.api.dsl.Cached.Exclusive;
62+
import com.oracle.truffle.api.dsl.Cached.Shared;
63+
import com.oracle.truffle.api.dsl.Fallback;
64+
import com.oracle.truffle.api.dsl.GenerateCached;
65+
import com.oracle.truffle.api.dsl.GenerateInline;
66+
import com.oracle.truffle.api.dsl.GenerateUncached;
67+
import com.oracle.truffle.api.dsl.Specialization;
68+
import com.oracle.truffle.api.frame.Frame;
69+
import com.oracle.truffle.api.frame.VirtualFrame;
70+
import com.oracle.truffle.api.nodes.Node;
71+
import com.oracle.truffle.api.strings.TruffleString;
72+
73+
/**
74+
* Equivalent of CPython's {@code PyObject_Format}. The {@code formatSpec} argument must be a Python
75+
* string, otherwise this raises a SystemError. Unlike CPython's {@code PyObject_Format}, this does
76+
* not accept native {@code NULL} as {@code formatSpec}. Convert it to an empty string to get
77+
* equivalent behavior.
78+
*/
79+
@GenerateInline
80+
@GenerateUncached
81+
public abstract class PyObjectFormat extends PNodeWithContext {
82+
public final Object executeNonInlined(VirtualFrame frame, Object obj, Object formatSpec) {
83+
return execute(frame, null, obj, formatSpec);
84+
}
85+
86+
public abstract Object execute(VirtualFrame frame, Node node, Object obj, Object formatSpec);
87+
88+
@Specialization
89+
static Object doNone(VirtualFrame frame, Node inliningTarget, Object obj, PNone formatSpec,
90+
@Shared("impl") @Cached PyObjectFormatStr formatStr) {
91+
return formatStr.execute(frame, inliningTarget, obj, T_EMPTY_STRING);
92+
}
93+
94+
@Fallback
95+
static Object doOthers(VirtualFrame frame, Node inliningTarget, Object obj, Object formatSpec,
96+
@Shared("impl") @Cached PyObjectFormatStr formatStr) {
97+
return formatStr.execute(frame, inliningTarget, obj, formatSpec);
98+
}
99+
100+
@GenerateInline
101+
@GenerateUncached
102+
@GenerateCached(false)
103+
public abstract static class PyObjectFormatStr extends PNodeWithContext {
104+
public abstract Object execute(Frame frame, Node inliningTarget, Object obj, Object formatSpec);
105+
106+
static boolean isEmptyString(Object formatSpec) {
107+
// to keep the fast-path optimization guard simple, we ignore empty PStrings
108+
return (formatSpec instanceof TruffleString && ((TruffleString) formatSpec).isEmpty());
109+
}
110+
111+
@Specialization(guards = {"isString(obj)", "isEmptyString(formatSpec)"})
112+
static Object doString(Object obj, Object formatSpec) {
113+
return obj;
114+
}
115+
116+
@Specialization(guards = {"isEmptyString(formatSpec)"})
117+
static Object doLong(long obj, Object formatSpec) {
118+
return obj;
119+
}
120+
121+
// Note: PRaiseNode is @Exclusive to workaround a bug in DSL
122+
@Specialization(guards = "isString(formatSpec)")
123+
static Object doGeneric(VirtualFrame frame, Object obj, Object formatSpec,
124+
@Cached(parameters = "Format", inline = false) LookupAndCallBinaryNode callFormat,
125+
@Exclusive @Cached(inline = false) PRaiseNode raiseNode) {
126+
Object res = callFormat.executeObject(frame, obj, formatSpec);
127+
if (res == NO_VALUE) {
128+
throw raiseNode.raise(TypeError, ErrorMessages.TYPE_DOESNT_DEFINE_FORMAT, obj);
129+
}
130+
if (!PGuards.isString(res)) {
131+
throw raiseNode.raise(TypeError, ErrorMessages.S_MUST_RETURN_S_NOT_P, T___FORMAT__, "str", res);
132+
}
133+
return res;
134+
}
135+
136+
@Specialization(guards = "isString(formatSpec)", replaces = "doGeneric")
137+
@TruffleBoundary
138+
static Object doGenericUncached(Object obj, Object formatSpec) {
139+
Object klass = GetClassNode.getUncached().execute(obj);
140+
Object slot = LookupCallableSlotInMRONode.getUncached(SpecialMethodSlot.Format).execute(klass);
141+
return CallBinaryMethodNode.getUncached().executeObject(null, slot, obj, formatSpec);
142+
}
143+
144+
// Note: PRaiseNode is @Exclusive to workaround a bug in DSL
145+
@Fallback
146+
static Object doNonStringFormat(Object obj, Object formatSpec,
147+
@Exclusive @Cached(inline = false) PRaiseNode raiseNode) {
148+
throw raiseNode.raise(PythonBuiltinClassType.SystemError, ErrorMessages.S_MUST_BE_S_NOT_P, "Format specifier", "string", formatSpec);
149+
}
150+
}
151+
}

graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/attributes/DeleteAttributeNode.java

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2017, 2022, Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 2017, 2023, 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
@@ -40,13 +40,19 @@
4040
*/
4141
package com.oracle.graal.python.nodes.attributes;
4242

43+
import com.oracle.graal.python.builtins.objects.type.SpecialMethodSlot;
4344
import com.oracle.graal.python.nodes.PNodeWithContext;
45+
import com.oracle.graal.python.nodes.call.special.CallBinaryMethodNode;
4446
import com.oracle.graal.python.nodes.call.special.LookupAndCallBinaryNode;
47+
import com.oracle.graal.python.nodes.object.GetClassNode;
48+
import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
4549
import com.oracle.truffle.api.dsl.Cached;
50+
import com.oracle.truffle.api.dsl.GenerateUncached;
4651
import com.oracle.truffle.api.dsl.NeverDefault;
4752
import com.oracle.truffle.api.dsl.Specialization;
4853
import com.oracle.truffle.api.frame.VirtualFrame;
4954

55+
@GenerateUncached
5056
public abstract class DeleteAttributeNode extends PNodeWithContext {
5157
@NeverDefault
5258
public static DeleteAttributeNode create() {
@@ -60,4 +66,12 @@ protected void doIt(VirtualFrame frame, Object object, Object key,
6066
@Cached("create(DelAttr)") LookupAndCallBinaryNode call) {
6167
call.executeObject(frame, object, key);
6268
}
69+
70+
@Specialization(replaces = "doIt")
71+
@TruffleBoundary
72+
protected void doItUncached(Object object, Object key) {
73+
Object klass = GetClassNode.getUncached().execute(object);
74+
Object method = LookupCallableSlotInMRONode.getUncached(SpecialMethodSlot.DelAttr).execute(klass);
75+
CallBinaryMethodNode.getUncached().executeObject(null, method, object, key);
76+
}
6377
}

graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/bytecode/PBytecodeRootNode.java

Lines changed: 16 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -288,10 +288,10 @@ public final class PBytecodeRootNode extends PRootNode implements BytecodeOSRNod
288288
private static final GetAwaitableNode UNCACHED_OBJECT_GET_AWAITABLE = GetAwaitableNode.getUncached();
289289
private static final NodeSupplier<PyObjectSetAttr> NODE_OBJECT_SET_ATTR = PyObjectSetAttr::create;
290290
private static final PyObjectSetAttr UNCACHED_OBJECT_SET_ATTR = PyObjectSetAttr.getUncached();
291-
private static final NodeSupplier<ReadGlobalOrBuiltinNode> NODE_READ_GLOBAL_OR_BUILTIN_BUILD_CLASS = () -> ReadGlobalOrBuiltinNode.create(T___BUILD_CLASS__);
292-
private static final NodeFunction<TruffleString, ReadGlobalOrBuiltinNode> NODE_READ_GLOBAL_OR_BUILTIN = ReadGlobalOrBuiltinNode::create;
293-
private static final NodeFunction<TruffleString, ReadNameNode> NODE_READ_NAME = ReadNameNode::create;
294-
private static final NodeFunction<TruffleString, WriteNameNode> NODE_WRITE_NAME = WriteNameNode::create;
291+
private static final NodeSupplier<ReadGlobalOrBuiltinNode> NODE_READ_GLOBAL_OR_BUILTIN_BUILD_CLASS = () -> ReadGlobalOrBuiltinNode.create();
292+
private static final NodeSupplier<ReadGlobalOrBuiltinNode> NODE_READ_GLOBAL_OR_BUILTIN = ReadGlobalOrBuiltinNode::create;
293+
private static final NodeSupplier<ReadNameNode> NODE_READ_NAME = ReadNameNode::create;
294+
private static final NodeSupplier<WriteNameNode> NODE_WRITE_NAME = WriteNameNode::create;
295295
private static final ReadGlobalOrBuiltinNode UNCACHED_READ_GLOBAL_OR_BUILTIN = ReadGlobalOrBuiltinNode.getUncached();
296296
private static final NodeSupplier<PyObjectSetItem> NODE_OBJECT_SET_ITEM = PyObjectSetItem::create;
297297
private static final PyObjectSetItem UNCACHED_OBJECT_SET_ITEM = PyObjectSetItem.getUncached();
@@ -355,8 +355,8 @@ public final class PBytecodeRootNode extends PRootNode implements BytecodeOSRNod
355355
private static final NodeSupplier<SendNode> NODE_SEND = SendNode::create;
356356
private static final NodeSupplier<ThrowNode> NODE_THROW = ThrowNode::create;
357357
private static final WriteGlobalNode UNCACHED_WRITE_GLOBAL = WriteGlobalNode.getUncached();
358-
private static final NodeFunction<TruffleString, WriteGlobalNode> NODE_WRITE_GLOBAL = WriteGlobalNode::create;
359-
private static final NodeFunction<TruffleString, DeleteGlobalNode> NODE_DELETE_GLOBAL = DeleteGlobalNode::create;
358+
private static final NodeSupplier<WriteGlobalNode> NODE_WRITE_GLOBAL = WriteGlobalNode::create;
359+
private static final NodeSupplier<DeleteGlobalNode> NODE_DELETE_GLOBAL = DeleteGlobalNode::create;
360360
private static final PrintExprNode UNCACHED_PRINT_EXPR = PrintExprNode.getUncached();
361361
private static final NodeSupplier<PrintExprNode> NODE_PRINT_EXPR = PrintExprNode::create;
362362
private static final ReadFromLocalsNode UNCACHED_READ_FROM_LOCALS = ReadFromLocalsNode.getUncached();
@@ -4621,21 +4621,21 @@ private void bytecodeDeleteFast(Frame localFrame, int bci, Node[] localNodes, in
46214621

46224622
@BytecodeInterpreterSwitch
46234623
private int bytecodeLoadGlobal(VirtualFrame virtualFrame, Object globals, int stackTop, int bci, TruffleString localName, Node[] localNodes, boolean useCachedNodes) {
4624-
ReadGlobalOrBuiltinNode read = insertChildNode(localNodes, bci, UNCACHED_READ_GLOBAL_OR_BUILTIN, ReadGlobalOrBuiltinNodeGen.class, NODE_READ_GLOBAL_OR_BUILTIN, localName, useCachedNodes);
4624+
ReadGlobalOrBuiltinNode read = insertChildNode(localNodes, bci, UNCACHED_READ_GLOBAL_OR_BUILTIN, ReadGlobalOrBuiltinNodeGen.class, NODE_READ_GLOBAL_OR_BUILTIN, useCachedNodes);
46254625
virtualFrame.setObject(++stackTop, read.read(virtualFrame, globals, localName));
46264626
return stackTop;
46274627
}
46284628

46294629
private void bytecodeDeleteGlobal(VirtualFrame virtualFrame, Object globals, int bci, int oparg, Node[] localNodes, TruffleString[] localNames) {
46304630
TruffleString varname = localNames[oparg];
4631-
DeleteGlobalNode deleteGlobalNode = insertChildNode(localNodes, bci, DeleteGlobalNodeGen.class, NODE_DELETE_GLOBAL, varname);
4632-
deleteGlobalNode.executeWithGlobals(virtualFrame, globals);
4631+
DeleteGlobalNode deleteGlobalNode = insertChildNode(localNodes, bci, DeleteGlobalNodeGen.class, NODE_DELETE_GLOBAL);
4632+
deleteGlobalNode.executeWithGlobals(virtualFrame, globals, varname);
46334633
}
46344634

46354635
@BytecodeInterpreterSwitch
46364636
private int bytecodeStoreGlobal(VirtualFrame virtualFrame, Object globals, int stackTop, int bci, int oparg, Node[] localNodes, TruffleString[] localNames, boolean useCachedNodes) {
46374637
TruffleString varname = localNames[oparg];
4638-
WriteGlobalNode writeGlobalNode = insertChildNode(localNodes, bci, UNCACHED_WRITE_GLOBAL, WriteGlobalNodeGen.class, NODE_WRITE_GLOBAL, varname, useCachedNodes);
4638+
WriteGlobalNode writeGlobalNode = insertChildNode(localNodes, bci, UNCACHED_WRITE_GLOBAL, WriteGlobalNodeGen.class, NODE_WRITE_GLOBAL, useCachedNodes);
46394639
writeGlobalNode.write(virtualFrame, globals, varname, virtualFrame.getObject(stackTop));
46404640
virtualFrame.setObject(stackTop--, null);
46414641
return stackTop;
@@ -4668,8 +4668,8 @@ private void bytecodeDeleteName(VirtualFrame virtualFrame, Object globals, Objec
46684668
PyObjectDelItem delItemNode = insertChildNode(localNodes, bci, UNCACHED_OBJECT_DEL_ITEM, PyObjectDelItemNodeGen.class, NODE_OBJECT_DEL_ITEM, useCachedNodes);
46694669
delItemNode.execute(virtualFrame, locals, varname);
46704670
} else {
4671-
DeleteGlobalNode deleteGlobalNode = insertChildNode(localNodes, bci + 1, DeleteGlobalNodeGen.class, NODE_DELETE_GLOBAL, varname);
4672-
deleteGlobalNode.executeWithGlobals(virtualFrame, globals);
4671+
DeleteGlobalNode deleteGlobalNode = insertChildNode(localNodes, bci + 1, DeleteGlobalNodeGen.class, NODE_DELETE_GLOBAL);
4672+
deleteGlobalNode.executeWithGlobals(virtualFrame, globals, varname);
46734673
}
46744674
}
46754675

@@ -4974,8 +4974,8 @@ private void bytecodeCallMethodVarargs(VirtualFrame virtualFrame, int stackTop,
49744974
@BytecodeInterpreterSwitch
49754975
private int bytecodeLoadName(VirtualFrame virtualFrame, int initialStackTop, int bci, int oparg, Node[] localNodes, TruffleString[] localNames) {
49764976
int stackTop = initialStackTop;
4977-
ReadNameNode readNameNode = insertChildNode(localNodes, bci, ReadNameNodeGen.class, NODE_READ_NAME, localNames[oparg]);
4978-
virtualFrame.setObject(++stackTop, readNameNode.execute(virtualFrame));
4977+
ReadNameNode readNameNode = insertChildNode(localNodes, bci, ReadNameNodeGen.class, NODE_READ_NAME);
4978+
virtualFrame.setObject(++stackTop, readNameNode.execute(virtualFrame, localNames[oparg]));
49794979
return stackTop;
49804980
}
49814981

@@ -5209,8 +5209,8 @@ private int bytecodeStoreName(VirtualFrame virtualFrame, int initialStackTop, in
52095209
int stackTop = initialStackTop;
52105210
Object value = virtualFrame.getObject(stackTop);
52115211
virtualFrame.setObject(stackTop--, null);
5212-
WriteNameNode writeNameNode = insertChildNode(localNodes, bci, WriteNameNodeGen.class, NODE_WRITE_NAME, localNames[oparg]);
5213-
writeNameNode.execute(virtualFrame, value);
5212+
WriteNameNode writeNameNode = insertChildNode(localNodes, bci, WriteNameNodeGen.class, NODE_WRITE_NAME);
5213+
writeNameNode.execute(virtualFrame, localNames[oparg], value);
52145214
return stackTop;
52155215
}
52165216

graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/frame/DeleteGlobalNode.java

Lines changed: 14 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2018, 2022, Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 2018, 2023, 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
@@ -45,47 +45,49 @@
4545
import com.oracle.graal.python.lib.PyObjectDelItem;
4646
import com.oracle.graal.python.nodes.PNodeWithContext;
4747
import com.oracle.graal.python.nodes.attributes.DeleteAttributeNode;
48+
import com.oracle.truffle.api.CompilerAsserts;
4849
import com.oracle.truffle.api.dsl.Cached;
4950
import com.oracle.truffle.api.dsl.Cached.Shared;
51+
import com.oracle.truffle.api.dsl.GenerateUncached;
5052
import com.oracle.truffle.api.dsl.Specialization;
5153
import com.oracle.truffle.api.frame.VirtualFrame;
5254
import com.oracle.truffle.api.strings.TruffleString;
5355

56+
@GenerateUncached
5457
public abstract class DeleteGlobalNode extends PNodeWithContext {
55-
private final TruffleString attributeId;
56-
57-
DeleteGlobalNode(TruffleString attributeId) {
58-
this.attributeId = attributeId;
58+
public static DeleteGlobalNode create() {
59+
return DeleteGlobalNodeGen.create();
5960
}
6061

61-
public static DeleteGlobalNode create(TruffleString attributeId) {
62-
return DeleteGlobalNodeGen.create(attributeId);
62+
public final void executeWithGlobals(VirtualFrame frame, Object globals, TruffleString attributeId) {
63+
CompilerAsserts.partialEvaluationConstant(attributeId);
64+
executeWithGlobalsImpl(frame, globals, attributeId);
6365
}
6466

65-
public abstract void executeWithGlobals(VirtualFrame frame, Object globals);
67+
public abstract void executeWithGlobalsImpl(VirtualFrame frame, Object globals, TruffleString attributeId);
6668

6769
@Specialization(guards = {"isSingleContext()", "globals == cachedGlobals"}, limit = "1")
68-
void deleteDictCached(VirtualFrame frame, @SuppressWarnings("unused") PDict globals,
70+
void deleteDictCached(VirtualFrame frame, @SuppressWarnings("unused") PDict globals, TruffleString attributeId,
6971
@Cached(value = "globals", weak = true) PDict cachedGlobals,
7072
@Shared("delItem") @Cached PyObjectDelItem deleteNode) {
7173
deleteNode.execute(frame, cachedGlobals, attributeId);
7274
}
7375

7476
@Specialization(replaces = "deleteDictCached")
75-
void deleteDict(VirtualFrame frame, PDict globals,
77+
void deleteDict(VirtualFrame frame, PDict globals, TruffleString attributeId,
7678
@Shared("delItem") @Cached PyObjectDelItem deleteNode) {
7779
deleteNode.execute(frame, globals, attributeId);
7880
}
7981

8082
@Specialization(guards = {"isSingleContext()", "globals == cachedGlobals"}, limit = "1")
81-
void deleteModuleCached(VirtualFrame frame, @SuppressWarnings("unused") PythonModule globals,
83+
void deleteModuleCached(VirtualFrame frame, @SuppressWarnings("unused") PythonModule globals, TruffleString attributeId,
8284
@Cached(value = "globals", weak = true) PythonModule cachedGlobals,
8385
@Cached DeleteAttributeNode storeNode) {
8486
storeNode.execute(frame, cachedGlobals, attributeId);
8587
}
8688

8789
@Specialization(replaces = "deleteModuleCached")
88-
void deleteModule(VirtualFrame frame, PythonModule globals,
90+
void deleteModule(VirtualFrame frame, PythonModule globals, TruffleString attributeId,
8991
@Cached DeleteAttributeNode storeNode) {
9092
storeNode.execute(frame, globals, attributeId);
9193
}

0 commit comments

Comments
 (0)