diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/util/json/JSONParser.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/util/json/JSONParser.java index ce060c4d6263..18f795263f12 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/util/json/JSONParser.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/util/json/JSONParser.java @@ -547,7 +547,7 @@ private static String readFully(final Reader reader) throws IOException { try { int numChars; - while ((numChars = reader.read(arr, 0, arr.length)) > 0) { + while ((numChars = reader.read(arr, 0, arr.length)) >= 0) { sb.append(arr, 0, numChars); } } finally { diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/interop/PolyglotTypeMappings.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/interop/PolyglotTypeMappings.java index b5493e043e44..07a4843cd2a1 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/interop/PolyglotTypeMappings.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/interop/PolyglotTypeMappings.java @@ -27,6 +27,7 @@ import java.util.Map; import java.util.Set; +import com.oracle.truffle.espresso.impl.ArrayKlass; import org.graalvm.collections.EconomicMap; import org.graalvm.collections.UnmodifiableEconomicMap; import org.graalvm.options.OptionMap; @@ -141,6 +142,14 @@ private void addInternalConverters(Meta meta) { } else { warn(current, meta.getContext()); } + converters.put("byte[]", new PrimitiveArrayConverter(meta._byte_array)); + converters.put("boolean[]", new PrimitiveArrayConverter(meta._boolean_array)); + converters.put("char[]", new PrimitiveArrayConverter(meta._char_array)); + converters.put("short[]", new PrimitiveArrayConverter(meta._short_array)); + converters.put("int[]", new PrimitiveArrayConverter(meta._int_array)); + converters.put("long[]", new PrimitiveArrayConverter(meta._long_array)); + converters.put("float[]", new PrimitiveArrayConverter(meta._float_array)); + converters.put("double[]", new PrimitiveArrayConverter(meta._double_array)); internalTypeConverterFunctions = EconomicMap.create(converters); } @@ -331,4 +340,21 @@ private StaticObject toByteArray(BigInteger bigInteger, Meta meta) { return StaticObject.wrap(bigInteger.toByteArray(), meta); } } + + public final class PrimitiveArrayConverter implements InternalTypeConverter { + + private final ArrayKlass klass; + + public PrimitiveArrayConverter(ArrayKlass klass) { + this.klass = klass; + } + + @Override + public StaticObject convertInternal(InteropLibrary interop, Object value, Meta meta, ToReference.DynamicToReference toEspresso) { + if (!interop.hasArrayElements(value)) { + throw new ClassCastException(); + } + return StaticObject.createForeign(toEspresso.getLanguage(), klass, value, interop); + } + } } diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/substitutions/Target_java_lang_reflect_Array.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/substitutions/Target_java_lang_reflect_Array.java index 3493d043cf5c..8437785a30ab 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/substitutions/Target_java_lang_reflect_Array.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/substitutions/Target_java_lang_reflect_Array.java @@ -27,6 +27,10 @@ import com.oracle.truffle.api.CompilerDirectives; import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary; +import com.oracle.truffle.api.interop.InteropLibrary; +import com.oracle.truffle.api.interop.InvalidArrayIndexException; +import com.oracle.truffle.api.interop.UnsupportedMessageException; +import com.oracle.truffle.api.interop.UnsupportedTypeException; import com.oracle.truffle.espresso.EspressoLanguage; import com.oracle.truffle.espresso.descriptors.Types; import com.oracle.truffle.espresso.impl.ArrayKlass; @@ -158,11 +162,22 @@ public static boolean getBoolean(@JavaType(Object.class) StaticObject array, int profiler.profile(1); throw meta.throwException(meta.java_lang_IllegalArgumentException); } - try { - return Array.getByte(array.unwrap(language), index) != 0; - } catch (ArrayIndexOutOfBoundsException e) { - profiler.profile(5); - throw rethrowAsGuestException(e, meta, profiler); + if (array.isForeignObject()) { + try { + InteropLibrary library = InteropLibrary.getUncached(); + return library.asBoolean(library.readArrayElement(array.rawForeignObject(language), index)); + } catch (UnsupportedMessageException e) { + throw meta.throwException(meta.java_lang_IllegalArgumentException); + } catch (InvalidArrayIndexException e) { + throw meta.throwException(meta.java_lang_ArrayIndexOutOfBoundsException); + } + } else { + try { + return Array.getByte(array.unwrap(language), index) != 0; + } catch (ArrayIndexOutOfBoundsException e) { + profiler.profile(5); + throw rethrowAsGuestException(e, meta, profiler); + } } } @@ -172,11 +187,22 @@ public static byte getByte(@JavaType(Object.class) StaticObject array, int index @Inject Meta meta, @Inject SubstitutionProfiler profiler) { checkNonNullArray(array, meta, profiler); - try { - return Array.getByte(array.unwrap(language), index); - } catch (NullPointerException | ArrayIndexOutOfBoundsException | IllegalArgumentException e) { - profiler.profile(5); - throw rethrowAsGuestException(e, meta, profiler); + if (array.isForeignObject()) { + try { + InteropLibrary library = InteropLibrary.getUncached(); + return library.asByte(library.readArrayElement(array.rawForeignObject(language), index)); + } catch (UnsupportedMessageException e) { + throw meta.throwException(meta.java_lang_IllegalArgumentException); + } catch (InvalidArrayIndexException e) { + throw meta.throwException(meta.java_lang_ArrayIndexOutOfBoundsException); + } + } else { + try { + return Array.getByte(array.unwrap(language), index); + } catch (NullPointerException | ArrayIndexOutOfBoundsException | IllegalArgumentException e) { + profiler.profile(5); + throw rethrowAsGuestException(e, meta, profiler); + } } } @@ -186,11 +212,29 @@ public static char getChar(@JavaType(Object.class) StaticObject array, int index @Inject Meta meta, @Inject SubstitutionProfiler profiler) { checkNonNullArray(array, meta, profiler); - try { - return Array.getChar(array.unwrap(language), index); - } catch (NullPointerException | ArrayIndexOutOfBoundsException | IllegalArgumentException e) { - profiler.profile(5); - throw rethrowAsGuestException(e, meta, profiler); + if (array.isForeignObject()) { + try { + InteropLibrary library = InteropLibrary.getUncached(); + String str = library.asString(library.readArrayElement(array.rawForeignObject(language), index)); + if (str.isEmpty()) { + return '\u0000'; + } else if (str.length() > 1) { + throw meta.throwException(meta.java_lang_IllegalArgumentException); + } else { + return str.charAt(0); + } + } catch (UnsupportedMessageException e) { + throw meta.throwException(meta.java_lang_IllegalArgumentException); + } catch (InvalidArrayIndexException e) { + throw meta.throwException(meta.java_lang_ArrayIndexOutOfBoundsException); + } + } else { + try { + return Array.getChar(array.unwrap(language), index); + } catch (NullPointerException | ArrayIndexOutOfBoundsException | IllegalArgumentException e) { + profiler.profile(5); + throw rethrowAsGuestException(e, meta, profiler); + } } } @@ -200,11 +244,22 @@ public static short getShort(@JavaType(Object.class) StaticObject array, int ind @Inject Meta meta, @Inject SubstitutionProfiler profiler) { checkNonNullArray(array, meta, profiler); - try { - return Array.getShort(array.unwrap(language), index); - } catch (NullPointerException | ArrayIndexOutOfBoundsException | IllegalArgumentException e) { - profiler.profile(5); - throw rethrowAsGuestException(e, meta, profiler); + if (array.isForeignObject()) { + try { + InteropLibrary library = InteropLibrary.getUncached(); + return library.asShort(library.readArrayElement(array.rawForeignObject(language), index)); + } catch (UnsupportedMessageException e) { + throw meta.throwException(meta.java_lang_IllegalArgumentException); + } catch (InvalidArrayIndexException e) { + throw meta.throwException(meta.java_lang_ArrayIndexOutOfBoundsException); + } + } else { + try { + return Array.getShort(array.unwrap(language), index); + } catch (NullPointerException | ArrayIndexOutOfBoundsException | IllegalArgumentException e) { + profiler.profile(5); + throw rethrowAsGuestException(e, meta, profiler); + } } } @@ -214,11 +269,22 @@ public static int getInt(@JavaType(Object.class) StaticObject array, int index, @Inject Meta meta, @Inject SubstitutionProfiler profiler) { checkNonNullArray(array, meta, profiler); - try { - return Array.getInt(array.unwrap(language), index); - } catch (NullPointerException | ArrayIndexOutOfBoundsException | IllegalArgumentException e) { - profiler.profile(5); - throw rethrowAsGuestException(e, meta, profiler); + if (array.isForeignObject()) { + try { + InteropLibrary library = InteropLibrary.getUncached(); + return library.asInt(library.readArrayElement(array.rawForeignObject(language), index)); + } catch (UnsupportedMessageException e) { + throw meta.throwException(meta.java_lang_IllegalArgumentException); + } catch (InvalidArrayIndexException e) { + throw meta.throwException(meta.java_lang_ArrayIndexOutOfBoundsException); + } + } else { + try { + return Array.getInt(array.unwrap(language), index); + } catch (NullPointerException | ArrayIndexOutOfBoundsException | IllegalArgumentException e) { + profiler.profile(5); + throw rethrowAsGuestException(e, meta, profiler); + } } } @@ -228,11 +294,22 @@ public static float getFloat(@JavaType(Object.class) StaticObject array, int ind @Inject Meta meta, @Inject SubstitutionProfiler profiler) { checkNonNullArray(array, meta, profiler); - try { - return Array.getFloat(array.unwrap(language), index); - } catch (NullPointerException | ArrayIndexOutOfBoundsException | IllegalArgumentException e) { - profiler.profile(5); - throw rethrowAsGuestException(e, meta, profiler); + if (array.isForeignObject()) { + try { + InteropLibrary library = InteropLibrary.getUncached(); + return library.asFloat(library.readArrayElement(array.rawForeignObject(language), index)); + } catch (UnsupportedMessageException e) { + throw meta.throwException(meta.java_lang_IllegalArgumentException); + } catch (InvalidArrayIndexException e) { + throw meta.throwException(meta.java_lang_ArrayIndexOutOfBoundsException); + } + } else { + try { + return Array.getFloat(array.unwrap(language), index); + } catch (NullPointerException | ArrayIndexOutOfBoundsException | IllegalArgumentException e) { + profiler.profile(5); + throw rethrowAsGuestException(e, meta, profiler); + } } } @@ -242,11 +319,22 @@ public static double getDouble(@JavaType(Object.class) StaticObject array, int i @Inject Meta meta, @Inject SubstitutionProfiler profiler) { checkNonNullArray(array, meta, profiler); - try { - return Array.getDouble(array.unwrap(language), index); - } catch (NullPointerException | ArrayIndexOutOfBoundsException | IllegalArgumentException e) { - profiler.profile(5); - throw rethrowAsGuestException(e, meta, profiler); + if (array.isForeignObject()) { + try { + InteropLibrary library = InteropLibrary.getUncached(); + return library.asDouble(library.readArrayElement(array.rawForeignObject(language), index)); + } catch (UnsupportedMessageException e) { + throw meta.throwException(meta.java_lang_IllegalArgumentException); + } catch (InvalidArrayIndexException e) { + throw meta.throwException(meta.java_lang_ArrayIndexOutOfBoundsException); + } + } else { + try { + return Array.getDouble(array.unwrap(language), index); + } catch (NullPointerException | ArrayIndexOutOfBoundsException | IllegalArgumentException e) { + profiler.profile(5); + throw rethrowAsGuestException(e, meta, profiler); + } } } @@ -256,11 +344,22 @@ public static long getLong(@JavaType(Object.class) StaticObject array, int index @Inject Meta meta, @Inject SubstitutionProfiler profiler) { checkNonNullArray(array, meta, profiler); - try { - return Array.getLong(array.unwrap(language), index); - } catch (NullPointerException | ArrayIndexOutOfBoundsException | IllegalArgumentException e) { - profiler.profile(5); - throw rethrowAsGuestException(e, meta, profiler); + if (array.isForeignObject()) { + try { + InteropLibrary library = InteropLibrary.getUncached(); + return library.asLong(library.readArrayElement(array.rawForeignObject(language), index)); + } catch (UnsupportedMessageException e) { + throw meta.throwException(meta.java_lang_IllegalArgumentException); + } catch (InvalidArrayIndexException e) { + throw meta.throwException(meta.java_lang_ArrayIndexOutOfBoundsException); + } + } else { + try { + return Array.getLong(array.unwrap(language), index); + } catch (NullPointerException | ArrayIndexOutOfBoundsException | IllegalArgumentException e) { + profiler.profile(5); + throw rethrowAsGuestException(e, meta, profiler); + } } } @@ -315,11 +414,15 @@ public static void setBoolean(@JavaType(Object.class) StaticObject array, int in profiler.profile(1); throw meta.throwException(meta.java_lang_IllegalArgumentException); } - try { - Array.setByte(array.unwrap(language), index, value ? (byte) 1 : (byte) 0); - } catch (ArrayIndexOutOfBoundsException e) { - profiler.profile(5); - throw rethrowAsGuestException(e, meta, profiler); + if (array.isForeignObject()) { + writeForeignArrayElement(array, language, index, value, meta); + } else { + try { + Array.setByte(array.unwrap(language), index, value ? (byte) 1 : (byte) 0); + } catch (ArrayIndexOutOfBoundsException e) { + profiler.profile(5); + throw rethrowAsGuestException(e, meta, profiler); + } } } @@ -329,11 +432,15 @@ public static void setByte(@JavaType(Object.class) StaticObject array, int index @Inject Meta meta, @Inject SubstitutionProfiler profiler) { checkNonNullArray(array, meta, profiler); - try { - Array.setByte(array.unwrap(language), index, value); - } catch (NullPointerException | ArrayIndexOutOfBoundsException | IllegalArgumentException e) { - profiler.profile(5); - throw rethrowAsGuestException(e, meta, profiler); + if (array.isForeignObject()) { + writeForeignArrayElement(array, language, index, value, meta); + } else { + try { + Array.setByte(array.unwrap(language), index, value); + } catch (NullPointerException | ArrayIndexOutOfBoundsException | IllegalArgumentException e) { + profiler.profile(5); + throw rethrowAsGuestException(e, meta, profiler); + } } } @@ -343,11 +450,15 @@ public static void setChar(@JavaType(Object.class) StaticObject array, int index @Inject Meta meta, @Inject SubstitutionProfiler profiler) { checkNonNullArray(array, meta, profiler); - try { - Array.setChar(array.unwrap(language), index, value); - } catch (NullPointerException | ArrayIndexOutOfBoundsException | IllegalArgumentException e) { - profiler.profile(5); - throw rethrowAsGuestException(e, meta, profiler); + if (array.isForeignObject()) { + writeForeignArrayElement(array, language, index, value, meta); + } else { + try { + Array.setChar(array.unwrap(language), index, value); + } catch (NullPointerException | ArrayIndexOutOfBoundsException | IllegalArgumentException e) { + profiler.profile(5); + throw rethrowAsGuestException(e, meta, profiler); + } } } @@ -357,11 +468,15 @@ public static void setShort(@JavaType(Object.class) StaticObject array, int inde @Inject Meta meta, @Inject SubstitutionProfiler profiler) { checkNonNullArray(array, meta, profiler); - try { - Array.setShort(array.unwrap(language), index, value); - } catch (NullPointerException | ArrayIndexOutOfBoundsException | IllegalArgumentException e) { - profiler.profile(5); - throw rethrowAsGuestException(e, meta, profiler); + if (array.isForeignObject()) { + writeForeignArrayElement(array, language, index, value, meta); + } else { + try { + Array.setShort(array.unwrap(language), index, value); + } catch (NullPointerException | ArrayIndexOutOfBoundsException | IllegalArgumentException e) { + profiler.profile(5); + throw rethrowAsGuestException(e, meta, profiler); + } } } @@ -371,11 +486,15 @@ public static void setInt(@JavaType(Object.class) StaticObject array, int index, @Inject Meta meta, @Inject SubstitutionProfiler profiler) { checkNonNullArray(array, meta, profiler); - try { - Array.setInt(array.unwrap(language), index, value); - } catch (NullPointerException | ArrayIndexOutOfBoundsException | IllegalArgumentException e) { - profiler.profile(5); - throw rethrowAsGuestException(e, meta, profiler); + if (array.isForeignObject()) { + writeForeignArrayElement(array, language, index, value, meta); + } else { + try { + Array.setInt(array.unwrap(language), index, value); + } catch (NullPointerException | ArrayIndexOutOfBoundsException | IllegalArgumentException e) { + profiler.profile(5); + throw rethrowAsGuestException(e, meta, profiler); + } } } @@ -385,11 +504,15 @@ public static void setFloat(@JavaType(Object.class) StaticObject array, int inde @Inject Meta meta, @Inject SubstitutionProfiler profiler) { checkNonNullArray(array, meta, profiler); - try { - Array.setFloat(array.unwrap(language), index, value); - } catch (NullPointerException | ArrayIndexOutOfBoundsException | IllegalArgumentException e) { - profiler.profile(5); - throw rethrowAsGuestException(e, meta, profiler); + if (array.isForeignObject()) { + writeForeignArrayElement(array, language, index, value, meta); + } else { + try { + Array.setFloat(array.unwrap(language), index, value); + } catch (NullPointerException | ArrayIndexOutOfBoundsException | IllegalArgumentException e) { + profiler.profile(5); + throw rethrowAsGuestException(e, meta, profiler); + } } } @@ -399,11 +522,15 @@ public static void setDouble(@JavaType(Object.class) StaticObject array, int ind @Inject Meta meta, @Inject SubstitutionProfiler profiler) { checkNonNullArray(array, meta, profiler); - try { - Array.setDouble(array.unwrap(language), index, value); - } catch (NullPointerException | ArrayIndexOutOfBoundsException | IllegalArgumentException e) { - profiler.profile(5); - throw rethrowAsGuestException(e, meta, profiler); + if (array.isForeignObject()) { + writeForeignArrayElement(array, language, index, value, meta); + } else { + try { + Array.setDouble(array.unwrap(language), index, value); + } catch (NullPointerException | ArrayIndexOutOfBoundsException | IllegalArgumentException e) { + profiler.profile(5); + throw rethrowAsGuestException(e, meta, profiler); + } } } @@ -413,11 +540,26 @@ public static void setLong(@JavaType(Object.class) StaticObject array, int index @Inject Meta meta, @Inject SubstitutionProfiler profiler) { checkNonNullArray(array, meta, profiler); + if (array.isForeignObject()) { + writeForeignArrayElement(array, language, index, value, meta); + } else { + try { + Array.setLong(array.unwrap(language), index, value); + } catch (NullPointerException | ArrayIndexOutOfBoundsException | IllegalArgumentException e) { + profiler.profile(5); + throw rethrowAsGuestException(e, meta, profiler); + } + } + } + + private static void writeForeignArrayElement(StaticObject array, EspressoLanguage language, int index, Object value, Meta meta) { try { - Array.setLong(array.unwrap(language), index, value); - } catch (NullPointerException | ArrayIndexOutOfBoundsException | IllegalArgumentException e) { - profiler.profile(5); - throw rethrowAsGuestException(e, meta, profiler); + InteropLibrary library = InteropLibrary.getUncached(); + library.writeArrayElement(array.rawForeignObject(language), index, value); + } catch (UnsupportedMessageException | UnsupportedTypeException e) { + throw meta.throwException(meta.java_lang_IllegalArgumentException); + } catch (InvalidArrayIndexException e) { + throw meta.throwException(meta.java_lang_ArrayIndexOutOfBoundsException); } } @@ -445,17 +587,28 @@ public static void set(@JavaType(Object.class) StaticObject array, int index, @J throw meta.throwNullPointerException(); } if (array.isArray()) { - // @formatter:off Object widenValue = Target_sun_reflect_NativeMethodAccessorImpl.checkAndWiden(meta, value, ((ArrayKlass) array.getKlass()).getComponentType()); + if (array.isForeignObject()) { + try { + InteropLibrary library = InteropLibrary.getUncached(); + library.writeArrayElement(array.rawForeignObject(language), index, widenValue); + return; + } catch (UnsupportedMessageException | UnsupportedTypeException e) { + throw meta.throwException(meta.java_lang_IllegalArgumentException); + } catch (InvalidArrayIndexException e) { + throw meta.throwException(meta.java_lang_ArrayIndexOutOfBoundsException); + } + } + // @formatter:off switch (((ArrayKlass) array.getKlass()).getComponentType().getJavaKind()) { case Boolean : vm.setArrayByte(language, ((boolean) widenValue) ? (byte) 1 : (byte) 0, index, array); break; - case Byte : vm.setArrayByte(language, ((byte) widenValue), index, array); break; - case Short : vm.setArrayShort(language, ((short) widenValue), index, array); break; - case Char : vm.setArrayChar(language, ((char) widenValue), index, array); break; - case Int : vm.setArrayInt(language, ((int) widenValue), index, array); break; - case Float : vm.setArrayFloat(language, ((float) widenValue), index, array); break; - case Long : vm.setArrayLong(language, ((long) widenValue), index, array); break; - case Double : vm.setArrayDouble(language, ((double) widenValue), index, array); break; + case Byte : vm.setArrayByte(language, ((byte) widenValue), index, array); break; + case Short : vm.setArrayShort(language, ((short) widenValue), index, array); break; + case Char : vm.setArrayChar(language, ((char) widenValue), index, array); break; + case Int : vm.setArrayInt(language, ((int) widenValue), index, array); break; + case Float : vm.setArrayFloat(language, ((float) widenValue), index, array); break; + case Long : vm.setArrayLong(language, ((long) widenValue), index, array); break; + case Double : vm.setArrayDouble(language, ((double) widenValue), index, array); break; case Object : vm.setArrayObject(language, value, index, array); break; default : CompilerDirectives.transferToInterpreter(); @@ -489,25 +642,131 @@ public static void set(@JavaType(Object.class) StaticObject array, int index, @J throw meta.throwNullPointerException(); } if (array.isArray()) { - // @formatter:off - switch (((ArrayKlass) array.getKlass()).getComponentType().getJavaKind()) { - case Boolean : return meta.boxBoolean(vm.getArrayByte(language, index, array) != 0); - case Byte : return meta.boxByte(vm.getArrayByte(language, index, array)); - case Short : return meta.boxShort(vm.getArrayShort(language, index, array)); - case Char : return meta.boxCharacter(vm.getArrayChar(language, index, array)); - case Int : return meta.boxInteger(vm.getArrayInt(language, index, array)); - case Float : return meta.boxFloat(vm.getArrayFloat(language, index, array)); - case Long : return meta.boxLong(vm.getArrayLong(language, index, array)); - case Double : return meta.boxDouble(vm.getArrayDouble(language, index, array)); - case Object : return vm.getArrayObject(language, index, array); - default : - CompilerDirectives.transferToInterpreter(); - throw EspressoError.shouldNotReachHere("invalid array type: " + array); + try { + switch (((ArrayKlass) array.getKlass()).getComponentType().getJavaKind()) { + case Boolean: { + boolean result; + if (array.isForeignObject()) { + InteropLibrary library = InteropLibrary.getUncached(); + result = library.asBoolean(library.readArrayElement(array.rawForeignObject(language), index)); + } else { + result = vm.getArrayByte(language, index, array) != 0; + } + return meta.boxBoolean(result); + } + case Byte: { + byte result; + if (array.isForeignObject()) { + InteropLibrary library = InteropLibrary.getUncached(); + result = library.asByte(library.readArrayElement(array.rawForeignObject(language), index)); + } else { + result = vm.getArrayByte(language, index, array); + } + return meta.boxByte(result); + } + case Short: { + short result; + if (array.isForeignObject()) { + InteropLibrary library = InteropLibrary.getUncached(); + result = library.asShort(library.readArrayElement(array.rawForeignObject(language), index)); + } else { + result = vm.getArrayShort(language, index, array); + } + return meta.boxShort(result); + } + case Char: { + char result; + if (array.isForeignObject()) { + InteropLibrary library = InteropLibrary.getUncached(); + String str = library.asString(library.readArrayElement(array.rawForeignObject(language), index)); + if (str.isEmpty()) { + result = '\u0000'; + } else if (str.length() > 1) { + throw meta.throwException(meta.java_lang_IllegalArgumentException); + } else { + result = str.charAt(0); + } + } else { + result = vm.getArrayChar(language, index, array); + } + return meta.boxCharacter(result); + } + case Int: { + int result; + if (array.isForeignObject()) { + InteropLibrary library = InteropLibrary.getUncached(); + result = library.asInt(library.readArrayElement(array.rawForeignObject(language), index)); + } else { + result = vm.getArrayInt(language, index, array); + } + return meta.boxInteger(result); + } + case Float: { + float result; + if (array.isForeignObject()) { + InteropLibrary library = InteropLibrary.getUncached(); + result = library.asFloat(library.readArrayElement(array.rawForeignObject(language), index)); + } else { + result = vm.getArrayFloat(language, index, array); + } + return meta.boxFloat(result); + } + case Long: { + long result; + if (array.isForeignObject()) { + InteropLibrary library = InteropLibrary.getUncached(); + result = library.asLong(library.readArrayElement(array.rawForeignObject(language), index)); + } else { + result = vm.getArrayLong(language, index, array); + } + return meta.boxLong(result); + } + case Double: { + double result; + if (array.isForeignObject()) { + InteropLibrary library = InteropLibrary.getUncached(); + result = library.asDouble(library.readArrayElement(array.rawForeignObject(language), index)); + } else { + result = vm.getArrayDouble(language, index, array); + } + return meta.boxDouble(result); + } + case Object: { + if (array.isForeignObject()) { + InteropLibrary library = InteropLibrary.getUncached(); + Object result = library.readArrayElement(array.rawForeignObject(language), index); + return StaticObject.createForeign(language, meta.java_lang_Object, result, InteropLibrary.getUncached(result)); + } else { + return vm.getArrayObject(language, index, array); + } + } + default: + CompilerDirectives.transferToInterpreter(); + throw EspressoError.shouldNotReachHere("invalid array type: " + array); + } + } catch (UnsupportedMessageException e) { + throw meta.throwException(meta.java_lang_IllegalArgumentException); + } catch (InvalidArrayIndexException e) { + int length = getForeignArrayLength(array, language, meta); + throw meta.throwExceptionWithMessage(meta.java_lang_ArrayIndexOutOfBoundsException, InterpreterToVM.outOfBoundsMessage(index, length)); } - // @formatter:on } else { throw meta.throwException(meta.java_lang_IllegalArgumentException); } } + private static int getForeignArrayLength(StaticObject array, EspressoLanguage language, Meta meta) { + assert array.isForeignObject(); + try { + Object foreignObject = array.rawForeignObject(language); + InteropLibrary library = InteropLibrary.getUncached(foreignObject); + long arrayLength = library.getArraySize(foreignObject); + if (arrayLength > Integer.MAX_VALUE) { + return Integer.MAX_VALUE; + } + return (int) arrayLength; + } catch (UnsupportedMessageException e) { + throw meta.throwExceptionWithMessage(meta.java_lang_IllegalArgumentException, "can't get array length because foreign object is not an array"); + } + } } diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/vm/InterpreterToVM.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/vm/InterpreterToVM.java index 58b69a62307d..d72c981239bf 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/vm/InterpreterToVM.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/vm/InterpreterToVM.java @@ -114,7 +114,7 @@ public int getArrayInt(EspressoLanguage language, int index, @JavaType(int[].cla } @TruffleBoundary - private static String outOfBoundsMessage(int index, int length) { + public static String outOfBoundsMessage(int index, int length) { return "Index " + index + " out of bounds for length " + length; } diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/vm/VM.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/vm/VM.java index c438e8a40c3c..0c6168c361d0 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/vm/VM.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/vm/VM.java @@ -2531,15 +2531,31 @@ private Map buildPropertiesMap() { @VmImpl(isJni = true) public int JVM_GetArrayLength(@JavaType(Object.class) StaticObject array, @Inject EspressoLanguage language, @Inject SubstitutionProfiler profiler) { - try { - return Array.getLength(MetaUtil.unwrapArrayOrNull(language, array)); - } catch (IllegalArgumentException e) { - profiler.profile(0); - Meta meta = getMeta(); - throw meta.throwExceptionWithMessage(meta.java_lang_IllegalArgumentException, e.getMessage()); - } catch (NullPointerException e) { - profiler.profile(1); - throw getMeta().throwNullPointerException(); + if (array.isForeignObject()) { + try { + Object foreignObject = array.rawForeignObject(language); + InteropLibrary library = InteropLibrary.getUncached(foreignObject); + long arrayLength = library.getArraySize(foreignObject); + if (arrayLength > Integer.MAX_VALUE) { + return Integer.MAX_VALUE; + } + return (int) arrayLength; + } catch (UnsupportedMessageException e) { + profiler.profile(0); + Meta meta = getMeta(); + throw meta.throwExceptionWithMessage(meta.java_lang_IllegalArgumentException, "can't get array length because foreign object is not an array"); + } + } else { + try { + return Array.getLength(MetaUtil.unwrapArrayOrNull(language, array)); + } catch (IllegalArgumentException e) { + profiler.profile(1); + Meta meta = getMeta(); + throw meta.throwExceptionWithMessage(meta.java_lang_IllegalArgumentException, e.getMessage()); + } catch (NullPointerException e) { + profiler.profile(2); + throw getMeta().throwNullPointerException(); + } } } diff --git a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/heap/HeapSnapshotVerifier.java b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/heap/HeapSnapshotVerifier.java index d72d31940e9c..66865b2e440d 100644 --- a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/heap/HeapSnapshotVerifier.java +++ b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/heap/HeapSnapshotVerifier.java @@ -187,31 +187,37 @@ private boolean verifyFieldValue(JavaConstant receiver, AnalysisField field, Jav if (field.isStatic()) { TypeData typeData = field.getDeclaringClass().getOrComputeData(); JavaConstant fieldSnapshot = typeData.readFieldValue(field); - verifyStaticFieldValue(typeData, field, maybeUnwrapSnapshot(fieldSnapshot, fieldValue instanceof ImageHeapConstant), fieldValue, reason); + verifyStaticFieldValue(typeData, field, fieldSnapshot, fieldValue, reason); } else { - ImageHeapInstance receiverObject = (ImageHeapInstance) getReceiverObject(receiver, reason); + ImageHeapInstance receiverObject = (ImageHeapInstance) getSnapshot(receiver, reason); JavaConstant fieldSnapshot = receiverObject.readFieldValue(field); - verifyInstanceFieldValue(field, receiver, receiverObject, maybeUnwrapSnapshot(fieldSnapshot, fieldValue instanceof ImageHeapConstant), fieldValue, reason); + verifyInstanceFieldValue(field, receiver, receiverObject, fieldSnapshot, fieldValue, reason); } return false; } private void verifyStaticFieldValue(TypeData typeData, AnalysisField field, JavaConstant fieldSnapshot, JavaConstant fieldValue, ScanReason reason) { - if (!Objects.equals(fieldSnapshot, fieldValue)) { + JavaConstant result = fieldSnapshot; + JavaConstant unwrappedSnapshot = maybeUnwrapSnapshot(fieldSnapshot, fieldValue instanceof ImageHeapConstant); + if (!Objects.equals(unwrappedSnapshot, fieldValue)) { String format = "Value mismatch for static field %s %n snapshot: %s %n new value: %s %n"; - Consumer onAnalysisModified = analysisModified(reason, format, field, fieldSnapshot, fieldValue); - scanner.patchStaticField(typeData, field, fieldValue, reason, onAnalysisModified).ensureDone(); + Consumer onAnalysisModified = analysisModified(reason, format, field, unwrappedSnapshot, fieldValue); + result = scanner.patchStaticField(typeData, field, fieldValue, reason, onAnalysisModified).ensureDone(); heapPatched = true; } + ImageHeapScanner.ensureReaderInstalled(result); } private void verifyInstanceFieldValue(AnalysisField field, JavaConstant receiver, ImageHeapInstance receiverObject, JavaConstant fieldSnapshot, JavaConstant fieldValue, ScanReason reason) { - if (!Objects.equals(fieldSnapshot, fieldValue)) { + JavaConstant result = fieldSnapshot; + JavaConstant unwrappedSnapshot = maybeUnwrapSnapshot(fieldSnapshot, fieldValue instanceof ImageHeapConstant); + if (!Objects.equals(unwrappedSnapshot, fieldValue)) { String format = "Value mismatch for instance field %s of %s %n snapshot: %s %n new value: %s %n"; - Consumer onAnalysisModified = analysisModified(reason, format, field, asString(receiver), fieldSnapshot, fieldValue); - scanner.patchInstanceField(receiverObject, field, fieldValue, reason, onAnalysisModified).ensureDone(); + Consumer onAnalysisModified = analysisModified(reason, format, field, asString(receiver), unwrappedSnapshot, fieldValue); + result = scanner.patchInstanceField(receiverObject, field, fieldValue, reason, onAnalysisModified).ensureDone(); heapPatched = true; } + ImageHeapScanner.ensureReaderInstalled(result); } private Consumer analysisModified(ScanReason reason, String format, Object... args) { @@ -249,40 +255,48 @@ public boolean forNonNullArrayElement(JavaConstant array, AnalysisType arrayType * fields that become available but may have not yet been consumed. We simply execute * the future, then compare the produced value. */ - ImageHeapObjectArray arrayObject = (ImageHeapObjectArray) getReceiverObject(array, reason); + ImageHeapObjectArray arrayObject = (ImageHeapObjectArray) getSnapshot(array, reason); JavaConstant elementSnapshot = arrayObject.readElementValue(index); verifyArrayElementValue(elementValue, index, reason, array, arrayObject, elementSnapshot); return false; } private void verifyArrayElementValue(JavaConstant elementValue, int index, ScanReason reason, JavaConstant array, ImageHeapObjectArray arrayObject, JavaConstant elementSnapshot) { + JavaConstant result = elementSnapshot; if (!Objects.equals(maybeUnwrapSnapshot(elementSnapshot, elementValue instanceof ImageHeapConstant), elementValue)) { String format = "Value mismatch for array element at index %s of %s %n snapshot: %s %n new value: %s %n"; Consumer onAnalysisModified = analysisModified(reason, format, index, asString(array), elementSnapshot, elementValue); - scanner.patchArrayElement(arrayObject, index, elementValue, reason, onAnalysisModified).ensureDone(); + result = scanner.patchArrayElement(arrayObject, index, elementValue, reason, onAnalysisModified).ensureDone(); heapPatched = true; } + ImageHeapScanner.ensureReaderInstalled(result); } @SuppressWarnings({"unchecked", "rawtypes"}) - private ImageHeapConstant getReceiverObject(JavaConstant constant, ScanReason reason) { + private ImageHeapConstant getSnapshot(JavaConstant constant, ScanReason reason) { + ImageHeapConstant result; if (constant instanceof ImageHeapConstant) { /* This is a simulated constant. */ - return (ImageHeapConstant) constant; - } - Object task = imageHeap.getSnapshot(constant); - if (task == null) { - throw error(reason, "Task is null for constant %s.", constant); - } else if (task instanceof ImageHeapConstant) { - return (ImageHeapConstant) task; + result = (ImageHeapConstant) constant; } else { - AnalysisFuture future = ((AnalysisFuture) task); - if (future.isDone()) { - return future.guardedGet(); + Object task = imageHeap.getSnapshot(constant); + if (task == null) { + throw error(reason, "Task is null for constant %s.", constant); + } else if (task instanceof ImageHeapConstant) { + result = (ImageHeapConstant) task; } else { - throw error(reason, "Task not yet executed for constant %s.", constant); + AnalysisFuture future = ((AnalysisFuture) task); + if (future.isDone()) { + result = future.guardedGet(); + } else { + throw error(reason, "Task not yet executed for constant %s.", constant); + } } } + if (!result.isReaderInstalled()) { + throw error(reason, "Reader not yet installed for constant %s.", constant); + } + return result; } @Override diff --git a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/heap/ImageHeapArray.java b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/heap/ImageHeapArray.java index 815c03e12ea2..0bc8f603b24a 100644 --- a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/heap/ImageHeapArray.java +++ b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/heap/ImageHeapArray.java @@ -27,7 +27,6 @@ import com.oracle.graal.pointsto.meta.AnalysisType; import jdk.vm.ci.meta.JavaConstant; -import jdk.vm.ci.meta.ResolvedJavaType; public abstract class ImageHeapArray extends ImageHeapConstant { @@ -36,9 +35,9 @@ public static ImageHeapArray create(AnalysisType type, int length) { return type.getComponentType().getStorageKind().isPrimitive() ? new ImageHeapPrimitiveArray(type, length) : new ImageHeapObjectArray(type, length); } - protected ImageHeapArray(ResolvedJavaType type, JavaConstant object, int identityHashCode, boolean compressed) { - super(type, object, identityHashCode, compressed); - assert type.isArray() : type; + protected ImageHeapArray(ConstantData constantData, boolean compressed) { + super(constantData, compressed); + assert constantData.type.isArray() : constantData.type; } public abstract Object getElement(int idx); diff --git a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/heap/ImageHeapConstant.java b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/heap/ImageHeapConstant.java index 81c3c741dcfe..76f167f363db 100644 --- a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/heap/ImageHeapConstant.java +++ b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/heap/ImageHeapConstant.java @@ -24,21 +24,23 @@ */ package com.oracle.graal.pointsto.heap; +import java.lang.invoke.MethodHandles; +import java.lang.invoke.VarHandle; import java.util.Objects; -import java.util.concurrent.atomic.AtomicReferenceFieldUpdater; -import jdk.graal.compiler.core.common.type.CompressibleConstant; -import jdk.graal.compiler.core.common.type.TypedConstant; import org.graalvm.nativeimage.Platform; import org.graalvm.nativeimage.Platforms; import com.oracle.graal.pointsto.ObjectScanner; -import com.oracle.graal.pointsto.util.AtomicUtils; +import com.oracle.graal.pointsto.meta.AnalysisType; +import com.oracle.graal.pointsto.util.AnalysisFuture; +import com.oracle.svm.util.ReflectionUtil; +import jdk.graal.compiler.core.common.type.CompressibleConstant; +import jdk.graal.compiler.core.common.type.TypedConstant; import jdk.vm.ci.meta.JavaConstant; import jdk.vm.ci.meta.JavaKind; import jdk.vm.ci.meta.MetaAccessProvider; -import jdk.vm.ci.meta.ResolvedJavaType; import jdk.vm.ci.meta.VMConstant; /** @@ -49,35 +51,87 @@ */ @Platforms(Platform.HOSTED_ONLY.class) public abstract class ImageHeapConstant implements JavaConstant, TypedConstant, CompressibleConstant, VMConstant { - /** Stores the type of this object. */ - protected ResolvedJavaType type; - /** - * Stores the hosted object, already processed by the object transformers. It is null for - * instances of partially evaluated classes. - */ - protected final JavaConstant hostedObject; - protected final int identityHashCode; - protected final boolean compressed; + public static final VarHandle isReachableHandle = ReflectionUtil.unreflectField(ConstantData.class, "isReachable", MethodHandles.lookup()); - @SuppressWarnings("unused") private volatile Object isReachable; + public abstract static class ConstantData { + /** + * Stores the type of this object. + */ + protected final AnalysisType type; + /** + * Stores the hosted object, already processed by the object transformers. It is null for + * instances of partially evaluated classes. + */ + private final JavaConstant hostedObject; + /** + * See {@link #createIdentityHashCode(JavaConstant)}. + */ + private final int identityHashCode; + /** + * A future that reads the hosted field or array elements values lazily only when the + * receiver object is used. This way the shadow heap can contain hosted only objects, i.e., + * objects that cannot be reachable at run time but are processed ahead-of-time. + */ + AnalysisFuture hostedValuesReader; + /** + * A constant is marked as reachable only when it is decided that it can be used at run-time + * and its field values/array elements need to be processed. The value of the field is + * initially null, then it stores the reason why this constant became reachable. + */ + @SuppressWarnings("unused") private volatile Object isReachable; - private static final AtomicReferenceFieldUpdater isReachableUpdater = AtomicReferenceFieldUpdater - .newUpdater(ImageHeapConstant.class, Object.class, "isReachable"); + ConstantData(AnalysisType type, JavaConstant object, int identityHashCode) { + Objects.requireNonNull(type); + this.type = type; + this.hostedObject = object; + this.identityHashCode = identityHashCode; + } + + @Override + public int hashCode() { + return hostedObject != null ? hostedObject.hashCode() : super.hashCode(); + } + } - ImageHeapConstant(ResolvedJavaType type, JavaConstant object, int identityHashCode, boolean compressed) { - this.type = type; - this.hostedObject = object; - this.identityHashCode = identityHashCode; + protected final ConstantData constantData; + protected final boolean compressed; + + ImageHeapConstant(ConstantData constantData, boolean compressed) { + this.constantData = constantData; this.compressed = compressed; } + public ConstantData getConstantData() { + return constantData; + } + + public void ensureReaderInstalled() { + if (constantData.hostedValuesReader != null) { + constantData.hostedValuesReader.ensureDone(); + } + } + + /** + * A regular image heap constant starts off without any fields or array elements installed. It + * instead contains a future task, the hostedValuesReader, which creates the tasks that read the + * hosted values. It must be executed before any values can be accessed. This ensures that the + * hosted values are only read when the constant is indeed used, i.e., it was not eliminated by + * constant folding. + * + * Simulated constants are fully initialized when they are created. + */ + protected boolean isReaderInstalled() { + return constantData.hostedValuesReader == null || constantData.hostedValuesReader.isDone(); + } + public boolean markReachable(ObjectScanner.ScanReason reason) { - return AtomicUtils.atomicSet(this, reason, isReachableUpdater); + ensureReaderInstalled(); + return isReachableHandle.compareAndSet(constantData, null, reason); } public boolean isReachable() { - return AtomicUtils.isSet(this, isReachableUpdater); + return isReachableHandle.get(constantData) != null; } static int createIdentityHashCode(JavaConstant object) { @@ -96,15 +150,15 @@ static int createIdentityHashCode(JavaConstant object) { @Override public int getIdentityHashCode() { - if (hostedObject != null) { - if (hostedObject.isNull()) { + if (constantData.hostedObject != null) { + if (constantData.hostedObject.isNull()) { /* * According to the JavaDoc of System.identityHashCode, the identity hash code of * null is 0. */ return 0; } else { - return ((TypedConstant) hostedObject).getIdentityHashCode(); + return ((TypedConstant) constantData.hostedObject).getIdentityHashCode(); } } else { /* @@ -112,17 +166,17 @@ public int getIdentityHashCode() { * hash code that has the same properties as the image builder VM, so we use the * identity hash code of a new and otherwise unused object in the image builder VM. */ - assert identityHashCode > 0 : "The Java HotSpot VM only returns positive numbers for the identity hash code, so we want to have the same restriction on Substrate VM in order to not surprise users"; - return identityHashCode; + assert constantData.identityHashCode > 0 : "The Java HotSpot VM only returns positive numbers for the identity hash code, so we want to have the same restriction on Substrate VM in order to not surprise users"; + return constantData.identityHashCode; } } public JavaConstant getHostedObject() { - return hostedObject; + return constantData.hostedObject; } public boolean isBackedByHostedObject() { - return hostedObject != null; + return constantData.hostedObject != null; } @Override @@ -141,12 +195,8 @@ public boolean isDefaultForKind() { } @Override - public ResolvedJavaType getType(MetaAccessProvider provider) { - return type; - } - - public void setType(ResolvedJavaType type) { - this.type = type; + public AnalysisType getType(MetaAccessProvider provider) { + return constantData.type; } @Override @@ -186,7 +236,7 @@ public boolean isCompressed() { @Override public String toValueString() { - return type.getName(); + return constantData.type.getName(); } /** @@ -203,13 +253,18 @@ public boolean equals(Object o) { * the previous behavior where the raw object was extracted and used as a key when * constructing the image heap map. */ - return Objects.equals(this.type, other.type) && Objects.equals(this.hostedObject, other.hostedObject); + return this.constantData == other.constantData; } return false; } @Override public int hashCode() { - return hostedObject != null ? hostedObject.hashCode() : 0; + return constantData.hashCode(); + } + + @Override + public String toString() { + return "ImageHeapConstant< " + constantData.type.toJavaName() + ", reachable: " + isReachable() + ", reader installed: " + isReaderInstalled() + ">"; } } diff --git a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/heap/ImageHeapInstance.java b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/heap/ImageHeapInstance.java index 4b28cd3feae3..f9844938095c 100644 --- a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/heap/ImageHeapInstance.java +++ b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/heap/ImageHeapInstance.java @@ -27,14 +27,17 @@ import java.lang.invoke.MethodHandles; import java.lang.invoke.VarHandle; import java.util.Arrays; +import java.util.Objects; import com.oracle.graal.pointsto.ObjectScanner; import com.oracle.graal.pointsto.heap.value.ValueSupplier; import com.oracle.graal.pointsto.meta.AnalysisField; +import com.oracle.graal.pointsto.meta.AnalysisType; +import com.oracle.graal.pointsto.util.AnalysisError; import com.oracle.graal.pointsto.util.AnalysisFuture; +import com.oracle.svm.util.ReflectionUtil; import jdk.vm.ci.meta.JavaConstant; -import jdk.vm.ci.meta.ResolvedJavaType; /** * This class implements an instance object snapshot. It stores the field values in an Object[], @@ -50,36 +53,82 @@ public final class ImageHeapInstance extends ImageHeapConstant { private static final VarHandle arrayHandle = MethodHandles.arrayElementVarHandle(Object[].class); + public static final VarHandle valuesHandle = ReflectionUtil.unreflectField(InstanceData.class, "fieldValues", MethodHandles.lookup()); + + public static class InstanceData extends ConstantData { + + /** + * Stores the field values, indexed by {@link AnalysisField#getPosition()}. For normal + * constants it is set via {@link #setFieldValues(Object[])} only when the constant is + * actually used and the hosted values of its fields may be read. For simulated constants it + * is set on creation. + *

+ * Each value is either an {@link AnalysisFuture} of {@link JavaConstant} or its result, a + * {@link JavaConstant}. Evaluating the {@link AnalysisFuture} runs + * {@link ImageHeapScanner#createFieldValue(AnalysisField, ImageHeapInstance, ValueSupplier, ObjectScanner.ScanReason)} + * which adds the result to the image heap. + */ + private Object[] fieldValues; + + InstanceData(AnalysisType type, JavaConstant object, int identityHashCode) { + super(type, object, identityHashCode); + } - /** - * Stores either an {@link AnalysisFuture} of {@link JavaConstant} or its result, a - * {@link JavaConstant}, indexed by {@link AnalysisField#getPosition()}. - *

- * Evaluating the {@link AnalysisFuture} runs - * {@link ImageHeapScanner#createFieldValue(AnalysisField, ImageHeapInstance, ValueSupplier, ObjectScanner.ScanReason)} - * which adds the result to the image heap. - */ - private final Object[] fieldValues; + InstanceData(AnalysisType type, JavaConstant object, int identityHashCode, Object[] fieldValues) { + super(type, object, identityHashCode); + this.fieldValues = fieldValues; + } + } + + ImageHeapInstance(AnalysisType type, JavaConstant object) { + super(new InstanceData(type, object, createIdentityHashCode(object)), false); + } - public ImageHeapInstance(ResolvedJavaType type) { + public ImageHeapInstance(AnalysisType type) { this(type, null, type.getInstanceFields(true).length); } - ImageHeapInstance(ResolvedJavaType type, JavaConstant object, int length) { - this(type, object, new Object[length], createIdentityHashCode(object), false); + private ImageHeapInstance(AnalysisType type, JavaConstant object, int length) { + this(type, object, createIdentityHashCode(object), new Object[length], false); } - private ImageHeapInstance(ResolvedJavaType type, JavaConstant object, Object[] fieldValues, int identityHashCode, boolean compressed) { - super(type, object, identityHashCode, compressed); - this.fieldValues = fieldValues; + private ImageHeapInstance(AnalysisType type, JavaConstant object, int identityHashCode, Object[] fieldValues, boolean compressed) { + super(new InstanceData(type, object, identityHashCode, fieldValues), compressed); + } + + ImageHeapInstance(ConstantData data, boolean compressed) { + super(data, compressed); + } + + @Override + public InstanceData getConstantData() { + return (InstanceData) super.getConstantData(); + } + + void setFieldValues(Object[] fieldValues) { + boolean success = valuesHandle.compareAndSet(constantData, null, fieldValues); + AnalysisError.guarantee(success, "Unexpected field values reference for constant %s", this); + } + + /** + * {@link InstanceData#fieldValues} are only set once, in {@link #setFieldValues(Object[])} and + * shouldn't be accessed before set, i.e., read access is guarded by + * {@link #isReaderInstalled()} which ensures that the future setting the field values was + * executed, therefore we can read the field directly. + */ + private Object[] getFieldValues() { + AnalysisError.guarantee(isReaderInstalled()); + Object[] fieldValues = getConstantData().fieldValues; + AnalysisError.guarantee(fieldValues != null); + return fieldValues; } /** * Record the task computing the field value. It will be retrieved and executed when the field * is marked as read. */ - public void setFieldTask(AnalysisField field, AnalysisFuture task) { - arrayHandle.setVolatile(this.fieldValues, field.getPosition(), task); + void setFieldTask(AnalysisField field, AnalysisFuture task) { + arrayHandle.setVolatile(getFieldValues(), field.getPosition(), task); } /** @@ -88,7 +137,7 @@ public void setFieldTask(AnalysisField field, AnalysisFuture task) * and replaced. */ public void setFieldValue(AnalysisField field, JavaConstant value) { - arrayHandle.setVolatile(this.fieldValues, field.getPosition(), value); + arrayHandle.setVolatile(getFieldValues(), field.getPosition(), value); } /** @@ -97,7 +146,7 @@ public void setFieldValue(AnalysisField field, JavaConstant value) { * or the result of executing the task, i.e., a {@link JavaConstant}. */ public Object getFieldValue(AnalysisField field) { - return arrayHandle.getVolatile(this.fieldValues, field.getPosition()); + return arrayHandle.getVolatile(getFieldValues(), field.getPosition()); } /** @@ -113,40 +162,26 @@ public JavaConstant readFieldValue(AnalysisField field) { @Override public JavaConstant compress() { assert !compressed : this; - return new ImageHeapInstance(type, hostedObject, fieldValues, identityHashCode, true); + return new ImageHeapInstance(constantData, true); } @Override public JavaConstant uncompress() { assert compressed : this; - return new ImageHeapInstance(type, hostedObject, fieldValues, identityHashCode, false); + return new ImageHeapInstance(constantData, false); } @Override public ImageHeapConstant forObjectClone() { - if (!type.isCloneableWithAllocation()) { + if (!constantData.type.isCloneableWithAllocation()) { return null; } + Object[] fieldValues = getFieldValues(); + Objects.requireNonNull(fieldValues, "Cannot clone an instance before the field values are set."); Object[] newFieldValues = Arrays.copyOf(fieldValues, fieldValues.length); /* The new constant is never backed by a hosted object, regardless of the input object. */ JavaConstant newObject = null; - return new ImageHeapInstance(type, newObject, newFieldValues, createIdentityHashCode(newObject), compressed); - } - - @Override - public boolean equals(Object o) { - if (o instanceof ImageHeapInstance) { - return super.equals(o) && this.fieldValues == ((ImageHeapInstance) o).fieldValues; - } - return false; - } - - @Override - public int hashCode() { - final int prime = 31; - int result = super.hashCode(); - result = prime * result + System.identityHashCode(fieldValues); - return result; + return new ImageHeapInstance(constantData.type, newObject, createIdentityHashCode(newObject), newFieldValues, compressed); } } diff --git a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/heap/ImageHeapObjectArray.java b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/heap/ImageHeapObjectArray.java index 859a9c71cc3d..a222b3fd3ee8 100644 --- a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/heap/ImageHeapObjectArray.java +++ b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/heap/ImageHeapObjectArray.java @@ -27,41 +27,91 @@ import java.lang.invoke.MethodHandles; import java.lang.invoke.VarHandle; import java.util.Arrays; +import java.util.Objects; import java.util.function.Consumer; import com.oracle.graal.pointsto.ObjectScanner; import com.oracle.graal.pointsto.meta.AnalysisType; +import com.oracle.graal.pointsto.util.AnalysisError; import com.oracle.graal.pointsto.util.AnalysisFuture; +import com.oracle.svm.util.ReflectionUtil; import jdk.vm.ci.meta.JavaConstant; -import jdk.vm.ci.meta.ResolvedJavaType; public final class ImageHeapObjectArray extends ImageHeapArray { private static final VarHandle arrayHandle = MethodHandles.arrayElementVarHandle(Object[].class); + private static final VarHandle elementsHandle = ReflectionUtil.unreflectField(ArrayData.class, "arrayElementValues", MethodHandles.lookup()); + + public static class ArrayData extends ConstantData { + + /** + * Stores the array element values, indexed by array index. For normal constants it is set + * via {@link #setElementValues(Object[])} only when the constant is actually used and the + * hosted values of its elements may be read. For simulated constants it is set on creation. + *

+ * Each value is either an {@link AnalysisFuture} of {@link JavaConstant} or its result, a + * {@link JavaConstant}. Evaluating the {@link AnalysisFuture} runs + * {@link ImageHeapScanner#createImageHeapConstant(JavaConstant, ObjectScanner.ScanReason)} + * which adds the result to the image heap. + */ + private Object[] arrayElementValues; + + final int length; + + public ArrayData(AnalysisType type, JavaConstant object, int identityHashCode, int length) { + super(type, object, identityHashCode); + this.length = length; + } - /** - * Stores either an {@link AnalysisFuture} of {@link JavaConstant} or its result, a - * {@link JavaConstant}, indexed by array index. - */ - private final Object[] arrayElementValues; + public ArrayData(AnalysisType type, JavaConstant object, int identityHashCode, Object[] arrayElementValues, int length) { + super(type, object, identityHashCode); + this.arrayElementValues = arrayElementValues; + this.length = length; + } + } + + ImageHeapObjectArray(AnalysisType type, JavaConstant object, int length) { + super(new ArrayData(type, object, createIdentityHashCode(object), length), false); + } + + ImageHeapObjectArray(AnalysisType type, int length) { + this(type, null, new Object[length], length); + } - ImageHeapObjectArray(ResolvedJavaType type, int length) { - this(type, null, new Object[length]); + ImageHeapObjectArray(AnalysisType type, JavaConstant object, Object[] arrayElementValues, int length) { + this(type, object, createIdentityHashCode(object), arrayElementValues, length, false); } - ImageHeapObjectArray(ResolvedJavaType type, JavaConstant object, int length) { - this(type, object, new Object[length]); + private ImageHeapObjectArray(AnalysisType type, JavaConstant object, int identityHashCode, Object[] arrayElementValues, int length, boolean compressed) { + super(new ArrayData(type, object, identityHashCode, arrayElementValues, length), compressed); } - ImageHeapObjectArray(ResolvedJavaType type, JavaConstant object, Object[] arrayElementValues) { - this(type, object, arrayElementValues, createIdentityHashCode(object), false); + ImageHeapObjectArray(ConstantData data, boolean compressed) { + super(data, compressed); + } + + @Override + public ArrayData getConstantData() { + return (ArrayData) super.getConstantData(); } - private ImageHeapObjectArray(ResolvedJavaType type, JavaConstant object, Object[] arrayElementValues, int identityHashCode, boolean compressed) { - super(type, object, identityHashCode, compressed); - assert type.isArray() : type; - this.arrayElementValues = arrayElementValues; + void setElementValues(Object[] elementValues) { + boolean success = elementsHandle.compareAndSet(constantData, null, elementValues); + AnalysisError.guarantee(success, "Unexpected field values reference for constant %s", this); + } + + /** + * {@link ArrayData#arrayElementValues} are only set once, in + * {@link #setElementValues(Object[])} and shouldn't be accessed before set, i.e., read access + * is guarded by {@link #isReaderInstalled()} which ensures that the future setting the field + * values was executed, therefore we can read the field directly. + */ + private Object[] getElementValues() { + AnalysisError.guarantee(isReaderInstalled()); + Object[] arrayElements = getConstantData().arrayElementValues; + AnalysisError.guarantee(arrayElements != null); + return arrayElements; } /** @@ -70,7 +120,7 @@ private ImageHeapObjectArray(ResolvedJavaType type, JavaConstant object, Object[ */ @Override public Object getElement(int idx) { - return arrayHandle.getVolatile(this.arrayElementValues, idx); + return arrayHandle.getVolatile(getElementValues(), idx); } /** @@ -86,53 +136,39 @@ public JavaConstant readElementValue(int index) { @Override public void setElement(int idx, JavaConstant value) { - arrayHandle.setVolatile(this.arrayElementValues, idx, value); + arrayHandle.setVolatile(getElementValues(), idx, value); } - public void setElementTask(int idx, AnalysisFuture task) { - arrayHandle.setVolatile(this.arrayElementValues, idx, task); + void setElementTask(int idx, AnalysisFuture task) { + arrayHandle.setVolatile(getElementValues(), idx, task); } @Override public int getLength() { - return arrayElementValues.length; + return getConstantData().length; } @Override public JavaConstant compress() { assert !compressed : this; - return new ImageHeapObjectArray(type, hostedObject, arrayElementValues, identityHashCode, true); + return new ImageHeapObjectArray(constantData, true); } @Override public JavaConstant uncompress() { assert compressed : this; - return new ImageHeapObjectArray(type, hostedObject, arrayElementValues, identityHashCode, false); + return new ImageHeapObjectArray(constantData, false); } @Override public ImageHeapConstant forObjectClone() { - assert type.isCloneableWithAllocation() : "all arrays implement Cloneable"; + assert constantData.type.isCloneableWithAllocation() : "all arrays implement Cloneable"; - Object[] newArrayElementValues = Arrays.copyOf(arrayElementValues, arrayElementValues.length); + Object[] arrayElements = getElementValues(); + Objects.requireNonNull(arrayElements, "Cannot clone an array before the element values are set."); + Object[] newArrayElementValues = Arrays.copyOf(arrayElements, arrayElements.length); /* The new constant is never backed by a hosted object, regardless of the input object. */ JavaConstant newObject = null; - return new ImageHeapObjectArray(type, newObject, newArrayElementValues, createIdentityHashCode(newObject), compressed); - } - - @Override - public boolean equals(Object o) { - if (o instanceof ImageHeapObjectArray) { - return super.equals(o) && this.arrayElementValues == ((ImageHeapObjectArray) o).arrayElementValues; - } - return false; - } - - @Override - public int hashCode() { - final int prime = 31; - int result = super.hashCode(); - result = prime * result + System.identityHashCode(arrayElementValues); - return result; + return new ImageHeapObjectArray(constantData.type, newObject, createIdentityHashCode(newObject), newArrayElementValues, arrayElements.length, compressed); } } diff --git a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/heap/ImageHeapPrimitiveArray.java b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/heap/ImageHeapPrimitiveArray.java index 9b4c17d61193..931cdd2b26d0 100644 --- a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/heap/ImageHeapPrimitiveArray.java +++ b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/heap/ImageHeapPrimitiveArray.java @@ -33,12 +33,24 @@ import jdk.vm.ci.meta.JavaConstant; import jdk.vm.ci.meta.JavaKind; -import jdk.vm.ci.meta.ResolvedJavaType; public final class ImageHeapPrimitiveArray extends ImageHeapArray { - private final Object array; - private final int length; + public ImageHeapPrimitiveArray(ConstantData constantData, boolean compressed) { + super(constantData, compressed); + } + + public static class PrimitiveArrayData extends ConstantData { + + private final Object array; + private final int length; + + public PrimitiveArrayData(AnalysisType type, JavaConstant object, int identityHashCode, Object array, int length) { + super(type, object, identityHashCode); + this.array = array; + this.length = length; + } + } ImageHeapPrimitiveArray(AnalysisType type, int length) { this(type, null, @@ -54,11 +66,14 @@ public final class ImageHeapPrimitiveArray extends ImageHeapArray { createIdentityHashCode(hostedObject), false, length); } - private ImageHeapPrimitiveArray(ResolvedJavaType type, JavaConstant hostedObject, Object array, int identityHashCode, boolean compressed, int length) { - super(type, hostedObject, identityHashCode, compressed); + private ImageHeapPrimitiveArray(AnalysisType type, JavaConstant hostedObject, Object array, int identityHashCode, boolean compressed, int length) { + super(new PrimitiveArrayData(type, hostedObject, identityHashCode, array, length), compressed); assert type.isArray() && type.getComponentType().isPrimitive() : type; - this.array = array; - this.length = length; + } + + @Override + public PrimitiveArrayData getConstantData() { + return (PrimitiveArrayData) super.getConstantData(); } private static Object getClone(JavaKind kind, Object arrayObject) { @@ -76,7 +91,7 @@ private static Object getClone(JavaKind kind, Object arrayObject) { } public Object getArray() { - return array; + return getConstantData().array; } /** @@ -90,57 +105,42 @@ public Object getElement(int idx) { @Override public JavaConstant readElementValue(int idx) { - return JavaConstant.forBoxedPrimitive(Array.get(array, idx)); + return JavaConstant.forBoxedPrimitive(Array.get(getArray(), idx)); } @Override public void setElement(int idx, JavaConstant value) { - if (value.getJavaKind() != type.getComponentType().getJavaKind()) { - throw AnalysisError.shouldNotReachHere("Cannot store value of kind " + value.getJavaKind() + " into primitive array of type " + type); + if (value.getJavaKind() != constantData.type.getComponentType().getJavaKind()) { + throw AnalysisError.shouldNotReachHere("Cannot store value of kind " + value.getJavaKind() + " into primitive array of type " + getConstantData().type); } - Array.set(array, idx, value.asBoxedPrimitive()); + Array.set(getArray(), idx, value.asBoxedPrimitive()); } @Override public int getLength() { - return length; + return getConstantData().length; } @Override public JavaConstant compress() { assert !compressed : this; - return new ImageHeapPrimitiveArray(type, hostedObject, array, identityHashCode, true, length); + return new ImageHeapPrimitiveArray(constantData, true); } @Override public JavaConstant uncompress() { assert compressed : this; - return new ImageHeapPrimitiveArray(type, hostedObject, array, identityHashCode, false, length); + return new ImageHeapPrimitiveArray(constantData, false); } @Override public ImageHeapConstant forObjectClone() { - assert type.isCloneableWithAllocation() : "all arrays implement Cloneable"; + assert constantData.type.isCloneableWithAllocation() : "all arrays implement Cloneable"; - Object newArray = getClone(type.getComponentType().getJavaKind(), array); + PrimitiveArrayData data = getConstantData(); + Object newArray = getClone(data.type.getComponentType().getJavaKind(), data.array); /* The new constant is never backed by a hosted object, regardless of the input object. */ JavaConstant newHostedObject = null; - return new ImageHeapPrimitiveArray(type, newHostedObject, newArray, createIdentityHashCode(newHostedObject), compressed, length); - } - - @Override - public boolean equals(Object o) { - if (o instanceof ImageHeapPrimitiveArray) { - return super.equals(o) && this.array == ((ImageHeapPrimitiveArray) o).array; - } - return false; - } - - @Override - public int hashCode() { - final int prime = 31; - int result = super.hashCode(); - result = prime * result + System.identityHashCode(array); - return result; + return new ImageHeapPrimitiveArray(data.type, newHostedObject, newArray, createIdentityHashCode(newHostedObject), compressed, data.length); } } diff --git a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/heap/ImageHeapScanner.java b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/heap/ImageHeapScanner.java index e602a70397e6..0adbc47b1abe 100644 --- a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/heap/ImageHeapScanner.java +++ b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/heap/ImageHeapScanner.java @@ -33,10 +33,6 @@ import org.graalvm.collections.EconomicMap; import org.graalvm.collections.MapCursor; -import jdk.graal.compiler.api.replacements.SnippetReflectionProvider; -import jdk.graal.compiler.core.common.SuppressFBWarnings; -import jdk.graal.compiler.core.common.type.TypedConstant; -import jdk.graal.compiler.debug.GraalError; import com.oracle.graal.pointsto.BigBang; import com.oracle.graal.pointsto.ObjectScanner; @@ -59,6 +55,10 @@ import com.oracle.graal.pointsto.util.GraalAccess; import com.oracle.svm.util.ReflectionUtil; +import jdk.graal.compiler.api.replacements.SnippetReflectionProvider; +import jdk.graal.compiler.core.common.SuppressFBWarnings; +import jdk.graal.compiler.core.common.type.TypedConstant; +import jdk.graal.compiler.debug.GraalError; import jdk.vm.ci.code.BytecodePosition; import jdk.vm.ci.meta.ConstantReflectionProvider; import jdk.vm.ci.meta.JavaConstant; @@ -252,40 +252,51 @@ protected ImageHeapConstant createImageHeapObject(JavaConstant constant, ScanRea private ImageHeapArray createImageHeapObjectArray(JavaConstant constant, AnalysisType type, int length, ScanReason reason) { ImageHeapObjectArray array = new ImageHeapObjectArray(type, constant, length); - ScanReason arrayReason = new ArrayScan(type, constant, reason); - for (int idx = 0; idx < length; idx++) { - final JavaConstant rawElementValue = constantReflection.readArrayElement(constant, idx); - int finalIdx = idx; - array.setElementTask(idx, new AnalysisFuture<>(() -> { - JavaConstant arrayElement = createImageHeapConstant(rawElementValue, arrayReason); - array.setElement(finalIdx, arrayElement); - return arrayElement; - })); - } + /* Read hosted array element values only when the array is initialized. */ + array.constantData.hostedValuesReader = new AnalysisFuture<>(() -> { + type.registerAsReachable(reason); + ScanReason arrayReason = new ArrayScan(type, constant, reason); + Object[] elementValues = new Object[length]; + for (int idx = 0; idx < length; idx++) { + final JavaConstant rawElementValue = constantReflection.readArrayElement(constant, idx); + int finalIdx = idx; + elementValues[idx] = new AnalysisFuture<>(() -> { + JavaConstant arrayElement = createImageHeapConstant(rawElementValue, arrayReason); + array.setElement(finalIdx, arrayElement); + return arrayElement; + }); + } + array.setElementValues(elementValues); + }); return array; } private ImageHeapInstance createImageHeapInstance(JavaConstant constant, AnalysisType type, ScanReason reason) { - /* We are about to query the type's fields, the type must be marked as reachable. */ - type.registerAsReachable(reason); - ResolvedJavaField[] instanceFields = type.getInstanceFields(true); - ImageHeapInstance instance = new ImageHeapInstance(type, constant, instanceFields.length); - for (ResolvedJavaField javaField : instanceFields) { - AnalysisField field = (AnalysisField) javaField; - ValueSupplier rawFieldValue; - try { - rawFieldValue = readHostedFieldValue(field, universe.toHosted(constant)); - } catch (InternalError | TypeNotPresentException | LinkageError e) { - /* Ignore missing type errors. */ - continue; + ImageHeapInstance instance = new ImageHeapInstance(type, constant); + /* Read hosted field values only when the receiver is initialized. */ + instance.constantData.hostedValuesReader = new AnalysisFuture<>(() -> { + /* We are about to query the type's fields, the type must be marked as reachable. */ + type.registerAsReachable(reason); + ResolvedJavaField[] instanceFields = type.getInstanceFields(true); + Object[] hostedFieldValues = new Object[instanceFields.length]; + for (ResolvedJavaField javaField : instanceFields) { + AnalysisField field = (AnalysisField) javaField; + ValueSupplier rawFieldValue; + try { + rawFieldValue = readHostedFieldValue(field, universe.toHosted(constant)); + } catch (InternalError | TypeNotPresentException | LinkageError e) { + /* Ignore missing type errors. */ + continue; + } + hostedFieldValues[field.getPosition()] = new AnalysisFuture<>(() -> { + ScanReason fieldReason = new FieldScan(field, constant, reason); + JavaConstant value = createFieldValue(field, instance, rawFieldValue, fieldReason); + instance.setFieldValue(field, value); + return value; + }); } - instance.setFieldTask(field, new AnalysisFuture<>(() -> { - ScanReason fieldReason = new FieldScan(field, constant, reason); - JavaConstant value = createFieldValue(field, instance, rawFieldValue, fieldReason); - instance.setFieldValue(field, value); - return value; - })); - } + instance.setFieldValues(hostedFieldValues); + }); return instance; } @@ -349,7 +360,8 @@ JavaConstant onFieldValueReachable(AnalysisField field, ImageHeapInstance receiv } JavaConstant onFieldValueReachable(AnalysisField field, ImageHeapInstance receiver, ValueSupplier rawValue, ScanReason reason, Consumer onAnalysisModified) { - AnalysisError.guarantee(field.isReachable(), "Field value is only reachable when field is reachable: %s", field); + // Static field can be read for constant folding before being marked as reachable. + AnalysisError.guarantee(field.isStatic() || field.isReachable(), "Field value is only reachable when field is reachable: %s", field); JavaConstant fieldValue = createFieldValue(field, receiver, rawValue, reason); markReachable(fieldValue, reason, onAnalysisModified); notifyAnalysis(field, receiver, fieldValue, reason, onAnalysisModified); @@ -474,7 +486,7 @@ protected void onObjectReachable(ImageHeapConstant imageHeapConstant, ScanReason markTypeInstantiated(objectType, reason); if (imageHeapConstant instanceof ImageHeapObjectArray imageHeapArray) { - AnalysisType arrayType = (AnalysisType) imageHeapArray.getType(metaAccess); + AnalysisType arrayType = imageHeapArray.getType(metaAccess); for (int idx = 0; idx < imageHeapArray.getLength(); idx++) { JavaConstant elementValue = imageHeapArray.readElementValue(idx); ArrayScan arrayScanReason = new ArrayScan(arrayType, imageHeapArray, reason, idx); @@ -550,13 +562,27 @@ public void rescanField(Object receiver, Field reflectionField) { ImageHeapInstance receiverObject = (ImageHeapInstance) toImageHeapObject(receiverConstant, OtherReason.RESCAN); AnalysisFuture fieldTask = patchInstanceField(receiverObject, field, fieldValue, OtherReason.RESCAN, null); if (field.isRead() || field.isFolded()) { - rescanCollectionElements(fieldTask.ensureDone()); + JavaConstant constant = fieldTask.ensureDone(); + ensureReaderInstalled(constant); + rescanCollectionElements(constant); } } } }); } + /** + * For image heap constants created during verification, i.e., either correct values set lazily + * but for which the {@link ImageHeapConstant} was not yet created or values created by patching + * a wrong snapshot, we need to manually ensure that the readers are installed since the + * verification will continue expanding them. + */ + static void ensureReaderInstalled(JavaConstant constant) { + if (constant.getJavaKind() == JavaKind.Object && constant.isNonNull()) { + ((ImageHeapConstant) constant).ensureReaderInstalled(); + } + } + protected AnalysisFuture patchStaticField(TypeData typeData, AnalysisField field, JavaConstant fieldValue, ScanReason reason, Consumer onAnalysisModified) { AnalysisFuture task = new AnalysisFuture<>(() -> { JavaConstant value = onFieldValueReachable(field, fieldValue, reason, onAnalysisModified); @@ -581,7 +607,7 @@ protected AnalysisFuture patchInstanceField(ImageHeapInstance rece protected AnalysisFuture patchArrayElement(ImageHeapObjectArray arrayObject, int index, JavaConstant elementValue, ScanReason reason, Consumer onAnalysisModified) { AnalysisFuture task = new AnalysisFuture<>(() -> { - JavaConstant value = onArrayElementReachable(arrayObject, (AnalysisType) arrayObject.getType(metaAccess), elementValue, index, reason, onAnalysisModified); + JavaConstant value = onArrayElementReachable(arrayObject, arrayObject.getType(metaAccess), elementValue, index, reason, onAnalysisModified); arrayObject.setElement(index, value); return value; }); diff --git a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/util/AnalysisFuture.java b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/util/AnalysisFuture.java index 9188cf9493fc..10221ac4ab8d 100644 --- a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/util/AnalysisFuture.java +++ b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/util/AnalysisFuture.java @@ -37,6 +37,10 @@ public AnalysisFuture(Callable callable) { super(callable); } + public AnalysisFuture(Runnable runnable) { + super(runnable, null); + } + public AnalysisFuture(Runnable runnable, V result) { super(runnable, result); } diff --git a/substratevm/src/com.oracle.svm.graal/src/com/oracle/svm/graal/hosted/GraalGraphObjectReplacer.java b/substratevm/src/com.oracle.svm.graal/src/com/oracle/svm/graal/hosted/GraalGraphObjectReplacer.java index 662fbc7b8590..4d92ce3d8986 100644 --- a/substratevm/src/com.oracle.svm.graal/src/com/oracle/svm/graal/hosted/GraalGraphObjectReplacer.java +++ b/substratevm/src/com.oracle.svm.graal/src/com/oracle/svm/graal/hosted/GraalGraphObjectReplacer.java @@ -462,6 +462,7 @@ public void updateSubstrateDataAfterCompilation(HostedUniverse hUniverse, Provid JavaConstant constantValue = hField.isStatic() && ((HostedConstantFieldProvider) providers.getConstantFieldProvider()).isFinalField(hField, null) ? providers.getConstantReflection().readFieldValue(hField, null) : null; + constantValue = providers.getSnippetReflection().unwrapConstant(constantValue); sField.setSubstrateData(hField.getLocation(), hField.isAccessed(), hField.isWritten() || !hField.isValueAvailable(), constantValue); } } diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/ameta/AnalysisConstantReflectionProvider.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/ameta/AnalysisConstantReflectionProvider.java index 906840657422..a0ef920660b0 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/ameta/AnalysisConstantReflectionProvider.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/ameta/AnalysisConstantReflectionProvider.java @@ -28,8 +28,6 @@ import java.util.Set; import java.util.function.ObjIntConsumer; -import jdk.graal.compiler.core.common.type.TypedConstant; -import jdk.graal.compiler.word.Word; import org.graalvm.nativeimage.Platform; import org.graalvm.nativeimage.Platforms; import org.graalvm.word.WordBase; @@ -60,6 +58,8 @@ import com.oracle.svm.hosted.meta.HostedMetaAccess; import com.oracle.svm.hosted.meta.RelocatableConstant; +import jdk.graal.compiler.core.common.type.TypedConstant; +import jdk.graal.compiler.word.Word; import jdk.vm.ci.meta.Constant; import jdk.vm.ci.meta.JavaConstant; import jdk.vm.ci.meta.JavaKind; @@ -121,8 +121,9 @@ public JavaConstant unboxPrimitive(JavaConstant source) { * Unbox by reading the known single field "value", which is a primitive field of the * correct unboxed type. */ - AnalysisType type = (AnalysisType) imageHeapConstant.getType(metaAccess); + AnalysisType type = imageHeapConstant.getType(metaAccess); if (BOXING_CLASSES.contains(type.getJavaClass())) { + imageHeapConstant.ensureReaderInstalled(); ResolvedJavaField[] fields = type.getInstanceFields(true); assert fields.length == 1 && fields[0].getName().equals("value"); return ((ImageHeapInstance) imageHeapConstant).readFieldValue((AnalysisField) fields[0]); @@ -144,8 +145,8 @@ public Integer readArrayLength(JavaConstant array) { return null; } if (array instanceof ImageHeapConstant) { - if (array instanceof ImageHeapArray) { - return ((ImageHeapArray) array).getLength(); + if (array instanceof ImageHeapArray heapArray) { + return heapArray.getLength(); } return null; } @@ -162,6 +163,7 @@ public JavaConstant readArrayElement(JavaConstant array, int index) { if (index < 0 || index >= heapArray.getLength()) { return null; } + heapArray.ensureReaderInstalled(); return replaceObject(heapArray.readElementValue(index)); } return null; @@ -174,6 +176,7 @@ public JavaConstant readArrayElement(JavaConstant array, int index) { public void forEachArrayElement(JavaConstant array, ObjIntConsumer consumer) { if (array instanceof ImageHeapConstant) { if (array instanceof ImageHeapArray heapArray) { + heapArray.ensureReaderInstalled(); for (int index = 0; index < heapArray.getLength(); index++) { JavaConstant element = heapArray.readElementValue(index); consumer.accept(replaceObject(element), index); @@ -191,6 +194,10 @@ public JavaConstant readFieldValue(ResolvedJavaField field, JavaConstant receive } public JavaConstant readValue(UniverseMetaAccess suppliedMetaAccess, AnalysisField field, JavaConstant receiver, boolean returnSimulatedValues) { + return readValue(suppliedMetaAccess, field, receiver, returnSimulatedValues, true); + } + + public JavaConstant readValue(UniverseMetaAccess suppliedMetaAccess, AnalysisField field, JavaConstant receiver, boolean returnSimulatedValues, boolean readFromShadowHeap) { if (!field.isStatic()) { if (receiver.isNull() || !field.getDeclaringClass().isAssignableFrom(((TypedConstant) receiver).getType(metaAccess))) { /* @@ -210,7 +217,24 @@ public JavaConstant readValue(UniverseMetaAccess suppliedMetaAccess, AnalysisFie if (returnSimulatedValues) { value = readSimulatedValue(field); } - if (value == null && receiver instanceof ImageHeapConstant) { + if (value == null && field.isStatic() && returnSimulatedValues && readFromShadowHeap) { + /* + * The shadow heap uses simulated values for static fields by default. So, only when + * simulated values are explicitly requested we can read via the shadow heap. Otherwise, + * this will lead to recursive parsing request errors. + */ + if (SimulateClassInitializerSupport.singleton().isEnabled()) { + /* + * The "late initialization" doesn't work with heap snapshots because the wrong + * value will be snapshot for classes proven late, so we only read via the shadow + * heap if simulation of class initializers is enabled. The check and this comment + * will be removed when the old initialization strategy is removed. + */ + value = field.getDeclaringClass().getOrComputeData().readFieldValue(field); + } + } + if (value == null && receiver instanceof ImageHeapConstant heapConstant) { + heapConstant.ensureReaderInstalled(); AnalysisError.guarantee(ReadableJavaField.isValueAvailable(field), "Value not yet available for %s", field); ImageHeapInstance heapObject = (ImageHeapInstance) receiver; value = heapObject.readFieldValue(field); diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/c/CGlobalDataFeature.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/c/CGlobalDataFeature.java index 05e57d0f36fc..741315a0a6dd 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/c/CGlobalDataFeature.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/c/CGlobalDataFeature.java @@ -130,7 +130,7 @@ public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Rec JavaConstant nonConstantRegistryJavaConstant = snippetReflection.forObject(nonConstantRegistry); ValueNode cGlobalDataNode = receiver.get(); if (cGlobalDataNode.isConstant()) { - CGlobalDataImpl data = providers.getSnippetReflection().asObject(CGlobalDataImpl.class, cGlobalDataNode.asJavaConstant()); + CGlobalDataImpl data = snippetReflection.asObject(CGlobalDataImpl.class, cGlobalDataNode.asJavaConstant()); CGlobalDataInfo info = CGlobalDataFeature.this.map.get(data); b.addPush(targetMethod.getSignature().getReturnKind(), new CGlobalDataLoadAddressNode(info)); } else { diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/classinitialization/SimulateClassInitializerGraphDecoder.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/classinitialization/SimulateClassInitializerGraphDecoder.java index 2bbee446a63e..bce10d710a84 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/classinitialization/SimulateClassInitializerGraphDecoder.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/classinitialization/SimulateClassInitializerGraphDecoder.java @@ -52,6 +52,24 @@ import org.graalvm.collections.EconomicMap; import org.graalvm.collections.EconomicSet; +import org.graalvm.nativeimage.ImageSingletons; + +import com.oracle.graal.pointsto.BigBang; +import com.oracle.graal.pointsto.ObjectScanner; +import com.oracle.graal.pointsto.heap.ImageHeapArray; +import com.oracle.graal.pointsto.heap.ImageHeapConstant; +import com.oracle.graal.pointsto.heap.ImageHeapInstance; +import com.oracle.graal.pointsto.meta.AnalysisField; +import com.oracle.graal.pointsto.meta.AnalysisType; +import com.oracle.graal.pointsto.phases.InlineBeforeAnalysisGraphDecoder; +import com.oracle.svm.core.classinitialization.EnsureClassInitializedNode; +import com.oracle.svm.core.config.ObjectLayout; +import com.oracle.svm.core.meta.SubstrateObjectConstant; +import com.oracle.svm.core.util.VMError; +import com.oracle.svm.hosted.classinitialization.SimulateClassInitializerPolicy.SimulateClassInitializerInlineScope; +import com.oracle.svm.hosted.fieldfolding.IsStaticFinalFieldInitializedNode; +import com.oracle.svm.hosted.fieldfolding.MarkStaticFinalFieldInitializedNode; + import jdk.graal.compiler.core.common.type.TypedConstant; import jdk.graal.compiler.debug.DebugContext; import jdk.graal.compiler.graph.Node; @@ -78,24 +96,6 @@ import jdk.graal.compiler.nodes.virtual.VirtualObjectNode; import jdk.graal.compiler.replacements.arraycopy.ArrayCopyNode; import jdk.graal.compiler.replacements.nodes.ObjectClone; -import org.graalvm.nativeimage.ImageSingletons; - -import com.oracle.graal.pointsto.BigBang; -import com.oracle.graal.pointsto.ObjectScanner; -import com.oracle.graal.pointsto.heap.ImageHeapArray; -import com.oracle.graal.pointsto.heap.ImageHeapConstant; -import com.oracle.graal.pointsto.heap.ImageHeapInstance; -import com.oracle.graal.pointsto.meta.AnalysisField; -import com.oracle.graal.pointsto.meta.AnalysisType; -import com.oracle.graal.pointsto.phases.InlineBeforeAnalysisGraphDecoder; -import com.oracle.svm.core.classinitialization.EnsureClassInitializedNode; -import com.oracle.svm.core.config.ObjectLayout; -import com.oracle.svm.core.meta.SubstrateObjectConstant; -import com.oracle.svm.core.util.VMError; -import com.oracle.svm.hosted.classinitialization.SimulateClassInitializerPolicy.SimulateClassInitializerInlineScope; -import com.oracle.svm.hosted.fieldfolding.IsStaticFinalFieldInitializedNode; -import com.oracle.svm.hosted.fieldfolding.MarkStaticFinalFieldInitializedNode; - import jdk.vm.ci.meta.JavaConstant; import jdk.vm.ci.meta.JavaKind; import jdk.vm.ci.meta.MetaAccessProvider; @@ -306,7 +306,7 @@ private Node handleStoreIndexedNode(StoreIndexedNode node) { int idx = asIntegerOrMinusOne(node.index()); if (array != null && value != null && idx >= 0 && idx < array.getLength()) { - var componentType = (AnalysisType) array.getType(metaAccess).getComponentType(); + var componentType = array.getType(metaAccess).getComponentType(); if (node.elementKind().isPrimitive() || value.isNull() || componentType.isAssignableFrom(((TypedConstant) value).getType(metaAccess))) { array.setElement(idx, adaptForImageHeap(value, componentType.getStorageKind())); return null; @@ -341,8 +341,8 @@ protected boolean handleArrayCopy(ImageHeapArray source, int sourcePos, ImageHea return false; } - var sourceComponentType = (AnalysisType) source.getType(metaAccess).getComponentType(); - var destComponentType = (AnalysisType) dest.getType(metaAccess).getComponentType(); + var sourceComponentType = source.getType(metaAccess).getComponentType(); + var destComponentType = dest.getType(metaAccess).getComponentType(); if (sourceComponentType.getJavaKind() != destComponentType.getJavaKind()) { return false; } @@ -534,7 +534,7 @@ private ValueNode handleBoxNode(BoxNode node) { private ValueNode handleObjectClone(SimulateClassInitializerInlineScope countersScope, ObjectClone node) { var originalImageHeapConstant = asActiveImageHeapConstant(node.getObject()); if (originalImageHeapConstant != null) { - var type = (AnalysisType) originalImageHeapConstant.getType(metaAccess); + var type = originalImageHeapConstant.getType(metaAccess); if ((originalImageHeapConstant instanceof ImageHeapArray originalArray && accumulateNewArraySize(countersScope, type, originalArray.getLength(), node.asNode())) || (type.isCloneableWithAllocation() && accumulateNewInstanceSize(countersScope, type, node.asNode()))) { var cloned = originalImageHeapConstant.forObjectClone(); diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/heap/SVMImageHeapVerifier.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/heap/SVMImageHeapVerifier.java index a2409d35d082..e0de60c7d63a 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/heap/SVMImageHeapVerifier.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/heap/SVMImageHeapVerifier.java @@ -95,7 +95,11 @@ private static final class VerifierObjectScanner extends ObjectScanner { @Override protected JavaConstant readFieldValue(AnalysisField field, JavaConstant receiver) { AnalysisConstantReflectionProvider constantReflectionProvider = (AnalysisConstantReflectionProvider) bb.getConstantReflectionProvider(); - return constantReflectionProvider.readValue(metaAccess, field, receiver, true); + /* + * The verifier compares the hosted values with the ones from the shadow heap, so the + * constant reflection must not return shadow heap values. + */ + return constantReflectionProvider.readValue(metaAccess, field, receiver, true, false); } } diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/meta/HostedMetaAccess.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/meta/HostedMetaAccess.java index b91752e7a556..70040834ee8e 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/meta/HostedMetaAccess.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/meta/HostedMetaAccess.java @@ -33,14 +33,12 @@ import java.util.stream.Collectors; import java.util.stream.Stream; -import jdk.graal.compiler.core.common.type.TypedConstant; - import com.oracle.graal.pointsto.heap.ImageHeapConstant; import com.oracle.graal.pointsto.infrastructure.UniverseMetaAccess; import com.oracle.graal.pointsto.meta.AnalysisMetaAccess; -import com.oracle.graal.pointsto.meta.AnalysisType; import com.oracle.svm.core.deopt.Deoptimizer; +import jdk.graal.compiler.core.common.type.TypedConstant; import jdk.vm.ci.meta.DeoptimizationAction; import jdk.vm.ci.meta.DeoptimizationReason; import jdk.vm.ci.meta.JavaConstant; @@ -62,7 +60,6 @@ public HostedType lookupJavaType(Class clazz) { public HostedType lookupJavaType(JavaConstant constant) { if (constant instanceof ImageHeapConstant) { ResolvedJavaType type = ((TypedConstant) constant).getType(this); - assert type == null || type instanceof AnalysisType : type; return (HostedType) universe.lookup(type); } return (HostedType) super.lookupJavaType(constant); diff --git a/substratevm/src/com.oracle.svm.util/src/com/oracle/svm/util/ReflectionUtil.java b/substratevm/src/com.oracle.svm.util/src/com/oracle/svm/util/ReflectionUtil.java index ad5087d8add8..8cc0e06e3899 100644 --- a/substratevm/src/com.oracle.svm.util/src/com/oracle/svm/util/ReflectionUtil.java +++ b/substratevm/src/com.oracle.svm.util/src/com/oracle/svm/util/ReflectionUtil.java @@ -24,6 +24,8 @@ */ package com.oracle.svm.util; +import java.lang.invoke.MethodHandles; +import java.lang.invoke.VarHandle; import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.lang.reflect.Method; @@ -117,6 +119,16 @@ public static T newInstance(Constructor constructor, Object... initArgs) } } + public static VarHandle unreflectField(Class declaringClass, String fieldName, MethodHandles.Lookup lookup) { + try { + Field field = ReflectionUtil.lookupField(declaringClass, fieldName); + MethodHandles.Lookup privateLookup = MethodHandles.privateLookupIn(declaringClass, lookup); + return privateLookup.unreflectVarHandle(field); + } catch (IllegalAccessException ex) { + throw new ReflectionUtilError(ex); + } + } + public static Field lookupField(Class declaringClass, String fieldName) { return lookupField(false, declaringClass, fieldName); } diff --git a/tools/src/com.oracle.truffle.tools.chromeinspector/src/com/oracle/truffle/tools/chromeinspector/ScriptsHandler.java b/tools/src/com.oracle.truffle.tools.chromeinspector/src/com/oracle/truffle/tools/chromeinspector/ScriptsHandler.java index a3fc385b22cb..e7c49b4cd8b7 100644 --- a/tools/src/com.oracle.truffle.tools.chromeinspector/src/com/oracle/truffle/tools/chromeinspector/ScriptsHandler.java +++ b/tools/src/com.oracle.truffle.tools.chromeinspector/src/com/oracle/truffle/tools/chromeinspector/ScriptsHandler.java @@ -185,7 +185,7 @@ private String getSourceURL(Source source, TruffleContext truffleContext) { } else { try { return env.getTruffleFile(truffleContext, path).getAbsoluteFile().toUri().toString(); - } catch (SecurityException ex) { + } catch (UnsupportedOperationException | IllegalArgumentException | SecurityException ex) { if (File.separatorChar == '/') { return path; } else { diff --git a/tools/src/com.oracle.truffle.tools.dap/src/com/oracle/truffle/tools/dap/server/LoadedSourcesHandler.java b/tools/src/com.oracle.truffle.tools.dap/src/com/oracle/truffle/tools/dap/server/LoadedSourcesHandler.java index 1ad2e853f645..60e89b7be1f6 100644 --- a/tools/src/com.oracle.truffle.tools.dap/src/com/oracle/truffle/tools/dap/server/LoadedSourcesHandler.java +++ b/tools/src/com.oracle.truffle.tools.dap/src/com/oracle/truffle/tools/dap/server/LoadedSourcesHandler.java @@ -34,7 +34,7 @@ import com.oracle.truffle.tools.dap.types.LoadedSourceEvent; import java.net.URI; -import java.nio.file.InvalidPathException; +import java.nio.file.FileSystemNotFoundException; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; @@ -205,7 +205,7 @@ private Pair getPath(Source source, TruffleContext truffleConte tFile = context.getEnv().getTruffleFile(truffleContext, path); } } - } catch (UnsupportedOperationException | InvalidPathException ex) { + } catch (UnsupportedOperationException | IllegalArgumentException | FileSystemNotFoundException ex) { // Unsupported URI/path } if (tFile != null) { @@ -218,7 +218,7 @@ private Pair getPath(Source source, TruffleContext truffleConte } else if (path != null) { try { srcRef = !context.getEnv().getTruffleFile(truffleContext, path).isReadable(); - } catch (SecurityException | InvalidPathException ex) { + } catch (UnsupportedOperationException | SecurityException | IllegalArgumentException ex) { // Can not verify readability srcRef = true; } diff --git a/truffle/src/com.oracle.truffle.api.instrumentation/src/com/oracle/truffle/api/instrumentation/TruffleInstrument.java b/truffle/src/com.oracle.truffle.api.instrumentation/src/com/oracle/truffle/api/instrumentation/TruffleInstrument.java index d4e98ef846c2..37ec94abc536 100644 --- a/truffle/src/com.oracle.truffle.api.instrumentation/src/com/oracle/truffle/api/instrumentation/TruffleInstrument.java +++ b/truffle/src/com.oracle.truffle.api.instrumentation/src/com/oracle/truffle/api/instrumentation/TruffleInstrument.java @@ -855,6 +855,10 @@ public TruffleFile getTruffleFile(URI uri) { * context. * @param path the absolute or relative path to create {@link TruffleFile} for * @return {@link TruffleFile} + * @throws UnsupportedOperationException when the {@link FileSystem} supports only + * {@link URI} + * @throws IllegalArgumentException if the {@code path} string cannot be converted to a + * {@link Path} * @since 23.0 */ public TruffleFile getTruffleFile(TruffleContext context, String path) { @@ -872,6 +876,10 @@ public TruffleFile getTruffleFile(TruffleContext context, String path) { * context. * @param uri the {@link URI} to create {@link TruffleFile} for * @return {@link TruffleFile} + * @throws UnsupportedOperationException when {@link URI} scheme is not supported + * @throws IllegalArgumentException if preconditions on the {@code uri} do not hold. + * @throws FileSystemNotFoundException is the file system, identified by the {@code uri}, + * does not exist and cannot be created automatically * @since 23.0 */ public TruffleFile getTruffleFile(TruffleContext context, URI uri) { diff --git a/truffle/src/com.oracle.truffle.api/src/com/oracle/truffle/api/TruffleLanguage.java b/truffle/src/com.oracle.truffle.api/src/com/oracle/truffle/api/TruffleLanguage.java index 0f90bcf606f4..e64d56a184f4 100644 --- a/truffle/src/com.oracle.truffle.api/src/com/oracle/truffle/api/TruffleLanguage.java +++ b/truffle/src/com.oracle.truffle.api/src/com/oracle/truffle/api/TruffleLanguage.java @@ -2899,6 +2899,8 @@ public TruffleFile getPublicTruffleFile(String path) { * @return {@link TruffleFile} * @throws UnsupportedOperationException when {@link URI} scheme is not supported * @throws IllegalArgumentException if preconditions on the {@code uri} do not hold. + * @throws FileSystemNotFoundException is the file system, identified by the {@code uri}, + * does not exist and cannot be created automatically * @since 19.3.0 */ @TruffleBoundary @@ -2960,6 +2962,8 @@ public TruffleFile getInternalTruffleFile(String path) { * @since 19.3.0 * @throws UnsupportedOperationException when {@link URI} scheme is not supported * @throws IllegalArgumentException if preconditions on the {@code uri} do not hold. + * @throws FileSystemNotFoundException is the file system, identified by the {@code uri}, + * does not exist and cannot be created automatically * @see #getTruffleFileInternal(URI, Predicate) * @see #getPublicTruffleFile(java.net.URI) */ @@ -3039,6 +3043,8 @@ public TruffleFile getTruffleFileInternal(String path, Predicate fi * @throws UnsupportedOperationException when the {@link FileSystem} supports only * {@link URI} * @throws IllegalArgumentException if preconditions on the {@code uri} do not hold. + * @throws FileSystemNotFoundException is the file system, identified by the {@code uri}, + * does not exist and cannot be created automatically * @since 21.1.0 * @see #getTruffleFileInternal(String, Predicate) * @see #getPublicTruffleFile(URI)