Skip to content

Commit 927486a

Browse files
committed
Split writeAttributeToObjectNode into writeAttributeToObjectNode and writeAttributeToDynamicObjectNode
- add unittest to test for persisting attrs in the object's __dict__
1 parent c2287de commit 927486a

File tree

7 files changed

+289
-110
lines changed

7 files changed

+289
-110
lines changed

graalpython/com.oracle.graal.python.test/src/tests/test_object.py

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ class MyList(list):
5858
assert lst.__dict__ == {'a': 9}
5959

6060

61-
def test_set_dict_attr():
61+
def test_get_dict_attr():
6262
o = object()
6363

6464
def get_dict_attr():
@@ -70,6 +70,8 @@ def set_dict_attr():
7070
assert_raises(AttributeError, get_dict_attr)
7171
assert_raises(AttributeError, set_dict_attr)
7272

73+
74+
def test_set_dict_attr():
7375
class MyClass(object):
7476
def __init__(self):
7577
self.a = 9
@@ -81,7 +83,29 @@ def __init__(self):
8183
m.__dict__ = {'a': 10}
8284
assert m.__dict__ == {'a': 10}
8385
assert m.a == 10
86+
m.d = 20
87+
assert m.d == 20
88+
assert "d" in m.__dict__
89+
assert m.__dict__ == {'a': 10, 'd': 20}
90+
91+
92+
def test_set_attr_builtins():
93+
lst = list()
94+
95+
def set_attr():
96+
lst.a = 10
97+
98+
assert_raises(AttributeError, set_attr)
99+
100+
class MyList(list):
101+
pass
102+
103+
mlst = MyList()
104+
mlst.a = 10
105+
assert mlst.a == 10
106+
84107

108+
def test_set_dict_attr_with_getattr_defined():
85109
class MyOtherClass(object):
86110
def __getattribute__(self, item):
87111
return object.__getattribute__(self, item)

graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/cext/NativeWrappers.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,10 @@ public DynamicObjectNativeWrapper(Object delegate) {
113113
super(delegate);
114114
}
115115

