Skip to content

Commit e44b815

Browse files
committed
Added the org.graalvm.continuations.IdentityHashCodes class, providing utilities for restoring identity hashcodes
1 parent 1768167 commit e44b815

File tree

16 files changed

+316
-24
lines changed

16 files changed

+316
-24
lines changed

espresso/CHANGELOG.md

+1
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
### User-visible changes
55
* Added experimental support for JVMCI. It can be enabled with the `java.EnableJVMCI` option.
66
* Added experimentation support for `-javaagent`. It can also be enabled from the polyglot API with `java.JavaAgent.$i` option set to `/path/to/jar=agent-options` where `$i` starts at 0 and increments by 1 for each extra java agent.
7+
* Added the `org.graalvm.continuations.IdentityHashCodes` class, providing utilities for restoring identity hashcodes. This may be used for more properly deserializing continuations.
78

89
## Version 24.2.0
910
### User-visible changes

espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/EspressoLanguage.java

+21-1
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,7 @@ public final class EspressoLanguage extends TruffleLanguage<EspressoContext> imp
121121
@CompilationFinal private SignatureSymbols signatureSymbols;
122122

123123
private final StaticProperty arrayProperty = new DefaultStaticProperty("array");
124+
private final StaticProperty arrayHashCodeProperty = new DefaultStaticProperty("ihashcode");
124125
// This field should be final, but creating a shape requires a fully-initialized instance of
125126
// TruffleLanguage.
126127
@CompilationFinal //
@@ -145,6 +146,7 @@ public final class EspressoLanguage extends TruffleLanguage<EspressoContext> imp
145146
@CompilationFinal private boolean whiteBoxEnabled;
146147
@CompilationFinal private boolean eagerFrameAnalysis;
147148
@CompilationFinal private boolean internalJvmciEnabled;
149+
@CompilationFinal private boolean continuum;
148150
// endregion Options
149151

