Skip to content

[GR-61283] API for restoring ihashcode in continuations package. #11160

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions espresso/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
### User-visible changes
* Added experimental support for JVMCI. It can be enabled with the `java.EnableJVMCI` option.
* 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.
* Added the `org.graalvm.continuations.IdentityHashCodes` class, providing utilities for restoring identity hashcodes. This may be used for more properly deserializing continuations.

## Version 24.2.0
### User-visible changes
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,7 @@ public final class EspressoLanguage extends TruffleLanguage<EspressoContext> imp
@CompilationFinal private SignatureSymbols signatureSymbols;

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

// region Allocation
Expand Down Expand Up @@ -243,6 +245,7 @@ private void initializeOptions(final TruffleLanguage.Env env) {
previewEnabled = env.getOptions().get(EspressoOptions.EnablePreview);
whiteBoxEnabled = env.getOptions().get(EspressoOptions.WhiteBoxAPI);
internalJvmciEnabled = env.getOptions().get(EspressoOptions.EnableJVMCI);
continuum = env.getOptions().get(EspressoOptions.Continuum);

EspressoOptions.GuestFieldOffsetStrategyEnum strategy = env.getOptions().get(EspressoOptions.GuestFieldOffsetStrategy);
guestFieldOffsetStrategy = switch (strategy) {
Expand Down Expand Up @@ -339,6 +342,7 @@ protected boolean areOptionsCompatible(OptionValues oldOptions, OptionValues new
isOptionCompatible(newOptions, oldOptions, EspressoOptions.EnablePreview) &&
isOptionCompatible(newOptions, oldOptions, EspressoOptions.WhiteBoxAPI) &&
isOptionCompatible(newOptions, oldOptions, EspressoOptions.EnableJVMCI) &&
isOptionCompatible(newOptions, oldOptions, EspressoOptions.Continuum) &&
isOptionCompatible(newOptions, oldOptions, EspressoOptions.GuestFieldOffsetStrategy);
}

Expand Down Expand Up @@ -480,6 +484,14 @@ public StaticProperty getArrayProperty() {
return arrayProperty;
}

public StaticProperty getArrayHashCodeProperty() {
if (!continuum) {
CompilerDirectives.transferToInterpreterAndInvalidate();
throw EspressoError.shouldNotReachHere("Accessing array hash code property without continuum set up.");
}
return arrayHashCodeProperty;
}

public StaticShape<StaticObjectFactory> getArrayShape() {
assert fullyInitialized : "Array shape accessed before language is fully initialized";
return arrayShape;
Expand All @@ -488,7 +500,11 @@ public StaticShape<StaticObjectFactory> getArrayShape() {
@TruffleBoundary
private StaticShape<StaticObjectFactory> createArrayShape() {
assert arrayShape == null;
return StaticShape.newBuilder(this).property(arrayProperty, Object.class, true).build(StaticObject.class, StaticObjectFactory.class);
StaticShape.Builder builder = StaticShape.newBuilder(this).property(arrayProperty, Object.class, true);
if (continuum) {
builder.property(arrayHashCodeProperty, int.class, false);
}
return builder.build(StaticObject.class, StaticObjectFactory.class);
}

public StaticProperty getForeignProperty() {
Expand Down Expand Up @@ -565,6 +581,10 @@ public boolean isJVMCIEnabled() {
return internalJvmciEnabled;
}

public boolean isContinuumEnabled() {
return continuum;
}

public EspressoLanguageCache getLanguageCache() {
return languageCache;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -858,6 +858,8 @@ public static class Names {
public static final Symbol<Name> entrySet = SYMBOLS.putName("entrySet");
public static final Symbol<Name> hasNext = SYMBOLS.putName("hasNext");
public static final Symbol<Name> toArray = SYMBOLS.putName("toArray");
// j.l.Object
public static final Symbol<Name> HIDDEN_SYSTEM_IHASHCODE = SYMBOLS.putName("0HIDDEN_SYSTEM_IHASHCODE");
// MemberName
public static final Symbol<Name> HIDDEN_VMINDEX = SYMBOLS.putName("0HIDDEN_VMINDEX");
public static final Symbol<Name> HIDDEN_VMTARGET = SYMBOLS.putName("0HIDDEN_VMTARGET");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@
import com.oracle.truffle.espresso.meta.InteropKlassesDispatch;
import com.oracle.truffle.espresso.meta.Meta;
import com.oracle.truffle.espresso.meta.MetaUtil;
import com.oracle.truffle.espresso.nodes.interop.IHashCodeNode;
import com.oracle.truffle.espresso.nodes.interop.InteropUnwrapNode;
import com.oracle.truffle.espresso.nodes.interop.InteropUnwrapNodeGen;
import com.oracle.truffle.espresso.nodes.interop.LookupDeclaredMethod;
Expand All @@ -105,7 +106,6 @@
import com.oracle.truffle.espresso.shared.meta.TypeAccess;
import com.oracle.truffle.espresso.substitutions.JavaType;
import com.oracle.truffle.espresso.vm.InterpreterToVM;
import com.oracle.truffle.espresso.vm.VM;

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

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

// endregion ### Identity/hashCode
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
package com.oracle.truffle.espresso.impl;

import static com.oracle.truffle.espresso.classfile.Constants.ACC_HIDDEN;
import static com.oracle.truffle.espresso.classfile.Constants.ACC_VOLATILE;
import static java.util.Map.entry;

import java.util.HashSet;
Expand Down Expand Up @@ -177,6 +178,9 @@ private static class HiddenField {
private static final int NO_ADDITIONAL_FLAGS = 0;
private static final HiddenField[] EMPTY = new HiddenField[0];
private static final Map<Symbol<Type>, HiddenField[]> REGISTRY = Map.ofEntries(
entry(Types.java_lang_Object, new HiddenField[]{
new HiddenField(Names.HIDDEN_SYSTEM_IHASHCODE, Types._int, EspressoLanguage::isContinuumEnabled, ACC_VOLATILE),
}),
entry(Types.java_lang_invoke_MemberName, new HiddenField[]{
new HiddenField(Names.HIDDEN_VMTARGET),
new HiddenField(Names.HIDDEN_VMINDEX)
Expand All @@ -196,7 +200,6 @@ private static class HiddenField {
// All references (including strong) get an extra hidden field, this
// simplifies the code for weak/soft/phantom/final references.
entry(Types.java_lang_ref_Reference, new HiddenField[]{

new HiddenField(Names.HIDDEN_HOST_REFERENCE)
}),
entry(Types.java_lang_Throwable, new HiddenField[]{
Expand Down Expand Up @@ -248,7 +251,6 @@ private static class HiddenField {
new HiddenField(Names.HIDDEN_TREGEX_SEARCH_FROM_BACKUP),
new HiddenField(Names.HIDDEN_TREGEX_MATCHING_MODE_BACKUP)
}),

entry(Types.com_oracle_truffle_espresso_polyglot_TypeLiteral, new HiddenField[]{
new HiddenField(Names.HIDDEN_INTERNAL_TYPE)}),
entry(Types.org_graalvm_continuations_ContinuationImpl, new HiddenField[]{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,7 @@ public Meta(EspressoContext context) {
// Object and Class (+ Class fields) must be initialized before all other classes in order
// to eagerly create the guest Class instances.
java_lang_Object = knownKlass(Types.java_lang_Object);
HIDDEN_SYSTEM_IHASHCODE = context.getLanguage().isContinuumEnabled() ? java_lang_Object.requireHiddenField(Names.HIDDEN_SYSTEM_IHASHCODE) : null;
// Cloneable must be loaded before Serializable.
java_lang_Cloneable = knownKlass(Types.java_lang_Cloneable);
java_lang_Class = knownKlass(Types.java_lang_Class);
Expand Down Expand Up @@ -1320,7 +1321,7 @@ public void postSystemInit() {
}

// Continuations
boolean continuumSupport = getContext().getEspressoEnv().Continuum;
boolean continuumSupport = getLanguage().isContinuumEnabled();
this.continuum = continuumSupport ? new ContinuumSupport() : null;
}

Expand Down Expand Up @@ -1353,6 +1354,11 @@ private DiffVersionLoadHelper diff() {

public final ObjectKlass java_lang_Object;
public final ArrayKlass java_lang_Object_array;
/*
* Though only used when Continuum is enabled, the hashcode is used during VM initialization, so
* it cannot be put in the ContinuumSupport object.
*/
public final Field HIDDEN_SYSTEM_IHASHCODE;

public final ObjectKlass java_lang_String;
public final ArrayKlass java_lang_String_array;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
/*
* Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package com.oracle.truffle.espresso.nodes.interop;

import com.oracle.truffle.api.dsl.Bind;
import com.oracle.truffle.api.dsl.GenerateUncached;
import com.oracle.truffle.api.dsl.Specialization;
import com.oracle.truffle.espresso.EspressoLanguage;
import com.oracle.truffle.espresso.nodes.EspressoNode;
import com.oracle.truffle.espresso.runtime.EspressoContext;
import com.oracle.truffle.espresso.runtime.staticobject.StaticObject;
import com.oracle.truffle.espresso.vm.VM;

@GenerateUncached
public abstract class IHashCodeNode extends EspressoNode {
public abstract int execute(StaticObject obj);

@Specialization
public static int doCached(StaticObject obj,
@Bind("getLanguage()") EspressoLanguage lang,
@Bind("getContext()") EspressoContext ctx) {
assert !obj.isForeignObject();
return VM.JVM_IHashCode(obj, ctx.getMeta(), lang);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,6 @@ public final class EspressoEnv {
public final boolean SoftExit;
public final boolean AllowHostExit;
public final boolean Polyglot;
public final boolean Continuum;
public final boolean BuiltInPolyglotCollections;
public final boolean HotSwapAPI;
public final boolean UseBindingsLoader;
Expand Down Expand Up @@ -169,7 +168,6 @@ public EspressoEnv(EspressoContext context, TruffleLanguage.Env env) {
this.multiThreadingDisabled = multiThreadingDisabledReason;
this.NativeAccessAllowed = env.isNativeAccessAllowed();
this.Polyglot = env.getOptions().get(EspressoOptions.Polyglot);
this.Continuum = env.getOptions().get(EspressoOptions.Continuum);
this.HotSwapAPI = env.getOptions().get(EspressoOptions.HotSwapAPI);
this.BuiltInPolyglotCollections = env.getOptions().get(EspressoOptions.BuiltInPolyglotCollections);
this.polyglotTypeMappings = new PolyglotTypeMappings(env.getOptions().get(EspressoOptions.PolyglotInterfaceMappings), env.getOptions().get(EspressoOptions.PolyglotTypeConverters),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,10 +41,10 @@
import com.oracle.truffle.espresso.impl.Klass;
import com.oracle.truffle.espresso.meta.EspressoError;
import com.oracle.truffle.espresso.meta.Meta;
import com.oracle.truffle.espresso.nodes.interop.IHashCodeNode;
import com.oracle.truffle.espresso.runtime.dispatch.messages.GenerateInteropNodes;
import com.oracle.truffle.espresso.runtime.dispatch.messages.Shareable;
import com.oracle.truffle.espresso.runtime.staticobject.StaticObject;
import com.oracle.truffle.espresso.vm.VM;

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

@ExportMessage
public static int identityHashCode(StaticObject object) {
public static int identityHashCode(StaticObject object,
@Cached IHashCodeNode iHashCodeNode) {
object.checkNotForeign();
if (StaticObject.isNull(object)) {
return 0;
}
// Working with espresso objects here, guaranteed to have identity.
return VM.JVM_IHashCode(object, null /*- path where language is needed is never reached through here. */);
return iHashCodeNode.execute(object);
}

// endregion ### Identity/hashCode
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@
*/
public final class ModuleExtension {
private static final ModuleExtension[] ESPRESSO_EXTENSION_MODULES = {
new Builder("org.graalvm.continuations", "continuations.jar", (context) -> context.getEspressoEnv().Continuum).build(),
new Builder("org.graalvm.continuations", "continuations.jar", (context) -> context.getLanguage().isContinuumEnabled()).build(),
new Builder("espresso.hotswap", "hotswap.jar", (context) -> context.getEspressoEnv().JDWPOptions != null).build(),
new Builder("espresso.polyglot", "espresso-polyglot.jar", (context) -> context.getEspressoEnv().Polyglot).build(),
new Builder("jdk.graal.compiler.espresso", "espresso-graal.jar", (context) -> context.getLanguage().isInternalJVMCIEnabled()) //
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
import com.oracle.truffle.api.dsl.Cached;
import com.oracle.truffle.api.dsl.Specialization;
import com.oracle.truffle.api.nodes.DirectCallNode;
import com.oracle.truffle.espresso.EspressoLanguage;
import com.oracle.truffle.espresso.meta.Meta;
import com.oracle.truffle.espresso.runtime.EspressoContext;
import com.oracle.truffle.espresso.runtime.staticobject.StaticObject;
Expand Down Expand Up @@ -71,12 +72,12 @@ StaticObject doCached(
@Bind("getMeta()") Meta meta,
@Cached("create(meta.java_lang_invoke_LambdaMetafactory_altMetafactory.getCallTargetNoSubstitution())") DirectCallNode altMetafactory,
@Cached("create(meta.java_lang_invoke_LambdaMetafactory_metafactory.getCallTargetNoSubstitution())") DirectCallNode original,
@Bind("getContext()") EspressoContext context) {
if (context.getEspressoEnv().Continuum) {
@Bind("getLanguage()") EspressoLanguage lang) {
if (lang.isContinuumEnabled()) {
// altMetafactory has a curious calling convention, apparently designed for
// extensibility.
StaticObject extraArgsRef = context.getAllocator().createNewReferenceArray(meta.java_lang_Object, 4);
StaticObject[] extraArgs = extraArgsRef.unwrap(context.getLanguage());
StaticObject extraArgsRef = lang.getAllocator().createNewReferenceArray(meta.java_lang_Object, 4);
StaticObject[] extraArgs = extraArgsRef.unwrap(lang);
extraArgs[0] = interfaceMethodType;
extraArgs[1] = implementation;
extraArgs[2] = dynamicMethodType;
Expand Down Expand Up @@ -106,7 +107,7 @@ StaticObject doCached(
@Bind("getMeta()") Meta meta,
@Cached("create(meta.java_lang_invoke_LambdaMetafactory_altMetafactory.getCallTargetNoSubstitution())") DirectCallNode original,
@Bind("getContext()") EspressoContext context) {
if (context.getEspressoEnv().Continuum) {
if (context.getLanguage().isContinuumEnabled()) {
StaticObject[] extraArgs = args.unwrap(context.getLanguage());
extraArgs[3] = meta.boxInteger(meta.unboxInteger(extraArgs[3]) | LambdaMetafactory.FLAG_SERIALIZABLE);
return (StaticObject) original.call(caller, interfaceMethodName, factoryType, StaticObject.wrap(extraArgs, meta));
Expand Down
Loading