diff --git a/graal/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/CheckGraalInvariants.java b/graal/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/CheckGraalInvariants.java index 340bb888ad16..eff1ffe0dcb1 100644 --- a/graal/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/CheckGraalInvariants.java +++ b/graal/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/CheckGraalInvariants.java @@ -72,6 +72,7 @@ import org.graalvm.compiler.phases.PhaseSuite; import org.graalvm.compiler.phases.VerifyPhase; import org.graalvm.compiler.phases.VerifyPhase.VerificationError; +import org.graalvm.compiler.phases.contract.VerifyNodeCosts; import org.graalvm.compiler.phases.tiers.HighTierContext; import org.graalvm.compiler.phases.util.Providers; import org.graalvm.compiler.phases.verify.VerifyBailoutUsage; @@ -114,17 +115,52 @@ private static boolean shouldVerifyEquals(ResolvedJavaMethod m) { return true; } - private static boolean shouldProcess(String classpathEntry) { + public static String relativeFileName(String absolutePath) { + int lastFileSeparatorIndex = absolutePath.lastIndexOf(File.separator); + return absolutePath.substring(lastFileSeparatorIndex >= 0 ? lastFileSeparatorIndex : 0); + } + + public static class InvariantsTool { + + protected boolean shouldProcess(String classpathEntry) { if (classpathEntry.endsWith(".jar")) { String name = new File(classpathEntry).getName(); return name.contains("jvmci") || name.contains("graal") || name.contains("jdk.internal.vm.compiler"); } return false; + } + + protected String getClassPath() { + String bootclasspath; + if (Java8OrEarlier) { + bootclasspath = System.getProperty("sun.boot.class.path"); + } else { + bootclasspath = System.getProperty("jdk.module.path") + File.pathSeparatorChar + System.getProperty("jdk.module.upgrade.path"); + } + return bootclasspath; + } + + protected boolean shouldLoadClass(String className) { + return !className.equals("module-info"); + } + + protected void handleClassLoadingException(Throwable t) { + GraalError.shouldNotReachHere(t); + } + + protected void handleParsingException(Throwable t) { + GraalError.shouldNotReachHere(t); + } } @Test @SuppressWarnings("try") public void test() { + runTest(new InvariantsTool()); + } + + @SuppressWarnings("try") + public static void runTest(InvariantsTool tool) { RuntimeProvider rt = Graal.getRequiredCapability(RuntimeProvider.class); Providers providers = rt.getHostBackend().getProviders(); MetaAccessProvider metaAccess = providers.getMetaAccess(); @@ -137,17 +173,12 @@ public void test() { Assume.assumeTrue(VerifyPhase.class.desiredAssertionStatus()); - String bootclasspath; - if (Java8OrEarlier) { - bootclasspath = System.getProperty("sun.boot.class.path"); - } else { - bootclasspath = System.getProperty("jdk.module.path") + File.pathSeparatorChar + System.getProperty("jdk.module.upgrade.path"); - } + String bootclasspath = tool.getClassPath(); Assert.assertNotNull("Cannot find boot class path", bootclasspath); final List classNames = new ArrayList<>(); for (String path : bootclasspath.split(File.pathSeparator)) { - if (shouldProcess(path)) { + if (tool.shouldProcess(path)) { try { final ZipFile zipFile = new ZipFile(new File(path)); for (final Enumeration entry = zipFile.entries(); entry.hasMoreElements();) { @@ -200,7 +231,7 @@ public GraalDebugConfig getDebugConfig() { // Order outer classes before the inner classes classNames.sort((String a, String b) -> a.compareTo(b)); // Initialize classes in single thread to avoid deadlocking issues during initialization - List> classes = initializeClasses(classNames); + List> classes = initializeClasses(tool, classNames); for (Class c : classes) { String className = c.getName(); executor.execute(() -> { @@ -234,7 +265,11 @@ public GraalDebugConfig getDebugConfig() { // Graal bail outs on certain patterns in Java bytecode (e.g., // unbalanced monitors introduced by jacoco). } catch (Throwable e) { - errors.add(String.format("Error while checking %s:%n%s", methodName, printStackTraceToString(e))); + try { + tool.handleParsingException(e); + } catch (Throwable t) { + errors.add(String.format("Error while checking %s:%n%s", methodName, printStackTraceToString(e))); + } } }); } @@ -261,17 +296,17 @@ public GraalDebugConfig getDebugConfig() { } } - private static List> initializeClasses(List classNames) { + private static List> initializeClasses(InvariantsTool tool, List classNames) { List> classes = new ArrayList<>(classNames.size()); for (String className : classNames) { - if (className.equals("module-info")) { + if (!tool.shouldLoadClass(className)) { continue; } try { Class c = Class.forName(className, true, CheckGraalInvariants.class.getClassLoader()); classes.add(c); - } catch (ClassNotFoundException e) { - e.printStackTrace(); + } catch (Throwable t) { + tool.handleClassLoadingException(t); } } return classes; @@ -285,6 +320,7 @@ private static void checkClass(Class c, MetaAccessProvider metaAccess) { if (c.getAnnotation(NodeInfo.class) == null) { throw new AssertionError(String.format("Node subclass %s requires %s annotation", c.getName(), NodeClass.class.getSimpleName())); } + VerifyNodeCosts.verifyNodeClass(c); } } diff --git a/graal/org.graalvm.compiler.graph/src/org/graalvm/compiler/graph/NodeClass.java b/graal/org.graalvm.compiler.graph/src/org/graalvm/compiler/graph/NodeClass.java index 5a982a8cb2cc..db8f4ea603b4 100644 --- a/graal/org.graalvm.compiler.graph/src/org/graalvm/compiler/graph/NodeClass.java +++ b/graal/org.graalvm.compiler.graph/src/org/graalvm/compiler/graph/NodeClass.java @@ -941,6 +941,10 @@ public int getLeafId() { return this.leafId; } + public NodeClass getSuperNodeClass() { + return superNodeClass; + } + public long inputsIteration() { return inputsIteration; } diff --git a/graal/org.graalvm.compiler.phases/src/org/graalvm/compiler/phases/contract/VerifyNodeCosts.java b/graal/org.graalvm.compiler.phases/src/org/graalvm/compiler/phases/contract/VerifyNodeCosts.java new file mode 100644 index 000000000000..0047dc970272 --- /dev/null +++ b/graal/org.graalvm.compiler.phases/src/org/graalvm/compiler/phases/contract/VerifyNodeCosts.java @@ -0,0 +1,90 @@ +/* + * Copyright (c) 2017, 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.phases.contract; + +import java.lang.reflect.Field; +import java.lang.reflect.Modifier; +import java.util.function.Predicate; + +import org.graalvm.compiler.graph.Node; +import org.graalvm.compiler.graph.NodeClass; +import org.graalvm.compiler.nodeinfo.NodeCycles; +import org.graalvm.compiler.nodeinfo.NodeInfo; +import org.graalvm.compiler.nodeinfo.NodeSize; +import org.graalvm.compiler.phases.VerifyPhase; + +/** + * Utility class that verifies that every {@link Class} extending {@link Node} specifies non default + * values for {@link NodeCycles} and {@link NodeSize} in its {@link NodeInfo} annotation. + */ +public class VerifyNodeCosts { + + public static void verifyNodeClass(Class clazz) { + Class nodeClass = Node.class; + if (nodeClass.isAssignableFrom(clazz)) { + if (!clazz.isAnnotationPresent(NodeInfo.class)) { + throw new VerifyPhase.VerificationError("%s.java extends Node.java but does not specify a NodeInfo annotation.", clazz.getName()); + } + + if (!Modifier.isAbstract(clazz.getModifiers())) { + boolean cyclesSet = walkCHUntil(getType(clazz), getType(nodeClass), cur -> { + return cur.cycles() != NodeCycles.CYCLES_UNSET; + }); + boolean sizeSet = walkCHUntil(getType(clazz), getType(nodeClass), cur -> { + return cur.size() != NodeSize.SIZE_UNSET; + }); + if (!cyclesSet) { + throw new VerifyPhase.VerificationError("%s.java does not specify a NodeCycles value in its class hierarchy.", clazz.getName()); + } + if (!sizeSet) { + throw new VerifyPhase.VerificationError("%s.java does not specify a NodeSize value in its class hierarchy.", clazz.getName()); + } + } + } + } + + private static NodeClass getType(Class c) { + Field f; + try { + f = c.getField("TYPE"); + f.setAccessible(true); + Object val = f.get(null); + NodeClass nodeType = (NodeClass) val; + return nodeType; + } catch (Throwable t) { + throw new VerifyPhase.VerificationError("%s.java does not specify a TYPE field.", c.getName()); + } + } + + private static boolean walkCHUntil(NodeClass start, NodeClass until, Predicate> p) { + NodeClass cur = start; + while (cur != until && cur != null) { + if (p.test(cur)) { + return true; + } + cur = cur.getSuperNodeClass(); + } + return false; + } + +}