Skip to content

Commit f70e252

Browse files
committed
Fix hash code computation of cyclical objects
This fixes an issue where a cyclical object will stack overflow when computing its hash code.
1 parent fcce5c7 commit f70e252

File tree

24 files changed

+143
-45
lines changed

24 files changed

+143
-45
lines changed

pkl-core/src/main/java/org/pkl/core/runtime/VmClass.java

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -803,8 +803,7 @@ public boolean equals(@Nullable Object obj) {
803803
}
804804

805805
@Override
806-
public int hashCode() {
807-
// use a more deterministic hash code than System.identityHashCode()
806+
int computeHashCode(Set<VmValue> seenValues) {
808807
return classInfo.hashCode();
809808
}
810809
}

pkl-core/src/main/java/org/pkl/core/runtime/VmDataSize.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright © 2024 Apple Inc. and the Pkl project authors. All rights reserved.
2+
* Copyright © 2024-2025 Apple Inc. and the Pkl project authors. All rights reserved.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -151,7 +151,7 @@ public boolean equals(@Nullable Object obj) {
151151
}
152152

153153
@Override
154-
public int hashCode() {
154+
int computeHashCode(Set<VmValue> seenValues) {
155155
return Double.hashCode(convertValueTo(DataSizeUnit.BYTES));
156156
}
157157

pkl-core/src/main/java/org/pkl/core/runtime/VmDuration.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright © 2024 Apple Inc. and the Pkl project authors. All rights reserved.
2+
* Copyright © 2024-2025 Apple Inc. and the Pkl project authors. All rights reserved.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -147,7 +147,7 @@ public boolean equals(@Nullable Object obj) {
147147
}
148148

149149
@Override
150-
public int hashCode() {
150+
int computeHashCode(Set<VmValue> seenValues) {
151151
return Double.hashCode(getValue(DurationUnit.NANOS));
152152
}
153153

pkl-core/src/main/java/org/pkl/core/runtime/VmDynamic.java

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright © 2024 Apple Inc. and the Pkl project authors. All rights reserved.
2+
* Copyright © 2024-2025 Apple Inc. and the Pkl project authors. All rights reserved.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -18,6 +18,7 @@
1818
import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
1919
import com.oracle.truffle.api.frame.MaterializedFrame;
2020
import java.util.Objects;
21+
import java.util.Set;
2122
import org.graalvm.collections.UnmodifiableEconomicMap;
2223
import org.pkl.core.PClassInfo;
2324
import org.pkl.core.PObject;
@@ -124,7 +125,7 @@ public boolean equals(Object obj) {
124125

125126
@Override
126127
@TruffleBoundary
127-
public int hashCode() {
128+
int computeHashCode(Set<VmValue> seenValues) {
128129
if (cachedHash != 0) return cachedHash;
129130

130131
force(false);
@@ -137,7 +138,8 @@ public int hashCode() {
137138

138139
var value = cursor.getValue();
139140
assert value != null;
140-
result += key.hashCode() ^ value.hashCode();
141+
result +=
142+
VmUtils.computeHashCode(key, seenValues) ^ VmUtils.computeHashCode(value, seenValues);
141143
}
142144

143145
cachedHash = result;

pkl-core/src/main/java/org/pkl/core/runtime/VmFunction.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
1919
import com.oracle.truffle.api.RootCallTarget;
2020
import com.oracle.truffle.api.frame.MaterializedFrame;
21+
import java.util.Set;
2122
import java.util.function.BiFunction;
2223
import org.graalvm.collections.UnmodifiableEconomicMap;
2324
import org.pkl.core.ast.PklRootNode;
@@ -173,7 +174,7 @@ public boolean equals(Object obj) {
173174
}
174175

175176
@Override
176-
public int hashCode() {
177+
int computeHashCode(Set<VmValue> seenValues) {
177178
return System.identityHashCode(this);
178179
}
179180
}

pkl-core/src/main/java/org/pkl/core/runtime/VmIntSeq.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright © 2024 Apple Inc. and the Pkl project authors. All rights reserved.
2+
* Copyright © 2024-2025 Apple Inc. and the Pkl project authors. All rights reserved.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -20,6 +20,7 @@
2020
import com.oracle.truffle.api.CompilerDirectives.ValueType;
2121
import java.util.NoSuchElementException;
2222
import java.util.PrimitiveIterator;
23+
import java.util.Set;
2324
import org.pkl.core.util.Nullable;
2425

2526
// Some code copied from kotlin.ranges.Progressions, kotlin.ranges.ProgressionIterators,
@@ -114,7 +115,7 @@ public boolean equals(@Nullable Object obj) {
114115
}
115116

116117
@Override
117-
public int hashCode() {
118+
int computeHashCode(Set<VmValue> seenValues) {
118119
if (isEmpty()) return 1;
119120

120121
var result = 1;

pkl-core/src/main/java/org/pkl/core/runtime/VmList.java

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright © 2024 Apple Inc. and the Pkl project authors. All rights reserved.
2+
* Copyright © 2024-2025 Apple Inc. and the Pkl project authors. All rights reserved.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -19,6 +19,7 @@
1919
import java.util.ArrayList;
2020
import java.util.Iterator;
2121
import java.util.List;
22+
import java.util.Set;
2223
import org.organicdesign.fp.collections.RrbTree;
2324
import org.organicdesign.fp.collections.RrbTree.ImRrbt;
2425
import org.organicdesign.fp.collections.RrbTree.MutRrbt;
@@ -443,9 +444,17 @@ public boolean equals(@Nullable Object other) {
443444
}
444445

445446
@Override
446-
@TruffleBoundary
447-
public int hashCode() {
448-
return rrbt.hashCode();
447+
int computeHashCode(Set<VmValue> seenValues) {
448+
int ret = 1;
449+
450+
for (Object item : rrbt) {
451+
ret *= 31;
452+
if (item != null) {
453+
ret += VmUtils.computeHashCode(item, seenValues);
454+
}
455+
}
456+
457+
return ret;
449458
}
450459

451460
private static final class Builder implements VmCollection.Builder<VmList> {

pkl-core/src/main/java/org/pkl/core/runtime/VmListing.java

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright © 2024 Apple Inc. and the Pkl project authors. All rights reserved.
2+
* Copyright © 2024-2025 Apple Inc. and the Pkl project authors. All rights reserved.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -19,6 +19,7 @@
1919
import com.oracle.truffle.api.frame.MaterializedFrame;
2020
import java.util.ArrayList;
2121
import java.util.List;
22+
import java.util.Set;
2223
import org.graalvm.collections.UnmodifiableEconomicMap;
2324
import org.pkl.core.ast.member.ListingOrMappingTypeCastNode;
2425
import org.pkl.core.ast.member.ObjectMember;
@@ -137,7 +138,7 @@ public boolean equals(@Nullable Object obj) {
137138

138139
@Override
139140
@TruffleBoundary
140-
public int hashCode() {
141+
int computeHashCode(Set<VmValue> seenValues) {
141142
if (cachedHash != 0) return cachedHash;
142143

143144
force(false);
@@ -150,7 +151,7 @@ public int hashCode() {
150151

151152
var value = cursor.getValue();
152153
assert value != null;
153-
result = 31 * result + value.hashCode();
154+
result = 31 * result + VmUtils.computeHashCode(value, seenValues);
154155
}
155156

156157
cachedHash = result;

pkl-core/src/main/java/org/pkl/core/runtime/VmMap.java

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright © 2024 Apple Inc. and the Pkl project authors. All rights reserved.
2+
* Copyright © 2024-2025 Apple Inc. and the Pkl project authors. All rights reserved.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -18,6 +18,7 @@
1818
import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
1919
import java.util.Iterator;
2020
import java.util.Map;
21+
import java.util.Set;
2122
import java.util.function.Consumer;
2223
import org.organicdesign.fp.collections.ImMap;
2324
import org.organicdesign.fp.collections.MutMap;
@@ -279,9 +280,15 @@ public boolean equals(@Nullable Object other) {
279280
}
280281

281282
@Override
282-
@TruffleBoundary
283-
public int hashCode() {
284-
return map.hashCode();
283+
int computeHashCode(Set<VmValue> seenValues) {
284+
var result = 0;
285+
for (var entry : map) {
286+
var key = entry.getKey();
287+
var value = entry.getValue();
288+
result +=
289+
VmUtils.computeHashCode(key, seenValues) ^ VmUtils.computeHashCode(value, seenValues);
290+
}
291+
return result;
285292
}
286293

287294
@TruffleBoundary

pkl-core/src/main/java/org/pkl/core/runtime/VmMapping.java

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright © 2024 Apple Inc. and the Pkl project authors. All rights reserved.
2+
* Copyright © 2024-2025 Apple Inc. and the Pkl project authors. All rights reserved.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -18,6 +18,7 @@
1818
import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
1919
import com.oracle.truffle.api.frame.MaterializedFrame;
2020
import java.util.Map;
21+
import java.util.Set;
2122
import javax.annotation.concurrent.GuardedBy;
2223
import org.graalvm.collections.UnmodifiableEconomicMap;
2324
import org.pkl.core.ast.member.ListingOrMappingTypeCastNode;
@@ -141,8 +142,7 @@ public boolean equals(Object obj) {
141142
}
142143

143144
@Override
144-
@TruffleBoundary
145-
public int hashCode() {
145+
int computeHashCode(Set<VmValue> seenValues) {
146146
if (cachedHash != 0) return cachedHash;
147147

148148
force(false);
@@ -155,7 +155,8 @@ public int hashCode() {
155155

156156
var value = cursor.getValue();
157157
assert value != null;
158-
result += key.hashCode() ^ value.hashCode();
158+
result +=
159+
VmUtils.computeHashCode(key, seenValues) ^ VmUtils.computeHashCode(value, seenValues);
159160
}
160161

161162
cachedHash = result;

0 commit comments

Comments
 (0)