Skip to content

Commit c820002

Browse files
committed
Implement __delete__ for GetSetDescriptor
1 parent 2993208 commit c820002

File tree

7 files changed

+160
-11
lines changed

7 files changed

+160
-11
lines changed

graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/Builtin.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,8 @@
4747

4848
boolean isSetter() default false;
4949

50+
boolean allowsDelete() default false;
51+
5052
boolean takesVarArgs() default false;
5153

5254
boolean varArgsMarker() default false;

graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/PythonBuiltins.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,7 @@ public void initialize(PythonCore core) {
9393
assert !builtin.isClassmethod() && !builtin.isStaticmethod();
9494
PBuiltinFunction get = builtin.isGetter() ? function : null;
9595
PBuiltinFunction set = builtin.isSetter() ? function : null;
96-
callable = core.factory().createGetSetDescriptor(get, set, builtin.name(), null);
96+
callable = core.factory().createGetSetDescriptor(get, set, builtin.name(), null, builtin.allowsDelete());
9797
} else if (builtin.isClassmethod()) {
9898
assert !builtin.isStaticmethod();
9999
callable = core.factory().createClassmethod(function);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
/*
2+
* Copyright (c) 2020, 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.builtins.objects.getsetdescriptor;
42+
43+
public class DescriptorDeleteMarker {
44+
public static final DescriptorDeleteMarker INSTANCE = new DescriptorDeleteMarker();
45+
46+
private DescriptorDeleteMarker() {
47+
}
48+
}

graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/getsetdescriptor/GetSetDescriptor.java

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,14 +50,20 @@ public final class GetSetDescriptor extends PythonBuiltinObject implements Bound
5050
private final Object get;
5151
private final Object set;
5252
private final String name;
53+
private final boolean allowsDelete;
5354
private final LazyPythonClass type;
5455

5556
public GetSetDescriptor(Object get, Object set, String name, LazyPythonClass type) {
57+
this(get, set, name, type, false);
58+
}
59+
60+
public GetSetDescriptor(Object get, Object set, String name, LazyPythonClass type, boolean allowsDelete) {
5661
super(PythonBuiltinClassType.GetSetDescriptor, PythonBuiltinClassType.GetSetDescriptor.newInstance());
5762
this.get = get;
5863
this.set = set;
5964
this.name = name;
6065
this.type = type;
66+
this.allowsDelete = allowsDelete;
6167
}
6268

6369
public Object getGet() {
@@ -76,6 +82,10 @@ public LazyPythonClass getType() {
7682
return type;
7783
}
7884

85+
public boolean allowsDelete() {
86+
return allowsDelete;
87+
}
88+
7989
public GetSetDescriptor boundToObject(PythonBuiltinClassType klass, PythonObjectFactory factory) {
8090
if (klass == type) {
8191
return this;

graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/getsetdescriptor/GetSetDescriptorTypeBuiltins.java

Lines changed: 93 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@
4040
*/
4141
package com.oracle.graal.python.builtins.objects.getsetdescriptor;
4242

43+
import static com.oracle.graal.python.nodes.SpecialMethodNames.__DELETE__;
4344
import static com.oracle.graal.python.nodes.SpecialMethodNames.__GET__;
4445
import static com.oracle.graal.python.nodes.SpecialMethodNames.__REPR__;
4546
import static com.oracle.graal.python.nodes.SpecialMethodNames.__SET__;
@@ -61,11 +62,14 @@
6162
import com.oracle.graal.python.builtins.objects.type.TypeNodesFactory.IsSameTypeNodeGen;
6263
import com.oracle.graal.python.nodes.ErrorMessages;
6364
import com.oracle.graal.python.nodes.PGuards;
65+
import com.oracle.graal.python.nodes.PNodeWithContext;
66+
import com.oracle.graal.python.nodes.PRaiseNode;
6467
import com.oracle.graal.python.nodes.attributes.ReadAttributeFromObjectNode;
6568
import com.oracle.graal.python.nodes.attributes.WriteAttributeToObjectNode;
6669
import com.oracle.graal.python.nodes.call.special.CallBinaryMethodNode;
6770
import com.oracle.graal.python.nodes.call.special.CallUnaryMethodNode;
6871
import com.oracle.graal.python.nodes.function.PythonBuiltinBaseNode;
72+
import com.oracle.graal.python.nodes.function.builtins.PythonBinaryBuiltinNode;
6973
import com.oracle.graal.python.nodes.function.builtins.PythonTernaryBuiltinNode;
7074
import com.oracle.graal.python.nodes.function.builtins.PythonUnaryBuiltinNode;
7175
import com.oracle.graal.python.nodes.object.GetLazyClassNode;
@@ -97,20 +101,24 @@ Object repr(GetSetDescriptor descr) {
97101
}
98102
}
99103

