Skip to content

Commit c51cb85

Browse files
committed
Fix boot issue: ensure that __eq__ is never called during bootstrapping
1 parent e0db1b4 commit c51cb85

File tree

7 files changed

+78
-15
lines changed

7 files changed

+78
-15
lines changed

graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/ErrnoModuleBuiltins.java

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -48,8 +48,10 @@
4848
import com.oracle.graal.python.builtins.CoreFunctions;
4949
import com.oracle.graal.python.builtins.Python3Core;
5050
import com.oracle.graal.python.builtins.PythonBuiltins;
51+
import com.oracle.graal.python.builtins.objects.common.EconomicMapStorage;
5152
import com.oracle.graal.python.builtins.objects.dict.PDict;
5253
import com.oracle.graal.python.builtins.objects.exception.OSErrorEnum;
54+
import com.oracle.graal.python.lib.PyObjectHashNode;
5355
import com.oracle.graal.python.nodes.function.PythonBuiltinBaseNode;
5456
import com.oracle.truffle.api.dsl.NodeFactory;
5557
import com.oracle.truffle.api.strings.TruffleString;
@@ -64,19 +66,21 @@ protected List<? extends NodeFactory<? extends PythonBuiltinBaseNode>> getNodeFa
6466
@Override
6567
public void initialize(Python3Core core) {
6668
super.initialize(core);
67-
PDict errorCode = core.factory().createDict();
69+
OSErrorEnum[] enumValues = OSErrorEnum.values();
70+
EconomicMapStorage storage = EconomicMapStorage.create(enumValues.length + 1);
6871

69-
for (OSErrorEnum value : OSErrorEnum.values()) {
72+
for (OSErrorEnum value : enumValues) {
7073
// if more OSError have the same number -> the last one wins
71-
addConstant(value.getNumber(), toTruffleStringUncached(value.name()), errorCode);
74+
addConstant(value.getNumber(), toTruffleStringUncached(value.name()), storage);
7275
}
7376

7477
// publish the dictionary with mapping code -> string name
78+
PDict errorCode = core.factory().createDict(storage);
7579
addBuiltinConstant("errorcode", errorCode);
7680
}
7781

78-
private void addConstant(int number, TruffleString name, PDict dict) {
82+
private void addConstant(int number, TruffleString name, EconomicMapStorage storage) {
7983
addBuiltinConstant(name, number);
80-
dict.setItem(number, name);
84+
storage.putUncachedWithJavaEq(number, PyObjectHashNode.hash(number), name);
8185
}
8286
}

graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/SysModuleBuiltins.java

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -673,8 +673,7 @@ public void postInitialize0(Python3Core core) {
673673
private static PFrozenSet createStdLibModulesSet(PythonObjectFactory factory) {
674674
EconomicMapStorage storage = EconomicMapStorage.create(STDLIB_MODULE_NAMES.length);
675675
for (String s : STDLIB_MODULE_NAMES) {
676-
TruffleString ts = toTruffleStringUncached(s);
677-
storage.putUncached(ts, PNone.NONE);
676+
storage.putUncachedWithJavaEq(s, PNone.NONE);
678677
}
679678
return factory.createFrozenSet(storage);
680679
}

graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/hashlib/HashlibModuleBuiltins.java

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,6 @@
4949
import java.security.NoSuchAlgorithmException;
5050
import java.security.Security;
5151
import java.util.ArrayList;
52-
import java.util.LinkedHashMap;
5352
import java.util.List;
5453
import java.util.Map;
5554

@@ -153,11 +152,11 @@ protected List<? extends NodeFactory<? extends PythonBuiltinBaseNode>> getNodeFa
153152

154153
@Override
155154
public void initialize(Python3Core core) {
156-
LinkedHashMap<String, Object> algos = new LinkedHashMap<>();
155+
EconomicMapStorage algos = EconomicMapStorage.create(DIGEST_ALGORITHMS.length);
157156
for (var digest : DIGEST_ALGORITHMS) {
158-
algos.put(digest, PNone.NONE);
157+
algos.putUncachedWithJavaEq(digest, PNone.NONE);
159158
}
160-
addBuiltinConstant("openssl_md_meth_names", core.factory().createFrozenSet(EconomicMapStorage.create(algos)));
159+
addBuiltinConstant("openssl_md_meth_names", core.factory().createFrozenSet(algos));
161160

162161
EconomicMapStorage storage = EconomicMapStorage.create();
163162
addBuiltinConstant(CONSTRUCTORS, core.factory().createMappingproxy(core.factory().createDict(storage)));

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

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@
4141
package com.oracle.graal.python.builtins.objects.common;
4242

4343
import static com.oracle.graal.python.util.PythonUtils.TS_ENCODING;
44+
import static com.oracle.graal.python.util.PythonUtils.toTruffleStringUncached;
4445

4546
import java.util.LinkedHashMap;
4647
import java.util.Map.Entry;
@@ -153,6 +154,17 @@ private void putUncached(Object key, Object value) {
153154
ObjectHashMap.PutNode.putUncached(this.map, key, PyObjectHashNode.executeUncached(key), value);
154155
}
155156

157+
// Solves boot-order problem, do not use in normal code or during startup when __eq__ of
158+
// builtins may not be properly set-up
159+
public void putUncachedWithJavaEq(Object key, long keyHash, Object value) {
160+
ObjectHashMap.PutNode.putUncachedWithJavaEq(this.map, key, keyHash, value);
161+
}
162+
163+
public void putUncachedWithJavaEq(String key, Object value) {
164+
TruffleString ts = toTruffleStringUncached(key);
165+
putUncachedWithJavaEq(ts, PyObjectHashNode.hash(ts, HashCodeNode.getUncached()), value);
166+
}
167+
156168
private static void putAllUncached(LinkedHashMap<String, Object> map, EconomicMapStorage result) {
157169
CompilerAsserts.neverPartOfCompilation();
158170
for (Entry<String, Object> entry : map.entrySet()) {

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

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,9 +44,14 @@
4444

4545
import java.util.Arrays;
4646

47+
import com.oracle.graal.python.builtins.PythonBuiltinClassType;
48+
import com.oracle.graal.python.builtins.objects.cext.PythonNativeClass;
49+
import com.oracle.graal.python.builtins.objects.type.PythonManagedClass;
50+
import com.oracle.graal.python.builtins.objects.type.SpecialMethodSlot;
4751
import com.oracle.graal.python.lib.PyObjectRichCompareBool;
4852
import com.oracle.graal.python.lib.PyObjectRichCompareBool.EqNode;
4953
import com.oracle.graal.python.util.PythonUtils;
54+
import com.oracle.truffle.api.CompilerAsserts;
5055
import com.oracle.truffle.api.CompilerDirectives;
5156
import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
5257
import com.oracle.truffle.api.HostCompilerDirectives.InliningCutoff;
@@ -60,6 +65,7 @@
6065
import com.oracle.truffle.api.nodes.Node;
6166
import com.oracle.truffle.api.profiles.InlinedBranchProfile;
6267
import com.oracle.truffle.api.profiles.InlinedCountingConditionProfile;
68+
import com.oracle.truffle.api.strings.TruffleString;
6369

6470
/**
6571
* Generic dictionary/set backing storage implementation.
@@ -347,6 +353,8 @@ public static Object doGetWithRestart(Frame frame, Node inliningTarget, ObjectHa
347353
@Cached InlinedCountingConditionProfile collisionFoundNoValue,
348354
@Cached InlinedCountingConditionProfile collisionFoundEqKey,
349355
@Cached PyObjectRichCompareBool.EqNode eqNode) {
356+
// must not call generic __eq__ before this
357+
assert map.size == 0 || SpecialMethodSlot.areBuiltinSlotsInitialized();
350358
while (true) {
351359
try {
352360
return doGet(frame, map, key, keyHash, inliningTarget, foundNullKey, foundSameHashKey,
@@ -449,6 +457,14 @@ public static void putUncached(ObjectHashMap map, Object key, long keyHash, Obje
449457

450458
abstract void execute(Frame frame, Node inliningTarget, ObjectHashMap map, Object key, long keyHash, Object value);
451459

460+
static void putUncachedWithJavaEq(ObjectHashMap map, Object key, long keyHash, Object value) {
461+
assert isJavaEqualsAllowed(key) : key;
462+
doPutWithRestart(null, null, map, key, keyHash, value,
463+
InlinedBranchProfile.getUncached(), InlinedCountingConditionProfile.getUncached(), InlinedCountingConditionProfile.getUncached(),
464+
InlinedCountingConditionProfile.getUncached(), InlinedCountingConditionProfile.getUncached(), InlinedBranchProfile.getUncached(), InlinedBranchProfile.getUncached(),
465+
null);
466+
}
467+
452468
// "public" for testing...
453469
@Specialization
454470
public static void doPutWithRestart(Frame frame, Node inliningTarget, ObjectHashMap map, Object key, long keyHash, Object value,
@@ -460,6 +476,7 @@ public static void doPutWithRestart(Frame frame, Node inliningTarget, ObjectHash
460476
@Cached InlinedBranchProfile rehash1Profile,
461477
@Cached InlinedBranchProfile rehash2Profile,
462478
@Cached PyObjectRichCompareBool.EqNode eqNode) {
479+
assert map.size == 0 || (SpecialMethodSlot.areBuiltinSlotsInitialized() || eqNode == null);
463480
while (true) {
464481
try {
465482
doPut(frame, map, key, keyHash, value, inliningTarget, foundNullKey, foundEqKey,
@@ -728,6 +745,10 @@ private boolean keysEqual(int[] originalIndices, Frame frame, Node inliningTarge
728745
if (originalKey == key) {
729746
return true;
730747
}
748+
if (CompilerDirectives.inInterpreter() && eqNode == null) {
749+
// this is hack, see putUncachedWithJavaEq
750+
return javaEquals(originalKey, key);
751+
}
731752
boolean result = eqNode.compare(frame, inliningTarget, originalKey, key);
732753
if (getKey(index) != originalKey || indices != originalIndices) {
733754
// Either someone overridden the slot we are just examining, or rehasing reallocated the
@@ -746,6 +767,18 @@ private boolean keysEqual(int[] originalIndices, Frame frame, Node inliningTarge
746767
return result;
747768
}
748769

770+
private static boolean javaEquals(Object a, Object b) {
771+
CompilerAsserts.neverPartOfCompilation();
772+
assert isJavaEqualsAllowed(a) : a;
773+
assert isJavaEqualsAllowed(b) : b;
774+
return a.equals(b);
775+
}
776+
777+
private static boolean isJavaEqualsAllowed(Object o) {
778+
return o instanceof PythonManagedClass || o instanceof PythonBuiltinClassType || //
779+
o instanceof PythonNativeClass || o instanceof Number || o instanceof TruffleString;
780+
}
781+
749782
/**
750783
* Called when we need space for new entry. It determines the new size from the number of slots
751784
* occupied by real values (i.e., does not count dummy entries), so the new size may be actually

graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/type/SpecialMethodSlot.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -444,6 +444,10 @@ private static void initializeBuiltinClassSpecialMethodSlots(Python3Core core, P
444444
private static final Object builtinSlotsInitializationLock = new Object();
445445
private static volatile boolean builtinSlotsInitialized;
446446

447+
public static boolean areBuiltinSlotsInitialized() {
448+
return builtinSlotsInitialized;
449+
}
450+
447451
/**
448452
* Initialized builtin type according to its respective builtin class. Only context independent
449453
* values are pushed from the class to the type, because types are shared across contexts. This

graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/type/TypeNodes.java

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -750,9 +750,19 @@ protected static void unsafeAddSubclass(Object base, Object subclass) {
750750
long hash = ObjectBuiltins.HashNode.hash(subclass);
751751
PDict dict = executeUncached(base);
752752
HashingStorage storage = dict.getDictStorage();
753-
HashingStorageSetItemWithHash setItem = HashingStorageSetItemWithHashNodeGen.getUncached();
754-
storage = setItem.execute(null, null, storage, subclass, hash, subclass);
755-
dict.setDictStorage(storage);
753+
// Booting order problem: special method slot for object.__eq__ is not initialized yet
754+
// In the unlikely event of hash collision __eq__ would fail
755+
if (HashingStorageLen.executeUncached(storage) == 0) {
756+
// This should not call __eq__
757+
HashingStorageSetItemWithHash setItem = HashingStorageSetItemWithHashNodeGen.getUncached();
758+
storage = setItem.execute(null, null, storage, subclass, hash, subclass);
759+
dict.setDictStorage(storage);
760+
} else {
761+
// the storage must be EconomicMap, because keys should not be Strings so there is
762+
// no other option left
763+
EconomicMapStorage mapStorage = (EconomicMapStorage) storage;
764+
mapStorage.putUncachedWithJavaEq(subclass, hash, subclass);
765+
}
756766
}
757767

758768
protected static void unsafeRemoveSubclass(Object base, Object subclass) {
@@ -829,10 +839,12 @@ abstract static class EachSubclassAdd extends HashingStorageForEachCallback<Pyth
829839
static PythonAbstractClassList doIt(Frame frame, Node inliningTarget, HashingStorage storage, HashingStorageIterator it, PythonAbstractClassList subclasses,
830840
@Cached HashingStorageIteratorKey itKey,
831841
@Cached HashingStorageIteratorKeyHash itKeyHash,
842+
@Cached HashingStorageIteratorValue itValue,
832843
@Cached HashingStorageGetItemWithHash getItemNode) {
833844
long hash = itKeyHash.execute(inliningTarget, storage, it);
834845
Object key = itKey.execute(inliningTarget, storage, it);
835-
subclasses.add(PythonAbstractClass.cast(getItemNode.execute(frame, inliningTarget, storage, key, hash)));
846+
Object value = itValue.execute(inliningTarget, storage, it);
847+
subclasses.add(PythonAbstractClass.cast(value));
836848
return subclasses;
837849
}
838850
}

0 commit comments

Comments
 (0)