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 }