116+
public PythonObjectDictStorage createNativeMemberStore() {
117+
return createNativeMemberStore(null);
118+
}
119+
116120
public PythonObjectDictStorage createNativeMemberStore(Assumption dictStableAssumption) {
117121
if (nativeMemberStore == null) {
118122
nativeMemberStore = new PythonObjectDictStorage(SHAPE.newInstance(), dictStableAssumption);

graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/cext/PythonObjectNativeWrapperMR.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -735,7 +735,7 @@ Object doGeneric(Object object, String key, Object value) {
735735
DynamicObjectNativeWrapper nativeWrapper = ((PythonAbstractObject) object).getNativeWrapper();
736736
assert nativeWrapper != null;
737737
logGeneric(key);
738-
getSetItemNode().execute(nativeWrapper.createNativeMemberStore(null), key, value);
738+
getSetItemNode().execute(nativeWrapper.createNativeMemberStore(), key, value);
739739
return value;
740740
}
741741
throw UnknownIdentifierException.raise(key);

graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/common/DynamicObjectStorage.java

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -160,22 +160,26 @@ public HashingStorage copy(Equivalence eq) {
160160
}
161161

162162
public static class PythonObjectDictStorage extends DynamicObjectStorage {
163-
private final Assumption dictStableAssumption;
163+
private final Assumption dictUnsetOrSameAsStorage;
164164

165-
public PythonObjectDictStorage(DynamicObject store, Assumption dictStableAssumption) {
165+
public PythonObjectDictStorage(DynamicObject store) {
166+
this(store, null);
167+
}
168+
169+
public PythonObjectDictStorage(DynamicObject store, Assumption dictUnsetOrSameAsStorage) {
166170
super(store);
167-
this.dictStableAssumption = dictStableAssumption;
171+
this.dictUnsetOrSameAsStorage = dictUnsetOrSameAsStorage;
168172
}
169173

170-
public Assumption getDictStableAssumption() {
171-
return dictStableAssumption;
174+
public Assumption getDictUnsetOrSameAsStorage() {
175+
return dictUnsetOrSameAsStorage;
172176
}
173177

174178
@Override
175179
@TruffleBoundary
176180
public HashingStorage copy(Equivalence eq) {
177181
assert eq == HashingStorage.DEFAULT_EQIVALENCE;
178-
return new PythonObjectDictStorage(getStore().copy(getStore().getShape()), null);
182+
return new PythonObjectDictStorage(getStore().copy(getStore().getShape()));
179183
}
180184
}
181185

graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/common/HashingStorageNodes.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -241,9 +241,9 @@ protected static DynamicObjectStorage switchToFastDictStorage(HashingStorage sto
241241
}
242242

243243
protected static PythonObjectHybridDictStorage switchToHybridDictStorage(PythonObjectDictStorage dictStorage) {
244-
Assumption dictStableAssumption = dictStorage.getDictStableAssumption();
245-
if (dictStableAssumption != null) {
246-
dictStableAssumption.invalidate();
244+
Assumption dictUnsetOrSameAsStorage = dictStorage.getDictUnsetOrSameAsStorage();
245+
if (dictUnsetOrSameAsStorage != null) {
246+
dictUnsetOrSameAsStorage.invalidate();
247247
}
248248
return new PythonObjectHybridDictStorage(dictStorage);
249249
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,176 @@
1+
/*
2+
* Copyright (c) 2017, 2018, 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.nodes.attributes;
42+
43+
import com.oracle.graal.python.builtins.objects.str.PString;
44+
import com.oracle.graal.python.nodes.PNodeWithContext;
45+
import com.oracle.graal.python.runtime.PythonOptions;
46+
import com.oracle.truffle.api.Assumption;
47+
import com.oracle.truffle.api.CompilerAsserts;
48+
import com.oracle.truffle.api.CompilerDirectives;
49+
import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
50+
import com.oracle.truffle.api.dsl.Cached;
51+
import com.oracle.truffle.api.dsl.ImportStatic;
52+
import com.oracle.truffle.api.dsl.Specialization;
53+
import com.oracle.truffle.api.object.DynamicObject;
54+
import com.oracle.truffle.api.object.FinalLocationException;
55+
import com.oracle.truffle.api.object.IncompatibleLocationException;
56+
import com.oracle.truffle.api.object.Location;
57+
import com.oracle.truffle.api.object.Property;
58+
import com.oracle.truffle.api.object.Shape;
59+
60+
@ImportStatic(PythonOptions.class)
61+
public abstract class WriteAttributeToDynamicObjectNode extends PNodeWithContext {
62+
63+
public abstract boolean execute(Object primary, Object key, Object value);
64+
65+
public abstract boolean execute(Object primary, String key, Object value);
66+
67+
public static WriteAttributeToDynamicObjectNode create() {
68+
return WriteAttributeToDynamicObjectNodeGen.create();
69+
}
70+
71+
protected Location getLocationOrNull(Property prop) {
72+
return prop == null ? null : prop.getLocation();
73+
}
74+
75+
protected Object attrKey(Object key) {
76+
if (key instanceof PString) {
77+
return ((PString) key).getValue();
78+
} else {
79+
return key;
80+
}
81+
}
82+
83+
@SuppressWarnings("unused")
84+
@Specialization(guards = {
85+
"dynamicObject.getShape() == cachedShape",
86+
"!layoutAssumption.isValid()"
87+
})
88+
protected boolean updateShapeAndWrite(DynamicObject dynamicObject, Object key, Object value,
89+
@Cached("dynamicObject.getShape()") Shape cachedShape,
90+
@Cached("cachedShape.getValidAssumption()") Assumption layoutAssumption,
91+
@Cached("create()") WriteAttributeToDynamicObjectNode nextNode) {
92+
dynamicObject.updateShape();
93+
return nextNode.execute(dynamicObject, key, value);
94+
}
95+
96+
protected static boolean compareKey(Object cachedKey, Object key) {
97+
return cachedKey == key;
98+
}
99+
100+
@SuppressWarnings("unused")
101+
@Specialization(limit = "getIntOption(getContext(), AttributeAccessInlineCacheMaxDepth)", //
102+
guards = {
103+
"dynamicObject.getShape() == cachedShape",
104+
"compareKey(cachedKey, key)",
105+
"loc != null",
106+
"loc.canSet(value)"
107+
}, //
108+
assumptions = {
109+
"layoutAssumption"
110+
111+
})
112+
protected boolean doDirect(DynamicObject dynamicObject, Object key, Object value,
113+
@Cached("key") Object cachedKey,
114+
@Cached("attrKey(cachedKey)") Object attrKey,
115+
@Cached("dynamicObject.getShape()") Shape cachedShape,
116+
@Cached("cachedShape.getValidAssumption()") Assumption layoutAssumption,
117+
@Cached("getLocationOrNull(cachedShape.getProperty(attrKey))") Location loc) {
118+
try {
119+
loc.set(dynamicObject, value);
120+
} catch (IncompatibleLocationException | FinalLocationException e) {
121+
CompilerDirectives.transferToInterpreter();
122+
// cannot happen due to guard
123+
throw new RuntimeException("Location.canSet is inconsistent with Location.set");
124+
}
125+
return true;
126+
}
127+
128+
@SuppressWarnings("unused")
129+
@Specialization(limit = "getIntOption(getContext(), AttributeAccessInlineCacheMaxDepth)", //
130+
guards = {
131+
"dynamicObject.getShape() == cachedShape",
132+
"compareKey(cachedKey, key)",
133+
"loc == null || !loc.canSet(value)",
134+
"newLoc.canSet(value)"
135+
}, //
136+
assumptions = {
137+
"layoutAssumption",
138+
"newLayoutAssumption"
139+
})
140+
protected boolean defineDirect(DynamicObject dynamicObject, Object key, Object value,
141+
@Cached("key") Object cachedKey,
142+
@Cached("attrKey(key)") Object attrKey,
143+
@Cached("dynamicObject.getShape()") Shape cachedShape,
144+
@Cached("cachedShape.getValidAssumption()") Assumption layoutAssumption,
145+
@Cached("getLocationOrNull(cachedShape.getProperty(attrKey))") Location loc,
146+
@Cached("cachedShape.defineProperty(attrKey, value, 0)") Shape newShape,
147+
@Cached("newShape.getValidAssumption()") Assumption newLayoutAssumption,
148+
@Cached("getLocationOrNull(newShape.getProperty(attrKey))") Location newLoc) {
149+
try {
150+
newLoc.set(dynamicObject, value, cachedShape, newShape);
151+
} catch (IncompatibleLocationException e) {
152+
CompilerDirectives.transferToInterpreter();
153+
// cannot happen due to guard
154+
throw new RuntimeException("Location.canSet is inconsistent with Location.set");
155+
}
156+
return true;
157+
}
158+
159+
@TruffleBoundary
160+
@Specialization(guards = {
161+
"dynamicObject.getShape().isValid()"
162+
}, replaces = {"doDirect", "defineDirect"})
163+
protected boolean doIndirect(DynamicObject dynamicObject, Object key, Object value) {
164+
Object attrKey = attrKey(key);
165+
CompilerAsserts.neverPartOfCompilation();
166+
dynamicObject.define(attrKey, value);
167+
return true;
168+
}
169+
170+
@Specialization(guards = "!dynamicObject.getShape().isValid()")
171+
protected boolean defineDirect(DynamicObject dynamicObject, Object key, Object value) {
172+
CompilerDirectives.transferToInterpreter();
173+
dynamicObject.updateShape();
174+
return doIndirect(dynamicObject, key, value);
175+
}
176+
}

0 commit comments

Comments
 (0)