150152
// region Allocation
@@ -243,6 +245,7 @@ private void initializeOptions(final TruffleLanguage.Env env) {
243245
previewEnabled = env.getOptions().get(EspressoOptions.EnablePreview);
244246
whiteBoxEnabled = env.getOptions().get(EspressoOptions.WhiteBoxAPI);
245247
internalJvmciEnabled = env.getOptions().get(EspressoOptions.EnableJVMCI);
248+
continuum = env.getOptions().get(EspressoOptions.Continuum);
246249

247250
EspressoOptions.GuestFieldOffsetStrategyEnum strategy = env.getOptions().get(EspressoOptions.GuestFieldOffsetStrategy);
248251
guestFieldOffsetStrategy = switch (strategy) {
@@ -339,6 +342,7 @@ protected boolean areOptionsCompatible(OptionValues oldOptions, OptionValues new
339342
isOptionCompatible(newOptions, oldOptions, EspressoOptions.EnablePreview) &&
340343
isOptionCompatible(newOptions, oldOptions, EspressoOptions.WhiteBoxAPI) &&
341344
isOptionCompatible(newOptions, oldOptions, EspressoOptions.EnableJVMCI) &&
345+
isOptionCompatible(newOptions, oldOptions, EspressoOptions.Continuum) &&
342346
isOptionCompatible(newOptions, oldOptions, EspressoOptions.GuestFieldOffsetStrategy);
343347
}
344348

@@ -480,6 +484,14 @@ public StaticProperty getArrayProperty() {
480484
return arrayProperty;
481485
}
482486

487+
public StaticProperty getArrayHashCodeProperty() {
488+
if (!continuum) {
489+
CompilerDirectives.transferToInterpreterAndInvalidate();
490+
throw EspressoError.shouldNotReachHere("Accessing array hash code property without continuum set up.");
491+
}
492+
return arrayHashCodeProperty;
493+
}
494+
483495
public StaticShape<StaticObjectFactory> getArrayShape() {
484496
assert fullyInitialized : "Array shape accessed before language is fully initialized";
485497
return arrayShape;
@@ -488,7 +500,11 @@ public StaticShape<StaticObjectFactory> getArrayShape() {
488500
@TruffleBoundary
489501
private StaticShape<StaticObjectFactory> createArrayShape() {
490502
assert arrayShape == null;
491-
return StaticShape.newBuilder(this).property(arrayProperty, Object.class, true).build(StaticObject.class, StaticObjectFactory.class);
503+
StaticShape.Builder builder = StaticShape.newBuilder(this).property(arrayProperty, Object.class, true);
504+
if (continuum) {
505+
builder.property(arrayHashCodeProperty, int.class, false);
506+
}
507+
return builder.build(StaticObject.class, StaticObjectFactory.class);
492508
}
493509

494510
public StaticProperty getForeignProperty() {
@@ -565,6 +581,10 @@ public boolean isJVMCIEnabled() {
565581
return internalJvmciEnabled;
566582
}
567583

584+
public boolean isContinuumEnabled() {
585+
return continuum;
586+
}
587+
568588
public EspressoLanguageCache getLanguageCache() {
569589
return languageCache;
570590
}

espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/descriptors/EspressoSymbols.java

+2
Original file line numberDiff line numberDiff line change
@@ -858,6 +858,8 @@ public static class Names {
858858
public static final Symbol<Name> entrySet = SYMBOLS.putName("entrySet");
859859
public static final Symbol<Name> hasNext = SYMBOLS.putName("hasNext");
860860
public static final Symbol<Name> toArray = SYMBOLS.putName("toArray");
861+
// j.l.Object
862+
public static final Symbol<Name> HIDDEN_SYSTEM_IHASHCODE = SYMBOLS.putName("0HIDDEN_SYSTEM_IHASHCODE");
861863
// MemberName
862864
public static final Symbol<Name> HIDDEN_VMINDEX = SYMBOLS.putName("0HIDDEN_VMINDEX");
863865
public static final Symbol<Name> HIDDEN_VMTARGET = SYMBOLS.putName("0HIDDEN_VMTARGET");

espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/impl/Klass.java

+3-3
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,7 @@
8282
import com.oracle.truffle.espresso.meta.InteropKlassesDispatch;
8383
import com.oracle.truffle.espresso.meta.Meta;
8484
import com.oracle.truffle.espresso.meta.MetaUtil;
85+
import com.oracle.truffle.espresso.nodes.interop.IHashCodeNode;
8586
import com.oracle.truffle.espresso.nodes.interop.InteropUnwrapNode;
8687
import com.oracle.truffle.espresso.nodes.interop.InteropUnwrapNodeGen;
8788
import com.oracle.truffle.espresso.nodes.interop.LookupDeclaredMethod;
@@ -105,7 +106,6 @@
105106
import com.oracle.truffle.espresso.shared.meta.TypeAccess;
106107
import com.oracle.truffle.espresso.substitutions.JavaType;
107108
import com.oracle.truffle.espresso.vm.InterpreterToVM;
108-
import com.oracle.truffle.espresso.vm.VM;
109109

110110
@ExportLibrary(InteropLibrary.class)
111111
public abstract class Klass extends ContextAccessImpl implements KlassRef, TruffleObject, EspressoType, TypeAccess<Klass, Method, Field> {
@@ -606,11 +606,11 @@ static TriState doOther(@SuppressWarnings("unused") Klass receiver, @SuppressWar
606606
}
607607

608608
@ExportMessage
609-
int identityHashCode() {
609+
int identityHashCode(@Cached IHashCodeNode iHashCodeNode) {
610610
// In unit tests, Truffle performs additional sanity checks, this assert causes stack
611611
// overflow.
612612
// assert InteropLibrary.getUncached().hasIdentity(this);
613-
return VM.JVM_IHashCode(mirror(), null /*- path where language is needed is never reached through here. */);
613+
return iHashCodeNode.execute(mirror());
614614
}
615615

616616
// endregion ### Identity/hashCode

espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/impl/LinkedKlassFieldLayout.java

+4-2
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
package com.oracle.truffle.espresso.impl;
2424

2525
import static com.oracle.truffle.espresso.classfile.Constants.ACC_HIDDEN;
26+
import static com.oracle.truffle.espresso.classfile.Constants.ACC_VOLATILE;
2627
import static java.util.Map.entry;
2728

2829
import java.util.HashSet;
@@ -177,6 +178,9 @@ private static class HiddenField {
177178
private static final int NO_ADDITIONAL_FLAGS = 0;
178179
private static final HiddenField[] EMPTY = new HiddenField[0];
179180
private static final Map<Symbol<Type>, HiddenField[]> REGISTRY = Map.ofEntries(
181+
entry(Types.java_lang_Object, new HiddenField[]{
182+
new HiddenField(Names.HIDDEN_SYSTEM_IHASHCODE, Types._int, EspressoLanguage::isContinuumEnabled, ACC_VOLATILE),
183+
}),
180184
entry(Types.java_lang_invoke_MemberName, new HiddenField[]{
181185
new HiddenField(Names.HIDDEN_VMTARGET),
182186
new HiddenField(Names.HIDDEN_VMINDEX)
@@ -196,7 +200,6 @@ private static class HiddenField {
196200
// All references (including strong) get an extra hidden field, this
197201
// simplifies the code for weak/soft/phantom/final references.
198202
entry(Types.java_lang_ref_Reference, new HiddenField[]{
199-
200203
new HiddenField(Names.HIDDEN_HOST_REFERENCE)
201204
}),
202205
entry(Types.java_lang_Throwable, new HiddenField[]{
@@ -248,7 +251,6 @@ private static class HiddenField {
248251
new HiddenField(Names.HIDDEN_TREGEX_SEARCH_FROM_BACKUP),
249252
new HiddenField(Names.HIDDEN_TREGEX_MATCHING_MODE_BACKUP)
250253
}),
251-
252254
entry(Types.com_oracle_truffle_espresso_polyglot_TypeLiteral, new HiddenField[]{
253255
new HiddenField(Names.HIDDEN_INTERNAL_TYPE)}),
254256
entry(Types.org_graalvm_continuations_ContinuationImpl, new HiddenField[]{

espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/meta/Meta.java

+7-1
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,7 @@ public Meta(EspressoContext context) {
9999
// Object and Class (+ Class fields) must be initialized before all other classes in order
100100
// to eagerly create the guest Class instances.
101101
java_lang_Object = knownKlass(Types.java_lang_Object);
102+
HIDDEN_SYSTEM_IHASHCODE = context.getLanguage().isContinuumEnabled() ? java_lang_Object.requireHiddenField(Names.HIDDEN_SYSTEM_IHASHCODE) : null;
102103
// Cloneable must be loaded before Serializable.
103104
java_lang_Cloneable = knownKlass(Types.java_lang_Cloneable);
104105
java_lang_Class = knownKlass(Types.java_lang_Class);
@@ -1320,7 +1321,7 @@ public void postSystemInit() {
13201321
}
13211322

13221323
// Continuations
1323-
boolean continuumSupport = getContext().getEspressoEnv().Continuum;
1324+
boolean continuumSupport = getLanguage().isContinuumEnabled();
13241325
this.continuum = continuumSupport ? new ContinuumSupport() : null;
13251326
}
13261327

@@ -1353,6 +1354,11 @@ private DiffVersionLoadHelper diff() {
13531354

13541355
public final ObjectKlass java_lang_Object;
13551356
public final ArrayKlass java_lang_Object_array;
1357+
/*
1358+
* Though only used when Continuum is enabled, the hashcode is used during VM initialization, so
1359+
* it cannot be put in the ContinuumSupport object.
1360+
*/
1361+
public final Field HIDDEN_SYSTEM_IHASHCODE;
13561362

13571363
public final ObjectKlass java_lang_String;
13581364
public final ArrayKlass java_lang_String_array;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
/*
2+
* Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved.
3+
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4+
*
5+
* This code is free software; you can redistribute it and/or modify it
6+
* under the terms of the GNU General Public License version 2 only, as
7+
* published by the Free Software Foundation.
8+
*
9+
* This code is distributed in the hope that it will be useful, but WITHOUT
10+
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11+
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
12+
* version 2 for more details (a copy is included in the LICENSE file that
13+
* accompanied this code).
14+
*
15+
* You should have received a copy of the GNU General Public License version
16+
* 2 along with this work; if not, write to the Free Software Foundation,
17+
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18+
*
19+
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20+
* or visit www.oracle.com if you need additional information or have any
21+
* questions.
22+
*/
23+
package com.oracle.truffle.espresso.nodes.interop;
24+
25+
import com.oracle.truffle.api.dsl.Bind;
26+
import com.oracle.truffle.api.dsl.GenerateUncached;
27+
import com.oracle.truffle.api.dsl.Specialization;
28+
import com.oracle.truffle.espresso.EspressoLanguage;
29+
import com.oracle.truffle.espresso.nodes.EspressoNode;
30+
import com.oracle.truffle.espresso.runtime.EspressoContext;
31+
import com.oracle.truffle.espresso.runtime.staticobject.StaticObject;
32+
import com.oracle.truffle.espresso.vm.VM;
33+
34+
@GenerateUncached
35+
public abstract class IHashCodeNode extends EspressoNode {
36+
public abstract int execute(StaticObject obj);
37+
38+
@Specialization
39+
public static int doCached(StaticObject obj,
40+
@Bind("getLanguage()") EspressoLanguage lang,
41+
@Bind("getContext()") EspressoContext ctx) {
42+
assert !obj.isForeignObject();
43+
return VM.JVM_IHashCode(obj, ctx.getMeta(), lang);
44+
}
45+
}

espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/runtime/EspressoEnv.java

-2
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,6 @@ public final class EspressoEnv {
9292
public final boolean SoftExit;
9393
public final boolean AllowHostExit;
9494
public final boolean Polyglot;
95-
public final boolean Continuum;
9695
public final boolean BuiltInPolyglotCollections;
9796
public final boolean HotSwapAPI;
9897
public final boolean UseBindingsLoader;
@@ -169,7 +168,6 @@ public EspressoEnv(EspressoContext context, TruffleLanguage.Env env) {
169168
this.multiThreadingDisabled = multiThreadingDisabledReason;
170169
this.NativeAccessAllowed = env.isNativeAccessAllowed();
171170
this.Polyglot = env.getOptions().get(EspressoOptions.Polyglot);
172-
this.Continuum = env.getOptions().get(EspressoOptions.Continuum);
173171
this.HotSwapAPI = env.getOptions().get(EspressoOptions.HotSwapAPI);
174172
this.BuiltInPolyglotCollections = env.getOptions().get(EspressoOptions.BuiltInPolyglotCollections);
175173
this.polyglotTypeMappings = new PolyglotTypeMappings(env.getOptions().get(EspressoOptions.PolyglotInterfaceMappings), env.getOptions().get(EspressoOptions.PolyglotTypeConverters),

espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/runtime/dispatch/staticobject/BaseInterop.java

+7-3
Original file line numberDiff line numberDiff line change
@@ -41,10 +41,10 @@
4141
import com.oracle.truffle.espresso.impl.Klass;
4242
import com.oracle.truffle.espresso.meta.EspressoError;
4343
import com.oracle.truffle.espresso.meta.Meta;
44+
import com.oracle.truffle.espresso.nodes.interop.IHashCodeNode;
4445
import com.oracle.truffle.espresso.runtime.dispatch.messages.GenerateInteropNodes;
4546
import com.oracle.truffle.espresso.runtime.dispatch.messages.Shareable;
4647
import com.oracle.truffle.espresso.runtime.staticobject.StaticObject;
47-
import com.oracle.truffle.espresso.vm.VM;
4848

4949
/**
5050
* BaseInterop (isNull, is/asString, meta-instance, identity, exceptions, toDisplayString) Support
@@ -209,10 +209,14 @@ public static TriState isIdenticalOrUndefined(StaticObject receiver, Object othe
209209
}
210210

211211
@ExportMessage
212-
public static int identityHashCode(StaticObject object) {
212+
public static int identityHashCode(StaticObject object,
213+
@Cached IHashCodeNode iHashCodeNode) {
213214
object.checkNotForeign();
215+
if (StaticObject.isNull(object)) {
216+
return 0;
217+
}
214218
// Working with espresso objects here, guaranteed to have identity.
215-
return VM.JVM_IHashCode(object, null /*- path where language is needed is never reached through here. */);
219+
return iHashCodeNode.execute(object);
216220
}
217221

218222
// endregion ### Identity/hashCode

espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/substitutions/ModuleExtension.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@
3737
*/
3838
public final class ModuleExtension {
3939
private static final ModuleExtension[] ESPRESSO_EXTENSION_MODULES = {
40-
new Builder("org.graalvm.continuations", "continuations.jar", (context) -> context.getEspressoEnv().Continuum).build(),
40+
new Builder("org.graalvm.continuations", "continuations.jar", (context) -> context.getLanguage().isContinuumEnabled()).build(),
4141
new Builder("espresso.hotswap", "hotswap.jar", (context) -> context.getEspressoEnv().JDWPOptions != null).build(),
4242
new Builder("espresso.polyglot", "espresso-polyglot.jar", (context) -> context.getEspressoEnv().Polyglot).build(),
4343
new Builder("jdk.graal.compiler.espresso", "espresso-graal.jar", (context) -> context.getLanguage().isInternalJVMCIEnabled()) //

espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/substitutions/continuations/Target_java_lang_invoke_LambdaMetafactory.java

+6-5
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232
import com.oracle.truffle.api.dsl.Cached;
3333
import com.oracle.truffle.api.dsl.Specialization;
3434
import com.oracle.truffle.api.nodes.DirectCallNode;
35+
import com.oracle.truffle.espresso.EspressoLanguage;
3536
import com.oracle.truffle.espresso.meta.Meta;
3637
import com.oracle.truffle.espresso.runtime.EspressoContext;
3738
import com.oracle.truffle.espresso.runtime.staticobject.StaticObject;
@@ -71,12 +72,12 @@ StaticObject doCached(
7172
@Bind("getMeta()") Meta meta,
7273
@Cached("create(meta.java_lang_invoke_LambdaMetafactory_altMetafactory.getCallTargetNoSubstitution())") DirectCallNode altMetafactory,
7374
@Cached("create(meta.java_lang_invoke_LambdaMetafactory_metafactory.getCallTargetNoSubstitution())") DirectCallNode original,
74-
@Bind("getContext()") EspressoContext context) {
75-
if (context.getEspressoEnv().Continuum) {
75+
@Bind("getLanguage()") EspressoLanguage lang) {
76+
if (lang.isContinuumEnabled()) {
7677
// altMetafactory has a curious calling convention, apparently designed for
7778
// extensibility.
78-
StaticObject extraArgsRef = context.getAllocator().createNewReferenceArray(meta.java_lang_Object, 4);
79-
StaticObject[] extraArgs = extraArgsRef.unwrap(context.getLanguage());
79+
StaticObject extraArgsRef = lang.getAllocator().createNewReferenceArray(meta.java_lang_Object, 4);
80+
StaticObject[] extraArgs = extraArgsRef.unwrap(lang);
8081
extraArgs[0] = interfaceMethodType;
8182
extraArgs[1] = implementation;
8283
extraArgs[2] = dynamicMethodType;
@@ -106,7 +107,7 @@ StaticObject doCached(
106107
@Bind("getMeta()") Meta meta,
107108
@Cached("create(meta.java_lang_invoke_LambdaMetafactory_altMetafactory.getCallTargetNoSubstitution())") DirectCallNode original,
108109
@Bind("getContext()") EspressoContext context) {
109-
if (context.getEspressoEnv().Continuum) {
110+
if (context.getLanguage().isContinuumEnabled()) {
110111
StaticObject[] extraArgs = args.unwrap(context.getLanguage());
111112
extraArgs[3] = meta.boxInteger(meta.unboxInteger(extraArgs[3]) | LambdaMetafactory.FLAG_SERIALIZABLE);
112113
return (StaticObject) original.call(caller, interfaceMethodName, factoryType, StaticObject.wrap(extraArgs, meta));

0 commit comments

Comments
 (0)