001/*
002 * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved.
003 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
004 *
005 * This code is free software; you can redistribute it and/or modify it
006 * under the terms of the GNU General Public License version 2 only, as
007 * published by the Free Software Foundation.
008 *
009 * This code is distributed in the hope that it will be useful, but WITHOUT
010 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
011 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
012 * version 2 for more details (a copy is included in the LICENSE file that
013 * accompanied this code).
014 *
015 * You should have received a copy of the GNU General Public License version
016 * 2 along with this work; if not, write to the Free Software Foundation,
017 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
018 *
019 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
020 * or visit www.oracle.com if you need additional information or have any
021 * questions.
022 */
023package com.oracle.graal.compiler.test;
024
025import java.io.*;
026import java.lang.reflect.*;
027import java.util.*;
028
029import com.oracle.graal.debug.*;
030import jdk.internal.jvmci.meta.*;
031import jdk.internal.jvmci.meta.Assumptions.*;
032
033import org.junit.*;
034
035import com.oracle.graal.graphbuilderconf.*;
036import com.oracle.graal.java.*;
037import com.oracle.graal.nodes.*;
038import com.oracle.graal.nodes.StructuredGraph.AllowAssumptions;
039import com.oracle.graal.nodes.java.*;
040import com.oracle.graal.phases.*;
041import com.oracle.graal.phases.common.*;
042import com.oracle.graal.phases.common.inlining.*;
043import com.oracle.graal.phases.tiers.*;
044
045public class FinalizableSubclassTest extends GraalCompilerTest {
046
047    /**
048     * used as template to generate class files at runtime.
049     */
050    public static class NoFinalizerEverAAAA {
051    }
052
053    public static class NoFinalizerYetAAAA {
054    }
055
056    public static class WithFinalizerAAAA extends NoFinalizerYetAAAA {
057
058        @Override
059        protected void finalize() throws Throwable {
060            super.finalize();
061        }
062    }
063
064    private StructuredGraph parseAndProcess(Class<?> cl, AllowAssumptions allowAssumptions) {
065        Constructor<?>[] constructors = cl.getConstructors();
066        Assert.assertTrue(constructors.length == 1);
067        final ResolvedJavaMethod javaMethod = getMetaAccess().lookupJavaMethod(constructors[0]);
068        StructuredGraph graph = new StructuredGraph(javaMethod, allowAssumptions);
069
070        GraphBuilderConfiguration conf = GraphBuilderConfiguration.getSnippetDefault(getDefaultGraphBuilderPlugins());
071        new GraphBuilderPhase.Instance(getMetaAccess(), getProviders().getStampProvider(), getProviders().getConstantReflection(), conf, OptimisticOptimizations.ALL, null).apply(graph);
072        HighTierContext context = new HighTierContext(getProviders(), getDefaultGraphBuilderSuite(), OptimisticOptimizations.ALL);
073        new InliningPhase(new CanonicalizerPhase()).apply(graph, context);
074        new CanonicalizerPhase().apply(graph, context);
075        return graph;
076    }
077
078    private void checkForRegisterFinalizeNode(Class<?> cl, boolean shouldContainFinalizer, AllowAssumptions allowAssumptions) {
079        StructuredGraph graph = parseAndProcess(cl, allowAssumptions);
080        Assert.assertTrue(graph.getNodes().filter(RegisterFinalizerNode.class).count() == (shouldContainFinalizer ? 1 : 0));
081        int noFinalizerAssumption = 0;
082        Assumptions assumptions = graph.getAssumptions();
083        if (assumptions != null) {
084            for (Assumption a : assumptions) {
085                if (a instanceof NoFinalizableSubclass) {
086                    noFinalizerAssumption++;
087                }
088            }
089        }
090        Assert.assertTrue(noFinalizerAssumption == (shouldContainFinalizer ? 0 : 1));
091    }
092
093    /**
094     * Use a custom class loader to generate classes, to make sure the given classes are loaded in
095     * correct order.
096     */
097    @Test
098    public void test1() throws ClassNotFoundException {
099        for (int i = 0; i < 2; i++) {
100            ClassTemplateLoader loader = new ClassTemplateLoader();
101            checkForRegisterFinalizeNode(loader.findClass("NoFinalizerEverAAAA"), true, AllowAssumptions.NO);
102            checkForRegisterFinalizeNode(loader.findClass("NoFinalizerEverAAAA"), false, AllowAssumptions.YES);
103
104            checkForRegisterFinalizeNode(loader.findClass("NoFinalizerYetAAAA"), false, AllowAssumptions.YES);
105
106            checkForRegisterFinalizeNode(loader.findClass("WithFinalizerAAAA"), true, AllowAssumptions.YES);
107            checkForRegisterFinalizeNode(loader.findClass("NoFinalizerYetAAAA"), true, AllowAssumptions.YES);
108        }
109    }
110
111    private static class ClassTemplateLoader extends ClassLoader {
112
113        private static int loaderInstance = 0;
114
115        private final String replaceTo;
116        private HashMap<String, Class<?>> cache = new HashMap<>();
117
118        public ClassTemplateLoader() {
119            loaderInstance++;
120            replaceTo = String.format("%04d", loaderInstance);
121        }
122
123        @Override
124        protected Class<?> findClass(final String name) throws ClassNotFoundException {
125            String nameReplaced = name.replaceAll("AAAA", replaceTo);
126            if (cache.containsKey(nameReplaced)) {
127                return cache.get(nameReplaced);
128            }
129
130            // copy classfile to byte array
131            byte[] classData = null;
132            try {
133                InputStream is = FinalizableSubclassTest.class.getResourceAsStream("FinalizableSubclassTest$" + name + ".class");
134                assert is != null;
135                ByteArrayOutputStream baos = new ByteArrayOutputStream();
136
137                byte[] buf = new byte[1024];
138                int size;
139                while ((size = is.read(buf, 0, buf.length)) != -1) {
140                    baos.write(buf, 0, size);
141                }
142                baos.flush();
143                classData = baos.toByteArray();
144            } catch (IOException e) {
145                Assert.fail("can't access class: " + name);
146            }
147            dumpStringsInByteArray(classData);
148
149            // replace all occurrences of "AAAA" in classfile
150            int index = -1;
151            while ((index = indexOfAAAA(classData, index + 1)) != -1) {
152                replaceAAAA(classData, index, replaceTo);
153            }
154            dumpStringsInByteArray(classData);
155
156            Class<?> c = defineClass(null, classData, 0, classData.length);
157            cache.put(nameReplaced, c);
158            return c;
159        }
160
161        private static int indexOfAAAA(byte[] b, int index) {
162            for (int i = index; i < b.length; i++) {
163                boolean match = true;
164                for (int j = i; j < i + 4; j++) {
165                    if (b[j] != (byte) 'A') {
166                        match = false;
167                        break;
168                    }
169                }
170                if (match) {
171                    return i;
172                }
173            }
174            return -1;
175        }
176
177        private static void replaceAAAA(byte[] b, int index, String replacer) {
178            assert replacer.length() == 4;
179            for (int i = index; i < index + 4; i++) {
180                b[i] = (byte) replacer.charAt(i - index);
181            }
182        }
183
184        private static void dumpStringsInByteArray(byte[] b) {
185            boolean wasChar = true;
186            StringBuilder sb = new StringBuilder();
187            for (Byte x : b) {
188                // check for [a-zA-Z0-9]
189                if ((x >= 0x41 && x <= 0x7a) || (x >= 0x30 && x <= 0x39)) {
190                    if (!wasChar) {
191                        Debug.log(sb + "");
192                        sb.setLength(0);
193                    }
194                    sb.append(String.format("%c", x));
195                    wasChar = true;
196                } else {
197                    wasChar = false;
198                }
199            }
200            Debug.log(sb + "");
201        }
202    }
203}