diff --git a/src/hotspot/share/classfile/classFileParser.cpp b/src/hotspot/share/classfile/classFileParser.cpp index 5bee4cd4916..541b6eb8655 100644 --- a/src/hotspot/share/classfile/classFileParser.cpp +++ b/src/hotspot/share/classfile/classFileParser.cpp @@ -2250,6 +2250,7 @@ void ClassFileParser::copy_method_annotations(ConstMethod* cm, Method* ClassFileParser::parse_method(const ClassFileStream* const cfs, bool is_interface, + bool is_identity_class, const ConstantPool* cp, bool* const has_localvariable_table, TRAPS) { @@ -2826,7 +2827,8 @@ Method* ClassFileParser::parse_method(const ClassFileStream* const cfs, if (InstanceKlass::is_finalization_enabled() && name == vmSymbols::finalize_method_name() && - signature == vmSymbols::void_method_signature()) { + signature == vmSymbols::void_method_signature() && + is_identity_class) { if (m->is_empty_method()) { _has_empty_finalizer = true; } else { @@ -2866,6 +2868,7 @@ void ClassFileParser::parse_methods(const ClassFileStream* const cfs, for (int index = 0; index < length; index++) { Method* method = parse_method(cfs, is_interface, + is_identity_class(), _cp, has_localvariable_table, CHECK); @@ -4157,7 +4160,8 @@ void ClassFileParser::set_precomputed_flags(InstanceKlass* ik) { const Method* const m = ik->lookup_method(vmSymbols::finalize_method_name(), vmSymbols::void_method_signature()); if (InstanceKlass::is_finalization_enabled() && - (m != nullptr) && !m->is_empty_method()) { + (m != nullptr) && !m->is_empty_method() && + m->method_holder()->access_flags().is_identity_class()) { f = true; } diff --git a/src/hotspot/share/classfile/classFileParser.hpp b/src/hotspot/share/classfile/classFileParser.hpp index 428203f21b9..29550a25121 100644 --- a/src/hotspot/share/classfile/classFileParser.hpp +++ b/src/hotspot/share/classfile/classFileParser.hpp @@ -311,6 +311,7 @@ class ClassFileParser { // Method parsing Method* parse_method(const ClassFileStream* const cfs, bool is_interface, + bool is_identity_class, const ConstantPool* cp, bool* const has_localvariable_table, TRAPS); diff --git a/src/hotspot/share/prims/jvm.cpp b/src/hotspot/share/prims/jvm.cpp index c525e592740..1e29cbf6d06 100644 --- a/src/hotspot/share/prims/jvm.cpp +++ b/src/hotspot/share/prims/jvm.cpp @@ -466,7 +466,6 @@ JVM_ENTRY(jarray, JVM_CopyOfSpecialArray(JNIEnv *env, jarray orig, jint from, ji array = dst; } else { const ArrayProperties props = ArrayProperties::Default().with_null_restricted(ak->is_null_free_array_klass()); - array = oopFactory::new_objArray(vk, len, props, CHECK_NULL); int end = to < oh()->length() ? to : oh()->length(); for (int i = from; i < end; i++) { diff --git a/test/hotspot/jtreg/runtime/valhalla/inlinetypes/TestFinalizableValues.java b/test/hotspot/jtreg/runtime/valhalla/inlinetypes/TestFinalizableValues.java new file mode 100644 index 00000000000..3abfd323c90 --- /dev/null +++ b/test/hotspot/jtreg/runtime/valhalla/inlinetypes/TestFinalizableValues.java @@ -0,0 +1,140 @@ +/* + * Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test TestFinalizableValues + * @library /test/lib + * @enablePreview + * @run main TestFinalizableValues + */ + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +import jdk.test.lib.process.OutputAnalyzer; +import jdk.test.lib.process.ProcessTools; + +public class TestFinalizableValues { + + + public static class TestHelper { + static boolean finalizer1WasCalled = false; + static boolean finalizer2WasCalled = false; + + static value class MyVal { + static volatile boolean finalizerWasCalled = false; + int i = 0; + + @SuppressWarnings("deprecation") + protected void finalize() { + finalizerWasCalled = true; + } + } + + static abstract value class MyAbstractVal { + int i = 0; + + @SuppressWarnings("deprecation") + protected void finalize() { + finalizer1WasCalled = true; + } + } + + static value class MyVal2 extends MyAbstractVal {} + + static class MyId extends MyAbstractVal {} + + static class MyId2 extends MyAbstractVal { + int i = 0; + + @SuppressWarnings("deprecation") + protected void finalize() { + finalizer2WasCalled = true; + } + } + + static void create(Class c) { + try { + c.newInstance(); + } catch(InstantiationException | IllegalAccessException e) { + throw new RuntimeException(e); + } + } + + public static void main(String[] args) throws ClassNotFoundException { + Class c = Class.forName(args[0]); + boolean expectFinalizer1 = Boolean.valueOf(args[1]); + boolean expectFinalizer2 = Boolean.valueOf(args[2]); + + create(c); + for (int i = 0; i < 100; i++) { + System.gc(); + try { + Thread.sleep(10L); + } catch (InterruptedException ie) { + Thread.currentThread().interrupt(); + } + // if (finalizer1WasCalled) { + // break; + // } + } + if (finalizer1WasCalled != expectFinalizer1) { + throw new RuntimeException("Finalizer1 was " + + (finalizer1WasCalled ? "" : "not ") + + "executed"); + } + if (finalizer2WasCalled != expectFinalizer2) { + throw new RuntimeException("Finalizer2 was " + + (finalizer2WasCalled ? "" : "not ") + + "executed"); + } + } + } + + static void test(String classname, boolean expectRegistration, String... args) throws IOException { + List argsList = new ArrayList<>(); + Collections.addAll(argsList, "--enable-preview"); + Collections.addAll(argsList, "--add-exports", "java.base/jdk.internal.value=ALL-UNNAMED"); + Collections.addAll(argsList, "-Dtest.class.path=" + System.getProperty("test.class.path", ".")); + Collections.addAll(argsList, "-XX:+TraceFinalizerRegistration"); + Collections.addAll(argsList, "TestFinalizableValues$TestHelper"); + Collections.addAll(argsList, "TestFinalizableValues$TestHelper$" + classname); + Collections.addAll(argsList, args); + ProcessBuilder pb = ProcessTools.createTestJavaProcessBuilder(argsList); + OutputAnalyzer out = new OutputAnalyzer(pb.start()); + if (expectRegistration) { + out.shouldContain("as finalizable"); + } else { + out.shouldNotContain("as finalizable"); + } + out.shouldHaveExitValue(0); + } + public static void main(String[] args) throws IOException { + test("MyVal", false, "false", "false"); + test("MyVal2", false, "false", "false"); + test("MyId", false, "false", "false"); + test("MyId2", true, "false", "true"); + } +}