1 /* 2 * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. 8 * 9 * This code is distributed in the hope that it will be useful, but WITHOUT 10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 12 * version 2 for more details (a copy is included in the LICENSE file that 13 * accompanied this code). 14 * 15 * You should have received a copy of the GNU General Public License version 16 * 2 along with this work; if not, write to the Free Software Foundation, 17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 18 * 19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 20 * or visit www.oracle.com if you need additional information or have any 21 * questions. 22 */ 23 24 25 package org.graalvm.compiler.serviceprovider; 26 27 import java.util.Arrays; 28 import java.util.HashSet; 29 import java.util.Set; 30 import java.util.concurrent.atomic.AtomicInteger; 31 32 import jdk.vm.ci.code.BytecodePosition; 33 import jdk.vm.ci.meta.ResolvedJavaMethod; 34 import jdk.vm.ci.meta.ResolvedJavaType; 35 import jdk.vm.ci.meta.SpeculationLog.SpeculationReason; 36 37 /** 38 * Facility for creating speculation reasons partitioned in groups. 39 */ 40 public final class SpeculationReasonGroup { 41 42 private final int id; 43 private final String name; 44 private final Class<?>[] signature; 45 46 private static final AtomicInteger nextId = new AtomicInteger(1); 47 48 /** 49 * Creates speculation group whose context will always match {@code signature}. 50 */ 51 public SpeculationReasonGroup(String name, Class<?>... signature) { 52 this.id = nextId.get(); 53 this.name = name; 54 this.signature = signature; 55 for (Class<?> c : signature) { 56 if (!isOfSupportedType(c)) { 57 throw new IllegalArgumentException("Unsupported speculation context type: " + c.getName()); 58 } 59 } 60 } 61 62 @Override 63 public String toString() { 64 return String.format("%s{id:%d, sig=%s}", name, id, Arrays.toString(signature)); 65 } 66 67 /** 68 * Creates a speculation reason described by this group. 69 * 70 * @param context the components of the reason instance being created 71 */ 72 public SpeculationReason createSpeculationReason(Object... context) { 73 assert checkSignature(context); 74 return GraalServices.createSpeculationReason(id, name, context); 75 } 76 77 private static final Set<Class<?>> SUPPORTED_EXACT_TYPES = new HashSet<>(Arrays.asList( 78 String.class, 79 int.class, 80 long.class, 81 float.class, 82 double.class, 83 BytecodePosition.class)); 84 85 private static boolean isOfSupportedType(Class<?> c) { 86 if (SUPPORTED_EXACT_TYPES.contains(c)) { 87 return true; 88 } 89 if (Enum.class.isAssignableFrom(c)) { 90 // Trust the ordinal of an Enum to be unique 91 return true; 92 } 93 if (SpeculationContextObject.class.isAssignableFrom(c)) { 94 return true; 95 } 96 if (ResolvedJavaMethod.class.isAssignableFrom(c) || ResolvedJavaType.class.isAssignableFrom(c)) { 97 // Only the JVMCI implementation specific concrete subclasses 98 // of these types will be accepted but we cannot test for that 99 // here since we are in JVMCI implementation agnostic code. 100 return true; 101 } 102 return false; 103 } 104 105 static Class<?> toBox(Class<?> c) { 106 if (c == int.class) { 107 return Integer.class; 108 } 109 if (c == long.class) { 110 return Long.class; 111 } 112 if (c == float.class) { 113 return Float.class; 114 } 115 if (c == double.class) { 116 return Double.class; 117 } 118 return c; 119 } 120 121 private boolean checkSignature(Object[] context) { 122 assert signature.length == context.length : name + ": Incorrect number of context arguments. Expected " + signature.length + ", got " + context.length; 123 for (int i = 0; i < context.length; i++) { 124 Object o = context[i]; 125 Class<?> c = signature[i]; 126 if (o != null) { 127 if (c == ResolvedJavaMethod.class || c == ResolvedJavaType.class || SpeculationContextObject.class.isAssignableFrom(c)) { 128 c.cast(o); 129 } else { 130 Class<?> oClass = o.getClass(); 131 assert toBox(c) == oClass : name + ": Context argument " + i + " is not a " + c.getName() + " but a " + oClass.getName(); 132 } 133 } else { 134 if (c.isPrimitive() || Enum.class.isAssignableFrom(c)) { 135 throw new AssertionError(name + ": Cannot pass null for argument " + i); 136 } 137 } 138 } 139 return true; 140 } 141 142 /** 143 * Denotes part of a {@linkplain SpeculationReasonGroup#createSpeculationReason(Object...) 144 * reason} that can have its attributes {@linkplain #accept(Visitor) visited}. 145 */ 146 public interface SpeculationContextObject { 147 void accept(Visitor v); 148 149 public interface Visitor { 150 void visitBoolean(boolean v); 151 152 void visitByte(byte v); 153 154 void visitChar(char v); 155 156 void visitShort(short v); 157 158 void visitInt(int v); 159 160 void visitLong(long v); 161 162 void visitFloat(float v); 163 164 void visitDouble(double v); 165 166 void visitObject(Object v); 167 } 168 } 169 }