diff --git a/compiler/src/jdk.internal.vm.compiler.test/src/org/graalvm/compiler/core/test/GraalCompilerTest.java b/compiler/src/jdk.internal.vm.compiler.test/src/org/graalvm/compiler/core/test/GraalCompilerTest.java index 431b0a000000..961fd22ad2ec 100644 --- a/compiler/src/jdk.internal.vm.compiler.test/src/org/graalvm/compiler/core/test/GraalCompilerTest.java +++ b/compiler/src/jdk.internal.vm.compiler.test/src/org/graalvm/compiler/core/test/GraalCompilerTest.java @@ -52,6 +52,7 @@ import java.util.List; import java.util.ListIterator; import java.util.Map; +import java.util.Random; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.function.Supplier; @@ -1075,10 +1076,13 @@ protected final InstalledCode getCode(final ResolvedJavaMethod installedCodeOwne protected InstalledCode getCode(final ResolvedJavaMethod installedCodeOwner, StructuredGraph graph, boolean forceCompile, boolean installAsDefault, OptionValues options) { boolean useCache = !forceCompile && getArgumentToBind() == null; if (useCache && graph == null) { - InstalledCode cached = cache.get().get(installedCodeOwner); + HashMap tlCache = cache.get(); + InstalledCode cached = tlCache.get(installedCodeOwner); if (cached != null) { if (cached.isValid()) { return cached; + } else { + tlCache.remove(installedCodeOwner); } } } @@ -1671,4 +1675,35 @@ protected boolean isArchitecture(String name) { protected CanonicalizerPhase createCanonicalizerPhase() { return CanonicalizerPhase.create(); } + + /** + * Defines property name for seed value. + */ + public static final String SEED_PROPERTY_NAME = "test.graal.random.seed"; + + /** + * Globally shared, lazily initialized random generator. + */ + private static volatile Random randomGenerator; + + /** + * Returns a global {@link java.util.Random} generator. The generator is seeded with the value + * specified by {@link #SEED_PROPERTY_NAME} if it exists. + * + * The used seed printed to stdout for reproducing test failures. + */ + public static Random getRandomInstance() { + if (randomGenerator == null) { + synchronized (GraalCompilerTest.class) { + if (randomGenerator == null) { + var seedLong = Long.getLong(SEED_PROPERTY_NAME); + var seed = seedLong != null ? seedLong : new Random().nextLong(); + System.out.printf("Random generator seed: %d%n", seed); + System.out.printf("To re-run test with same seed, set \"-D%s=%d\" on command line.%n", SEED_PROPERTY_NAME, seed); + randomGenerator = new Random(seed); + } + } + } + return randomGenerator; + } } diff --git a/compiler/src/jdk.internal.vm.compiler.test/src/org/graalvm/compiler/hotspot/test/Base64SubstitutionsTest.java b/compiler/src/jdk.internal.vm.compiler.test/src/org/graalvm/compiler/hotspot/test/Base64SubstitutionsTest.java new file mode 100644 index 000000000000..ce9a14f5c3b7 --- /dev/null +++ b/compiler/src/jdk.internal.vm.compiler.test/src/org/graalvm/compiler/hotspot/test/Base64SubstitutionsTest.java @@ -0,0 +1,554 @@ +/* + * Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.graalvm.compiler.hotspot.test; + +import java.nio.ByteBuffer; +import java.nio.charset.StandardCharsets; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Base64; +import java.util.Base64.Decoder; +import java.util.Base64.Encoder; +import java.util.HashSet; +import java.util.HexFormat; +import java.util.List; +import java.util.Random; +import java.util.Set; +import java.util.function.Supplier; +import java.util.stream.Stream; + +import org.graalvm.compiler.core.common.GraalOptions; +import org.graalvm.compiler.options.OptionValues; +import org.junit.Assert; +import org.junit.Test; + +import jdk.vm.ci.meta.ResolvedJavaMethod; + +/** + * Tests {@link java.util.Base64} intrinsics. + */ +@SuppressWarnings("javadoc") +public class Base64SubstitutionsTest extends GraalOSRTestBase { + + private static final HexFormat HF = HexFormat.ofDelimiter(", ").withPrefix("0x"); + + private void assertByteArraysEqual(Object expect, Object actual) { + String message = String.format("%n%s !=%n%s", HF.formatHex((byte[]) expect), HF.formatHex((byte[]) actual)); + assertDeepEquals(message, expect, actual); + } + + record EncoderTestCase(Encoder encoder, byte[][] encoded) { + } + + record DecoderTestCase(String type, Decoder decoder, byte[][] encoded) { + } + + record Results(Result expect, Result actual) { + } + + private Results lastResults; + + /** + * Interpose to capture last test results. + */ + @Override + protected void assertEquals(Result expect, Result actual) { + lastResults = new Results(expect, actual); + super.assertEquals(expect, actual); + } + + /** + * A byte[] supplier that creates an array given a length and an optional suffix. + */ + static class ByteArraySupplier implements ArgSupplier { + final int length; + final byte[] suffix; + List supplied = new ArrayList<>(); + + ByteArraySupplier(int length) { + this(length, null); + } + + ByteArraySupplier(int length, byte[] suffix) { + this.length = length; + this.suffix = suffix; + } + + @Override + public Object get() { + if (suffix != null) { + byte[] res = new byte[length + suffix.length]; + System.arraycopy(suffix, 0, res, length, suffix.length); + supplied.add(res); + return res; + } + byte[] res = new byte[length]; + supplied.add(res); + return res; + } + } + + private static EncoderTestCase[] getEncoders() { + return new EncoderTestCase[]{ + new EncoderTestCase(Base64.getEncoder(), BASE_ENCODED_TEXT), + new EncoderTestCase(Base64.getMimeEncoder(), MIME_ENCODED_TEXT), + new EncoderTestCase(Base64.getUrlEncoder(), URL_ENCODED_TEXT) + }; + } + + private static DecoderTestCase[] getDecoders() { + return new DecoderTestCase[]{ + new DecoderTestCase("base", Base64.getDecoder(), BASE_ENCODED_TEXT), + new DecoderTestCase("mime", Base64.getMimeDecoder(), MIME_ENCODED_TEXT), + new DecoderTestCase("url", Base64.getUrlDecoder(), URL_ENCODED_TEXT) + }; + } + + /** + * Tests {@link Encoder#encode(byte[])}. + */ + @Test + public void testEncodeByteArray1() { + for (EncoderTestCase tc : getEncoders()) { + ResolvedJavaMethod m = getResolvedJavaMethod(Encoder.class, "encode", byte[].class); + for (int i = 0; i < PLAIN_TEXT_BYTES.length; i++) { + byte[] srcBytes = PLAIN_TEXT_BYTES[i]; + test(m, tc.encoder, srcBytes); + assertByteArraysEqual(tc.encoded[i], lastResults.actual.returnValue); + } + } + } + + /** + * The test at + * https://github.com/openjdk/jdk/blob/f2922682688a40529df269e1551246ac8da5d7ee/src/java.base/share/classes/java/util/Base64.java#L878 + * is converted to a guard and (too eagerly) floated outside the loop, leading to deopt even + * when the input byte array has a correct ending. + * + * GR-48430. + */ + private static OptionValues workaroundTooEagerDeopt() { + return new OptionValues(getInitialOptions(), + GraalOptions.OptConvertDeoptsToGuards, false, + GraalOptions.SpeculativeGuardMovement, false); + } + + /** + * Tests {@link Encoder#encode(byte[], byte[])}. + */ + @Test + public void testEncodeByteArray2() { + ResolvedJavaMethod m = getResolvedJavaMethod(Encoder.class, "encode", byte[].class, byte[].class); + for (EncoderTestCase tc : getEncoders()) { + for (int i = 0; i < PLAIN_TEXT_BYTES.length; i++) { + byte[] srcBytes = PLAIN_TEXT_BYTES[i]; + ByteArraySupplier bas = new ByteArraySupplier(tc.encoded[i].length); + test(m, tc.encoder, srcBytes, bas); + Assert.assertEquals(lastResults.actual.returnValue, tc.encoded[i].length); + Assert.assertEquals(bas.supplied.size(), 2); + byte[] expect = bas.supplied.get(0); + byte[] actual = bas.supplied.get(1); + assertByteArraysEqual(expect, actual); + assertByteArraysEqual(tc.encoded[i], actual); + } + } + } + + public static byte[] encodeByteBufferSnippet(Encoder encoder, ByteBuffer srcBuf) { + ByteBuffer dstBuf = encoder.encode(srcBuf); + srcBuf.rewind(); + return dstBuf.array(); + } + + /** + * Test {@link Encoder#encode(ByteBuffer)}. + */ + @Test + public void testEncodeByteBuffer() { + for (EncoderTestCase tc : getEncoders()) { + for (int i = 0; i < PLAIN_TEXT_BYTES.length; i++) { + byte[] srcBytes = PLAIN_TEXT_BYTES[i]; + ByteBuffer srcBuf = ByteBuffer.wrap(srcBytes); + test("encodeByteBufferSnippet", tc.encoder, srcBuf); + assertByteArraysEqual(tc.encoded[i], lastResults.actual.returnValue); + } + } + } + + /** + * Tests {@link Encoder#encodeToString(byte[])}. + */ + @Test + public void testEncodeToString() { + ResolvedJavaMethod m = getResolvedJavaMethod(Encoder.class, "encodeToString", byte[].class); + for (EncoderTestCase tc : getEncoders()) { + for (int i = 0; i < PLAIN_TEXT_BYTES.length; i++) { + byte[] srcBytes = PLAIN_TEXT_BYTES[i]; + test(m, tc.encoder, srcBytes); + } + } + } + + /** + * Tests OSR compilation of {@code Encoder#encode0(byte[], int, int, byte[])}. + */ + @Test + public void testEncode0() { + ResolvedJavaMethod method = getResolvedJavaMethod(Encoder.class, "encode0", byte[].class, int.class, int.class, byte[].class); + compileOSR(getInitialOptions(), method); + } + + /** + * Supplies illegal Base64 characters. + */ + static class IllegalBase64CharSupplier implements Supplier { + final byte[] illegals; + + IllegalBase64CharSupplier(boolean url) { + Set set = new HashSet<>(); + for (int val = 0; val < 256; val++) { + if ((val >= 'A' && val <= 'Z') || + (val >= 'a' && val <= 'z') || + (val >= '0' && val <= '9') || val == '=') { + continue; + } + if (url) { + if (val == '-' || val == '_') { + continue; + } + } else { + if (val == '+' || val == '/') { + continue; + } + } + set.add((byte) val); + } + illegals = new byte[set.size()]; + int i = 0; + for (Byte b : set) { + illegals[i++] = b; + } + assert i == illegals.length; + } + + int next; + + @Override + public Byte get() { + return illegals[next++ % illegals.length]; + } + } + + /** + * Tests {@link Decoder#decode(byte[])}. + */ + @Test + public void testDecodeByteArray1() { + Random ran = getRandomInstance(); + ResolvedJavaMethod m = getResolvedJavaMethod(Decoder.class, "decode", byte[].class); + for (DecoderTestCase tc : getDecoders()) { + IllegalBase64CharSupplier illegals = new IllegalBase64CharSupplier(tc.type.equals("url")); + for (int i = 0; i < PLAIN_TEXT_BYTES.length; i++) { + byte[] srcBytes = tc.encoded[i]; + test(m, tc.decoder, srcBytes); + assertDeepEquals(PLAIN_TEXT_BYTES[i], lastResults.actual.returnValue); + + // test that an illegal Base64 character is detected + if (!tc.type.equals("mime") && srcBytes.length != 0) { + byte[] srcBytesCopy = srcBytes.clone(); + int bytePosToCorrupt = ran.nextInt(srcBytesCopy.length); + byte illegal = illegals.get(); + srcBytesCopy[bytePosToCorrupt] = illegal; + Result result = executeActual(m, tc.decoder, srcBytesCopy); + if (!(result.exception instanceof IllegalArgumentException)) { + String hexBuf = HexFormat.ofDelimiter(", ").withPrefix("0x").formatHex(srcBytesCopy); + throw new AssertionError(String.format("%s decoder did not catch illegal base64 character 0x%02x at position %d in encoded buffer of length %d%nbuf:%s", + tc.type, illegal, bytePosToCorrupt, srcBytesCopy.length, hexBuf)); + } + } + } + } + } + + /** + * Tests {@link Decoder#decode(byte[], byte[])}. + */ + @Test + public void testDecodeByteArray2() { + OptionValues options = workaroundTooEagerDeopt(); + ResolvedJavaMethod m = getResolvedJavaMethod(Decoder.class, "decode", byte[].class, byte[].class); + for (DecoderTestCase tc : getDecoders()) { + for (int i = 0; i < PLAIN_TEXT_BYTES.length; i++) { + byte[] srcBytes = tc.encoded[i]; + // JDK-8273108: Test for output buffer overrun + byte[] suffix = {0, (byte) 167}; + ByteArraySupplier bas = new ByteArraySupplier(srcBytes.length, suffix); + test(options, m, tc.decoder, srcBytes, bas); + Assert.assertEquals(bas.supplied.size(), 2); + byte[] expect = Arrays.copyOfRange(bas.supplied.get(0), 0, srcBytes.length); + byte[] actual = Arrays.copyOfRange(bas.supplied.get(1), 0, srcBytes.length); + assertByteArraysEqual(expect, actual); + + byte[] actualSuffix = Arrays.copyOfRange(bas.supplied.get(1), srcBytes.length, srcBytes.length + suffix.length); + assertByteArraysEqual(suffix, actualSuffix); + } + } + } + + public static byte[] decodeByteBufferSnippet(Decoder decoder, ByteBuffer srcBuf) { + ByteBuffer dstBuf = decoder.decode(srcBuf); + srcBuf.rewind(); + return dstBuf.array(); + } + + /** + * Tests {@link Decoder#decode(ByteBuffer)}. + */ + @Test + public void testDecodeByteBuffer() { + OptionValues options = workaroundTooEagerDeopt(); + for (DecoderTestCase tc : getDecoders()) { + for (int i = 0; i < PLAIN_TEXT_BYTES.length; i++) { + byte[] srcBytes = tc.encoded[i]; + ByteBuffer srcBuf = ByteBuffer.wrap(srcBytes); + test(options, "decodeByteBufferSnippet", tc.decoder, srcBuf); + } + } + } + + /** + * Tests {@link Decoder#decode(String)}. + */ + @Test + public void testDecodeString() { + ResolvedJavaMethod m = getResolvedJavaMethod(Decoder.class, "decode", String.class); + for (DecoderTestCase tc : getDecoders()) { + for (int i = 0; i < PLAIN_TEXT_BYTES.length; i++) { + byte[] srcBytes = tc.encoded[i]; + String srcString = new String(srcBytes, StandardCharsets.ISO_8859_1); + test(m, tc.decoder, srcString); + assertByteArraysEqual(PLAIN_TEXT_BYTES[i], lastResults.actual.returnValue); + } + } + } + + /** + * Tests OSR compilation of {@code Decoder#decode0(byte[], int, int, byte[])}. + */ + @Test + public void testDecode0() { + ResolvedJavaMethod method = getResolvedJavaMethod(Decoder.class, "decode0", byte[].class, int.class, int.class, byte[].class); + compileOSR(getInitialOptions(), method); + } + + // @formatter:off + private static final String[] PLAIN_TEXT = """ + This test data is part of rfc2045 which includes all characters a~z A~Z, 0~9 and all symbols, + It is used to test java.util.Base64.Encoder, and will be encoded by org.apache.commons.codec.binary.Base64.java + to test java.util.Base64.Decoder; + + Freed & Borenstein Standards Track [Page 1] + RFC 2045 Internet Message Bodies November 1996 + + These documents are revisions of RFCs 1521, 1522, and 1590, which + themselves were revisions of RFCs 1341 and 1342. An appendix in RFC + 2049 describes differences and changes from previous versions. + + Table of Contents + + 1. Introduction ......................................... 3 + 2. Definitions, Conventions, and Generic BNF Grammar .... 5 + 3. MIME Header Fields ................................... 8 + 4. MIME-Version Header Field ............................ 8 + 5. Content-Type Header Field ............................ 10 + 6. Content-Transfer-Encoding Header Field ............... 14 + 7. Content-ID Header Field .............................. 26 + 8. Content-Description Header Field ..................... 27 + 9. Additional MIME Header Fields ........................ 27 + 10. Summary ............................................. 27 + 11. Security Considerations ............................. 27 + 12. Authors' Addresses .................................. 28 + A. Collected Grammar .................................... 29 + + Freed & Borenstein Standards Track [Page 7] + RFC 2045 Internet Message Bodies November 1996 + + 3. MIME Header Fields + + MIME defines a number of new RFC 822 header fields that are used to + describe the content of a MIME entity. These header fields occur in + at least two contexts: + + (1) As part of a regular RFC 822 message header. + + (2) In a MIME body part header within a multipart + construct. + + The formal definition of these header fields is as follows: + + MIME-message-headers := entity-headers + fields + version CRLF + ; The ordering of the header + ; fields implied by this BNF + ; definition should be ignored. + + MIME-part-headers := entity-headers + [ fields ] + ; Any field not beginning with + ; "content-" can have no defined + ; meaning and may be ignored. + ; The ordering of the header + ; fields implied by this BNF + ; definition should be ignored. + + The syntax of the various specific MIME header fields will be + described in the following sections. + + Freed & Borenstein Standards Track [Page 11] + RFC 2045 Internet Message Bodies November 1996 + + 5.1. Syntax of the Content-Type Header Field + + In the Augmented BNF notation of RFC 822, a Content-Type header field + value is defined as follows: + + content := "Content-Type" ":" type "/" subtype + *(";" parameter) + ; Matching of media type and subtype + ; is ALWAYS case-insensitive. + + type := discrete-type / composite-type + + discrete-type := "text" / "image" / "audio" / "video" / + "application" / extension-token + + composite-type := "message" / "multipart" / extension-token + + extension-token := ietf-token / x-token + + ietf-token := + + x-token := + + subtype := extension-token / iana-token + + iana-token := + + parameter := attribute "=" value + + attribute := token + ; Matching of attributes + ; is ALWAYS case-insensitive. + + value := token / quoted-string + + token := 1* + + tspecials := "(" / ")" / "<" / ">" / "@" / + "," / ";" / ":" / "\" / <"> + "/" / "[" / "]" / "?" / "=" + ; Must be in quoted-string, + ; to use within parameter values + + description := "Content-Description" ":" *text + + encoding := "Content-Transfer-Encoding" ":" mechanism + + entity-headers := [ content CRLF ] + [ encoding CRLF ] + [ id CRLF ] + [ description CRLF ] + *( MIME-extension-field CRLF ) + + hex-octet := "=" 2(DIGIT / "A" / "B" / "C" / "D" / "E" / "F") + ; Octet must be used for characters > 127, =, + ; SPACEs or TABs at the ends of lines, and is + ; recommended for any character not listed in + ; RFC 2049 as "mail-safe". + + RFC 2045 Internet Message Bodies November 1996 + + must be used. An equal sign as the last character on a + encoded line indicates such a non-significant ("soft") + line break in the encoded text. + + Thus if the "raw" form of the line is a single unencoded line that + says: + + Now's the time for all folk to come to the aid of their country. + + This can be represented, in the Quoted-Printable encoding, as: + + Now's the time = + for all folk to come= + to the aid of their country. + + Since the hyphen character ("-") may be represented as itself in the + Quoted-Printable encoding, care must be taken, when encapsulating a + quoted-printable encoded body inside one or more multipart entities, + to ensure that the boundary delimiter does not appear anywhere in the + encoded body. (A good strategy is to choose a boundary that includes + a character sequence such as "=_" which can never appear in a + quoted-printable body. See the definition of multipart messages in + RFC 2046.) + + !"#$@[\\]^`{|}~% + + Freed & + Borenstein Standards Track[Page 24] + + RFC 2045 + Internet Message + Bodies November 1996 + + Table 1: + The Base64 + Alphabet + + Value + Encoding Value + Encoding Value + Encoding Value Encoding 0 A 17 R 34 i 51 z 1 B 18 S 35 j 52 0 2 C 19 T 36 k 53 1 3 D 20 U 37 l 54 2 4 E 21 V 38 m 55 3 5 F 22 W 39 n 56 4 6 G 23 X 40 o 57 5 7 H 24 Y 41 p 58 6 8 I 25 Z 42 q 59 7 9 J 26 a 43 r 60 8 10 K 27 b 44 s 61 9 11 L 28 c 45 t 62+12 M 29 d 46 u 63/13 N 30 e 47 v 14 O 31 f 48 + + w (pad) = + 15 P 32 g 49 x + 16 Q 33 h 50 y + """ + .lines().toArray(String[]::new); + // @formatter:on + + private static final byte[][] PLAIN_TEXT_BYTES = Stream.of(PLAIN_TEXT).map(s -> s.getBytes(StandardCharsets.US_ASCII)).toArray(byte[][]::new); + + private static final byte[][] BASE_ENCODED_TEXT = Stream.of(PLAIN_TEXT_BYTES).map(s -> Base64.getEncoder().encode(s)).toArray(byte[][]::new); + private static final byte[][] MIME_ENCODED_TEXT = Stream.of(PLAIN_TEXT_BYTES).map(s -> Base64.getMimeEncoder().encode(s)).toArray(byte[][]::new); + private static final byte[][] URL_ENCODED_TEXT = Stream.of(PLAIN_TEXT_BYTES).map(s -> Base64.getUrlEncoder().encode(s)).toArray(byte[][]::new); +} diff --git a/compiler/src/jdk.internal.vm.compiler.test/src/org/graalvm/compiler/hotspot/test/GraalOSRTestBase.java b/compiler/src/jdk.internal.vm.compiler.test/src/org/graalvm/compiler/hotspot/test/GraalOSRTestBase.java index b9dee9f32b0a..fc254d835485 100644 --- a/compiler/src/jdk.internal.vm.compiler.test/src/org/graalvm/compiler/hotspot/test/GraalOSRTestBase.java +++ b/compiler/src/jdk.internal.vm.compiler.test/src/org/graalvm/compiler/hotspot/test/GraalOSRTestBase.java @@ -25,8 +25,6 @@ package org.graalvm.compiler.hotspot.test; -import java.util.Arrays; - import org.graalvm.compiler.bytecode.Bytecode; import org.graalvm.compiler.bytecode.BytecodeDisassembler; import org.graalvm.compiler.bytecode.BytecodeStream; @@ -36,13 +34,11 @@ import org.graalvm.compiler.core.test.GraalCompilerTest; import org.graalvm.compiler.debug.DebugContext; import org.graalvm.compiler.debug.GraalError; -import org.graalvm.compiler.debug.TTY; import org.graalvm.compiler.hotspot.CompilationTask; import org.graalvm.compiler.hotspot.GraalHotSpotVMConfig; import org.graalvm.compiler.hotspot.HotSpotGraalCompiler; import org.graalvm.compiler.hotspot.HotSpotGraalRuntimeProvider; import org.graalvm.compiler.java.BciBlockMapping; -import org.graalvm.compiler.java.BciBlockMapping.BciBlock; import org.graalvm.compiler.nodes.StructuredGraph; import org.graalvm.compiler.nodes.StructuredGraph.AllowAssumptions; import org.graalvm.compiler.options.OptionValues; @@ -55,6 +51,9 @@ import jdk.vm.ci.hotspot.HotSpotResolvedJavaMethod; import jdk.vm.ci.meta.ResolvedJavaMethod; +import java.util.ArrayList; +import java.util.List; + public abstract class GraalOSRTestBase extends GraalCompilerTest { protected void testOSR(OptionValues options, String methodName) { @@ -94,32 +93,30 @@ protected static void compile(DebugContext debug, ResolvedJavaMethod method, int } /** - * Returns the target BCI of the first bytecode backedge. This is where HotSpot triggers - * on-stack-replacement in case the backedge counter overflows. + * Returns the target BCIs of all bytecode backedges. */ - static int getBackedgeBCI(DebugContext debug, ResolvedJavaMethod method) { + public int[] getBackedgeBCIs(DebugContext debug, ResolvedJavaMethod method) { Bytecode code = new ResolvedJavaMethodBytecode(method); BytecodeStream stream = new BytecodeStream(code.getCode()); OptionValues options = debug.getOptions(); BciBlockMapping bciBlockMapping = BciBlockMapping.create(stream, code, options, debug, true); - for (BciBlock block : bciBlockMapping.getBlocks()) { + List backedgeBcis = new ArrayList<>(); + for (BciBlockMapping.BciBlock block : bciBlockMapping.getBlocks()) { if (block.getStartBci() != -1) { int bci = block.getEndBci(); - for (BciBlock succ : block.getSuccessors()) { + for (BciBlockMapping.BciBlock succ : block.getSuccessors()) { if (succ.getStartBci() != -1) { int succBci = succ.getStartBci(); if (succBci < bci) { // back edge - return succBci; + backedgeBcis.add(succBci); } } } } } - TTY.println("Cannot find loop back edge with bytecode loops at:%s", Arrays.toString(bciBlockMapping.getLoopHeaders())); - TTY.println(new BytecodeDisassembler().disassemble(code)); - return -1; + return backedgeBcis.stream().mapToInt(Integer::intValue).toArray(); } protected static void checkResult(Result result) { @@ -130,6 +127,10 @@ protected static void checkResult(Result result) { } protected void compileOSR(OptionValues options, ResolvedJavaMethod method) { + compileOSR(options, method, true); + } + + protected void compileOSR(OptionValues options, ResolvedJavaMethod method, boolean expectBackedge) { OptionValues goptions = options; // Silence diagnostics for permanent bailout errors as they // are expected for some OSR tests. @@ -139,9 +140,16 @@ protected void compileOSR(OptionValues options, ResolvedJavaMethod method) { // ensure eager resolving StructuredGraph graph = parseEager(method, AllowAssumptions.YES, goptions); DebugContext debug = graph.getDebug(); - int bci = getBackedgeBCI(debug, method); - assert bci != -1; - compile(debug, method, bci); + int[] backedgeBCIs = getBackedgeBCIs(debug, method); + if (expectBackedge && backedgeBCIs.length == 0) { + Bytecode code = new ResolvedJavaMethodBytecode(method); + throw new AssertionError(String.format("Cannot find any loop back edges in %s:%n%s", method.format("%H.%n(%p)"), + new BytecodeDisassembler().disassemble(code))); + + } + for (int bci : backedgeBCIs) { + compile(debug, method, bci); + } } protected enum ReturnValue { diff --git a/compiler/src/jdk.internal.vm.compiler.test/src/org/graalvm/compiler/jtt/JTTTest.java b/compiler/src/jdk.internal.vm.compiler.test/src/org/graalvm/compiler/jtt/JTTTest.java index 35802754a043..de4a0ee98763 100644 --- a/compiler/src/jdk.internal.vm.compiler.test/src/org/graalvm/compiler/jtt/JTTTest.java +++ b/compiler/src/jdk.internal.vm.compiler.test/src/org/graalvm/compiler/jtt/JTTTest.java @@ -34,7 +34,6 @@ import java.util.Formatter; import java.util.ListIterator; import java.util.Optional; -import java.util.Random; import java.util.Set; import org.graalvm.compiler.api.test.Graal; @@ -216,7 +215,7 @@ private void testAgainstExpectedWithFuzzedCompilationPlan(OptionValues options, // phases that cannot be serialized. removeTestPhases(originalSuites); StructuredGraph graph = parseForCompile(method, getCompilationId(method), options); - long randomSeed = Long.getLong(SEED_SYSTEM_PROPERTY, new Random().nextLong()); + long randomSeed = Long.getLong(SEED_SYSTEM_PROPERTY, getRandomInstance().nextLong()); RuntimeProvider runtime = Graal.getRequiredCapability(RuntimeProvider.class); MinimalFuzzedCompilationPlan minimalFuzzedCompilationPlan = MinimalFuzzedCompilationPlan.createMinimalFuzzedCompilationPlan( diff --git a/compiler/src/jdk.internal.vm.compiler.test/src/org/graalvm/compiler/replacements/test/ArrayCopyExceptionSeenTest.java b/compiler/src/jdk.internal.vm.compiler.test/src/org/graalvm/compiler/replacements/test/ArrayCopyExceptionSeenTest.java new file mode 100644 index 000000000000..04e3214833d4 --- /dev/null +++ b/compiler/src/jdk.internal.vm.compiler.test/src/org/graalvm/compiler/replacements/test/ArrayCopyExceptionSeenTest.java @@ -0,0 +1,67 @@ +/* + * Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.graalvm.compiler.replacements.test; + +import static org.graalvm.compiler.core.common.GraalOptions.StressInvokeWithExceptionNode; + +import org.graalvm.compiler.api.directives.GraalDirectives; +import org.graalvm.compiler.core.test.GraalCompilerTest; +import org.graalvm.compiler.nodes.spi.ProfileProvider; +import org.graalvm.compiler.options.OptionValues; +import org.junit.Test; + +import jdk.vm.ci.meta.ResolvedJavaMethod; + +public class ArrayCopyExceptionSeenTest extends GraalCompilerTest { + + @Override + protected ProfileProvider getProfileProvider(ResolvedJavaMethod method) { + return NO_PROFILE_PROVIDER; + } + + static class A { + int field = 0; + } + + static A a = null; + + public void copy(Object src, Object dst) { + try { + System.arraycopy(src, 0, dst, 0, 1); + } catch (Throwable e) { + try { + a.field++; + } catch (Throwable npe) { + GraalDirectives.deoptimize(); + } + } + } + + @Test + public void testCopy() { + getFinalGraph(getResolvedJavaMethod("copy"), new OptionValues(getInitialOptions(), StressInvokeWithExceptionNode, true)); + } + +} diff --git a/compiler/src/jdk.internal.vm.compiler/src/org/graalvm/compiler/hotspot/EncodedSnippets.java b/compiler/src/jdk.internal.vm.compiler/src/org/graalvm/compiler/hotspot/EncodedSnippets.java index 91af4bc9a682..2c6e61656c84 100644 --- a/compiler/src/jdk.internal.vm.compiler/src/org/graalvm/compiler/hotspot/EncodedSnippets.java +++ b/compiler/src/jdk.internal.vm.compiler/src/org/graalvm/compiler/hotspot/EncodedSnippets.java @@ -126,7 +126,7 @@ static class StaticGraphData extends GraphData { @Override int getStartOffset(Class receiverClass) { - assert receiverClass == null; + assert receiverClass == null : receiverClass; return startOffset; } } @@ -480,7 +480,7 @@ static class GraalCapability { public Object resolve(GraalRuntime runtime) { Object capability = runtime.getCapability(this.capabilityClass); if (capability != null) { - assert capability.getClass() == capabilityClass; + assert capability.getClass() == capabilityClass : capability.getClass() + " != " + capabilityClass; return capability; } throw new InternalError(this.capabilityClass.getName()); diff --git a/compiler/src/jdk.internal.vm.compiler/src/org/graalvm/compiler/hotspot/GraalHotSpotVMConfig.java b/compiler/src/jdk.internal.vm.compiler/src/org/graalvm/compiler/hotspot/GraalHotSpotVMConfig.java index 31e9a9d5fa1b..ab4a646e979f 100644 --- a/compiler/src/jdk.internal.vm.compiler/src/org/graalvm/compiler/hotspot/GraalHotSpotVMConfig.java +++ b/compiler/src/jdk.internal.vm.compiler/src/org/graalvm/compiler/hotspot/GraalHotSpotVMConfig.java @@ -105,6 +105,11 @@ private HotSpotGraalRuntime.HotSpotGC getSelectedGC() throws GraalError { return selected; } + /** + * Determines if {@code -Xcomp} (or the equivalent thereof) was specified as a JVM argument. + */ + public final boolean xcompMode = !access.getFlag("UseInterpreter", Boolean.class); + public final boolean ropProtection = access.getFieldValue("VM_Version::_rop_protection", Boolean.class, "bool", false); public final boolean cAssertions = getConstant("ASSERT", Boolean.class); diff --git a/compiler/src/jdk.internal.vm.compiler/src/org/graalvm/compiler/hotspot/meta/HotSpotGraphBuilderPlugins.java b/compiler/src/jdk.internal.vm.compiler/src/org/graalvm/compiler/hotspot/meta/HotSpotGraphBuilderPlugins.java index e24aeb4e5a2c..d4786e440e2c 100644 --- a/compiler/src/jdk.internal.vm.compiler/src/org/graalvm/compiler/hotspot/meta/HotSpotGraphBuilderPlugins.java +++ b/compiler/src/jdk.internal.vm.compiler/src/org/graalvm/compiler/hotspot/meta/HotSpotGraphBuilderPlugins.java @@ -285,6 +285,7 @@ private static void registerTrufflePlugins(InvocationPlugins plugins, WordTypes tl.register(new InvocationPlugin("get", Receiver.class) { @Override public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver) { + receiver.get(); int jvmciReservedReference0Offset = config.jvmciReservedReference0Offset; GraalError.guarantee(jvmciReservedReference0Offset != -1, "jvmciReservedReference0Offset is not available but used."); b.addPush(JavaKind.Object, new HotSpotLoadReservedReferenceNode(b.getMetaAccess(), wordTypes, jvmciReservedReference0Offset)); @@ -295,6 +296,7 @@ public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Rec @Override public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver, ValueNode value) { + receiver.get(); int jvmciReservedReference0Offset = config.jvmciReservedReference0Offset; GraalError.guarantee(jvmciReservedReference0Offset != -1, "jvmciReservedReference0Offset is not available but used."); b.add(new HotSpotStoreReservedReferenceNode(wordTypes, value, jvmciReservedReference0Offset)); @@ -770,6 +772,7 @@ public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Rec public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver, ValueNode hide) { if (config.doJVMTIVirtualThreadTransitions) { try (HotSpotInvocationPluginHelper helper = new HotSpotInvocationPluginHelper(b, targetMethod, config)) { + receiver.get(); // unconditionally update the temporary VTMS transition bit in current // JavaThread GraalError.guarantee(config.threadIsInTmpVTMSTransitionOffset != -1L, "JavaThread::_is_in_tmp_VTMS_transition is not exported"); @@ -1066,6 +1069,10 @@ private static void registerBase64Plugins(InvocationPlugins plugins, GraalHotSpo @Override public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver, ValueNode src, ValueNode sp, ValueNode sl, ValueNode dst, ValueNode dp, ValueNode isURL) { + if (receiver != null) { + // Side effect of call below is to add a receiver null check if required + receiver.get(); + } int byteArrayBaseOffset = metaAccess.getArrayBaseOffset(JavaKind.Byte); ComputeObjectAddressNode srcAddress = b.add(new ComputeObjectAddressNode(src, ConstantNode.forInt(byteArrayBaseOffset))); ComputeObjectAddressNode dstAddress = b.add(new ComputeObjectAddressNode(dst, ConstantNode.forInt(byteArrayBaseOffset))); @@ -1081,6 +1088,10 @@ public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Rec @Override public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver, ValueNode src, ValueNode sp, ValueNode sl, ValueNode dst, ValueNode dp, ValueNode isURL, ValueNode isMime) { + if (receiver != null) { + // Side effect of call below is to add a receiver null check if required + receiver.get(); + } int byteArrayBaseOffset = metaAccess.getArrayBaseOffset(JavaKind.Byte); ComputeObjectAddressNode srcAddress = b.add(new ComputeObjectAddressNode(src, ConstantNode.forInt(byteArrayBaseOffset))); ComputeObjectAddressNode dstAddress = b.add(new ComputeObjectAddressNode(dst, ConstantNode.forInt(byteArrayBaseOffset))); @@ -1094,6 +1105,10 @@ public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Rec @Override public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver, ValueNode src, ValueNode sp, ValueNode sl, ValueNode dst, ValueNode dp, ValueNode isURL) { + if (receiver != null) { + // Side effect of call below is to add a receiver null check if required + receiver.get(); + } int byteArrayBaseOffset = metaAccess.getArrayBaseOffset(JavaKind.Byte); ComputeObjectAddressNode srcAddress = b.add(new ComputeObjectAddressNode(src, ConstantNode.forInt(byteArrayBaseOffset))); ComputeObjectAddressNode dstAddress = b.add(new ComputeObjectAddressNode(dst, ConstantNode.forInt(byteArrayBaseOffset))); @@ -1168,6 +1183,7 @@ private static void registerPoly1305Plugins(InvocationPlugins plugins, GraalHotS @Override public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver, ValueNode input, ValueNode offset, ValueNode length, ValueNode aLimbs, ValueNode rLimbs) { try (InvocationPluginHelper helper = new InvocationPluginHelper(b, targetMethod)) { + receiver.get(); ValueNode inputNotNull = b.nullCheckedValue(input); ValueNode aLimbsNotNull = b.nullCheckedValue(aLimbs); ValueNode rLimbsNotNull = b.nullCheckedValue(rLimbs); @@ -1267,6 +1283,7 @@ public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Rec try (HotSpotInvocationPluginHelper helper = new HotSpotInvocationPluginHelper(b, targetMethod, config)) { ValueNode objectNonNull = b.nullCheckedValue(objectToSize); StructuredGraph graph = b.getGraph(); + receiver.get(); // Discharge receiver null check requirement LoadHubNode hub = b.add(new LoadHubNode(b.getStampProvider(), objectNonNull)); ValueNode layoutHelper = helper.klassLayoutHelper(hub); diff --git a/compiler/src/jdk.internal.vm.compiler/src/org/graalvm/compiler/hotspot/meta/HotSpotSuitesProvider.java b/compiler/src/jdk.internal.vm.compiler/src/org/graalvm/compiler/hotspot/meta/HotSpotSuitesProvider.java index 9cbf953b75c5..f863443297a2 100644 --- a/compiler/src/jdk.internal.vm.compiler/src/org/graalvm/compiler/hotspot/meta/HotSpotSuitesProvider.java +++ b/compiler/src/jdk.internal.vm.compiler/src/org/graalvm/compiler/hotspot/meta/HotSpotSuitesProvider.java @@ -116,6 +116,11 @@ protected PhaseSuite createGraphBuilderSuite() { * for equality. */ private boolean appendGraphEncoderTest(PhaseSuite suite) { + if (config.xcompMode) { + // Do not do this in -Xcomp mode. It adds too much compilation time. + // Testing coverage is provided by Graal unit testing instead. + return true; + } suite.appendPhase(new BasePhase() { @Override public Optional notApplicableTo(GraphState graphState) { diff --git a/compiler/src/jdk.internal.vm.compiler/src/org/graalvm/compiler/java/BytecodeParser.java b/compiler/src/jdk.internal.vm.compiler/src/org/graalvm/compiler/java/BytecodeParser.java index 5ad9214cf9b2..f045d3139f8f 100644 --- a/compiler/src/jdk.internal.vm.compiler/src/org/graalvm/compiler/java/BytecodeParser.java +++ b/compiler/src/jdk.internal.vm.compiler/src/org/graalvm/compiler/java/BytecodeParser.java @@ -259,7 +259,6 @@ import static org.graalvm.compiler.nodes.extended.BranchProbabilityNode.EXTREMELY_FAST_PATH_PROBABILITY; import static org.graalvm.compiler.nodes.extended.BranchProbabilityNode.EXTREMELY_SLOW_PATH_PROBABILITY; import static org.graalvm.compiler.nodes.graphbuilderconf.IntrinsicContext.CompilationContext.INLINE_DURING_PARSING; -import static org.graalvm.compiler.nodes.type.StampTool.isPointerNonNull; import java.util.ArrayList; import java.util.BitSet; @@ -2133,6 +2132,10 @@ protected ExceptionEdgeAction getActionForInvokeExceptionEdge(InlineInfo lastInl } } + static String pluginErrorMessage(InvocationPlugin plugin, String format, Object... a) { + return String.format(format, a) + String.format("%n\tplugin at %s", plugin.getSourceLocation()); + } + /** * Contains all the assertion checking logic around the application of an * {@link InvocationPlugin}. This class is only loaded when assertions are enabled. @@ -2143,7 +2146,6 @@ class InvocationPluginAssertions { final ResolvedJavaMethod targetMethod; final JavaKind resultType; final int beforeStackSize; - final boolean needsNullCheck; final int nodeCount; final Mark mark; @@ -2154,13 +2156,12 @@ class InvocationPluginAssertions { this.args = args; this.resultType = resultType; this.beforeStackSize = frameState.stackSize(); - this.needsNullCheck = !targetMethod.isStatic() && args[0].getStackKind() == JavaKind.Object && !StampTool.isPointerNonNull(args[0].stamp(NodeView.DEFAULT)); this.nodeCount = graph.getNodeCount(); this.mark = graph.getMark(); } String error(String format, Object... a) { - return String.format(format, a) + String.format("%n\tplugin at %s", plugin.getSourceLocation()); + return pluginErrorMessage(plugin, format, a); } boolean check(boolean pluginResult) { @@ -2175,8 +2176,6 @@ boolean check(boolean pluginResult) { frameState.stackSize()); NodeIterable newNodes = graph.getNewNodes(mark); - assert !needsNullCheck || isPointerNonNull(args[0].stamp(NodeView.DEFAULT)) : error("plugin needs to null check the receiver of %s: receiver=%s", targetMethod.format("%H.%n(%p)"), - args[0]); for (Node n : newNodes) { if (n instanceof StateSplit) { StateSplit stateSplit = (StateSplit) n; @@ -2299,10 +2298,17 @@ protected boolean applyInvocationPlugin(InvokeKind invokeKind, ValueNode[] args, assert invokeKind.isDirect() : "Cannot apply invocation plugin on an indirect call site."; InvocationPluginAssertions assertions = Assertions.assertionsEnabled() ? new InvocationPluginAssertions(plugin, args, targetMethod, resultType) : null; + boolean needsReceiverNullCheck = !(plugin instanceof GeneratedInvocationPlugin) && !targetMethod.isStatic() && args[0].getStackKind() == JavaKind.Object; try (DebugCloseable context = openNodeContext(targetMethod); InvocationPluginScope pluginScope = new InvocationPluginScope(invokeKind, args, targetMethod, resultType, plugin)) { Mark mark = graph.getMark(); if (plugin.execute(this, targetMethod, pluginReceiver, args)) { checkDeoptAfterPlugin(mark, targetMethod); + if (needsReceiverNullCheck && !pluginReceiver.nullCheckPerformed()) { + throw new GraalError(pluginErrorMessage(plugin, + "plugin needs to null check the receiver of %s: receiver=%s", + targetMethod.format("%H.%n(%p)"), + args[0])); + } assert assertions.check(true); return true; } else { diff --git a/compiler/src/jdk.internal.vm.compiler/src/org/graalvm/compiler/nodes/graphbuilderconf/GeneratedInvocationPlugin.java b/compiler/src/jdk.internal.vm.compiler/src/org/graalvm/compiler/nodes/graphbuilderconf/GeneratedInvocationPlugin.java index ebe4c1fbfae1..909ba89e24a0 100644 --- a/compiler/src/jdk.internal.vm.compiler/src/org/graalvm/compiler/nodes/graphbuilderconf/GeneratedInvocationPlugin.java +++ b/compiler/src/jdk.internal.vm.compiler/src/org/graalvm/compiler/nodes/graphbuilderconf/GeneratedInvocationPlugin.java @@ -67,7 +67,7 @@ public String getSourceLocation() { Class c = getClass(); for (Method m : c.getDeclaredMethods()) { if (m.getName().equals("execute")) { - return String.format("%s.%s()", m.getClass().getName(), m.getName()); + return String.format("%s.%s()", m.getDeclaringClass().getName(), m.getName()); } } throw new GraalError("could not find method named \"execute\" in " + c.getName()); diff --git a/compiler/src/jdk.internal.vm.compiler/src/org/graalvm/compiler/nodes/graphbuilderconf/InvocationPlugin.java b/compiler/src/jdk.internal.vm.compiler/src/org/graalvm/compiler/nodes/graphbuilderconf/InvocationPlugin.java index b4f5927eec56..c34a98da7839 100644 --- a/compiler/src/jdk.internal.vm.compiler/src/org/graalvm/compiler/nodes/graphbuilderconf/InvocationPlugin.java +++ b/compiler/src/jdk.internal.vm.compiler/src/org/graalvm/compiler/nodes/graphbuilderconf/InvocationPlugin.java @@ -66,6 +66,13 @@ default ValueNode get() { */ ValueNode get(boolean performNullCheck); + /** + * Gets the receiver value, asserting that its stamp is non-null. This does not emit any + * code but discharges the requirement that an invocation plugin must ensure the receiver of + * a non-static method is non-null. + */ + ValueNode requireNonNull(); + /** * Determines if the receiver is constant. */ @@ -393,7 +400,7 @@ public String getSourceLocation() { Class c = getClass(); for (Method m : c.getDeclaredMethods()) { if (m.getName().equals("apply") || m.getName().equals("defaultHandler")) { - return String.format("%s.%s()", m.getClass().getName(), m.getName()); + return String.format("%s.%s()", m.getDeclaringClass().getName(), m.getName()); } } if (IS_IN_NATIVE_IMAGE) { diff --git a/compiler/src/jdk.internal.vm.compiler/src/org/graalvm/compiler/nodes/graphbuilderconf/InvocationPlugins.java b/compiler/src/jdk.internal.vm.compiler/src/org/graalvm/compiler/nodes/graphbuilderconf/InvocationPlugins.java index 72a33a378b54..4bedd0a185f8 100644 --- a/compiler/src/jdk.internal.vm.compiler/src/org/graalvm/compiler/nodes/graphbuilderconf/InvocationPlugins.java +++ b/compiler/src/jdk.internal.vm.compiler/src/org/graalvm/compiler/nodes/graphbuilderconf/InvocationPlugins.java @@ -55,6 +55,7 @@ import org.graalvm.compiler.graph.iterators.NodeIterable; import org.graalvm.compiler.nodes.ValueNode; import org.graalvm.compiler.nodes.spi.Replacements; +import org.graalvm.compiler.nodes.type.StampTool; import org.graalvm.compiler.options.Option; import org.graalvm.compiler.options.OptionKey; import org.graalvm.compiler.options.OptionType; @@ -93,6 +94,10 @@ public static class Options { public static class InvocationPluginReceiver implements InvocationPlugin.Receiver { private final GraphBuilderContext parser; private ValueNode[] args; + /** + * Caches the null checked receiver value. If still {@code null} after application of a + * plugin, then the plugin never called {@link #get(boolean)} with {@code true}. + */ private ValueNode value; public InvocationPluginReceiver(GraphBuilderContext parser) { @@ -120,13 +125,33 @@ public boolean isConstant() { } public InvocationPluginReceiver init(ResolvedJavaMethod targetMethod, ValueNode[] newArgs) { + this.value = null; if (!targetMethod.isStatic()) { this.args = newArgs; - this.value = null; return this; } + this.args = null; return null; } + + @Override + public ValueNode requireNonNull() { + if (value == null) { + GraalError.guarantee(args != null, "target method is static"); + if (!StampTool.isPointerNonNull(args[0])) { + throw new GraalError("receiver might be null: %s", value); + } + value = args[0]; + } + return value; + } + + /** + * Determines if {@link #get(boolean)} was called with {@code true}. + */ + public boolean nullCheckPerformed() { + return value != null; + } } /** diff --git a/compiler/src/jdk.internal.vm.compiler/src/org/graalvm/compiler/phases/common/LoweringPhase.java b/compiler/src/jdk.internal.vm.compiler/src/org/graalvm/compiler/phases/common/LoweringPhase.java index 0884fe44b8f4..13952d669789 100644 --- a/compiler/src/jdk.internal.vm.compiler/src/org/graalvm/compiler/phases/common/LoweringPhase.java +++ b/compiler/src/jdk.internal.vm.compiler/src/org/graalvm/compiler/phases/common/LoweringPhase.java @@ -183,19 +183,21 @@ public GuardingNode createGuard(FixedNode before, LogicNode condition, Deoptimiz @Override public GuardingNode createGuard(FixedNode before, LogicNode condition, DeoptimizationReason deoptReason, DeoptimizationAction action, Speculation speculation, boolean negated, - NodeSourcePosition noDeoptSucccessorPosition) { + NodeSourcePosition noDeoptSuccessorPosition) { StructuredGraph graph = before.graph(); if (OptEliminateGuards.getValue(graph.getOptions())) { - for (Node usage : condition.usages()) { - if (!activeGuards.isNew(usage) && activeGuards.isMarked(usage) && ((GuardNode) usage).isNegated() == negated && - (before.graph().isAfterStage(StageFlag.VALUE_PROXY_REMOVAL) || - nodeMap.get(((GuardNode) usage).getAnchor().asNode()).isInSameOrOuterLoopOf(nodeMap.get(before)))) { - return (GuardNode) usage; + for (GuardNode usage : condition.usages().filter(GuardNode.class)) { + if (!activeGuards.isNew(usage) && activeGuards.isMarked(usage) && usage.isNegated() == negated) { + ValueNode anchor = usage.getAnchor().asNode(); + if (before.graph().isAfterStage(StageFlag.VALUE_PROXY_REMOVAL) || + !nodeMap.isNew(anchor) && nodeMap.get(anchor).isInSameOrOuterLoopOf(nodeMap.get(before))) { + return usage; + } } } } if (!condition.graph().getGuardsStage().allowsFloatingGuards()) { - FixedGuardNode fixedGuard = graph.add(new FixedGuardNode(condition, deoptReason, action, speculation, negated, noDeoptSucccessorPosition)); + FixedGuardNode fixedGuard = graph.add(new FixedGuardNode(condition, deoptReason, action, speculation, negated, noDeoptSuccessorPosition)); graph.addBeforeFixed(before, fixedGuard); DummyGuardHandle handle = graph.add(new DummyGuardHandle(fixedGuard)); fixedGuard.lower(this); @@ -203,7 +205,7 @@ public GuardingNode createGuard(FixedNode before, LogicNode condition, Deoptimiz handle.safeDelete(); return result; } else { - GuardNode newGuard = graph.unique(new GuardNode(condition, guardAnchor, deoptReason, action, negated, speculation, noDeoptSucccessorPosition)); + GuardNode newGuard = graph.unique(new GuardNode(condition, guardAnchor, deoptReason, action, negated, speculation, noDeoptSuccessorPosition)); if (OptEliminateGuards.getValue(graph.getOptions())) { activeGuards.markAndGrow(newGuard); } diff --git a/compiler/src/jdk.internal.vm.compiler/src/org/graalvm/compiler/replacements/IntrinsicGraphBuilder.java b/compiler/src/jdk.internal.vm.compiler/src/org/graalvm/compiler/replacements/IntrinsicGraphBuilder.java index 1e9a8b980b25..8030591a459f 100644 --- a/compiler/src/jdk.internal.vm.compiler/src/org/graalvm/compiler/replacements/IntrinsicGraphBuilder.java +++ b/compiler/src/jdk.internal.vm.compiler/src/org/graalvm/compiler/replacements/IntrinsicGraphBuilder.java @@ -333,11 +333,25 @@ public BailoutException bailout(String string) { throw GraalError.unimplementedOverride(); // ExcludeFromJacocoGeneratedReport } + /** + * An {@link IntrinsicGraphBuilder} is used to produce a graph for inlining. It assumes the + * inliner does any required null checking of the receiver as part of inlining. As such, + * {@code performNullCheck} is ignored here. + */ @Override public ValueNode get(boolean performNullCheck) { return arguments[0]; } + /** + * The non-null check assertion for the receiver is ignored for the reasons stated in + * {@link #get(boolean)}. + */ + @Override + public ValueNode requireNonNull() { + return get(false); + } + @SuppressWarnings("try") public final StructuredGraph buildGraph(InvocationPlugin plugin) { // The caller is expected to have filtered out decorator plugins since they cannot be diff --git a/compiler/src/jdk.internal.vm.compiler/src/org/graalvm/compiler/replacements/StandardGraphBuilderPlugins.java b/compiler/src/jdk.internal.vm.compiler/src/org/graalvm/compiler/replacements/StandardGraphBuilderPlugins.java index 985cce5b041a..7f1accbd3078 100644 --- a/compiler/src/jdk.internal.vm.compiler/src/org/graalvm/compiler/replacements/StandardGraphBuilderPlugins.java +++ b/compiler/src/jdk.internal.vm.compiler/src/org/graalvm/compiler/replacements/StandardGraphBuilderPlugins.java @@ -848,7 +848,9 @@ private static void registerCharacterDataLatin1Plugins(InvocationPlugins plugins r.register(new InvocationPlugin("isDigit", Receiver.class, int.class) { @Override public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver, ValueNode ch) { - b.nullCheckedValue(receiver.get()); + // Side effect of call below is to add a receiver null check if required + receiver.get(); + ValueNode sub = b.add(SubNode.create(ch, ConstantNode.forInt('0'), NodeView.DEFAULT)); LogicNode isDigit = b.add(IntegerBelowNode.create(sub, ConstantNode.forInt(10), NodeView.DEFAULT)); b.addPush(JavaKind.Boolean, ConditionalNode.create(isDigit, NodeView.DEFAULT)); diff --git a/sdk/mx.sdk/mx_sdk_vm.py b/sdk/mx.sdk/mx_sdk_vm.py index beb81671c227..053d9e3d4043 100644 --- a/sdk/mx.sdk/mx_sdk_vm.py +++ b/sdk/mx.sdk/mx_sdk_vm.py @@ -105,6 +105,7 @@ def __init__(self, destination, jar_distributions, build_args, use_modules=None, self.build_time = build_time self.build_args_enterprise = build_args_enterprise or [] self.relative_home_paths = {} + self.relative_extracted_lib_paths = {} assert isinstance(self.jar_distributions, list) assert isinstance(self.build_args, (list, types.GeneratorType)) @@ -141,6 +142,12 @@ def add_relative_home_path(self, language, path): language, self.relative_home_paths[language], path, self.destination)) self.relative_home_paths[language] = path + def add_relative_extracted_lib_path(self, name, path): + if name in self.relative_extracted_lib_paths and self.relative_extracted_lib_paths[name] != path: + raise Exception('the relative extracted lib path of {} is already set to {} and cannot also be set to {} for {}'.format( + name, self.relative_extracted_lib_paths[name], path, self.destination)) + self.relative_extracted_lib_paths[name] = path + class LauncherConfig(AbstractNativeImageConfig): def __init__(self, destination, jar_distributions, main_class, build_args, is_main_launcher=True, default_symlinks=True, is_sdk_launcher=False, custom_launcher_script=None, extra_jvm_args=None, diff --git a/sdk/mx.sdk/mx_sdk_vm_impl.py b/sdk/mx.sdk/mx_sdk_vm_impl.py index 55757b02d70d..0e4f50a61532 100644 --- a/sdk/mx.sdk/mx_sdk_vm_impl.py +++ b/sdk/mx.sdk/mx_sdk_vm_impl.py @@ -2735,6 +2735,8 @@ def require_svm(components): self.jvm_jars = [] self.jvm_modules = [] self.jvm_libs = [] + self.pre_extracted_libs = {} + self.native_image_configs = [] # type: List[mx_sdk_vm.AbstractNativeImageConfig] other_comp_names = [] dependencies = component.standalone_dependencies_enterprise if svm_support.is_ee_supported() else component.standalone_dependencies @@ -2778,13 +2780,15 @@ def add_jars_from_component(comp, force_modules_as_jars=False): raise mx.warn(f"JVM standalones do not yet support components with `jvmci_parent_jars` outside of the included JVM.\n Component '{comp.name}' adds '{comp.jvmci_parent_jars}', which is skipped") for jar_dist in jar_dists: - layout.setdefault(default_jvm_jars_dir if force_modules_as_jars else default_jvm_modules_dir, []).append({ - 'source_type': 'dependency', - 'dependency': jar_dist, - 'exclude': [], - 'path': None, - }) - (self.jvm_jars if force_modules_as_jars else self.jvm_modules).append(jar_dist) + component_list = (self.jvm_jars if force_modules_as_jars else self.jvm_modules) + if jar_dist not in component_list: + layout.setdefault(default_jvm_jars_dir if force_modules_as_jars else default_jvm_modules_dir, []).append({ + 'source_type': 'dependency', + 'dependency': jar_dist, + 'exclude': [], + 'path': None, + }) + component_list.append(jar_dist) def add_files_from_component(comp, is_main, path_prefix, excluded_paths, force_modules_as_jars=False): """ @@ -2798,11 +2802,12 @@ def add_files_from_component(comp, is_main, path_prefix, excluded_paths, force_m """ launcher_configs = _get_launcher_configs(comp) library_configs = _get_library_configs(comp) + self.native_image_configs += launcher_configs + library_configs if self.is_jvm: add_jars_from_component(comp, force_modules_as_jars) - for lib_dist in comp.support_libraries_distributions: + for lib_dist in [ld for ld in comp.support_libraries_distributions if ld not in self.jvm_libs]: layout.setdefault(default_jvm_libs_dir, []).append({ 'source_type': 'extracted-dependency', 'dependency': lib_dist, @@ -2894,7 +2899,7 @@ def add_files_from_component(comp, is_main, path_prefix, excluded_paths, force_m 'path': None, }) if is_main: - for jar_distribution in library_config.jar_distributions: + for jar_distribution in [j for j in library_config.jar_distributions if j not in self.jvm_modules]: layout.setdefault(default_jvm_modules_dir, []).append({ 'source_type': 'dependency', 'dependency': jar_distribution, @@ -2902,6 +2907,7 @@ def add_files_from_component(comp, is_main, path_prefix, excluded_paths, force_m 'path': None, }) self.jvm_modules.append(jar_distribution) + for language, path_from_root in home_paths.items(): destination = path_prefix + library_config.destination relative_path_from_launcher_dir = relpath(path_from_root, dirname(destination)) @@ -2926,6 +2932,7 @@ def add_files_from_component(comp, is_main, path_prefix, excluded_paths, force_m dependency_path = details[0] excluded_paths = details[1] if len(details) > 1 else [] dependency = get_component(dependency_name, fatalIfMissing=True) + assert dependency not in added_components excluded_paths = [mx_subst.path_substitutions.substitute(excluded) for excluded in excluded_paths] dependency_path_prefix = base_dir + ((dependency_path + '/') if dependency_path else '') add_files_from_component(dependency, is_main=False, path_prefix=dependency_path_prefix, excluded_paths=excluded_paths) @@ -2933,6 +2940,7 @@ def add_files_from_component(comp, is_main, path_prefix, excluded_paths, force_m # Add files from the main standalone component. # Must be done for both Native and JVM Standalones. + assert component not in added_components add_files_from_component(component, is_main=True, path_prefix=base_dir, excluded_paths=[]) added_components.append(component) @@ -2961,6 +2969,13 @@ def add_files_from_component(comp, is_main, path_prefix, excluded_paths, force_m add_jars_from_component(main_component_dependency) for boot_jar in main_component_dependency.boot_jars: mx.warn("Component '{}' declares '{}' as 'boot_jar', which is ignored by the build process of the '{}' {} standalone".format(main_component_dependency.name, boot_jar, 'java' if self.is_jvm else 'native', name)) + for jvm_lib_description in GraalVmStandaloneComponent.pre_extracted_lib_description(main_component_dependency): + layout_dict = jvm_lib_description['layout_dict'] + jvm_lib_id = layout_dict['dependency'] + '/' + layout_dict['path'] + if jvm_lib_id not in self.jvm_libs: + layout.setdefault(default_jvm_libs_dir, []).append(layout_dict) + self.pre_extracted_libs[jvm_lib_description['name']] = basename(mx_subst.string_substitutions.substitute(layout_dict['path'])) + self.jvm_libs.append(jvm_lib_id) added_components.append(main_component_dependency) # Add the JVM. @@ -2974,13 +2989,14 @@ def add_files_from_component(comp, is_main, path_prefix, excluded_paths, force_m # Add jars of components that must be on the module path. for default_module_component in GraalVmStandaloneComponent.default_module_components(): for dist in default_module_component.jar_distributions + default_module_component.boot_jars + default_module_component.jvmci_parent_jars: - layout.setdefault(default_jvm_modules_dir, []).append({ - 'source_type': 'dependency', - 'dependency': dist, - 'exclude': [], - 'path': None, - }) - self.jvm_modules.append(dist) + if dist not in self.jvm_modules: + layout.setdefault(default_jvm_modules_dir, []).append({ + 'source_type': 'dependency', + 'dependency': dist, + 'exclude': [], + 'path': None, + }) + self.jvm_modules.append(dist) # Add LibGraal. lg_component = _get_libgraal_component() @@ -3016,7 +3032,7 @@ def add_files_from_component(comp, is_main, path_prefix, excluded_paths, force_m # Here we add `support_libraries_distributions` to the `jvmLibs` directory. # For the other component dependencies, this is done as part of `add_files_from_component()`. for jvm_component in GraalVmStandaloneComponent.default_jvm_components(): - for lib_dist in jvm_component.support_libraries_distributions: + for lib_dist in [ld for ld in jvm_component.support_libraries_distributions if ld not in self.jvm_libs]: layout.setdefault(default_jvm_libs_dir, []).append({ 'source_type': 'extracted-dependency', 'dependency': lib_dist, @@ -3025,6 +3041,10 @@ def add_files_from_component(comp, is_main, path_prefix, excluded_paths, force_m }) self.jvm_libs.append(lib_dist) + for native_image_config in self.native_image_configs: + for lib_name, lib_file_name in self.pre_extracted_libs.items(): + native_image_config.add_relative_extracted_lib_path(lib_name, join('jvmlibs', lib_file_name)) + mx.logvv("{} standalone '{}' has layout:\n{}".format('Java' if self.is_jvm else 'Native', name, pprint.pformat(layout))) self.maven = _graalvm_maven_attributes(tag='standalone') @@ -3067,6 +3087,36 @@ def default_module_components(): return default_components + @staticmethod + def pre_extracted_lib_description(comp): + """ + Descriptions of libraries that we don't want to extract to the cache on first usage. Added only if the + corresponding component is part of the JVM Standalone. + :type comp: mx_sdk_vm.GraalVmComponent + :rtype: list[dict] + """ + jvm_lib_descriptions = { + 'Truffle NFI': [{ + 'name': 'truffle.nfi.library', + 'layout_dict': { + 'source_type': 'extracted-dependency', + 'dependency': 'truffle:TRUFFLE_NFI_NATIVE', + 'exclude': [], + 'path': 'bin/', + }, + }], + 'Truffle API': [{ + 'name': 'truffle.attach.library', + 'layout_dict': { + 'source_type': 'extracted-dependency', + 'dependency': 'truffle:TRUFFLE_RUNTIME', + 'exclude': [], + 'path': 'META-INF/resources/engine/libtruffleattach///bin/', + }, + }], + } + return jvm_lib_descriptions.get(comp.name, {}) + def get_artifact_metadata(self): return {'type': 'standalone', 'edition': get_graalvm_edition(), 'project': _project_name} @@ -3394,6 +3444,17 @@ def escaped_relpath(path): '-DLAUNCHER_LANG_HOME_NAMES="{\\"' + '\\", \\"'.join(lang_home_names) + '\\"}"', '-DLAUNCHER_LANG_HOME_PATHS="{\\"' + '\\", \\"'.join(bin_home_paths) + '\\"}"', ] + extracted_lib_names = [] + extracted_lib_paths = [] + for extracted_lib_name, extracted_lib_path in self.language_library_config.relative_extracted_lib_paths.items(): + bin_lib_path = escaped_relpath(join(self.jre_base, '..', extracted_lib_path)) + extracted_lib_names.append(escaped_path(extracted_lib_name)) + extracted_lib_paths.append(bin_lib_path) + if extracted_lib_names: + _dynamic_cflags += [ + '-DLAUNCHER_EXTRACTED_LIB_NAMES="{\\"' + '\\", \\"'.join(extracted_lib_names) + '\\"}"', + '-DLAUNCHER_EXTRACTED_LIB_PATHS="{\\"' + '\\", \\"'.join(extracted_lib_paths) + '\\"}"', + ] if len(self.language_library_config.default_vm_args) > 0: _dynamic_cflags += ['-DLAUNCHER_DEFAULT_VM_ARGS="{\\"' + '\\", \\"'.join(self.language_library_config.default_vm_args) + '\\"}"'] diff --git a/sdk/src/org.graalvm.launcher.native/src/launcher.cc b/sdk/src/org.graalvm.launcher.native/src/launcher.cc index 4e63ba720bb0..9ebbdfd820a7 100644 --- a/sdk/src/org.graalvm.launcher.native/src/launcher.cc +++ b/sdk/src/org.graalvm.launcher.native/src/launcher.cc @@ -458,6 +458,21 @@ void parse_vm_options(int argc, char **argv, std::string exeDir, JavaVMInitArgs } #endif + #if defined(LAUNCHER_EXTRACTED_LIB_NAMES) && defined(LAUNCHER_EXTRACTED_LIB_PATHS) + if (jvmMode) { + const char *extractedLibNames[] = LAUNCHER_EXTRACTED_LIB_NAMES; + const char *extractedLibPaths[] = LAUNCHER_EXTRACTED_LIB_PATHS; + int extractedLibCnt = sizeof(extractedLibNames) / sizeof(*extractedLibNames); + for (int i = 0; i < extractedLibCnt; i++) { + std::stringstream ss; + std::stringstream relativePath; + relativePath << exeDir << DIR_SEP_STR << extractedLibPaths[i]; + ss << "-D" << extractedLibNames[i] << "=" << canonicalize(relativePath.str()); + vmArgs.push_back(ss.str()); + } + } + #endif + /* Handle launcher default vm arguments. We apply these first, so they can be overridden by explicit arguments on the commandline. */ #ifdef LAUNCHER_DEFAULT_VM_ARGS diff --git a/substratevm/CHANGELOG.md b/substratevm/CHANGELOG.md index b5e0b19aa474..fee70eb13569 100644 --- a/substratevm/CHANGELOG.md +++ b/substratevm/CHANGELOG.md @@ -2,6 +2,9 @@ This changelog summarizes major changes to GraalVM Native Image. +## GraalVM for JDK 22 (Internal Version 24.0.0) +* (GR-48304) Red Hat added support for the JFR event ThreadAllocationStatistics. + ## GraalVM for JDK 21 (Internal Version 23.1.0) * (GR-35746) Lower the default aligned chunk size from 1 MB to 512 KB for the serial and epsilon GCs, reducing memory usage and image size in many cases. * (GR-45841) BellSoft added support for the JFR event ThreadCPULoad. diff --git a/substratevm/mx.substratevm/suite.py b/substratevm/mx.substratevm/suite.py index 7d02e944cae5..01390484a55a 100644 --- a/substratevm/mx.substratevm/suite.py +++ b/substratevm/mx.substratevm/suite.py @@ -186,7 +186,7 @@ "requiresConcealed" : { "java.base" : ["jdk.internal.module"], }, - "javaCompliance" : "17+", + "javaCompliance" : "21+", "annotationProcessors": [ "compiler:GRAAL_PROCESSOR", ], @@ -206,7 +206,7 @@ "jdk.vm.ci.meta", ] }, - "javaCompliance" : "17+", + "javaCompliance" : "21+", "annotationProcessors": [ "compiler:GRAAL_PROCESSOR", ], @@ -224,7 +224,7 @@ "requires" : [ "java.compiler" # javax.annotation.processing.* ], - "javaCompliance" : "17+", + "javaCompliance" : "21+", "checkstyle" : "com.oracle.svm.core", "workingSets" : "SVM", "jacoco" : "exclude", @@ -294,7 +294,7 @@ "jdk.vm.ci.code", ], }, - "javaCompliance" : "17+", + "javaCompliance" : "21+", "checkstyleVersion" : "10.7.0", "annotationProcessors": [ "compiler:GRAAL_PROCESSOR", @@ -327,7 +327,7 @@ ], }, "checkstyle": "com.oracle.svm.core", - "javaCompliance" : "17+", + "javaCompliance" : "21+", "annotationProcessors": [ "compiler:GRAAL_PROCESSOR", "SVM_PROCESSOR", @@ -348,7 +348,7 @@ ], }, "checkstyle": "com.oracle.svm.core", - "javaCompliance" : "17+", + "javaCompliance" : "21+", "annotationProcessors": [ "compiler:GRAAL_PROCESSOR", "SVM_PROCESSOR", @@ -368,7 +368,7 @@ ], }, "checkstyle": "com.oracle.svm.core", - "javaCompliance" : "17+", + "javaCompliance" : "21+", "annotationProcessors": [ "compiler:GRAAL_PROCESSOR", "SVM_PROCESSOR", @@ -388,7 +388,7 @@ ], }, "checkstyle": "com.oracle.svm.core", - "javaCompliance" : "17+", + "javaCompliance" : "21+", "annotationProcessors": [ "compiler:GRAAL_PROCESSOR", "SVM_PROCESSOR", @@ -415,7 +415,7 @@ ], }, "checkstyle": "com.oracle.svm.core", - "javaCompliance" : "17+", + "javaCompliance" : "21+", "annotationProcessors": [ "compiler:GRAAL_PROCESSOR", "SVM_PROCESSOR", @@ -442,7 +442,7 @@ ], }, "checkstyle": "com.oracle.svm.core", - "javaCompliance" : "17+", + "javaCompliance" : "21+", "annotationProcessors": [ "compiler:GRAAL_PROCESSOR", "SVM_PROCESSOR", @@ -465,7 +465,7 @@ ], }, "checkstyle": "com.oracle.svm.core", - "javaCompliance" : "17+", + "javaCompliance" : "21+", "annotationProcessors": [ "compiler:GRAAL_PROCESSOR", "SVM_PROCESSOR", @@ -494,7 +494,7 @@ ] }, "checkstyle": "com.oracle.svm.core", - "javaCompliance" : "17+", + "javaCompliance" : "21+", "annotationProcessors": [ "compiler:GRAAL_PROCESSOR", ], @@ -517,7 +517,7 @@ ] }, "checkstyle": "com.oracle.svm.core", - "javaCompliance" : "17+", + "javaCompliance" : "21+", "annotationProcessors": [ "compiler:GRAAL_PROCESSOR", ], @@ -547,7 +547,7 @@ "annotationProcessors": [ "compiler:GRAAL_PROCESSOR", ], - "javaCompliance" : "17+", + "javaCompliance" : "21+", "spotbugs": "false", "jacoco" : "exclude", }, @@ -565,7 +565,7 @@ ] }, "checkstyle": "com.oracle.svm.core", - "javaCompliance" : "17+", + "javaCompliance" : "21+", "annotationProcessors": [ "compiler:GRAAL_PROCESSOR", ], @@ -626,7 +626,7 @@ "jdk.jfr.internal.jfc", ], }, - "javaCompliance" : "17+", + "javaCompliance" : "21+", "checkstyleVersion": "10.7.0", "annotationProcessors": [ "compiler:GRAAL_PROCESSOR", @@ -836,7 +836,7 @@ "compiler:GRAAL_PROCESSOR", "SVM_PROCESSOR", ], - "javaCompliance" : "17+", + "javaCompliance" : "21+", "spotbugs": "false", "jacoco" : "exclude", }, @@ -853,7 +853,7 @@ "compiler:GRAAL_PROCESSOR", "SVM_PROCESSOR", ], - "javaCompliance" : "17+", + "javaCompliance" : "21+", "spotbugs": "false", "jacoco" : "exclude", }, @@ -874,7 +874,7 @@ "compiler:GRAAL_PROCESSOR", "SVM_PROCESSOR", ], - "javaCompliance" : "17+", + "javaCompliance" : "21+", "spotbugs": "false", "jacoco" : "exclude", }, @@ -907,7 +907,7 @@ "compiler:GRAAL_PROCESSOR", "SVM_PROCESSOR", ], - "javaCompliance" : "17+", + "javaCompliance" : "21+", "spotbugs": "false", "jacoco" : "exclude", }, @@ -926,7 +926,7 @@ "compiler:GRAAL_PROCESSOR", "SVM_PROCESSOR", ], - "javaCompliance" : "17+", + "javaCompliance" : "21+", "spotbugs": "false", "testProject": True, "jacoco" : "exclude", @@ -937,7 +937,7 @@ "sourceDirs" : ["src"], "dependencies" : ["sdk:NATIVEIMAGE"], "checkstyle" : "com.oracle.svm.hosted", - "javaCompliance" : "17+", + "javaCompliance" : "21+", "workingSets" : "SVM", "spotbugs" : "false", "jacoco" : "exclude", @@ -961,7 +961,7 @@ ], }, "checkstyle" : "com.oracle.svm.hosted", - "javaCompliance" : "17+", + "javaCompliance" : "21+", "annotationProcessors": [ "compiler:GRAAL_PROCESSOR", "SVM_PROCESSOR", @@ -988,7 +988,7 @@ ], }, "checkstyle": "com.oracle.svm.hosted", - "javaCompliance" : "17+", + "javaCompliance" : "21+", "annotationProcessors": [ "compiler:GRAAL_PROCESSOR", "SVM_PROCESSOR", @@ -1019,7 +1019,7 @@ "compiler:GRAAL_PROCESSOR", "SVM_PROCESSOR", ], - "javaCompliance" : "17+", + "javaCompliance" : "21+", "spotbugs": "false", "testProject": True, "jacoco" : "exclude", @@ -1032,7 +1032,7 @@ "com.oracle.svm.util", ], "checkstyle": "com.oracle.svm.core", - "javaCompliance" : "17+", + "javaCompliance" : "21+", "annotationProcessors": [ "compiler:GRAAL_PROCESSOR", "SVM_PROCESSOR", @@ -1048,7 +1048,7 @@ "sdk:NATIVEIMAGE", ], "checkstyle": "com.oracle.svm.core", - "javaCompliance" : "17+", + "javaCompliance" : "21+", "annotationProcessors": [ "compiler:GRAAL_PROCESSOR", "SVM_PROCESSOR", @@ -1075,7 +1075,7 @@ ] }, "checkstyle": "com.oracle.svm.hosted", - "javaCompliance" : "17+", + "javaCompliance" : "21+", "annotationProcessors": [ "compiler:GRAAL_PROCESSOR", "SVM_PROCESSOR", @@ -1087,7 +1087,7 @@ "com.oracle.svm.truffle.nfi.none": { "subDir": "src", "sourceDirs": ["src"], - "javaCompliance" : "17+", + "javaCompliance" : "21+", "workingSets": "SVM", "jacoco" : "exclude", }, @@ -1099,7 +1099,7 @@ "com.oracle.svm.truffle", ], "checkstyle": "com.oracle.svm.hosted", - "javaCompliance" : "17+", + "javaCompliance" : "21+", "annotationProcessors": [ "compiler:GRAAL_PROCESSOR", "SVM_PROCESSOR", @@ -1117,7 +1117,7 @@ "com.oracle.svm.core.posix", ], "checkstyle": "com.oracle.svm.hosted", - "javaCompliance" : "17+", + "javaCompliance" : "21+", "annotationProcessors": [ "compiler:GRAAL_PROCESSOR", "SVM_PROCESSOR", @@ -1135,7 +1135,7 @@ "com.oracle.svm.core.windows", ], "checkstyle": "com.oracle.svm.hosted", - "javaCompliance" : "17+", + "javaCompliance" : "21+", "annotationProcessors": [ "compiler:GRAAL_PROCESSOR", "SVM_PROCESSOR", @@ -1157,7 +1157,7 @@ "com.oracle.svm.graal", ], "checkstyle": "com.oracle.svm.core", - "javaCompliance" : "17+", + "javaCompliance" : "21+", "annotationProcessors": [ "compiler:GRAAL_PROCESSOR", "SVM_PROCESSOR", @@ -1179,7 +1179,7 @@ "com.oracle.svm.hosted", ], "checkstyle": "com.oracle.svm.core", - "javaCompliance" : "17+", + "javaCompliance" : "21+", "annotationProcessors" : [ "compiler:GRAAL_PROCESSOR", "SVM_PROCESSOR", @@ -1212,7 +1212,7 @@ ] }, "checkstyle" : "com.oracle.svm.hosted", - "javaCompliance" : "17+", + "javaCompliance" : "21+", "annotationProcessors": [ "truffle:TRUFFLE_LIBGRAAL_PROCESSOR", "compiler:GRAAL_PROCESSOR", @@ -1240,7 +1240,7 @@ "workingSets": "SVM", "annotationProcessors": [ ], - "javaCompliance" : "17+", + "javaCompliance" : "21+", "spotbugs": "false", "jacoco" : "exclude", }, @@ -1259,7 +1259,7 @@ "compiler:GRAAL_PROCESSOR", "SVM_PROCESSOR", ], - "javaCompliance" : "17+", + "javaCompliance" : "21+", "spotbugs": "false", "jacoco" : "exclude", }, @@ -1286,7 +1286,7 @@ "compiler:GRAAL_PROCESSOR", "SVM_PROCESSOR", ], - "javaCompliance" : "17+", + "javaCompliance" : "21+", "spotbugs": "false", "jacoco" : "exclude", }, @@ -1312,7 +1312,7 @@ "compiler:GRAAL_PROCESSOR", "SVM_PROCESSOR", ], - "javaCompliance" : "17+", + "javaCompliance" : "21+", "spotbugs": "false", "jacoco" : "exclude", }, @@ -1336,7 +1336,7 @@ "compiler:GRAAL_PROCESSOR", "SVM_PROCESSOR", ], - "javaCompliance" : "17+", + "javaCompliance" : "21+", "jacoco" : "exclude", }, }, @@ -1483,6 +1483,7 @@ "* to org.graalvm.nativeimage.builder", ], }, + "noMavenJavadoc": True, "maven": { "tag": ["default", "public"], }, @@ -1511,6 +1512,7 @@ "static hamcrest", ] }, + "noMavenJavadoc": True, "maven": { "tag": ["default", "public"], }, @@ -1542,6 +1544,7 @@ ], } }, + "noMavenJavadoc": True, "maven": { "tag": ["default", "public"], }, @@ -1635,7 +1638,7 @@ "GRAAL_HOTSPOT_LIBRARY": { "subDir": "src", "description" : "SubstrateVM HotSpot Graal library support", - "javaCompliance" : "17+", + "javaCompliance" : "21+", "dependencies": [ "com.oracle.svm.graal.hotspot.libgraal", ], @@ -1671,6 +1674,7 @@ ], }, "description" : "SubstrateVM image builder native components", + "noMavenJavadoc": True, "maven": { "tag": ["default", "public"], }, @@ -1799,6 +1803,7 @@ "com.oracle.svm.common.option to org.graalvm.nativeimage.pointsto,org.graalvm.nativeimage.builder,org.graalvm.nativeimage.driver,org.graalvm.nativeimage.foreign,org.graalvm.truffle.runtime.svm,com.oracle.truffle.enterprise.svm", ], }, + "noMavenJavadoc": True, "maven": { "tag": ["default", "public"], }, @@ -1854,6 +1859,7 @@ ], } }, + "noMavenJavadoc": True, "maven": { "tag": ["default", "public"], }, @@ -1896,6 +1902,7 @@ ] } }, + "noMavenJavadoc": True, "maven": { "tag": ["default", "public"], }, @@ -2105,6 +2112,7 @@ "description" : "Truffle TCK", "dependencies" : ["com.oracle.svm.truffle.tck"], "distDependencies" : ["SVM", "truffle:TRUFFLE_RUNTIME"], + "noMavenJavadoc": True, "maven" : { "tag": ["default", "public"], }, diff --git a/substratevm/src/com.oracle.graal.pointsto.standalone/src/com/oracle/graal/pointsto/standalone/heap/StandaloneHeapSnapshotVerifier.java b/substratevm/src/com.oracle.graal.pointsto.standalone/src/com/oracle/graal/pointsto/standalone/heap/StandaloneHeapSnapshotVerifier.java index 5a747d086c5a..98926736963a 100644 --- a/substratevm/src/com.oracle.graal.pointsto.standalone/src/com/oracle/graal/pointsto/standalone/heap/StandaloneHeapSnapshotVerifier.java +++ b/substratevm/src/com.oracle.graal.pointsto.standalone/src/com/oracle/graal/pointsto/standalone/heap/StandaloneHeapSnapshotVerifier.java @@ -31,6 +31,7 @@ import com.oracle.graal.pointsto.heap.HeapSnapshotVerifier; import com.oracle.graal.pointsto.heap.ImageHeap; import com.oracle.graal.pointsto.heap.ImageHeapScanner; +import com.oracle.graal.pointsto.infrastructure.UniverseMetaAccess; import com.oracle.graal.pointsto.standalone.StandaloneObjectScanner; import com.oracle.graal.pointsto.util.CompletionExecutor; @@ -40,7 +41,7 @@ public StandaloneHeapSnapshotVerifier(BigBang bb, ImageHeap imageHeap, ImageHeap } @Override - protected ObjectScanner installObjectScanner(CompletionExecutor executor) { + protected ObjectScanner installObjectScanner(UniverseMetaAccess metaAccess, CompletionExecutor executor) { StandaloneImageHeapScanner standaloneImageHeapScanner = (StandaloneImageHeapScanner) this.scanner; return new StandaloneObjectScanner(bb, executor, scannedObjects, new ScanningObserver(), standaloneImageHeapScanner.getShouldScanConstant(), standaloneImageHeapScanner.getShouldScanField()); diff --git a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/AbstractAnalysisEngine.java b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/AbstractAnalysisEngine.java index eab389132979..b03dbedd7f02 100644 --- a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/AbstractAnalysisEngine.java +++ b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/AbstractAnalysisEngine.java @@ -204,10 +204,10 @@ public void runAnalysis(DebugContext debugContext, Function fieldComparator, Co fields = fieldsList; } for (AnalysisField field : fields) { - if (Modifier.isStatic(field.getModifiers()) && field.getJavaKind() == JavaKind.Object && field.isRead()) { + if (Modifier.isStatic(field.getModifiers()) && field.isRead()) { execute(() -> scanRootField(field)); } } @@ -157,7 +157,9 @@ protected void scanField(AnalysisField field, JavaConstant receiver, ScanReason /* The value is not available yet. */ return; } - JavaConstant fieldValue = bb.getUniverse().getHeapScanner().readFieldValue(field, receiver); + assert isUnwrapped(receiver); + + JavaConstant fieldValue = readFieldValue(field, receiver); if (fieldValue == null) { StringBuilder backtrace = new StringBuilder(); buildObjectBacktrace(bb, reason, backtrace); @@ -179,6 +181,8 @@ protected void scanField(AnalysisField field, JavaConstant receiver, ScanReason * referenced elements are being scanned. */ scanConstant(fieldValue, reason); + } else if (fieldValue.getJavaKind().isNumericInteger()) { + scanningObserver.forPrimitiveFieldValue(receiver, field, fieldValue, reason); } } catch (UnsupportedFeatureException ex) { @@ -186,6 +190,30 @@ protected void scanField(AnalysisField field, JavaConstant receiver, ScanReason } } + protected JavaConstant readFieldValue(AnalysisField field, JavaConstant receiver) { + return bb.getConstantReflectionProvider().readFieldValue(field, receiver); + } + + /** + * Must unwrap the receiver if it is an ImageHeapConstant to scan the hosted value, if any, for + * verification, otherwise the verification just compares shadow heap with shadow heap for + * embedded roots, which is completely useless. + */ + private static JavaConstant maybeUnwrap(JavaConstant receiver) { + if (receiver instanceof ImageHeapConstant heapConstant && heapConstant.getHostedObject() != null) { + return heapConstant.getHostedObject(); + } + return receiver; + } + + private static boolean isUnwrapped(JavaConstant receiver) { + if (receiver instanceof ImageHeapConstant heapConstant) { + // Non hosted backed ImageHeapConstant is considered unwrapped + return heapConstant.getHostedObject() == null; + } + return true; + } + /** * Scans constant arrays, one element at the time. * @@ -193,6 +221,7 @@ protected void scanField(AnalysisField field, JavaConstant receiver, ScanReason */ protected final void scanArray(JavaConstant array, ScanReason prevReason) { + assert isUnwrapped(array); AnalysisType arrayType = bb.getMetaAccess().lookupJavaType(array); ScanReason reason = new ArrayScan(arrayType, array, prevReason); @@ -246,13 +275,14 @@ public void scanConstant(JavaConstant value, ScanReason reason) { bb.registerTypeAsInHeap(bb.getMetaAccess().lookupJavaType(value), reason); return; } - Object valueObj = (value instanceof ImageHeapConstant) ? value : constantAsObject(bb, value); + JavaConstant unwrappedValue = maybeUnwrap(value); + Object valueObj = unwrappedValue instanceof ImageHeapConstant ? unwrappedValue : constantAsObject(bb, unwrappedValue); if (scannedObjects.putAndAcquire(valueObj) == null) { try { - scanningObserver.forScannedConstant(value, reason); + scanningObserver.forScannedConstant(unwrappedValue, reason); } finally { scannedObjects.release(valueObj); - WorklistEntry worklistEntry = new WorklistEntry(value, reason); + WorklistEntry worklistEntry = new WorklistEntry(unwrappedValue, reason); if (executor != null) { executor.execute(debug -> doScan(worklistEntry)); } else { @@ -339,12 +369,22 @@ public static String asString(BigBang bb, JavaConstant constant, boolean appendT return "null"; } AnalysisType type = bb.getMetaAccess().lookupJavaType(constant); - if (constant instanceof ImageHeapConstant) { - // Checkstyle: allow Class.getSimpleName - return constant.getClass().getSimpleName() + "<" + type.toJavaName() + ">"; - // Checkstyle: disallow Class.getSimpleName + JavaConstant hosted = constant; + if (constant instanceof ImageHeapConstant heapConstant) { + JavaConstant hostedObject = heapConstant.getHostedObject(); + if (hostedObject == null) { + // Checkstyle: allow Class.getSimpleName + return constant.getClass().getSimpleName() + "<" + type.toJavaName() + ">"; + // Checkstyle: disallow Class.getSimpleName + } + hosted = hostedObject; + } + + if (hosted.getJavaKind().isPrimitive()) { + return hosted.toValueString(); } - Object obj = constantAsObject(bb, constant); + + Object obj = constantAsObject(bb, hosted); String str = type.toJavaName() + '@' + Integer.toHexString(System.identityHashCode(obj)); if (appendToString) { try { @@ -381,7 +421,7 @@ private void doScan(WorklistEntry entry) { /* Scan constant's instance fields. */ for (ResolvedJavaField javaField : type.getInstanceFields(true)) { AnalysisField field = (AnalysisField) javaField; - if (field.getJavaKind() == JavaKind.Object && field.isRead()) { + if (field.isRead()) { assert !Modifier.isStatic(field.getModifiers()); scanField(field, entry.constant, entry.reason); } @@ -589,15 +629,21 @@ public String toString() { public static class ArrayScan extends ScanReason { final AnalysisType arrayType; + final int idx; public ArrayScan(AnalysisType arrayType, JavaConstant array, ScanReason previous) { + this(arrayType, array, previous, -1); + } + + public ArrayScan(AnalysisType arrayType, JavaConstant array, ScanReason previous, int idx) { super(previous, array); this.arrayType = arrayType; + this.idx = idx; } @Override public String toString(BigBang bb) { - return "indexing into array " + asString(bb, constant); + return "indexing into array " + asString(bb, constant) + (idx != -1 ? " at index " + idx : ""); } @Override diff --git a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/ObjectScanningObserver.java b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/ObjectScanningObserver.java index d208dbbd5e21..76cec2f397fb 100644 --- a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/ObjectScanningObserver.java +++ b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/ObjectScanningObserver.java @@ -51,6 +51,13 @@ default boolean forRelocatedPointerFieldValue(JavaConstant receiver, AnalysisFie return false; } + /** + * Hook for scanned value of primitive field. + */ + default boolean forPrimitiveFieldValue(JavaConstant receiver, AnalysisField field, JavaConstant fieldValue, ScanReason reason) { + return false; + } + /** * Hook for scanned null field value. */ 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 5d498cab83ae..ef8f0538f036 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 @@ -25,7 +25,6 @@ package com.oracle.graal.pointsto.heap; import static com.oracle.graal.pointsto.ObjectScanner.ScanReason; -import static com.oracle.graal.pointsto.ObjectScanner.asString; import java.util.Objects; import java.util.function.Consumer; @@ -38,6 +37,7 @@ import com.oracle.graal.pointsto.ObjectScanner; import com.oracle.graal.pointsto.ObjectScanner.ReusableSet; import com.oracle.graal.pointsto.ObjectScanningObserver; +import com.oracle.graal.pointsto.infrastructure.UniverseMetaAccess; import com.oracle.graal.pointsto.meta.AnalysisField; import com.oracle.graal.pointsto.meta.AnalysisType; import com.oracle.graal.pointsto.util.AnalysisError; @@ -73,40 +73,49 @@ public HeapSnapshotVerifier(BigBang bb, ImageHeap imageHeap, ImageHeapScanner sc verbosity = Options.HeapVerifierVerbosity.getValue(bb.getOptions()); } - public boolean requireAnalysisIteration(CompletionExecutor executor) throws InterruptedException { - info("Verifying the heap snapshot..."); + public boolean checkHeapSnapshot(UniverseMetaAccess metaAccess, CompletionExecutor executor, String phase, boolean forAnalysis) { + info("Verifying the heap snapshot %s%s ...", phase, (forAnalysis ? ", iteration " + iterations : "")); analysisModified = false; heapPatched = false; int reachableTypesBefore = bb.getUniverse().getReachableTypes(); iterations++; scannedObjects.reset(); - ObjectScanner objectScanner = installObjectScanner(executor); + ObjectScanner objectScanner = installObjectScanner(metaAccess, executor); executor.start(); scanTypes(objectScanner); objectScanner.scanBootImageHeapRoots(); - executor.complete(); + try { + executor.complete(); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } executor.shutdown(); - int verificationReachableTypes = bb.getUniverse().getReachableTypes() - reachableTypesBefore; if (heapPatched) { info("Heap verification patched the heap snapshot."); } else { info("Heap verification didn't find any heap snapshot modifications."); } - if (verificationReachableTypes > 0) { - info("Heap verification made " + verificationReachableTypes + " new types reachable."); - } else { - info("Heap verification didn't make any new types reachable."); - } - if (analysisModified) { - info("Heap verification modified the analysis state. Executing an additional analysis iteration."); - } else { - info("Heap verification didn't modify the analysis state. Heap state stabilized after " + iterations + " iterations."); - info("Exiting analysis."); + int verificationReachableTypes = bb.getUniverse().getReachableTypes() - reachableTypesBefore; + if (forAnalysis) { + if (verificationReachableTypes > 0) { + info("Heap verification made %s new types reachable.", verificationReachableTypes); + } else { + info("Heap verification didn't make any new types reachable."); + } + if (analysisModified) { + info("Heap verification modified the analysis state. Executing an additional analysis iteration."); + } else { + info("Heap verification didn't modify the analysis state. Heap state stabilized after %s iterations.", iterations); + } + } else if (analysisModified || verificationReachableTypes > 0) { + String error = analysisModified ? "modified the analysis state" : ""; + error += verificationReachableTypes > 0 ? (analysisModified ? " and " : "") + "made " + verificationReachableTypes + " new types reachable" : ""; + throw AnalysisError.shouldNotReachHere("Heap verification " + error + ". This is illegal at this stage."); } return analysisModified || verificationReachableTypes > 0; } - protected ObjectScanner installObjectScanner(CompletionExecutor executor) { + protected ObjectScanner installObjectScanner(@SuppressWarnings("unused") UniverseMetaAccess metaAccess, CompletionExecutor executor) { return new ObjectScanner(bb, executor, scannedObjects, new ScanningObserver()); } @@ -114,7 +123,6 @@ protected void scanTypes(@SuppressWarnings("unused") ObjectScanner objectScanner } public void cleanupAfterAnalysis() { - scannedObjects = null; } protected final class ScanningObserver implements ObjectScanningObserver { @@ -124,119 +132,121 @@ public ScanningObserver() { @Override public boolean forRelocatedPointerFieldValue(JavaConstant receiver, AnalysisField field, JavaConstant fieldValue, ScanReason reason) { - boolean result = scanner.getScanningObserver().forRelocatedPointerFieldValue(receiver, field, fieldValue, reason); - if (result) { - analysisModified = true; + boolean result = false; + ObjectScanningObserver scanningObserver = scanner.getScanningObserver(); + if (scanningObserver != null) { + result = scanningObserver.forRelocatedPointerFieldValue(receiver, field, fieldValue, reason); + if (result) { + analysisModified = true; + } } return result; } + @Override + public boolean forPrimitiveFieldValue(JavaConstant receiver, AnalysisField field, JavaConstant fieldValue, ScanReason reason) { + return verifyFieldValue(receiver, field, fieldValue, reason); + } + @Override public boolean forNullFieldValue(JavaConstant receiver, AnalysisField field, ScanReason reason) { - boolean result = scanner.getScanningObserver().forNullFieldValue(receiver, field, reason); - if (result) { - analysisModified = true; + boolean result = false; + ObjectScanningObserver scanningObserver = scanner.getScanningObserver(); + if (scanningObserver != null) { + result = scanningObserver.forNullFieldValue(receiver, field, reason); + if (result) { + analysisModified = true; + } } return result; } @Override - @SuppressWarnings({"unchecked", "rawtypes"}) public boolean forNonNullFieldValue(JavaConstant receiver, AnalysisField field, JavaConstant fieldValue, ScanReason reason) { + return verifyFieldValue(receiver, field, fieldValue, reason); + } + + private boolean verifyFieldValue(JavaConstant receiver, AnalysisField field, JavaConstant fieldValue, ScanReason reason) { + /* + * We don't care if a field in the shadow heap was not yet read, i.e., the future is not + * yet materialized. This can happen with lazy fields that become available but may have + * not yet been consumed. We simply execute the future, then compare the produced value. + */ if (field.isStatic()) { TypeData typeData = field.getDeclaringClass().getOrComputeData(); - Object fieldValueTask = typeData.getFieldValue(field); - if (fieldValueTask instanceof JavaConstant fieldSnapshot) { - verifyStaticFieldValue(typeData, field, maybeUnwrapSnapshot(fieldSnapshot, fieldValue instanceof ImageHeapConstant), fieldValue, reason); - } else if (fieldValueTask instanceof AnalysisFuture) { - AnalysisFuture future = (AnalysisFuture) fieldValueTask; - if (future.isDone()) { - JavaConstant fieldSnapshot = future.guardedGet(); - verifyStaticFieldValue(typeData, field, maybeUnwrapSnapshot(fieldSnapshot, fieldValue instanceof ImageHeapConstant), fieldValue, reason); - } else { - onStaticFieldNotComputed(field, fieldValue, reason); - } - } else { - throw AnalysisError.shouldNotReachHere("Unexpected field value " + fieldValueTask + "."); - } + JavaConstant fieldSnapshot = typeData.readFieldValue(field); + verifyStaticFieldValue(typeData, field, maybeUnwrapSnapshot(fieldSnapshot, fieldValue instanceof ImageHeapConstant), fieldValue, reason); } else { ImageHeapInstance receiverObject = (ImageHeapInstance) getReceiverObject(receiver, reason); - Object fieldValueTask = receiverObject.getFieldValue(field); - if (fieldValueTask instanceof JavaConstant fieldSnapshot) { - verifyInstanceFieldValue(field, receiverObject, maybeUnwrapSnapshot(fieldSnapshot, fieldValue instanceof ImageHeapConstant), fieldValue, reason); - } else if (fieldValueTask instanceof AnalysisFuture) { - AnalysisFuture future = (AnalysisFuture) fieldValueTask; - if (future.isDone()) { - JavaConstant fieldSnapshot = future.guardedGet(); - verifyInstanceFieldValue(field, receiverObject, maybeUnwrapSnapshot(fieldSnapshot, fieldValue instanceof ImageHeapConstant), fieldValue, reason); - } else { - /* - * There may be some instance fields not yet computed because the verifier - * can insert new objects for annotation proxy implementations when scanning - * types. The annotations are set lazily, based on reachability, since we - * only want annotations in the heap that are otherwise marked as used. - */ - Consumer onAnalysisModified = (deepReason) -> onInstanceFieldNotComputed(receiverObject, field, fieldValue, deepReason); - scanner.patchInstanceField(receiverObject, field, fieldValue, reason, onAnalysisModified).ensureDone(); - heapPatched = true; - } - } else { - throw AnalysisError.shouldNotReachHere("Unexpected field value " + fieldValueTask + "."); - } + JavaConstant fieldSnapshot = receiverObject.readFieldValue(field); + verifyInstanceFieldValue(field, receiver, receiverObject, maybeUnwrapSnapshot(fieldSnapshot, fieldValue instanceof ImageHeapConstant), fieldValue, reason); } return false; } private void verifyStaticFieldValue(TypeData typeData, AnalysisField field, JavaConstant fieldSnapshot, JavaConstant fieldValue, ScanReason reason) { if (!Objects.equals(fieldSnapshot, fieldValue)) { - Consumer onAnalysisModified = (deepReason) -> onStaticFieldMismatch(field, fieldSnapshot, fieldValue, deepReason); + 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(); heapPatched = true; } } - private void verifyInstanceFieldValue(AnalysisField field, ImageHeapInstance receiverObject, JavaConstant fieldSnapshot, JavaConstant fieldValue, ScanReason reason) { + private void verifyInstanceFieldValue(AnalysisField field, JavaConstant receiver, ImageHeapInstance receiverObject, JavaConstant fieldSnapshot, JavaConstant fieldValue, ScanReason reason) { if (!Objects.equals(fieldSnapshot, fieldValue)) { - Consumer onAnalysisModified = (deepReason) -> onInstanceFieldMismatch(receiverObject, field, fieldSnapshot, fieldValue, deepReason); + 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(); heapPatched = true; } } + private Consumer analysisModified(ScanReason reason, String format, Object... args) { + if (printAll()) { + warning(reason, format, args); + return (deepReason) -> analysisModified = true; + } else { + return (deepReason) -> { + analysisModified = true; + if (printWarning()) { + analysisWarning(deepReason, format, args); + } + }; + } + } + @Override public boolean forNullArrayElement(JavaConstant array, AnalysisType arrayType, int elementIndex, ScanReason reason) { - boolean result = scanner.getScanningObserver().forNullArrayElement(array, arrayType, elementIndex, reason); - if (result) { - analysisModified = true; + boolean result = false; + ObjectScanningObserver scanningObserver = scanner.getScanningObserver(); + if (scanningObserver != null) { + result = scanningObserver.forNullArrayElement(array, arrayType, elementIndex, reason); + if (result) { + analysisModified = true; + } } return result; } @Override public boolean forNonNullArrayElement(JavaConstant array, AnalysisType arrayType, JavaConstant elementValue, AnalysisType elementType, int index, ScanReason reason) { + /* + * We don't care if an array element in the shadow heap was not yet read, i.e., the + * future is not yet materialized. This can happen with values originating from lazy + * 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); - Object elementValueTask = arrayObject.getElement(index); - if (elementValueTask instanceof JavaConstant elementSnapshot) { - verifyArrayElementValue(elementValue, index, reason, arrayObject, elementSnapshot); - } else if (elementValueTask instanceof AnalysisFuture future) { - if (future.isDone()) { - JavaConstant elementSnapshot = (JavaConstant) future.guardedGet(); - verifyArrayElementValue(elementValue, index, reason, arrayObject, elementSnapshot); - } else { - /* There may be some array elements not yet computed. */ - Consumer onAnalysisModified = (deepReason) -> onArrayElementNotComputed(arrayObject, index, elementValue, deepReason); - scanner.patchArrayElement(arrayObject, index, elementValue, reason, onAnalysisModified).ensureDone(); - heapPatched = true; - } - } else { - throw AnalysisError.shouldNotReachHere("Unexpected element value " + elementValueTask + "."); - } + JavaConstant elementSnapshot = arrayObject.readElementValue(index); + verifyArrayElementValue(elementValue, index, reason, array, arrayObject, elementSnapshot); return false; } - private void verifyArrayElementValue(JavaConstant elementValue, int index, ScanReason reason, ImageHeapObjectArray arrayObject, JavaConstant elementSnapshot) { + private void verifyArrayElementValue(JavaConstant elementValue, int index, ScanReason reason, JavaConstant array, ImageHeapObjectArray arrayObject, JavaConstant elementSnapshot) { if (!Objects.equals(maybeUnwrapSnapshot(elementSnapshot, elementValue instanceof ImageHeapConstant), elementValue)) { - Consumer onAnalysisModified = (deepReason) -> onArrayElementMismatch(elementSnapshot, elementValue, deepReason); + 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(); heapPatched = true; } @@ -329,7 +339,9 @@ private void ensureTypeScanned(JavaConstant typeConstant, AnalysisType type, Sca @SuppressWarnings({"unchecked", "rawtypes"}) private void ensureTypeScanned(JavaConstant value, JavaConstant typeConstant, AnalysisType type, ScanReason reason) { - AnalysisError.guarantee(type.isReachable(), "The heap snapshot verifier discovered a type not marked as reachable: %s", type); + if (!type.isReachable()) { + error(reason, "The heap snapshot verifier discovered a type not marked as reachable: %s", type); + } Object task = imageHeap.getSnapshot(typeConstant); /* Make sure the DynamicHub value is scanned. */ if (task == null) { @@ -378,47 +390,6 @@ private void onTaskForClassConstantNotDone(JavaConstant object, AnalysisType typ } } - private void onArrayElementMismatch(JavaConstant elementSnapshot, JavaConstant elementValue, ScanReason reason) { - analysisModified = true; - if (printWarning()) { - analysisWarning(reason, "Value mismatch for array element %n snapshot: %s %n new value: %s %n", elementSnapshot, elementValue); - } - } - - private void onStaticFieldMismatch(AnalysisField field, JavaConstant fieldSnapshot, JavaConstant fieldValue, ScanReason reason) { - analysisModified = true; - if (printWarning()) { - analysisWarning(reason, "Value mismatch for static field %s %n snapshot: %s %n new value: %s %n", field, fieldSnapshot, fieldValue); - } - } - - private void onStaticFieldNotComputed(AnalysisField field, JavaConstant fieldValue, ScanReason reason) { - error(reason, "Snapshot not yet computed for static field %s %n new value: %s %n", field, fieldValue); - } - - private void onInstanceFieldMismatch(ImageHeapInstance receiver, AnalysisField field, JavaConstant fieldSnapshot, JavaConstant fieldValue, ScanReason reason) { - analysisModified = true; - if (printWarning()) { - analysisWarning(reason, "Value mismatch for instance field %s of %s %n snapshot: %s %n new value: %s %n", - field, receiver, fieldSnapshot, fieldValue); - } - } - - private void onInstanceFieldNotComputed(ImageHeapInstance receiver, AnalysisField field, JavaConstant fieldValue, ScanReason reason) { - analysisModified = true; - if (printWarning()) { - analysisWarning(reason, "Snapshot not yet computed for instance field %s of %s %n new value: %s %n", - field, receiver, fieldValue); - } - } - - private void onArrayElementNotComputed(ImageHeapArray array, int index, JavaConstant elementValue, ScanReason reason) { - analysisModified = true; - if (printWarning()) { - analysisWarning(reason, "Snapshot not yet computed for array %s, index %s, %n new value: %s %n", array, index, elementValue); - } - } - private static final int INFO = 1; private static final int WARNING = 2; private static final int ALL = 3; @@ -435,14 +406,14 @@ private boolean printAll() { return verbosity >= ALL; } - private void info(String info) { + private void info(String format, Object... args) { if (printInfo()) { - LogUtils.info(info); + LogUtils.info(String.format(format, args)); } } private void warning(ScanReason reason, String format, Object... args) { - LogUtils.warning(message(reason, format, "Object was reached by", args)); + LogUtils.warning(message(reason, format, "Value was reached by", args)); } private void analysisWarning(ScanReason reason, String format, Object... args) { @@ -458,18 +429,22 @@ private String message(ScanReason reason, String format, Object... args) { } private String message(ScanReason reason, String format, String backtraceHeader, Object... args) { - String message = format(format, args); + String message = format(bb, format, args); StringBuilder objectBacktrace = new StringBuilder(); ObjectScanner.buildObjectBacktrace(bb, reason, objectBacktrace, backtraceHeader); message += objectBacktrace; return message; } - private String format(String msg, Object... args) { + private String asString(JavaConstant array) { + return ObjectScanner.asString(bb, array, false); + } + + public static String format(BigBang bb, String msg, Object... args) { if (args != null) { for (int i = 0; i < args.length; ++i) { if (args[i] instanceof JavaConstant) { - args[i] = asString(bb, (JavaConstant) args[i]); + args[i] = ObjectScanner.asString(bb, (JavaConstant) args[i]); } else if (args[i] instanceof AnalysisField) { args[i] = ((AnalysisField) args[i]).format("%H.%n"); } 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 4f5fcde13628..bf8f16b84a93 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 @@ -34,6 +34,7 @@ import org.graalvm.collections.EconomicMap; import org.graalvm.collections.MapCursor; import org.graalvm.compiler.api.replacements.SnippetReflectionProvider; +import org.graalvm.compiler.core.common.SuppressFBWarnings; import org.graalvm.compiler.debug.GraalError; import org.graalvm.word.WordBase; @@ -306,6 +307,9 @@ private Optional maybeReplace(JavaConstant constant, ScanReason re } else if (unwrapped instanceof ImageHeapConstant) { throw GraalError.shouldNotReachHere(formatReason("Double wrapping of constant. Most likely, the reachability analysis code itself is seen as reachable.", reason)); // ExcludeFromJacocoGeneratedReport } + if (unwrapped instanceof String || unwrapped instanceof Enum) { + forceHashCodeComputation(unwrapped); + } /* Run all registered object replacers. */ if (constant.getJavaKind() == JavaKind.Object) { @@ -324,6 +328,15 @@ private Optional maybeReplace(JavaConstant constant, ScanReason re return Optional.empty(); } + /** + * For immutable Strings and other objects in the native image heap, force eager computation of + * the hash field. + */ + @SuppressFBWarnings(value = "RV_RETURN_VALUE_IGNORED", justification = "eager hash field computation") + public static void forceHashCodeComputation(Object object) { + object.hashCode(); + } + JavaConstant onFieldValueReachable(AnalysisField field, JavaConstant fieldValue, ScanReason reason, Consumer onAnalysisModified) { return onFieldValueReachable(field, null, ValueSupplier.eagerValue(fieldValue), reason, onAnalysisModified); } @@ -460,16 +473,18 @@ protected void onObjectReachable(ImageHeapConstant imageHeapConstant, ScanReason markTypeInstantiated(objectType, reason); if (imageHeapConstant instanceof ImageHeapObjectArray imageHeapArray) { + AnalysisType arrayType = (AnalysisType) imageHeapArray.getType(metaAccess); for (int idx = 0; idx < imageHeapArray.getLength(); idx++) { JavaConstant elementValue = imageHeapArray.readElementValue(idx); - markReachable(elementValue, reason, onAnalysisModified); - notifyAnalysis(imageHeapArray, (AnalysisType) imageHeapArray.getType(metaAccess), idx, reason, onAnalysisModified, elementValue); + ArrayScan arrayScanReason = new ArrayScan(arrayType, imageHeapArray, reason, idx); + markReachable(elementValue, arrayScanReason, onAnalysisModified); + notifyAnalysis(imageHeapArray, arrayType, idx, arrayScanReason, onAnalysisModified, elementValue); } } else if (imageHeapConstant instanceof ImageHeapInstance imageHeapInstance) { for (ResolvedJavaField javaField : objectType.getInstanceFields(true)) { AnalysisField field = (AnalysisField) javaField; if (field.isRead()) { - updateInstanceField(field, imageHeapInstance, reason, onAnalysisModified); + updateInstanceField(field, imageHeapInstance, new FieldScan(field, imageHeapInstance, reason), onAnalysisModified); } } } diff --git a/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/HeapImpl.java b/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/HeapImpl.java index b07b78290cb8..abd387622f11 100644 --- a/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/HeapImpl.java +++ b/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/HeapImpl.java @@ -319,6 +319,7 @@ static void logZapValues(Log log) { } @Override + @Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true) public int getClassCount() { return imageHeapInfo.dynamicHubCount; } diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/heap/Heap.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/heap/Heap.java index 9e7b2c151388..396347e5132f 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/heap/Heap.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/heap/Heap.java @@ -102,6 +102,7 @@ protected Heap() { public abstract boolean walkCollectedHeapObjects(ObjectVisitor visitor); /** Returns the number of classes in the heap (initialized as well as uninitialized). */ + @Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true) public abstract int getClassCount(); /** Visits all loaded classes in the heap (see {@link PredefinedClassesSupport}). */ diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/heap/PhysicalMemory.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/heap/PhysicalMemory.java index ccfbf728b12e..4fe58e816ae6 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/heap/PhysicalMemory.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/heap/PhysicalMemory.java @@ -30,6 +30,7 @@ import com.oracle.svm.core.Containers; import com.oracle.svm.core.SubstrateOptions; +import com.oracle.svm.core.Uninterruptible; import com.oracle.svm.core.jdk.UninterruptibleUtils.AtomicInteger; import com.oracle.svm.core.stack.StackOverflowCheck; import com.oracle.svm.core.thread.PlatformThreads; @@ -58,6 +59,7 @@ default boolean hasSize() { private static final UnsignedWord UNSET_SENTINEL = UnsignedUtils.MAX_VALUE; private static UnsignedWord cachedSize = UNSET_SENTINEL; + @Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true) public static boolean isInitialized() { return cachedSize != UNSET_SENTINEL; } @@ -106,6 +108,7 @@ public static UnsignedWord size() { * Returns the size of physical memory in bytes that has been previously cached. This method * must not be called if {@link #isInitialized()} is still false. */ + @Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true) public static UnsignedWord getCachedSize() { VMError.guarantee(isInitialized(), "Cached physical memory size is not available"); return cachedSize; diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/RuntimeModuleSupport.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/RuntimeModuleSupport.java index 0c90464fb5f1..89babece5ed1 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/RuntimeModuleSupport.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/RuntimeModuleSupport.java @@ -24,21 +24,22 @@ */ package com.oracle.svm.core.jdk; +import java.util.function.Function; + import org.graalvm.nativeimage.ImageSingletons; import org.graalvm.nativeimage.Platform; import org.graalvm.nativeimage.Platforms; +import com.oracle.svm.core.BuildPhaseProvider.AfterHostedUniverse; import com.oracle.svm.core.heap.UnknownObjectField; -import java.util.function.Function; - public final class RuntimeModuleSupport { public static RuntimeModuleSupport instance() { return ImageSingletons.lookup(RuntimeModuleSupport.class); } - @UnknownObjectField private ModuleLayer bootLayer; + @UnknownObjectField(availability = AfterHostedUniverse.class) private ModuleLayer bootLayer; @Platforms(Platform.HOSTED_ONLY.class) // private Function hostedToRuntimeModuleMapper; diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/management/SubstrateThreadMXBean.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/management/SubstrateThreadMXBean.java index 3b5497142393..ca6a29eafd4f 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/management/SubstrateThreadMXBean.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/management/SubstrateThreadMXBean.java @@ -31,7 +31,6 @@ import javax.management.ObjectName; -import com.oracle.svm.core.thread.ThreadCpuTimeSupport; import org.graalvm.nativeimage.ImageSingletons; import org.graalvm.nativeimage.Platform; import org.graalvm.nativeimage.Platforms; @@ -40,6 +39,7 @@ import com.oracle.svm.core.jdk.UninterruptibleUtils.AtomicInteger; import com.oracle.svm.core.jdk.UninterruptibleUtils.AtomicLong; import com.oracle.svm.core.thread.PlatformThreads; +import com.oracle.svm.core.thread.ThreadCpuTimeSupport; import sun.management.Util; @@ -128,6 +128,7 @@ public int getThreadCount() { } @Override + @Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true) public int getPeakThreadCount() { return peakThreadCount.get(); } @@ -138,11 +139,13 @@ public void resetPeakThreadCount() { } @Override + @Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true) public long getTotalStartedThreadCount() { return totalStartedThreadCount.get(); } @Override + @Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true) public int getDaemonThreadCount() { return daemonThreadCount.get(); } diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jfr/JfrEvent.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jfr/JfrEvent.java index d315af251a8e..9edd9e57e45f 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jfr/JfrEvent.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jfr/JfrEvent.java @@ -62,6 +62,7 @@ public final class JfrEvent { public static final JfrEvent JavaMonitorInflate = create("jdk.JavaMonitorInflate", true); public static final JfrEvent ObjectAllocationInNewTLAB = create("jdk.ObjectAllocationInNewTLAB", false); public static final JfrEvent GCHeapSummary = create("jdk.GCHeapSummary", false); + public static final JfrEvent ThreadAllocationStatistics = create("jdk.ThreadAllocationStatistics", false); private final long id; private final String name; @@ -92,17 +93,23 @@ public String getName() { @Uninterruptible(reason = "Prevent races with VM operations that start/stop recording.", callerMustBe = true) public boolean shouldEmit() { assert !hasDuration; - return shouldEmit0(); + return shouldEmit0() && !SubstrateJVM.get().isExcluded(Thread.currentThread()); + } + + @Uninterruptible(reason = "Prevent races with VM operations that start/stop recording.", callerMustBe = true) + public boolean shouldEmit(Thread thread) { + assert !hasDuration; + return shouldEmit0() && !SubstrateJVM.get().isExcluded(thread); } @Uninterruptible(reason = "Prevent races with VM operations that start/stop recording.", callerMustBe = true) public boolean shouldEmit(long durationTicks) { assert hasDuration; - return shouldEmit0() && durationTicks >= SubstrateJVM.get().getThresholdTicks(this); + return shouldEmit0() && durationTicks >= SubstrateJVM.get().getThresholdTicks(this) && !SubstrateJVM.get().isExcluded(Thread.currentThread()); } @Uninterruptible(reason = "Prevent races with VM operations that start/stop recording.", callerMustBe = true) private boolean shouldEmit0() { - return SubstrateJVM.get().isRecording() && SubstrateJVM.get().isEnabled(this) && !SubstrateJVM.get().isCurrentThreadExcluded(); + return SubstrateJVM.get().isRecording() && SubstrateJVM.get().isEnabled(this); } } diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jfr/JfrThreadLocal.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jfr/JfrThreadLocal.java index fe6158a3dd9f..98cffec2a03b 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jfr/JfrThreadLocal.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jfr/JfrThreadLocal.java @@ -243,18 +243,18 @@ public static void setExcluded(Thread thread, boolean excluded) { /** * Allocation JFR events can be emitted along the allocation slow path. In some cases, when the - * slow path may be taken, a {@link Thread} object may not yet be assigned to the current thread - * See {@link PlatformThreads#ensureCurrentAssigned(String, ThreadGroup, boolean)} where a - * {@link Thread} object must be created before it can be assigned to the current thread. This + * slow path may be taken, a {@link Thread} object may not yet be assigned to the current + * thread, see {@link PlatformThreads#ensureCurrentAssigned(String, ThreadGroup, boolean)} where + * a {@link Thread} object must be created before it can be assigned to the current thread. This * may happen during shutdown in {@link JavaMainWrapper}. Therefore, this method must account * for the case where {@link Thread#currentThread()} returns null. */ @Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true) - public static boolean isCurrentThreadExcluded() { - if (Thread.currentThread() == null) { + public static boolean isThreadExcluded(Thread thread) { + if (thread == null) { return true; } - Target_java_lang_Thread tjlt = SubstrateUtil.cast(Thread.currentThread(), Target_java_lang_Thread.class); + Target_java_lang_Thread tjlt = SubstrateUtil.cast(thread, Target_java_lang_Thread.class); return tjlt.jfrExcluded; } @@ -286,7 +286,7 @@ public Target_jdk_jfr_internal_event_EventWriter newEventWriter() { throw new OutOfMemoryError("OOME for thread local buffer"); } - Target_jdk_jfr_internal_event_EventWriter result = JfrEventWriterAccess.newEventWriter(buffer, isCurrentThreadExcluded()); + Target_jdk_jfr_internal_event_EventWriter result = JfrEventWriterAccess.newEventWriter(buffer, isThreadExcluded(Thread.currentThread())); javaEventWriter.set(result); return result; } diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jfr/SubstrateJVM.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jfr/SubstrateJVM.java index b8092d7a89c1..46f302ce552d 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jfr/SubstrateJVM.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jfr/SubstrateJVM.java @@ -701,20 +701,9 @@ public void setExcluded(Thread thread, boolean excluded) { JfrThreadLocal.setExcluded(thread, excluded); } - public boolean isExcluded(Thread thread) { - /* - * Only the current thread is passed to this method in JDK 17, 19, and 20. Eventually, we - * will need to implement that in a more general way though, see GR-44616. - */ - if (!thread.equals(Thread.currentThread())) { - return false; - } - return isCurrentThreadExcluded(); - } - @Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true) - public boolean isCurrentThreadExcluded() { - return JfrThreadLocal.isCurrentThreadExcluded(); + public boolean isExcluded(Thread thread) { + return JfrThreadLocal.isThreadExcluded(thread); } private static class JfrBeginRecordingOperation extends JavaVMOperation { diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jfr/events/EndChunkNativePeriodicEvents.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jfr/events/EndChunkNativePeriodicEvents.java index 0a1c3b60ac8c..3db3496fbc44 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jfr/events/EndChunkNativePeriodicEvents.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jfr/events/EndChunkNativePeriodicEvents.java @@ -30,7 +30,6 @@ import org.graalvm.nativeimage.StackValue; import com.oracle.svm.core.Uninterruptible; -import com.oracle.svm.core.heap.Heap; import com.oracle.svm.core.jfr.JfrEvent; import com.oracle.svm.core.jfr.JfrEventWriteStatus; import com.oracle.svm.core.jfr.JfrNativeEventWriter; @@ -55,12 +54,10 @@ private static String formatOSInformation() { } public static void emit() { - emitClassLoadingStatistics(Heap.getHeap().getClassCount()); emitJVMInformation(JVMInformation.getJVMInfo()); emitOSInformation(formatOSInformation()); emitInitialEnvironmentVariables(getEnvironmentVariables()); emitInitialSystemProperties(getSystemProperties()); - emitThreadCPULoad(); } @Uninterruptible(reason = "Accesses a JFR buffer.") @@ -113,20 +110,6 @@ private static JfrEventWriteStatus emitInitialSystemProperty(JfrNativeEventWrite return JfrNativeEventWriter.endEvent(data, isLarge); } - @Uninterruptible(reason = "Accesses a JFR buffer.") - private static void emitClassLoadingStatistics(long loadedClassCount) { - if (JfrEvent.ClassLoadingStatistics.shouldEmit()) { - JfrNativeEventWriterData data = StackValue.get(JfrNativeEventWriterData.class); - JfrNativeEventWriterDataAccess.initializeThreadLocalNativeBuffer(data); - - JfrNativeEventWriter.beginSmallEvent(data, JfrEvent.ClassLoadingStatistics); - JfrNativeEventWriter.putLong(data, JfrTicks.elapsedTicks()); - JfrNativeEventWriter.putLong(data, loadedClassCount); - JfrNativeEventWriter.putLong(data, 0); /* unloadedClassCount */ - JfrNativeEventWriter.endSmallEvent(data); - } - } - @Uninterruptible(reason = "Accesses a JFR buffer.") private static void emitJVMInformation(JVMInformation jvmInformation) { if (JfrEvent.JVMInformation.shouldEmit()) { @@ -177,10 +160,6 @@ private static JfrEventWriteStatus emitOSInformation0(JfrNativeEventWriterData d return JfrNativeEventWriter.endEvent(data, isLarge); } - private static void emitThreadCPULoad() { - ThreadCPULoadEvent.emitEvents(); - } - private static StringEntry[] getEnvironmentVariables() { Map env = System.getenv(); StringEntry[] result = new StringEntry[env.size()]; diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jfr/events/EveryChunkNativePeriodicEvents.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jfr/events/EveryChunkNativePeriodicEvents.java index a25144e6b2fc..db0a82d9c853 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jfr/events/EveryChunkNativePeriodicEvents.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jfr/events/EveryChunkNativePeriodicEvents.java @@ -24,17 +24,22 @@ */ package com.oracle.svm.core.jfr.events; -import java.lang.management.ManagementFactory; -import java.lang.management.ThreadMXBean; - +import org.graalvm.nativeimage.ImageSingletons; +import org.graalvm.nativeimage.IsolateThread; import org.graalvm.nativeimage.StackValue; import com.oracle.svm.core.Uninterruptible; +import com.oracle.svm.core.heap.Heap; +import com.oracle.svm.core.heap.PhysicalMemory; +import com.oracle.svm.core.heap.VMOperationInfos; +import com.oracle.svm.core.jdk.management.SubstrateThreadMXBean; import com.oracle.svm.core.jfr.JfrEvent; import com.oracle.svm.core.jfr.JfrNativeEventWriter; import com.oracle.svm.core.jfr.JfrNativeEventWriterData; import com.oracle.svm.core.jfr.JfrNativeEventWriterDataAccess; import com.oracle.svm.core.jfr.JfrTicks; +import com.oracle.svm.core.thread.JavaVMOperation; +import com.oracle.svm.core.thread.VMThreads; import jdk.jfr.Event; import jdk.jfr.Name; @@ -45,40 +50,82 @@ public class EveryChunkNativePeriodicEvents extends Event { public static void emit() { - ThreadMXBean threadMXBean = ManagementFactory.getThreadMXBean(); - emitJavaThreadStats(threadMXBean.getThreadCount(), threadMXBean.getDaemonThreadCount(), - threadMXBean.getTotalStartedThreadCount(), threadMXBean.getPeakThreadCount()); - - emitPhysicalMemory(com.oracle.svm.core.heap.PhysicalMemory.size().rawValue(), 0); + emitJavaThreadStats(); + emitPhysicalMemory(); + emitClassLoadingStatistics(); + emitPerThreadEvents(); } @Uninterruptible(reason = "Accesses a JFR buffer.") - private static void emitJavaThreadStats(long activeCount, long daemonCount, long accumulatedCount, long peakCount) { + private static void emitJavaThreadStats() { if (JfrEvent.JavaThreadStatistics.shouldEmit()) { + SubstrateThreadMXBean threadMXBean = ImageSingletons.lookup(SubstrateThreadMXBean.class); + JfrNativeEventWriterData data = StackValue.get(JfrNativeEventWriterData.class); JfrNativeEventWriterDataAccess.initializeThreadLocalNativeBuffer(data); JfrNativeEventWriter.beginSmallEvent(data, JfrEvent.JavaThreadStatistics); JfrNativeEventWriter.putLong(data, JfrTicks.elapsedTicks()); - JfrNativeEventWriter.putLong(data, activeCount); - JfrNativeEventWriter.putLong(data, daemonCount); - JfrNativeEventWriter.putLong(data, accumulatedCount); - JfrNativeEventWriter.putLong(data, peakCount); + JfrNativeEventWriter.putLong(data, threadMXBean.getThreadCount()); + JfrNativeEventWriter.putLong(data, threadMXBean.getDaemonThreadCount()); + JfrNativeEventWriter.putLong(data, threadMXBean.getTotalStartedThreadCount()); + JfrNativeEventWriter.putLong(data, threadMXBean.getPeakThreadCount()); JfrNativeEventWriter.endSmallEvent(data); } } @Uninterruptible(reason = "Accesses a JFR buffer.") - private static void emitPhysicalMemory(long totalSize, long usedSize) { + private static void emitPhysicalMemory() { if (JfrEvent.PhysicalMemory.shouldEmit()) { JfrNativeEventWriterData data = StackValue.get(JfrNativeEventWriterData.class); JfrNativeEventWriterDataAccess.initializeThreadLocalNativeBuffer(data); JfrNativeEventWriter.beginSmallEvent(data, JfrEvent.PhysicalMemory); JfrNativeEventWriter.putLong(data, JfrTicks.elapsedTicks()); - JfrNativeEventWriter.putLong(data, totalSize); - JfrNativeEventWriter.putLong(data, usedSize); + JfrNativeEventWriter.putLong(data, PhysicalMemory.getCachedSize().rawValue()); + JfrNativeEventWriter.putLong(data, 0); /* used size */ + JfrNativeEventWriter.endSmallEvent(data); + } + } + + @Uninterruptible(reason = "Accesses a JFR buffer.") + private static void emitClassLoadingStatistics() { + if (JfrEvent.ClassLoadingStatistics.shouldEmit()) { + JfrNativeEventWriterData data = StackValue.get(JfrNativeEventWriterData.class); + JfrNativeEventWriterDataAccess.initializeThreadLocalNativeBuffer(data); + + JfrNativeEventWriter.beginSmallEvent(data, JfrEvent.ClassLoadingStatistics); + JfrNativeEventWriter.putLong(data, JfrTicks.elapsedTicks()); + JfrNativeEventWriter.putLong(data, Heap.getHeap().getClassCount()); + JfrNativeEventWriter.putLong(data, 0); /* unloadedClassCount */ JfrNativeEventWriter.endSmallEvent(data); } } + + private static void emitPerThreadEvents() { + if (needsVMOperation()) { + EmitPeriodicPerThreadEventsOperation vmOp = new EmitPeriodicPerThreadEventsOperation(); + vmOp.enqueue(); + } + } + + @Uninterruptible(reason = "Used to avoid the VM operation if it is not absolutely needed.") + private static boolean needsVMOperation() { + /* The returned value is racy. */ + return JfrEvent.ThreadCPULoad.shouldEmit() || JfrEvent.ThreadAllocationStatistics.shouldEmit(); + } + + private static final class EmitPeriodicPerThreadEventsOperation extends JavaVMOperation { + EmitPeriodicPerThreadEventsOperation() { + super(VMOperationInfos.get(EmitPeriodicPerThreadEventsOperation.class, "Emit periodic JFR events", SystemEffect.SAFEPOINT)); + } + + @Override + protected void operate() { + for (IsolateThread isolateThread = VMThreads.firstThread(); isolateThread.isNonNull(); isolateThread = VMThreads.nextThread(isolateThread)) { + ThreadCPULoadEvent.emit(isolateThread); + ThreadAllocationStatisticsEvent.emit(isolateThread); + } + } + } } diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jfr/events/ThreadAllocationStatisticsEvent.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jfr/events/ThreadAllocationStatisticsEvent.java new file mode 100644 index 000000000000..ac1cc1fb42f3 --- /dev/null +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jfr/events/ThreadAllocationStatisticsEvent.java @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2023, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2023, 2023, Red Hat Inc. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package com.oracle.svm.core.jfr.events; + +import org.graalvm.nativeimage.IsolateThread; +import org.graalvm.nativeimage.StackValue; + +import com.oracle.svm.core.Uninterruptible; +import com.oracle.svm.core.heap.Heap; +import com.oracle.svm.core.jfr.JfrEvent; +import com.oracle.svm.core.jfr.JfrNativeEventWriter; +import com.oracle.svm.core.jfr.JfrNativeEventWriterData; +import com.oracle.svm.core.jfr.JfrNativeEventWriterDataAccess; +import com.oracle.svm.core.jfr.JfrTicks; +import com.oracle.svm.core.thread.PlatformThreads; + +public class ThreadAllocationStatisticsEvent { + @Uninterruptible(reason = "Accesses a JFR buffer.") + public static void emit(IsolateThread isolateThread) { + Thread thread = PlatformThreads.fromVMThread(isolateThread); + if (JfrEvent.ThreadAllocationStatistics.shouldEmit(thread)) { + JfrNativeEventWriterData data = StackValue.get(JfrNativeEventWriterData.class); + JfrNativeEventWriterDataAccess.initializeThreadLocalNativeBuffer(data); + + JfrNativeEventWriter.beginSmallEvent(data, JfrEvent.ThreadAllocationStatistics); + JfrNativeEventWriter.putLong(data, JfrTicks.elapsedTicks()); + JfrNativeEventWriter.putLong(data, Heap.getHeap().getThreadAllocatedMemory(isolateThread)); + JfrNativeEventWriter.putThread(data, thread); + JfrNativeEventWriter.endSmallEvent(data); + } + } +} diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jfr/events/ThreadCPULoadEvent.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jfr/events/ThreadCPULoadEvent.java index bdcc8bf7e8f2..ba7a664eeb34 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jfr/events/ThreadCPULoadEvent.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jfr/events/ThreadCPULoadEvent.java @@ -29,7 +29,6 @@ import org.graalvm.nativeimage.StackValue; import com.oracle.svm.core.Uninterruptible; -import com.oracle.svm.core.heap.VMOperationInfos; import com.oracle.svm.core.jdk.Jvm; import com.oracle.svm.core.jdk.UninterruptibleUtils; import com.oracle.svm.core.jfr.JfrEvent; @@ -37,9 +36,7 @@ import com.oracle.svm.core.jfr.JfrNativeEventWriterData; import com.oracle.svm.core.jfr.JfrNativeEventWriterDataAccess; import com.oracle.svm.core.jfr.JfrTicks; -import com.oracle.svm.core.thread.JavaVMOperation; import com.oracle.svm.core.thread.ThreadCpuTimeSupport; -import com.oracle.svm.core.thread.VMThreads; import com.oracle.svm.core.threadlocal.FastThreadLocalFactory; import com.oracle.svm.core.threadlocal.FastThreadLocalLong; import com.oracle.svm.core.util.TimeUtils; @@ -127,14 +124,6 @@ public static void emit(IsolateThread isolateThread) { JfrNativeEventWriter.endSmallEvent(data); } - public static void emitEvents() { - /* This is safe because the VM operation rechecks if the event should be emitted. */ - if (shouldEmitUnsafe()) { - EmitThreadCPULoadEventsOperation vmOp = new EmitThreadCPULoadEventsOperation(); - vmOp.enqueue(); - } - } - @Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true) private static int getProcessorCount() { /* @@ -163,23 +152,4 @@ private static long getThreadCpuTime(IsolateThread isolateThread, boolean includ private static long getCurrentTime() { return System.nanoTime(); } - - @Uninterruptible(reason = "Used to avoid the VM operation if it is not absolutely needed.") - private static boolean shouldEmitUnsafe() { - /* The returned value is racy. */ - return JfrEvent.ThreadCPULoad.shouldEmit(); - } - - private static final class EmitThreadCPULoadEventsOperation extends JavaVMOperation { - EmitThreadCPULoadEventsOperation() { - super(VMOperationInfos.get(EmitThreadCPULoadEventsOperation.class, "Emit ThreadCPULoad events", SystemEffect.SAFEPOINT)); - } - - @Override - protected void operate() { - for (IsolateThread isolateThread = VMThreads.firstThread(); isolateThread.isNonNull(); isolateThread = VMThreads.nextThread(isolateThread)) { - emit(isolateThread); - } - } - } } diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/thread/ContinuationsFeature.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/thread/ContinuationsFeature.java index 2a909ab3f710..91d7a0ab0010 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/thread/ContinuationsFeature.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/thread/ContinuationsFeature.java @@ -28,7 +28,9 @@ import org.graalvm.compiler.serviceprovider.JavaVersionUtil; import org.graalvm.nativeimage.ImageSingletons; +import org.graalvm.nativeimage.Platform; import org.graalvm.nativeimage.hosted.RuntimeClassInitialization; +import org.graalvm.nativeimage.impl.InternalPlatform; import com.oracle.svm.core.SubstrateControlFlowIntegrity; import com.oracle.svm.core.SubstrateOptions; @@ -47,12 +49,12 @@ public class ContinuationsFeature implements InternalFeature { @Override public void afterRegistration(AfterRegistrationAccess access) { final int firstLoomPreviewVersion = 19; - final int lastLoomPreviewVersion = 21; // JDK-8303683 + final int lastLoomPreviewVersion = 20; boolean supportLoom = false; if (JavaVersionUtil.JAVA_SPEC >= firstLoomPreviewVersion) { boolean haveLoom; - if (JavaVersionUtil.JAVA_SPEC > lastLoomPreviewVersion) { + if (JavaVersionUtil.JAVA_SPEC > lastLoomPreviewVersion && Platform.includedIn(InternalPlatform.NATIVE_ONLY.class)) { haveLoom = true; } else { try { diff --git a/substratevm/src/com.oracle.svm.graal/src/com/oracle/svm/graal/RuntimeCPUFeatureRegion.java b/substratevm/src/com.oracle.svm.graal/src/com/oracle/svm/graal/RuntimeCPUFeatureRegion.java index 02038cc1fac4..9e6071bb1ca5 100644 --- a/substratevm/src/com.oracle.svm.graal/src/com/oracle/svm/graal/RuntimeCPUFeatureRegion.java +++ b/substratevm/src/com.oracle.svm.graal/src/com/oracle/svm/graal/RuntimeCPUFeatureRegion.java @@ -95,6 +95,7 @@ public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Rec r.register(new InvocationPlugin.RequiredInlineOnlyInvocationPlugin("leave", InvocationPlugin.Receiver.class) { @Override public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver) { + receiver.requireNonNull(); b.add(new CPUFeatureRegionLeaveNode()); return true; } diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/ModuleLayerFeature.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/ModuleLayerFeature.java index e2a5f40568d1..e013c0d086af 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/ModuleLayerFeature.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/ModuleLayerFeature.java @@ -70,6 +70,8 @@ import com.oracle.svm.core.jdk.Resources; import com.oracle.svm.core.jdk.RuntimeModuleSupport; import com.oracle.svm.core.util.VMError; +import com.oracle.svm.hosted.FeatureImpl.AfterAnalysisAccessImpl; +import com.oracle.svm.hosted.FeatureImpl.AnalysisAccessBase; import com.oracle.svm.hosted.FeatureImpl.BeforeAnalysisAccessImpl; import com.oracle.svm.util.LogUtils; import com.oracle.svm.util.ModuleSupport; @@ -176,14 +178,14 @@ public void beforeAnalysis(BeforeAnalysisAccess access) { private void scanRuntimeBootLayerPrototype(BeforeAnalysisAccessImpl accessImpl) { Set baseModules = ModuleLayer.boot().modules().stream().map(Module::getName).collect(Collectors.toSet()); Function clf = moduleLayerFeatureUtils::getClassLoaderForBootLayerModule; - ModuleLayer runtimeBootLayer = synthesizeRuntimeModuleLayer(new ArrayList<>(List.of(ModuleLayer.empty())), accessImpl.imageClassLoader, baseModules, Set.of(), clf, null); + ModuleLayer runtimeBootLayer = synthesizeRuntimeModuleLayer(new ArrayList<>(List.of(ModuleLayer.empty())), accessImpl, accessImpl.imageClassLoader, baseModules, Set.of(), clf, null); /* Only scan the value if module support is enabled and bootLayer field is reachable. */ accessImpl.registerReachabilityHandler((a) -> accessImpl.rescanObject(runtimeBootLayer), ReflectionUtil.lookupField(RuntimeModuleSupport.class, "bootLayer")); } @Override public void afterAnalysis(AfterAnalysisAccess access) { - FeatureImpl.AfterAnalysisAccessImpl accessImpl = (FeatureImpl.AfterAnalysisAccessImpl) access; + AfterAnalysisAccessImpl accessImpl = (AfterAnalysisAccessImpl) access; Set runtimeImageNamedModules = accessImpl.getUniverse().getTypes() .stream() @@ -241,8 +243,8 @@ public void afterAnalysis(AfterAnalysisAccess access) { * Ensure that runtime modules have the same relations (i.e., reads, opens and exports) as * the originals. */ - replicateVisibilityModifications(runtimeBootLayer, accessImpl.imageClassLoader, runtimeImageNamedModules); - replicateNativeAccess(runtimeImageNamedModules); + replicateVisibilityModifications(runtimeBootLayer, accessImpl, accessImpl.imageClassLoader, runtimeImageNamedModules); + replicateNativeAccess(accessImpl, runtimeImageNamedModules); } /** @@ -360,7 +362,7 @@ private static Stream extractRequiredModuleNames(Module m) { return Stream.concat(Stream.of(m.getName()), requiredModules); } - private List synthesizeRuntimeModuleLayers(FeatureImpl.AfterAnalysisAccessImpl accessImpl, List hostedModuleLayers, Collection reachableNamedModules, + private List synthesizeRuntimeModuleLayers(AfterAnalysisAccessImpl accessImpl, List hostedModuleLayers, Collection reachableNamedModules, Collection reachableSyntheticModules, Collection rootModuleNames) { /* * A mapping from hosted to runtime module layers. Used when looking up runtime module layer @@ -404,7 +406,7 @@ private List synthesizeRuntimeModuleLayers(FeatureImpl.AfterAnalysi Configuration cf = isBootModuleLayer ? null : hostedModuleLayer.configuration(); Function clf = name -> moduleLayerFeatureUtils.getClassLoaderForModuleInModuleLayer(hostedModuleLayer, name); - ModuleLayer runtimeModuleLayer = synthesizeRuntimeModuleLayer(parents, accessImpl.imageClassLoader, moduleNames, syntheticModules, clf, cf); + ModuleLayer runtimeModuleLayer = synthesizeRuntimeModuleLayer(parents, accessImpl, accessImpl.imageClassLoader, moduleNames, syntheticModules, clf, cf); moduleLayerPairs.put(hostedModuleLayer, runtimeModuleLayer); } @@ -412,8 +414,8 @@ private List synthesizeRuntimeModuleLayers(FeatureImpl.AfterAnalysi return new ArrayList<>(moduleLayerPairs.values()); } - private ModuleLayer synthesizeRuntimeModuleLayer(List parentLayers, ImageClassLoader cl, Set reachableModules, Set syntheticModules, - Function clf, Configuration cfOverride) { + private ModuleLayer synthesizeRuntimeModuleLayer(List parentLayers, AnalysisAccessBase accessImpl, ImageClassLoader cl, Set reachableModules, + Set syntheticModules, Function clf, Configuration cfOverride) { /** * For consistent module lookup we reuse the {@link ModuleFinder}s defined and used in * {@link NativeImageClassLoaderSupport}. @@ -431,20 +433,21 @@ private ModuleLayer synthesizeRuntimeModuleLayer(List parentLayers, ModuleLayer runtimeModuleLayer = null; try { runtimeModuleLayer = moduleLayerFeatureUtils.createNewModuleLayerInstance(runtimeModuleLayerConfiguration); - Map nameToModule = moduleLayerFeatureUtils.synthesizeNameToModule(runtimeModuleLayer, clf); + Map nameToModule = moduleLayerFeatureUtils.synthesizeNameToModule(accessImpl, runtimeModuleLayer, clf); for (Module syntheticModule : syntheticModules) { Module runtimeSyntheticModule = moduleLayerFeatureUtils.getOrCreateRuntimeModuleForHostedModule(syntheticModule); nameToModule.putIfAbsent(runtimeSyntheticModule.getName(), runtimeSyntheticModule); - moduleLayerFeatureUtils.patchModuleLayerField(runtimeSyntheticModule, runtimeModuleLayer); + moduleLayerFeatureUtils.patchModuleLayerField(accessImpl, runtimeSyntheticModule, runtimeModuleLayer); } - patchRuntimeModuleLayer(runtimeModuleLayer, nameToModule, parentLayers); + patchRuntimeModuleLayer(accessImpl, runtimeModuleLayer, nameToModule, parentLayers); + accessImpl.rescanField(runtimeModuleLayer, moduleLayerFeatureUtils.moduleLayerModulesField); return runtimeModuleLayer; } catch (InstantiationException | IllegalAccessException | InvocationTargetException ex) { throw VMError.shouldNotReachHere("Failed to synthesize the runtime module layer: " + runtimeModuleLayer, ex); } } - private void replicateVisibilityModifications(ModuleLayer runtimeBootLayer, ImageClassLoader cl, Set analysisReachableNamedModules) { + private void replicateVisibilityModifications(ModuleLayer runtimeBootLayer, AfterAnalysisAccessImpl accessImpl, ImageClassLoader cl, Set analysisReachableNamedModules) { List applicationModules = findApplicationModules(runtimeBootLayer, cl.applicationModulePath()); Map modulePairs = analysisReachableNamedModules @@ -470,27 +473,27 @@ private void replicateVisibilityModifications(ModuleLayer runtimeBootLayer, Imag } Module runtimeTo = e2.getValue(); if (ModuleLayerFeatureUtils.isModuleSynthetic(hostedFrom) || hostedFrom.canRead(hostedTo)) { - moduleLayerFeatureUtils.addReads(runtimeFrom, runtimeTo); + moduleLayerFeatureUtils.addReads(accessImpl, runtimeFrom, runtimeTo); if (hostedFrom == builderModule) { for (Module appModule : applicationModules) { - moduleLayerFeatureUtils.addReads(appModule, runtimeTo); + moduleLayerFeatureUtils.addReads(accessImpl, appModule, runtimeTo); } } } for (String pn : runtimeFrom.getPackages()) { if (ModuleLayerFeatureUtils.isModuleSynthetic(hostedFrom) || hostedFrom.isOpen(pn, hostedTo)) { - moduleLayerFeatureUtils.addOpens(runtimeFrom, pn, runtimeTo); + moduleLayerFeatureUtils.addOpens(accessImpl, runtimeFrom, pn, runtimeTo); if (hostedTo == builderModule) { for (Module appModule : applicationModules) { - moduleLayerFeatureUtils.addOpens(runtimeFrom, pn, appModule); + moduleLayerFeatureUtils.addOpens(accessImpl, runtimeFrom, pn, appModule); } } } if (ModuleLayerFeatureUtils.isModuleSynthetic(hostedFrom) || hostedFrom.isExported(pn, hostedTo)) { - moduleLayerFeatureUtils.addExports(runtimeFrom, pn, runtimeTo); + moduleLayerFeatureUtils.addExports(accessImpl, runtimeFrom, pn, runtimeTo); if (hostedTo == builderModule) { for (Module appModule : applicationModules) { - moduleLayerFeatureUtils.addExports(runtimeFrom, pn, appModule); + moduleLayerFeatureUtils.addExports(accessImpl, runtimeFrom, pn, appModule); } } } @@ -502,7 +505,7 @@ private void replicateVisibilityModifications(ModuleLayer runtimeBootLayer, Imag } } - private void replicateNativeAccess(Set analysisReachableNamedModules) { + private void replicateNativeAccess(AfterAnalysisAccessImpl accessImpl, Set analysisReachableNamedModules) { if (JavaVersionUtil.JAVA_SPEC < 19) { return; } @@ -518,7 +521,7 @@ private void replicateNativeAccess(Set analysisReachableNamedModules) { Module hosted = modulesPair.getKey(); Module runtime = modulesPair.getValue(); if (moduleLayerFeatureUtils.allowsNativeAccess(hosted)) { - moduleLayerFeatureUtils.setNativeAccess(runtime, true); + moduleLayerFeatureUtils.setNativeAccess(accessImpl, runtime, true); } } @@ -567,10 +570,10 @@ private static Configuration synthesizeRuntimeModuleLayerConfiguration(ModuleFin } } - private void patchRuntimeModuleLayer(ModuleLayer runtimeModuleLayer, Map nameToModule, List parents) { + private void patchRuntimeModuleLayer(AnalysisAccessBase accessImpl, ModuleLayer runtimeModuleLayer, Map nameToModule, List parents) { try { - moduleLayerFeatureUtils.patchModuleLayerNameToModuleField(runtimeModuleLayer, nameToModule); - moduleLayerFeatureUtils.patchModuleLayerParentsField(runtimeModuleLayer, parents); + moduleLayerFeatureUtils.patchModuleLayerNameToModuleField(accessImpl, runtimeModuleLayer, nameToModule); + moduleLayerFeatureUtils.patchModuleLayerParentsField(accessImpl, runtimeModuleLayer, parents); } catch (IllegalAccessException ex) { throw VMError.shouldNotReachHere("Failed to patch the runtime boot module layer.", ex); } @@ -605,6 +608,7 @@ private static final class ModuleLayerFeatureUtils { private final Constructor moduleLayerConstructor; private final Field moduleLayerNameToModuleField; private final Field moduleLayerParentsField; + private final Field moduleLayerModulesField; ModuleLayerFeatureUtils(ImageClassLoader cl) { runtimeModules = new HashMap<>(); @@ -660,6 +664,7 @@ private static final class ModuleLayerFeatureUtils { moduleLayerConstructor = ReflectionUtil.lookupConstructor(ModuleLayer.class, Configuration.class, List.class, Function.class); moduleLayerNameToModuleField = ReflectionUtil.lookupField(ModuleLayer.class, "nameToModule"); moduleLayerParentsField = ReflectionUtil.lookupField(ModuleLayer.class, "parents"); + moduleLayerModulesField = ReflectionUtil.lookupField(ModuleLayer.class, "modules"); } catch (ReflectiveOperationException | NoSuchElementException ex) { throw VMError.shouldNotReachHere("Failed to retrieve fields of the Module/ModuleLayer class.", ex); } @@ -821,7 +826,7 @@ public Module getOrCreateRuntimeModuleForHostedModule(ClassLoader loader, String * and removal of VM state updates (otherwise we would be re-defining modules to the host * VM). */ - Map synthesizeNameToModule(ModuleLayer runtimeModuleLayer, Function clf) + Map synthesizeNameToModule(AnalysisAccessBase access, ModuleLayer runtimeModuleLayer, Function clf) throws IllegalAccessException, InvocationTargetException { Configuration cf = runtimeModuleLayer.configuration(); @@ -840,8 +845,9 @@ Map synthesizeNameToModule(ModuleLayer runtimeModuleLayer, Funct Module m = getOrCreateRuntimeModuleForHostedModule(loader, name, descriptor); if (!descriptor.equals(m.getDescriptor())) { moduleDescriptorField.set(m, descriptor); + access.rescanField(m, moduleDescriptorField); } - patchModuleLayerField(m, runtimeModuleLayer); + patchModuleLayerField(access, m, runtimeModuleLayer); nameToModule.put(name, m); } @@ -868,6 +874,7 @@ Map synthesizeNameToModule(ModuleLayer runtimeModuleLayer, Funct reads.add(allUnnamedModule); } moduleReadsField.set(m, reads); + access.rescanField(m, moduleReadsField); if (!descriptor.isOpen() && !descriptor.isAutomatic()) { if (descriptor.opens().isEmpty()) { @@ -890,6 +897,7 @@ Map synthesizeNameToModule(ModuleLayer runtimeModuleLayer, Funct } } moduleExportedPackagesField.set(m, exportedPackages); + access.rescanField(m, moduleExportedPackagesField); } else { Map> openPackages = new HashMap<>(descriptor.opens().size()); for (ModuleDescriptor.Opens opens : descriptor.opens()) { @@ -937,26 +945,30 @@ Map synthesizeNameToModule(ModuleLayer runtimeModuleLayer, Funct } moduleOpenPackagesField.set(m, openPackages); + access.rescanField(m, moduleOpenPackagesField); moduleExportedPackagesField.set(m, exportedPackages); + access.rescanField(m, moduleExportedPackagesField); } } + access.rescanObject(m); } return nameToModule; } @SuppressWarnings("unchecked") - void addReads(Module module, Module other) throws IllegalAccessException { + void addReads(AfterAnalysisAccessImpl accessImpl, Module module, Module other) throws IllegalAccessException { Set reads = (Set) moduleReadsField.get(module); if (reads == null) { reads = new HashSet<>(1); moduleReadsField.set(module, reads); } reads.add(other == null ? allUnnamedModule : other); + accessImpl.rescanField(module, moduleReadsField); } @SuppressWarnings("unchecked") - void addExports(Module module, String pn, Module other) throws IllegalAccessException { + void addExports(AfterAnalysisAccessImpl accessImpl, Module module, String pn, Module other) throws IllegalAccessException { if (other != null && module.isExported(pn, other)) { return; } @@ -979,10 +991,11 @@ void addExports(Module module, String pn, Module other) throws IllegalAccessExce if (prev != null) { prev.add(other == null ? allUnnamedModule : other); } + accessImpl.rescanField(module, moduleExportedPackagesField); } @SuppressWarnings("unchecked") - void addOpens(Module module, String pn, Module other) throws IllegalAccessException { + void addOpens(AfterAnalysisAccessImpl accessImpl, Module module, String pn, Module other) throws IllegalAccessException { if (other != null && module.isOpen(pn, other)) { return; } @@ -1005,10 +1018,12 @@ void addOpens(Module module, String pn, Module other) throws IllegalAccessExcept if (prev != null) { prev.add(other == null ? allUnnamedModule : other); } + accessImpl.rescanField(module, moduleOpenPackagesField); } - void patchModuleLayerField(Module module, ModuleLayer runtimeBootLayer) throws IllegalAccessException { + void patchModuleLayerField(AnalysisAccessBase accessImpl, Module module, ModuleLayer runtimeBootLayer) throws IllegalAccessException { moduleLayerField.set(module, runtimeBootLayer); + accessImpl.rescanField(module, moduleLayerField); } void patchModuleLoaderField(Module module, ClassLoader loader) throws IllegalAccessException { @@ -1019,12 +1034,14 @@ ModuleLayer createNewModuleLayerInstance(Configuration cf) throws InvocationTarg return moduleLayerConstructor.newInstance(cf, List.of(), null); } - void patchModuleLayerNameToModuleField(ModuleLayer moduleLayer, Map nameToModule) throws IllegalAccessException { + void patchModuleLayerNameToModuleField(AnalysisAccessBase accessImpl, ModuleLayer moduleLayer, Map nameToModule) throws IllegalAccessException { moduleLayerNameToModuleField.set(moduleLayer, nameToModule); + accessImpl.rescanField(moduleLayer, moduleLayerNameToModuleField); } - void patchModuleLayerParentsField(ModuleLayer moduleLayer, List parents) throws IllegalAccessException { + void patchModuleLayerParentsField(AnalysisAccessBase accessImpl, ModuleLayer moduleLayer, List parents) throws IllegalAccessException { moduleLayerParentsField.set(moduleLayer, parents); + accessImpl.rescanField(moduleLayer, moduleLayerParentsField); } ClassLoader getClassLoaderForBootLayerModule(String name) { @@ -1093,10 +1110,11 @@ boolean allowsNativeAccess(Module module) { } - void setNativeAccess(Module module, boolean value) { + void setNativeAccess(AfterAnalysisAccessImpl accessImpl, Module module, boolean value) { assert moduleEnableNativeAccessField != null : "Only available on JDK19+"; try { moduleEnableNativeAccessField.set(module, value); + accessImpl.rescanField(module, moduleEnableNativeAccessField); } catch (IllegalAccessException e) { throw VMError.shouldNotReachHere("Failed to reflectively set Module.enableNativeAccess.", e); } diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/ProgressReporter.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/ProgressReporter.java index 298539a9389f..43a9ccec9e6d 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/ProgressReporter.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/ProgressReporter.java @@ -1416,7 +1416,7 @@ default void link(AbstractPrinter printer, Path path, boolean filenameOnly) { if (filenameOnly) { Path filename = normalized.getFileName(); if (filename != null) { - name = normalized.toString(); + name = filename.toString(); } else { throw VMError.shouldNotReachHere("filename should never be null, illegal path: " + path); } diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/analysis/DynamicHubInitializer.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/analysis/DynamicHubInitializer.java index 273be346a8f6..aaca57d73a02 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/analysis/DynamicHubInitializer.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/analysis/DynamicHubInitializer.java @@ -70,6 +70,7 @@ public class DynamicHubInitializer { private final Field dynamicHubClassInitializationInfoField; private final Field dynamicHubArrayHubField; + private final Field dynamicHubSignatureField; private final Field dynamicHubInterfacesEncodingField; private final Field dynamicHubAnnotationsEnumConstantsReferenceField; @@ -83,6 +84,7 @@ public DynamicHubInitializer(BigBang bb) { dynamicHubClassInitializationInfoField = ReflectionUtil.lookupField(DynamicHub.class, "classInitializationInfo"); dynamicHubArrayHubField = ReflectionUtil.lookupField(DynamicHub.class, "arrayHub"); + dynamicHubSignatureField = ReflectionUtil.lookupField(DynamicHub.class, "signature"); dynamicHubInterfacesEncodingField = ReflectionUtil.lookupField(DynamicHub.class, "interfacesEncoding"); dynamicHubAnnotationsEnumConstantsReferenceField = ReflectionUtil.lookupField(DynamicHub.class, "enumConstantsReference"); } @@ -104,7 +106,7 @@ public void initializeMetaData(ImageHeapScanner heapScanner, AnalysisType type) heapScanner.rescanObject(hub, OtherReason.HUB); buildClassInitializationInfo(heapScanner, type, hub); - fillSignature(type, hub); + fillSignature(heapScanner, type, hub); if (type.getJavaKind() == JavaKind.Object) { if (type.isArray()) { @@ -243,7 +245,7 @@ private ClassInitializationInfo buildRuntimeInitializationInfo(AnalysisType type private static final Method getSignature = ReflectionUtil.lookupMethod(Class.class, "getGenericSignature0"); - private static void fillSignature(AnalysisType type, DynamicHub hub) { + private void fillSignature(ImageHeapScanner heapScanner, AnalysisType type, DynamicHub hub) { AnalysisError.guarantee(hub.getSignature() == null, "Signature already computed for %s.", type.toJavaName(true)); Class javaClass = type.getJavaClass(); String signature; @@ -253,6 +255,7 @@ private static void fillSignature(AnalysisType type, DynamicHub hub) { throw GraalError.shouldNotReachHere(e); // ExcludeFromJacocoGeneratedReport } hub.setSignature(signature); + heapScanner.rescanField(hub, dynamicHubSignatureField); } class InterfacesEncodingKey { diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/heap/SVMImageHeapScanner.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/heap/SVMImageHeapScanner.java index 1b2725da4a0f..ad29e779b0fc 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/heap/SVMImageHeapScanner.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/heap/SVMImageHeapScanner.java @@ -68,6 +68,8 @@ public class SVMImageHeapScanner extends ImageHeapScanner { private final Class economicMapImpl; private final Field economicMapImplEntriesField; private final Field economicMapImplHashArrayField; + private final Field economicMapImplTotalEntriesField; + private final Field economicMapImplDeletedEntriesField; private final ReflectionHostedSupport reflectionSupport; private final Class memberNameClass; private final MethodHandleFeature methodHandleSupport; @@ -82,6 +84,8 @@ public SVMImageHeapScanner(BigBang bb, ImageHeap imageHeap, ImageClassLoader loa economicMapImpl = getClass("org.graalvm.collections.EconomicMapImpl"); economicMapImplEntriesField = ReflectionUtil.lookupField(economicMapImpl, "entries"); economicMapImplHashArrayField = ReflectionUtil.lookupField(economicMapImpl, "hashArray"); + economicMapImplTotalEntriesField = ReflectionUtil.lookupField(economicMapImpl, "totalEntries"); + economicMapImplDeletedEntriesField = ReflectionUtil.lookupField(economicMapImpl, "deletedEntries"); ImageSingletons.add(ImageHeapScanner.class, this); reflectionSupport = ImageSingletons.lookup(ReflectionHostedSupport.class); memberNameClass = getClass("java.lang.invoke.MemberName"); @@ -143,6 +147,8 @@ protected void rescanEconomicMap(EconomicMap map) { if (map.getClass() == economicMapImpl) { rescanField(map, economicMapImplEntriesField); rescanField(map, economicMapImplHashArrayField); + rescanField(map, economicMapImplTotalEntriesField); + rescanField(map, economicMapImplDeletedEntriesField); } } 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 96ffa7677ba5..a2409d35d082 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 @@ -28,12 +28,16 @@ import com.oracle.graal.pointsto.BigBang; import com.oracle.graal.pointsto.ObjectScanner; +import com.oracle.graal.pointsto.ObjectScanningObserver; import com.oracle.graal.pointsto.heap.HeapSnapshotVerifier; import com.oracle.graal.pointsto.heap.ImageHeap; import com.oracle.graal.pointsto.heap.ImageHeapScanner; +import com.oracle.graal.pointsto.infrastructure.UniverseMetaAccess; +import com.oracle.graal.pointsto.meta.AnalysisField; import com.oracle.graal.pointsto.meta.AnalysisType; import com.oracle.graal.pointsto.util.CompletionExecutor; import com.oracle.svm.hosted.SVMHost; +import com.oracle.svm.hosted.ameta.AnalysisConstantReflectionProvider; import jdk.vm.ci.meta.JavaConstant; @@ -43,8 +47,8 @@ public SVMImageHeapVerifier(BigBang bb, ImageHeap imageHeap, ImageHeapScanner sc } @Override - public boolean requireAnalysisIteration(CompletionExecutor executor) throws InterruptedException { - return super.requireAnalysisIteration(executor) || imageStateModified(); + public boolean checkHeapSnapshot(UniverseMetaAccess metaAccess, CompletionExecutor executor, String phase, boolean forAnalysis) { + return super.checkHeapSnapshot(metaAccess, executor, phase, forAnalysis) || imageStateModified(); } /** @@ -75,6 +79,26 @@ private void verifyHub(SVMHost svmHost, ObjectScanner objectScanner, AnalysisTyp objectScanner.scanConstant(hubConstant, ObjectScanner.OtherReason.HUB); } + @Override + protected ObjectScanner installObjectScanner(UniverseMetaAccess metaAccess, CompletionExecutor executor) { + return new VerifierObjectScanner(bb, metaAccess, executor, scannedObjects, new ScanningObserver()); + } + + private static final class VerifierObjectScanner extends ObjectScanner { + private final UniverseMetaAccess metaAccess; + + VerifierObjectScanner(BigBang bb, UniverseMetaAccess metaAccess, CompletionExecutor executor, ReusableSet scannedObjects, ObjectScanningObserver scanningObserver) { + super(bb, executor, scannedObjects, scanningObserver); + this.metaAccess = metaAccess; + } + + @Override + protected JavaConstant readFieldValue(AnalysisField field, JavaConstant receiver) { + AnalysisConstantReflectionProvider constantReflectionProvider = (AnalysisConstantReflectionProvider) bb.getConstantReflectionProvider(); + return constantReflectionProvider.readValue(metaAccess, field, receiver, true); + } + } + private SVMHost svmHost() { return (SVMHost) bb.getHostVM(); } diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/NativeImageHeap.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/NativeImageHeap.java index 84bfa0f1b57d..a55a33ceae8b 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/NativeImageHeap.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/NativeImageHeap.java @@ -44,7 +44,6 @@ import org.graalvm.compiler.api.replacements.Fold; import org.graalvm.compiler.core.common.CompressEncoding; import org.graalvm.compiler.core.common.NumUtil; -import org.graalvm.compiler.core.common.SuppressFBWarnings; import org.graalvm.compiler.core.common.type.CompressibleConstant; import org.graalvm.compiler.core.common.type.TypedConstant; import org.graalvm.nativeimage.ImageSingletons; @@ -53,6 +52,7 @@ import org.graalvm.word.WordBase; import com.oracle.graal.pointsto.heap.ImageHeapConstant; +import com.oracle.graal.pointsto.heap.ImageHeapScanner; import com.oracle.graal.pointsto.meta.AnalysisUniverse; import com.oracle.graal.pointsto.util.AnalysisError; import com.oracle.svm.core.StaticFieldsSupport; @@ -361,7 +361,7 @@ public void addConstant(final JavaConstant constant, boolean immutableFromParent * initialize the hash field. This is safe because Enum.hashCode() is a final method, * i.e., cannot be overwritten by the user. */ - forceHashCodeComputation(enumConstant); + ImageHeapScanner.forceHashCodeComputation(enumConstant); } final ObjectInfo existing = objects.get(uncompressed); @@ -457,7 +457,7 @@ private boolean assertFillerObjectSizes() { } private void handleImageString(final String str) { - forceHashCodeComputation(str); + ImageHeapScanner.forceHashCodeComputation(str); if (HostedStringDeduplication.isInternedString(str)) { /* The string is interned by the host VM, so it must also be interned in our image. */ assert internedStrings.containsKey(str) || internStringsPhase.isAllowed() : "Should not intern string during phase " + internStringsPhase.toString(); @@ -465,15 +465,6 @@ private void handleImageString(final String str) { } } - /** - * For immutable Strings and other objects in the native image heap, force eager computation of - * the hash field. - */ - @SuppressFBWarnings(value = "RV_RETURN_VALUE_IGNORED", justification = "eager hash field computation") - private static void forceHashCodeComputation(Object object) { - object.hashCode(); - } - /** * It has been determined that an object should be added to the model of the native image heap. * This is the mechanics of recursively adding the object and all its fields and array elements diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/methodhandles/MethodHandleFeature.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/methodhandles/MethodHandleFeature.java index 36b70f1220f0..6172426ff161 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/methodhandles/MethodHandleFeature.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/methodhandles/MethodHandleFeature.java @@ -94,6 +94,7 @@ public class MethodHandleFeature implements InternalFeature { private Field lambdaFormNFIdentity; private Field lambdaFormNFZero; private Field typedAccessors; + private Field typedCollectors; /** * A new {@link MethodType} interning table which contains only objects that are already part of @@ -123,6 +124,8 @@ public void duringSetup(DuringSetupAccess access) { Class arrayAccessorClass = access.findClassByName("java.lang.invoke.MethodHandleImpl$ArrayAccessor"); typedAccessors = ReflectionUtil.lookupField(arrayAccessorClass, "TYPED_ACCESSORS"); + Class methodHandleImplClass = access.findClassByName("java.lang.invoke.MethodHandleImpl$Makers"); + typedCollectors = ReflectionUtil.lookupField(methodHandleImplClass, "TYPED_COLLECTORS"); if (JavaVersionUtil.JAVA_SPEC >= 22) { try { @@ -384,6 +387,7 @@ public void duringAnalysis(DuringAnalysisAccess a) { access.rescanRoot(lambdaFormNFIdentity); access.rescanRoot(lambdaFormNFZero); access.rescanRoot(typedAccessors); + access.rescanRoot(typedCollectors); access.rescanObject(runtimeMethodTypeInternTable); } diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/phases/IntrinsifyMethodHandlesInvocationPlugin.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/phases/IntrinsifyMethodHandlesInvocationPlugin.java index d9104e65e22e..dd9abef15a8c 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/phases/IntrinsifyMethodHandlesInvocationPlugin.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/phases/IntrinsifyMethodHandlesInvocationPlugin.java @@ -577,6 +577,7 @@ public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Rec } catch (WrongMethodTypeException t) { return false; } + receiver.requireNonNull(); JavaConstant asTypeConstant = snippetReflection.forObject(asType); ConstantNode asTypeNode = ConstantNode.forConstant(asTypeConstant, b.getMetaAccess(), b.getGraph()); b.push(JavaKind.Object, asTypeNode); diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/snippets/ReflectionPlugins.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/snippets/ReflectionPlugins.java index 5379a54842b6..d0a26e9c9126 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/snippets/ReflectionPlugins.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/snippets/ReflectionPlugins.java @@ -429,6 +429,8 @@ private boolean processClassGetClassLoader(GraphBuilderContext b, ResolvedJavaMe if (PredefinedClassesSupport.isPredefined(clazz)) { return false; } + receiver.requireNonNull(); + return pushConstant(b, targetMethod, clazz::getName, JavaKind.Object, clazz.getClassLoader(), true) != null; } @@ -516,6 +518,9 @@ private boolean foldInvocationUsingReflection(GraphBuilderContext b, ResolvedJav Supplier targetParameters = () -> (receiverValue == null ? "" : receiverValue + "; ") + Stream.of(argValues).map(arg -> arg instanceof Object[] ? Arrays.toString((Object[]) arg) : Objects.toString(arg)).collect(Collectors.joining(", ")); + if (receiver != null) { + receiver.requireNonNull(); + } Object returnValue; try { returnValue = reflectionMethod.invoke(receiverValue, argValues); @@ -563,6 +568,7 @@ private boolean registerConstantBulkReflectionQuery(GraphBuilderContext b, R if (receiverValue == null || receiverValue == NULL_MARKER) { return false; } + receiver.requireNonNull(); b.add(ReachabilityRegistrationNode.create(() -> registerForRuntimeReflection((T) receiverValue, registrationCallback), reason)); return true; diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/snippets/SubstrateGraphBuilderPlugins.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/snippets/SubstrateGraphBuilderPlugins.java index 202090effd98..6cda332ac395 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/snippets/SubstrateGraphBuilderPlugins.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/snippets/SubstrateGraphBuilderPlugins.java @@ -772,6 +772,9 @@ private static void registerUnsafePlugins(MetaAccessProvider metaAccess, Invocat @Override public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver, ValueNode classNode, ValueNode nameNode) { if (classNode.isConstant() && nameNode.isConstant()) { + // Emits a null-check for the otherwise unused receiver + receiver.get(); + /* If the class and field name arguments are constant. */ Class clazz = snippetReflection.asObject(Class.class, classNode.asJavaConstant()); String fieldName = snippetReflection.asObject(String.class, nameNode.asJavaConstant()); @@ -798,6 +801,9 @@ private static void registerUnsafePlugins(MetaAccessProvider metaAccess, Registr @Override public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver, ValueNode fieldNode) { if (fieldNode.isConstant()) { + // Emits a null-check for the otherwise unused receiver + receiver.get(); + Field targetField = snippetReflection.asObject(Field.class, fieldNode.asJavaConstant()); return processFieldOffset(b, targetField, reason, metaAccess, isSunMiscUnsafe); } @@ -808,6 +814,9 @@ public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Rec @Override public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver, ValueNode fieldNode) { if (fieldNode.isConstant()) { + // Emits a null-check for the otherwise unused receiver + receiver.get(); + Field targetField = snippetReflection.asObject(Field.class, fieldNode.asJavaConstant()); return processStaticFieldBase(b, targetField, isSunMiscUnsafe); } @@ -819,6 +828,9 @@ public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Rec @Override public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver, ValueNode fieldNode) { if (fieldNode.isConstant()) { + // Emits a null-check for the otherwise unused receiver + receiver.get(); + Field targetField = snippetReflection.asObject(Field.class, fieldNode.asJavaConstant()); return processFieldOffset(b, targetField, reason, metaAccess, isSunMiscUnsafe); } @@ -1121,6 +1133,7 @@ private static void registerClassPlugins(InvocationPlugins plugins, SnippetRefle public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver) { JavaConstant constantReceiver = receiver.get(false).asJavaConstant(); if (constantReceiver != null) { + receiver.requireNonNull(); ResolvedJavaType type = b.getConstantReflection().asJavaType(constantReceiver); if (type != null) { /* @@ -1266,7 +1279,8 @@ private static void registerReferenceAccessPlugins(InvocationPlugins plugins) { Registration r = new Registration(plugins, ReferenceAccessImpl.class); r.register(new RequiredInvocationPlugin("getCompressedRepresentation", Receiver.class, Object.class) { @Override - public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver unused, ValueNode objectNode) { + public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver, ValueNode objectNode) { + receiver.requireNonNull(); if (ReferenceAccess.singleton().haveCompressedReferences()) { ValueNode compressedObj = SubstrateCompressionNode.compress(b.getGraph(), objectNode, ImageSingletons.lookup(CompressEncoding.class)); JavaKind compressedIntKind = JavaKind.fromWordSize(ConfigurationValues.getObjectLayout().getReferenceSize()); @@ -1280,7 +1294,8 @@ public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Rec }); r.register(new RequiredInvocationPlugin("uncompressReference", Receiver.class, UnsignedWord.class) { @Override - public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver unused, ValueNode wordNode) { + public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver, ValueNode wordNode) { + receiver.requireNonNull(); if (ReferenceAccess.singleton().haveCompressedReferences()) { CompressEncoding encoding = ImageSingletons.lookup(CompressEncoding.class); JavaKind compressedIntKind = JavaKind.fromWordSize(ConfigurationValues.getObjectLayout().getReferenceSize()); diff --git a/substratevm/src/com.oracle.svm.test/src/com/oracle/svm/test/jfr/TestThreadAllocationStatisticsEvent.java b/substratevm/src/com.oracle.svm.test/src/com/oracle/svm/test/jfr/TestThreadAllocationStatisticsEvent.java new file mode 100644 index 000000000000..2c7aae39acdb --- /dev/null +++ b/substratevm/src/com.oracle.svm.test/src/com/oracle/svm/test/jfr/TestThreadAllocationStatisticsEvent.java @@ -0,0 +1,105 @@ +/* + * Copyright (c) 2023, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2023, 2023, Red Hat Inc. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package com.oracle.svm.test.jfr; + +import static org.junit.Assert.assertTrue; + +import java.util.List; +import java.util.concurrent.atomic.AtomicInteger; + +import org.junit.Test; + +import com.oracle.svm.core.NeverInline; +import com.oracle.svm.core.jfr.JfrEvent; + +import jdk.jfr.Recording; +import jdk.jfr.consumer.RecordedEvent; + +public class TestThreadAllocationStatisticsEvent extends JfrRecordingTest { + private static final int ALLOCATION_SIZE = 128 * 1024; + private static final String THREAD_NAME_1 = "HeapAllocationThread-1"; + + @Test + public void test() throws Throwable { + String[] events = new String[]{JfrEvent.ThreadAllocationStatistics.getName()}; + Recording recording = startRecording(events); + + /* Start a thread and wait until it allocated some memory. */ + HeapAllocationThread thread = new HeapAllocationThread(THREAD_NAME_1); + thread.start(); + waitUntilTrue(() -> thread.getAllocationCount() > 0); + + /* Force chunk rotation so that ThreadAllocationStatistics are emitted for all threads. */ + recording.dump(createTempJfrFile()); + + /* Final chunk rotation also emits ThreadAllocationStatistics for all threads. */ + stopRecording(recording, TestThreadAllocationStatisticsEvent::validateEvents); + + assertTrue(thread.isAlive()); + thread.interrupt(); + } + + private static void validateEvents(List events) { + int found = 0; + for (RecordedEvent e : events) { + if (e.getThread("thread").getJavaName().equals(THREAD_NAME_1) && e. getValue("allocated") >= ALLOCATION_SIZE) { + found++; + } + } + assertTrue(found >= 2); + } + + private static class HeapAllocationThread extends Thread { + private final AtomicInteger allocationCount = new AtomicInteger(); + + HeapAllocationThread(String name) { + super(name); + } + + @Override + public void run() { + try { + while (true) { + allocateByteArray(ALLOCATION_SIZE); + allocationCount.incrementAndGet(); + Thread.sleep(10); + } + } catch (InterruptedException e) { + /* Normal way how this thread exits. */ + } + } + + public int getAllocationCount() { + return allocationCount.get(); + } + + @NeverInline("Prevent escape analysis.") + private static byte[] allocateByteArray(int length) { + return new byte[length]; + } + } +} diff --git a/substratevm/src/com.oracle.svm.test/src/com/oracle/svm/test/jfr/TestThreadCPULoadEvent.java b/substratevm/src/com.oracle.svm.test/src/com/oracle/svm/test/jfr/TestThreadCPULoadEvent.java index d5398b33c72e..2e57f07c3677 100644 --- a/substratevm/src/com.oracle.svm.test/src/com/oracle/svm/test/jfr/TestThreadCPULoadEvent.java +++ b/substratevm/src/com.oracle.svm.test/src/com/oracle/svm/test/jfr/TestThreadCPULoadEvent.java @@ -38,6 +38,7 @@ import org.junit.Assert; import org.junit.Test; +import com.oracle.svm.core.jfr.JfrEvent; import com.oracle.svm.core.util.TimeUtils; import jdk.jfr.Recording; @@ -51,7 +52,7 @@ public class TestThreadCPULoadEvent extends JfrRecordingTest { @Test public void test() throws Throwable { - String[] events = new String[]{"jdk.ThreadCPULoad"}; + String[] events = new String[]{JfrEvent.ThreadCPULoad.getName()}; Recording recording = startRecording(events); WeakReference thread1 = createAndStartBusyWaitThread(THREAD_NAME_1, 10, 250); diff --git a/substratevm/src/com.oracle.svm.truffle/src/com/oracle/svm/truffle/TruffleBaseFeature.java b/substratevm/src/com.oracle.svm.truffle/src/com/oracle/svm/truffle/TruffleBaseFeature.java index 10fb3e9e5b80..32e69437306e 100644 --- a/substratevm/src/com.oracle.svm.truffle/src/com/oracle/svm/truffle/TruffleBaseFeature.java +++ b/substratevm/src/com.oracle.svm.truffle/src/com/oracle/svm/truffle/TruffleBaseFeature.java @@ -1374,6 +1374,21 @@ final class Target_com_oracle_truffle_polyglot_InternalResourceCache { */ @Alias @RecomputeFieldValue(kind = Kind.Reset) // private static volatile Pair cacheRoot; + + @Alias @RecomputeFieldValue(kind = Kind.Custom, declClass = UseInternalResourcesComputer.class, isFinal = true) // + private static boolean useInternalResources; + + private static final class UseInternalResourcesComputer implements FieldValueTransformerWithAvailability { + @Override + public ValueAvailability valueAvailability() { + return ValueAvailability.BeforeAnalysis; + } + + @Override + public Object transform(Object receiver, Object originalValue) { + return TruffleBaseFeature.Options.CopyLanguageResources.getValue(); + } + } } @TargetClass(className = "com.oracle.truffle.polyglot.InternalResourceCache$ResettableCachedRoot", onlyWith = TruffleBaseFeature.IsEnabled.class) diff --git a/sulong/ci/ci.jsonnet b/sulong/ci/ci.jsonnet index 01d8658cdd94..883c771715b0 100644 --- a/sulong/ci/ci.jsonnet +++ b/sulong/ci/ci.jsonnet @@ -67,7 +67,7 @@ local sc = (import "ci_common/sulong-common.jsonnet"); [ [sc.linux_amd64, [sc.labsjdk21]], [sc.darwin_amd64, [sc.labsjdk21]], - [sc.windows_amd64, [sc.labsjdk21]], + [sc.windows_amd64 + { capabilities+: ["windows_server_2016"] /* work around native-image bug GR-48515 */ }, [sc.labsjdk21]], [sc.linux_aarch64, [sc.labsjdk21]], [sc.darwin_aarch64, [sc.labsjdk21]], ], diff --git a/sulong/mx.sulong/mx_sulong.py b/sulong/mx.sulong/mx_sulong.py index 6990a22d82f0..5851ca8e9d6a 100644 --- a/sulong/mx.sulong/mx_sulong.py +++ b/sulong/mx.sulong/mx_sulong.py @@ -613,7 +613,9 @@ def _get_jar_dists(self): standalone_dependencies_enterprise={**standalone_dependencies_common, **{ 'LLVM Runtime Enterprise': ('lib/sulong', []), 'LLVM Runtime Native Enterprise': ('lib/sulong', []), - 'LLVM Runtime Managed': ('lib/sulong', []), + **({} if mx.is_windows() else { + 'LLVM Runtime Managed': ('lib/sulong', []), + }), 'LLVM Runtime License Files EE': ('', []), 'GraalVM enterprise license files': ('', ['LICENSE.txt', 'GRAALVM-README.md']), }}, diff --git a/sulong/projects/com.oracle.truffle.llvm.parser/src/com/oracle/truffle/llvm/parser/binary/BinaryParser.java b/sulong/projects/com.oracle.truffle.llvm.parser/src/com/oracle/truffle/llvm/parser/binary/BinaryParser.java index 4f97a1f0966b..be98b91e16a7 100644 --- a/sulong/projects/com.oracle.truffle.llvm.parser/src/com/oracle/truffle/llvm/parser/binary/BinaryParser.java +++ b/sulong/projects/com.oracle.truffle.llvm.parser/src/com/oracle/truffle/llvm/parser/binary/BinaryParser.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, 2022, Oracle and/or its affiliates. + * Copyright (c) 2019, 2023, Oracle and/or its affiliates. * * All rights reserved. * @@ -29,6 +29,7 @@ */ package com.oracle.truffle.llvm.parser.binary; +import java.nio.file.InvalidPathException; import java.nio.file.Path; import java.nio.file.Paths; import java.util.ArrayList; @@ -107,11 +108,15 @@ public static String getOrigin(Source source) { if (sourcePath == null) { return null; } - Path parent = Paths.get(sourcePath).getParent(); - if (parent == null) { + try { + Path parent = Paths.get(sourcePath).getParent(); + if (parent == null) { + return null; + } + return parent.toString(); + } catch (InvalidPathException ex) { return null; } - return parent.toString(); } private ByteSequence parseBitcode(ByteSequence bytes, Source source) { diff --git a/truffle/ci/ci.jsonnet b/truffle/ci/ci.jsonnet index 2fd76537436a..b6c9e98c8e57 100644 --- a/truffle/ci/ci.jsonnet +++ b/truffle/ci/ci.jsonnet @@ -111,7 +111,7 @@ linux_amd64 + jdk + simple_tool_maven_project_gate + common.mach5_target, linux_amd64 + jdk + simple_language_maven_project_gate, darwin_amd64 + jdk + truffle_weekly + gate_lite + guard, - ] for jdk in [common.oraclejdk21, common.oraclejdk17] + ] for jdk in [common.oraclejdk21] ]) + [ linux_amd64 + common.oraclejdk17 + truffle_gate + guard + {timelimit: "45:00"}, diff --git a/truffle/src/com.oracle.truffle.polyglot/src/com/oracle/truffle/polyglot/InternalResourceCache.java b/truffle/src/com.oracle.truffle.polyglot/src/com/oracle/truffle/polyglot/InternalResourceCache.java index 1869391ec797..4ec0634530ce 100644 --- a/truffle/src/com.oracle.truffle.polyglot/src/com/oracle/truffle/polyglot/InternalResourceCache.java +++ b/truffle/src/com.oracle.truffle.polyglot/src/com/oracle/truffle/polyglot/InternalResourceCache.java @@ -302,18 +302,31 @@ private static Path findCacheRootOnNativeImage() { Pair res = cacheRoot; if (res == null) { assert ImageInfo.inImageRuntimeCode() : "Can be called only in the native-image execution time."; - Path executable = Path.of(ProcessProperties.getExecutableName()); - Path parent = executable.getParent(); - if (parent == null) { - throw new IllegalStateException("Could not locate native-image resources."); - } - Path cache = parent.resolve("resources"); + Path executable = getExecutablePath(); + Path cache = executable.resolveSibling("resources"); res = Pair.create(cache, false); cacheRoot = res; } return res.getLeft(); } + private static Path getExecutablePath() { + assert ImageInfo.inImageRuntimeCode(); + if (useInternalResources) { + return Path.of(ProcessProperties.getExecutableName()); + } else { + throw new IllegalArgumentException("Lookup an executable name is restricted. " + + "To enable it, use '-H:+CopyLanguageResources' during the native image build."); + } + } + + /** + * Recomputed before the analyses by a substitution in the {@code TruffleBaseFeature} based on + * the {@code CopyLanguageResources} option value. The field must not be declared as + * {@code final} to make the substitution function correctly. + */ + private static boolean useInternalResources = true; + /** * Collects optional internal resources for native-image build. This method is called * reflectively by the {@code TruffleBaseFeature#initializeTruffleReflectively}. diff --git a/vm/mx.vm/suite.py b/vm/mx.vm/suite.py index 14e1d17c2cca..0bde176c6adc 100644 --- a/vm/mx.vm/suite.py +++ b/vm/mx.vm/suite.py @@ -33,7 +33,7 @@ "name": "graal-nodejs", "subdir": True, "dynamic": True, - "version": "85841f2b0ba9986eab186ae3fb26d10020eced39", + "version": "42faffdba5c047ffbfb922a8ac5a7a8803dcdc52", "urls" : [ {"url" : "https://github.com/graalvm/graaljs.git", "kind" : "git"}, ] @@ -42,7 +42,7 @@ "name": "graal-js", "subdir": True, "dynamic": True, - "version": "85841f2b0ba9986eab186ae3fb26d10020eced39", + "version": "42faffdba5c047ffbfb922a8ac5a7a8803dcdc52", "urls": [ {"url": "https://github.com/graalvm/graaljs.git", "kind" : "git"}, ]