--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/FinalizableSubclassTest.java Thu Feb 16 15:46:09 2017 -0800
@@ -0,0 +1,217 @@
+/*
+ * Copyright (c) 2013, 2016, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package org.graalvm.compiler.core.test;
+
+import static org.graalvm.compiler.core.common.CompilationIdentifier.INVALID_COMPILATION_ID;
+import static org.graalvm.compiler.nodes.StructuredGraph.NO_PROFILING_INFO;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.lang.reflect.Constructor;
+import java.util.HashMap;
+
+import jdk.vm.ci.meta.Assumptions;
+import jdk.vm.ci.meta.Assumptions.Assumption;
+import jdk.vm.ci.meta.Assumptions.LeafType;
+import jdk.vm.ci.meta.Assumptions.NoFinalizableSubclass;
+import jdk.vm.ci.meta.ResolvedJavaMethod;
+
+import org.junit.Assert;
+import org.junit.Test;
+
+import org.graalvm.compiler.debug.Debug;
+import org.graalvm.compiler.java.GraphBuilderPhase;
+import org.graalvm.compiler.nodes.StructuredGraph;
+import org.graalvm.compiler.nodes.StructuredGraph.AllowAssumptions;
+import org.graalvm.compiler.nodes.graphbuilderconf.GraphBuilderConfiguration;
+import org.graalvm.compiler.nodes.java.RegisterFinalizerNode;
+import org.graalvm.compiler.phases.OptimisticOptimizations;
+import org.graalvm.compiler.phases.common.CanonicalizerPhase;
+import org.graalvm.compiler.phases.common.inlining.InliningPhase;
+import org.graalvm.compiler.phases.tiers.HighTierContext;
+
+public class FinalizableSubclassTest extends GraalCompilerTest {
+
+ /**
+ * used as template to generate class files at runtime.
+ */
+ public static class NoFinalizerEverAAAA {
+ }
+
+ public static class NoFinalizerYetAAAA {
+ }
+
+ public static final class WithFinalizerAAAA extends NoFinalizerYetAAAA {
+
+ @Override
+ protected void finalize() throws Throwable {
+ super.finalize();
+ }
+ }
+
+ private StructuredGraph parseAndProcess(Class<?> cl, AllowAssumptions allowAssumptions) {
+ Constructor<?>[] constructors = cl.getConstructors();
+ Assert.assertTrue(constructors.length == 1);
+ final ResolvedJavaMethod javaMethod = getMetaAccess().lookupJavaMethod(constructors[0]);
+ StructuredGraph graph = new StructuredGraph(javaMethod, allowAssumptions, NO_PROFILING_INFO, INVALID_COMPILATION_ID);
+
+ GraphBuilderConfiguration conf = GraphBuilderConfiguration.getSnippetDefault(getDefaultGraphBuilderPlugins());
+ new GraphBuilderPhase.Instance(getMetaAccess(), getProviders().getStampProvider(), getProviders().getConstantReflection(), getProviders().getConstantFieldProvider(), conf,
+ OptimisticOptimizations.ALL, null).apply(graph);
+ HighTierContext context = new HighTierContext(getProviders(), getDefaultGraphBuilderSuite(), OptimisticOptimizations.ALL);
+ new InliningPhase(new CanonicalizerPhase()).apply(graph, context);
+ new CanonicalizerPhase().apply(graph, context);
+ return graph;
+ }
+
+ private void checkForRegisterFinalizeNode(Class<?> cl, boolean shouldContainFinalizer, AllowAssumptions allowAssumptions) {
+ StructuredGraph graph = parseAndProcess(cl, allowAssumptions);
+ Assert.assertTrue(graph.getNodes().filter(RegisterFinalizerNode.class).count() == (shouldContainFinalizer ? 1 : 0));
+ int noFinalizerAssumption = 0;
+ Assumptions assumptions = graph.getAssumptions();
+ if (assumptions != null) {
+ for (Assumption a : assumptions) {
+ if (a instanceof NoFinalizableSubclass) {
+ noFinalizerAssumption++;
+ } else if (a instanceof LeafType) {
+ // Need to also allow leaf type assumption instead of no finalizable subclass
+ // assumption.
+ noFinalizerAssumption++;
+ }
+ }
+ }
+ Assert.assertTrue(noFinalizerAssumption == (shouldContainFinalizer ? 0 : 1));
+ }
+
+ /**
+ * Use a custom class loader to generate classes, to make sure the given classes are loaded in
+ * correct order.
+ */
+ @Test
+ public void test1() throws ClassNotFoundException {
+ for (int i = 0; i < 2; i++) {
+ ClassTemplateLoader loader = new ClassTemplateLoader();
+ checkForRegisterFinalizeNode(loader.findClass("NoFinalizerEverAAAA"), true, AllowAssumptions.NO);
+ checkForRegisterFinalizeNode(loader.findClass("NoFinalizerEverAAAA"), false, AllowAssumptions.YES);
+
+ checkForRegisterFinalizeNode(loader.findClass("NoFinalizerYetAAAA"), false, AllowAssumptions.YES);
+
+ checkForRegisterFinalizeNode(loader.findClass("WithFinalizerAAAA"), true, AllowAssumptions.YES);
+ checkForRegisterFinalizeNode(loader.findClass("NoFinalizerYetAAAA"), true, AllowAssumptions.YES);
+ }
+ }
+
+ private static class ClassTemplateLoader extends ClassLoader {
+
+ private static int loaderInstance = 0;
+
+ private final String replaceTo;
+ private HashMap<String, Class<?>> cache = new HashMap<>();
+
+ ClassTemplateLoader() {
+ loaderInstance++;
+ replaceTo = String.format("%04d", loaderInstance);
+ }
+
+ @Override
+ protected Class<?> findClass(final String name) throws ClassNotFoundException {
+ String nameReplaced = name.replaceAll("AAAA", replaceTo);
+ if (cache.containsKey(nameReplaced)) {
+ return cache.get(nameReplaced);
+ }
+
+ // copy classfile to byte array
+ byte[] classData = null;
+ try {
+ InputStream is = FinalizableSubclassTest.class.getResourceAsStream("FinalizableSubclassTest$" + name + ".class");
+ assert is != null;
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+
+ byte[] buf = new byte[1024];
+ int size;
+ while ((size = is.read(buf, 0, buf.length)) != -1) {
+ baos.write(buf, 0, size);
+ }
+ baos.flush();
+ classData = baos.toByteArray();
+ } catch (IOException e) {
+ Assert.fail("can't access class: " + name);
+ }
+ dumpStringsInByteArray(classData);
+
+ // replace all occurrences of "AAAA" in classfile
+ int index = -1;
+ while ((index = indexOfAAAA(classData, index + 1)) != -1) {
+ replaceAAAA(classData, index, replaceTo);
+ }
+ dumpStringsInByteArray(classData);
+
+ Class<?> c = defineClass(null, classData, 0, classData.length);
+ cache.put(nameReplaced, c);
+ return c;
+ }
+
+ private static int indexOfAAAA(byte[] b, int index) {
+ for (int i = index; i < b.length; i++) {
+ boolean match = true;
+ for (int j = i; j < i + 4; j++) {
+ if (b[j] != (byte) 'A') {
+ match = false;
+ break;
+ }
+ }
+ if (match) {
+ return i;
+ }
+ }
+ return -1;
+ }
+
+ private static void replaceAAAA(byte[] b, int index, String replacer) {
+ assert replacer.length() == 4;
+ for (int i = index; i < index + 4; i++) {
+ b[i] = (byte) replacer.charAt(i - index);
+ }
+ }
+
+ private static void dumpStringsInByteArray(byte[] b) {
+ boolean wasChar = true;
+ StringBuilder sb = new StringBuilder();
+ for (Byte x : b) {
+ // check for [a-zA-Z0-9]
+ if ((x >= 0x41 && x <= 0x7a) || (x >= 0x30 && x <= 0x39)) {
+ if (!wasChar) {
+ Debug.log(sb + "");
+ sb.setLength(0);
+ }
+ sb.append(String.format("%c", x));
+ wasChar = true;
+ } else {
+ wasChar = false;
+ }
+ }
+ Debug.log(sb + "");
+ }
+ }
+}