1 /*
   2  * Copyright (c) 2013, 2018, 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.hotspot.stubs;
  26 
  27 import static org.graalvm.compiler.replacements.nodes.CStringConstant.cstring;
  28 
  29 import java.lang.reflect.Method;
  30 import java.lang.reflect.Modifier;
  31 import java.util.Arrays;
  32 import java.util.List;
  33 
  34 import org.graalvm.compiler.api.replacements.Fold;
  35 import org.graalvm.compiler.api.replacements.Fold.InjectedParameter;
  36 import org.graalvm.compiler.core.common.spi.ForeignCallDescriptor;
  37 import org.graalvm.compiler.graph.Node.ConstantNodeParameter;
  38 import org.graalvm.compiler.graph.Node.NodeIntrinsic;
  39 import org.graalvm.compiler.hotspot.GraalHotSpotVMConfig;
  40 import org.graalvm.compiler.hotspot.nodes.StubForeignCallNode;
  41 import org.graalvm.compiler.hotspot.nodes.VMErrorNode;
  42 import org.graalvm.compiler.replacements.Log;
  43 import org.graalvm.compiler.word.Word;
  44 import jdk.internal.vm.compiler.word.WordFactory;
  45 
  46 //JaCoCo Exclude
  47 
  48 /**
  49  * A collection of methods used in {@link Stub}s.
  50  */
  51 public class StubUtil {
  52 
  53     public static final ForeignCallDescriptor VM_MESSAGE_C = newDescriptor(StubUtil.class, "vmMessageC", void.class, boolean.class, Word.class, long.class, long.class, long.class);
  54 
  55     public static ForeignCallDescriptor newDescriptor(Class<?> stubClass, String name, Class<?> resultType, Class<?>... argumentTypes) {
  56         ForeignCallDescriptor d = new ForeignCallDescriptor(name, resultType, argumentTypes);
  57         assert descriptorFor(stubClass, name).equals(d) : descriptorFor(stubClass, name) + " != " + d;
  58         return d;
  59     }
  60 
  61     /**
  62      * Looks for a {@link StubForeignCallNode} node intrinsic named {@code name} in
  63      * {@code stubClass} and returns a {@link ForeignCallDescriptor} based on its signature and the
  64      * value of {@code hasSideEffect}.
  65      */
  66     private static ForeignCallDescriptor descriptorFor(Class<?> stubClass, String name) {
  67         Method found = null;
  68         for (Method method : stubClass.getDeclaredMethods()) {
  69             if (Modifier.isStatic(method.getModifiers()) && method.getAnnotation(NodeIntrinsic.class) != null && method.getName().equals(name)) {
  70                 if (method.getAnnotation(NodeIntrinsic.class).value().equals(StubForeignCallNode.class)) {
  71                     assert found == null : "found more than one foreign call named " + name + " in " + stubClass;
  72                     assert method.getParameterTypes().length != 0 && method.getParameterTypes()[0] == ForeignCallDescriptor.class : "first parameter of foreign call '" + name + "' in " + stubClass +
  73                                     " must be of type " + ForeignCallDescriptor.class.getSimpleName();
  74                     found = method;
  75                 }
  76             }
  77         }
  78         assert found != null : "could not find foreign call named " + name + " in " + stubClass;
  79         List<Class<?>> paramList = Arrays.asList(found.getParameterTypes());
  80         Class<?>[] cCallTypes = paramList.subList(1, paramList.size()).toArray(new Class<?>[paramList.size() - 1]);
  81         return new ForeignCallDescriptor(name, found.getReturnType(), cCallTypes);
  82     }
  83 
  84     /**
  85      * Determines if this is a HotSpot build where the ASSERT mechanism is enabled.
  86      */
  87     @Fold
  88     public static boolean cAssertionsEnabled(@InjectedParameter GraalHotSpotVMConfig config) {
  89         return config.cAssertions;
  90     }
  91 
  92     @NodeIntrinsic(StubForeignCallNode.class)
  93     private static native void vmMessageC(@ConstantNodeParameter ForeignCallDescriptor stubPrintfC, boolean vmError, Word format, long v1, long v2, long v3);
  94 
  95     /**
  96      * Prints a message to the log stream.
  97      * <p>
  98      * <b>Stubs must use this instead of {@link Log#printf(String, long)} to avoid an object
  99      * constant in a RuntimeStub.</b>
 100      *
 101      * @param message a message string
 102      */
 103     public static void printf(String message) {
 104         vmMessageC(VM_MESSAGE_C, false, cstring(message), 0L, 0L, 0L);
 105     }
 106 
 107     /**
 108      * Prints a message to the log stream.
 109      * <p>
 110      * <b>Stubs must use this instead of {@link Log#printf(String, long)} to avoid an object
 111      * constant in a RuntimeStub.</b>
 112      *
 113      * @param format a C style printf format value
 114      * @param value the value associated with the first conversion specifier in {@code format}
 115      */
 116     public static void printf(String format, long value) {
 117         vmMessageC(VM_MESSAGE_C, false, cstring(format), value, 0L, 0L);
 118     }
 119 
 120     /**
 121      * Prints a message to the log stream.
 122      * <p>
 123      * <b>Stubs must use this instead of {@link Log#printf(String, long, long)} to avoid an object
 124      * constant in a RuntimeStub.</b>
 125      *
 126      * @param format a C style printf format value
 127      * @param v1 the value associated with the first conversion specifier in {@code format}
 128      * @param v2 the value associated with the second conversion specifier in {@code format}
 129      */
 130     public static void printf(String format, long v1, long v2) {
 131         vmMessageC(VM_MESSAGE_C, false, cstring(format), v1, v2, 0L);
 132     }
 133 
 134     /**
 135      * Prints a message to the log stream.
 136      * <p>
 137      * <b>Stubs must use this instead of {@link Log#printf(String, long, long, long)} to avoid an
 138      * object constant in a RuntimeStub.</b>
 139      *
 140      * @param format a C style printf format value
 141      * @param v1 the value associated with the first conversion specifier in {@code format}
 142      * @param v2 the value associated with the second conversion specifier in {@code format}
 143      * @param v3 the value associated with the third conversion specifier in {@code format}
 144      */
 145     public static void printf(String format, long v1, long v2, long v3) {
 146         vmMessageC(VM_MESSAGE_C, false, cstring(format), v1, v2, v3);
 147     }
 148 
 149     /**
 150      * Analyzes a given value and prints information about it to the log stream.
 151      */
 152     public static void decipher(long value) {
 153         vmMessageC(VM_MESSAGE_C, false, WordFactory.zero(), value, 0L, 0L);
 154     }
 155 
 156     /**
 157      * Exits the VM with a given error message.
 158      * <p>
 159      * <b>Stubs must use this instead of {@link VMErrorNode#vmError(String, long)} to avoid an
 160      * object constant in a RuntimeStub.</b>
 161      *
 162      * @param message an error message
 163      */
 164     public static void fatal(String message) {
 165         vmMessageC(VM_MESSAGE_C, true, cstring(message), 0L, 0L, 0L);
 166     }
 167 
 168     /**
 169      * Exits the VM with a given error message.
 170      * <p>
 171      * <b>Stubs must use this instead of {@link Log#printf(String, long, long, long)} to avoid an
 172      * object constant in a RuntimeStub.</b>
 173      *
 174      * @param format a C style printf format value
 175      * @param value the value associated with the first conversion specifier in {@code format}
 176      */
 177     public static void fatal(String format, long value) {
 178         vmMessageC(VM_MESSAGE_C, true, cstring(format), value, 0L, 0L);
 179     }
 180 
 181     /**
 182      * Exits the VM with a given error message.
 183      * <p>
 184      * <b>Stubs must use this instead of {@link Log#printf(String, long, long, long)} to avoid an
 185      * object constant in a RuntimeStub.</b>
 186      *
 187      * @param format a C style printf format value
 188      * @param v1 the value associated with the first conversion specifier in {@code format}
 189      * @param v2 the value associated with the second conversion specifier in {@code format}
 190      */
 191     public static void fatal(String format, long v1, long v2) {
 192         vmMessageC(VM_MESSAGE_C, true, cstring(format), v1, v2, 0L);
 193     }
 194 
 195     /**
 196      * Exits the VM with a given error message.
 197      * <p>
 198      * <b>Stubs must use this instead of {@link Log#printf(String, long, long, long)} to avoid an
 199      * object constant in a RuntimeStub.</b>
 200      *
 201      * @param format a C style printf format value
 202      * @param v1 the value associated with the first conversion specifier in {@code format}
 203      * @param v2 the value associated with the second conversion specifier in {@code format}
 204      * @param v3 the value associated with the third conversion specifier in {@code format}
 205      */
 206     public static void fatal(String format, long v1, long v2, long v3) {
 207         vmMessageC(VM_MESSAGE_C, true, cstring(format), v1, v2, v3);
 208     }
 209 
 210     /**
 211      * Print {@code number} as decimal string to {@code buffer}.
 212      *
 213      * @param buffer
 214      * @param number
 215      * @return A pointer pointing one byte right after the last printed digit in {@code buffer}.
 216      */
 217     public static Word printNumber(Word buffer, long number) {
 218         long tmpNumber = number;
 219         int offset;
 220         if (tmpNumber <= 0) {
 221             tmpNumber = -tmpNumber;
 222             offset = 1;
 223         } else {
 224             offset = 0;
 225         }
 226         while (tmpNumber > 0) {
 227             tmpNumber /= 10;
 228             offset++;
 229         }
 230         tmpNumber = number < 0 ? -number : number;
 231         Word ptr = buffer.add(offset);
 232         do {
 233             long digit = tmpNumber % 10;
 234             tmpNumber /= 10;
 235             ptr = ptr.subtract(1);
 236             ptr.writeByte(0, (byte) ('0' + digit));
 237         } while (tmpNumber > 0);
 238 
 239         if (number < 0) {
 240             ptr = ptr.subtract(1);
 241             ptr.writeByte(0, (byte) '-');
 242         }
 243         return buffer.add(offset);
 244     }
 245 
 246     /**
 247      * Copy {@code javaString} bytes to the memory location {@code ptr}.
 248      *
 249      * @param buffer
 250      * @param javaString
 251      * @return A pointer pointing one byte right after the last byte copied from {@code javaString}
 252      *         to {@code ptr}
 253      */
 254     public static Word printString(Word buffer, String javaString) {
 255         Word string = cstring(javaString);
 256         int i = 0;
 257         byte b;
 258         while ((b = string.readByte(i)) != 0) {
 259             buffer.writeByte(i, b);
 260             i++;
 261         }
 262         return buffer.add(i);
 263     }
 264 }