100-
abstract static class GetSetNode extends PythonTernaryBuiltinNode {
104+
abstract static class DescriptorCheckNode extends PNodeWithContext {
101105

102106
@Child private GetMroNode getMroNode;
103107
@Child private GetNameNode getNameNode;
104108
@Child private IsSameTypeNode isSameTypeNode;
105109
@Child private GetLazyClassNode getLazyClassNode;
110+
@Child private PRaiseNode raiseNode;
106111

107112
private final IsBuiltinClassProfile isBuiltinPythonClassObject = IsBuiltinClassProfile.create();
108113
private final ConditionProfile isBuiltinProfile = ConditionProfile.createBinaryProfile();
109114
private final IsBuiltinClassProfile isBuiltinClassProfile = IsBuiltinClassProfile.create();
110115
private final BranchProfile errorBranch = BranchProfile.create();
111116

117+
public abstract boolean execute(LazyPythonClass descrType, String name, Object obj);
118+
112119
// https://github.com/python/cpython/blob/e8b19656396381407ad91473af5da8b0d4346e88/Objects/descrobject.c#L70
113-
protected boolean descr_check(LazyPythonClass descrType, String name, Object obj) {
120+
@Specialization
121+
boolean descr_check(LazyPythonClass descrType, String name, Object obj) {
114122
if (PGuards.isNone(obj)) {
115123
// object's descriptors (__class__,...) need to work on every object including None
116124
if (!isBuiltinPythonClassObject.profileClass(descrType, PythonBuiltinClassType.PythonObject)) {
@@ -133,7 +141,7 @@ protected boolean descr_check(LazyPythonClass descrType, String name, Object obj
133141
}
134142
}
135143
errorBranch.enter();
136-
throw raise(TypeError, ErrorMessages.DESC_S_FOR_S_DOESNT_APPLY_TO_S, name, getTypeName(descrType), getTypeName(type));
144+
throw getRaiseNode().raise(TypeError, ErrorMessages.DESC_S_FOR_S_DOESNT_APPLY_TO_S, name, getTypeName(descrType), getTypeName(type));
137145
}
138146

139147
private LazyPythonClass getLazyClass(Object obj) {
@@ -152,7 +160,7 @@ private PythonAbstractClass[] getMro(LazyPythonClass clazz) {
152160
return getMroNode.execute(clazz);
153161
}
154162

155-
protected Object getTypeName(LazyPythonClass descrType) {
163+
private Object getTypeName(LazyPythonClass descrType) {
156164
if (getNameNode == null) {
157165
CompilerDirectives.transferToInterpreterAndInvalidate();
158166
getNameNode = insert(GetNameNode.create());
@@ -167,18 +175,28 @@ private boolean isSameType(Object left, Object right) {
167175
}
168176
return isSameTypeNode.execute(left, right);
169177
}
178+
179+
private PRaiseNode getRaiseNode() {
180+
if (raiseNode == null) {
181+
CompilerDirectives.transferToInterpreterAndInvalidate();
182+
raiseNode = insert(PRaiseNode.create());
183+
}
184+
return raiseNode;
185+
}
170186
}
171187

172188
@Builtin(name = __GET__, minNumOfPositionalArgs = 2, maxNumOfPositionalArgs = 3)
173189
@GenerateNodeFactory
174-
abstract static class GetSetGetNode extends GetSetNode {
190+
abstract static class GetSetGetNode extends PythonTernaryBuiltinNode {
191+
@Child private GetNameNode getNameNode;
175192
private final BranchProfile branchProfile = BranchProfile.create();
176193

177194
// https://github.com/python/cpython/blob/e8b19656396381407ad91473af5da8b0d4346e88/Objects/descrobject.c#L149
178195
@Specialization
179196
Object get(VirtualFrame frame, GetSetDescriptor descr, Object obj, @SuppressWarnings("unused") Object type,
197+
@Cached DescriptorCheckNode descriptorCheckNode,
180198
@Cached("create()") CallUnaryMethodNode callNode) {
181-
if (descr_check(descr.getType(), descr.getName(), obj)) {
199+
if (descriptorCheckNode.execute(descr.getType(), descr.getName(), obj)) {
182200
return descr;
183201
}
184202
if (descr.getGet() != null) {
@@ -191,9 +209,10 @@ Object get(VirtualFrame frame, GetSetDescriptor descr, Object obj, @SuppressWarn
191209

192210
@Specialization
193211
Object getSlot(HiddenKeyDescriptor descr, Object obj, @SuppressWarnings("unused") Object type,
212+
@Cached DescriptorCheckNode descriptorCheckNode,
194213
@Cached("create()") ReadAttributeFromObjectNode readNode,
195214
@Cached("createBinaryProfile()") ConditionProfile profile) {
196-
if (descr_check(descr.getType(), descr.getKey().getName(), obj)) {
215+
if (descriptorCheckNode.execute(descr.getType(), descr.getKey().getName(), obj)) {
197216
return descr;
198217
}
199218
Object val = readNode.execute(obj, descr.getKey());
@@ -202,17 +221,27 @@ Object getSlot(HiddenKeyDescriptor descr, Object obj, @SuppressWarnings("unused"
202221
}
203222
throw raise(AttributeError, descr.getKey().getName());
204223
}
224+
225+
private Object getTypeName(LazyPythonClass descrType) {
226+
if (getNameNode == null) {
227+
CompilerDirectives.transferToInterpreterAndInvalidate();
228+
getNameNode = insert(GetNameNode.create());
229+
}
230+
return getNameNode.execute(descrType);
231+
}
205232
}
206233

207234
@Builtin(name = __SET__, minNumOfPositionalArgs = 3)
208235
@GenerateNodeFactory
209-
abstract static class GetSetSetNode extends GetSetNode {
236+
abstract static class GetSetSetNode extends PythonTernaryBuiltinNode {
237+
@Child private GetNameNode getNameNode;
210238
private final BranchProfile branchProfile = BranchProfile.create();
211239

212240
@Specialization
213241
Object set(VirtualFrame frame, GetSetDescriptor descr, Object obj, Object value,
242+
@Cached DescriptorCheckNode descriptorCheckNode,
214243
@Cached("create()") CallBinaryMethodNode callNode) {
215-
if (descr_check(descr.getType(), descr.getName(), obj)) {
244+
if (descriptorCheckNode.execute(descr.getType(), descr.getName(), obj)) {
216245
return descr;
217246
}
218247
if (descr.getSet() != null) {
@@ -225,11 +254,65 @@ Object set(VirtualFrame frame, GetSetDescriptor descr, Object obj, Object value,
225254

226255
@Specialization
227256
Object setSlot(HiddenKeyDescriptor descr, Object obj, Object value,
257+
@Cached DescriptorCheckNode descriptorCheckNode,
228258
@Cached("create()") WriteAttributeToObjectNode writeNode) {
229-
if (descr_check(descr.getType(), descr.getKey().getName(), obj)) {
259+
if (descriptorCheckNode.execute(descr.getType(), descr.getKey().getName(), obj)) {
230260
return descr;
231261
}
232262
return writeNode.execute(obj, descr.getKey(), value);
233263
}
264+
265+
private Object getTypeName(LazyPythonClass descrType) {
266+
if (getNameNode == null) {
267+
CompilerDirectives.transferToInterpreterAndInvalidate();
268+
getNameNode = insert(GetNameNode.create());
269+
}
270+
return getNameNode.execute(descrType);
271+
}
272+
}
273+
274+
@Builtin(name = __DELETE__, minNumOfPositionalArgs = 2)
275+
@GenerateNodeFactory
276+
abstract static class GetSetDeleteNode extends PythonBinaryBuiltinNode {
277+
@Child private GetNameNode getNameNode;
278+
private final BranchProfile branchProfile = BranchProfile.create();
279+
280+
@Specialization
281+
Object delete(VirtualFrame frame, GetSetDescriptor descr, Object obj,
282+
@Cached DescriptorCheckNode descriptorCheckNode,
283+
@Cached("create()") CallBinaryMethodNode callNode) {
284+
if (descriptorCheckNode.execute(descr.getType(), descr.getName(), obj)) {
285+
return descr;
286+
}
287+
if (descr.allowsDelete()) {
288+
return callNode.executeObject(frame, descr.getSet(), obj, DescriptorDeleteMarker.INSTANCE);
289+
} else {
290+
branchProfile.enter();
291+
if (descr.getSet() != null) {
292+
throw raise(TypeError, ErrorMessages.CANNOT_DELETE_ATTRIBUTE, getTypeName(descr.getType()), descr.getName());
293+
} else {
294+
throw raise(AttributeError, ErrorMessages.READONLY_ATTRIBUTE);
295+
}
296+
}
297+
}
298+
299+
@Specialization
300+
Object deleteSlot(HiddenKeyDescriptor descr, Object obj,
301+
@Cached DescriptorCheckNode descriptorCheckNode,
302+
@Cached("create()") WriteAttributeToObjectNode writeNode) {
303+
if (descriptorCheckNode.execute(descr.getType(), descr.getKey().getName(), obj)) {
304+
return descr;
305+
}
306+
writeNode.execute(obj, descr.getKey(), PNone.NO_VALUE);
307+
return PNone.NONE;
308+
}
309+
310+
private Object getTypeName(LazyPythonClass descrType) {
311+
if (getNameNode == null) {
312+
CompilerDirectives.transferToInterpreterAndInvalidate();
313+
getNameNode = insert(GetNameNode.create());
314+
}
315+
return getNameNode.execute(descrType);
316+
}
234317
}
235318
}

graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/ErrorMessages.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,7 @@ public abstract class ErrorMessages {
113113
public static final String CANNOT_CREATE_CALL_TARGET = "cannot create a call target from the code object: %p";
114114
public static final String CANNOT_CREATE_INSTANCES = "cannot create '%s' instances";
115115
public static final String CANNOT_CREATE_WEAK_REFERENCE_TO = "cannot create weak reference to '%p' object";
116+
public static final String CANNOT_DELETE_ATTRIBUTE = "can't delete %s.%s";
116117
public static final String CANNOT_GET_SHAPE_OF_NATIVE_CLS = "cannot get shape of native class";
117118
public static final String CANNOT_HANDLE_ZIP_FILE = "cannot handle Zip file: '%s'";
118119
public static final String CANNOT_IMPORT_NAME = "cannot import name '%s'";
@@ -372,6 +373,7 @@ public abstract class ErrorMessages {
372373
public static final String RAW_FORMAT_NOT_SUPPORTED = "RAW format unsupported";
373374
public static final String READ_BYTE_OUT_OF_RANGE = "read byte out of range";
374375
public static final String READ_WRITE_BYTELIKE_OBJ = "read-write bytes-like object";
376+
public static final String READONLY_ATTRIBUTE = "readonly attribute";
375377
public static final String REQUIRES_CODE_OBJ = "%s() requires a code object with %d free vars, not %d";
376378
public static final String REQUIRES_INT_OR_CHAR = "%%%c requires int or char";
377379
public static final String REQUIRES_OBJ_THAT_IMPLEMENTS_S = " %%r requires an object that implements %s";

graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/object/PythonObjectFactory.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -435,6 +435,10 @@ public GetSetDescriptor createGetSetDescriptor(Object get, Object set, String na
435435
return trace(new GetSetDescriptor(get, set, name, type));
436436
}
437437

438+
public GetSetDescriptor createGetSetDescriptor(Object get, Object set, String name, LazyPythonClass type, boolean allowsDelete) {
439+
return trace(new GetSetDescriptor(get, set, name, type, allowsDelete));
440+
}
441+
438442
public HiddenKeyDescriptor createHiddenKeyDescriptor(HiddenKey key, LazyPythonClass type) {
439443
return trace(new HiddenKeyDescriptor(key, type));
440444
}

0 commit comments

Comments
 (0)