1 /*
   2  * Copyright (c) 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.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 package com.sun.tools.javac.intrinsics;
  27 
  28 import com.sun.tools.javac.util.Context;
  29 import com.sun.tools.javac.util.Options;
  30 
  31 import java.lang.constant.ClassDesc;
  32 import java.lang.constant.ConstantDesc;
  33 import java.lang.constant.ConstantDescs;
  34 import java.lang.constant.MethodTypeDesc;
  35 import java.lang.invoke.MethodHandles;
  36 import java.util.*;
  37 import java.util.stream.IntStream;
  38 
  39 /**
  40  *  <p><b>This is NOT part of any supported API.
  41  *  If you write code that depends on this, you do so at your own risk.
  42  *  This code and its internal interfaces are subject to change or
  43  *  deletion without notice.</b>
  44  */
  45 public class Intrinsics {
  46     private static final Context.Key<Intrinsics> intrinsicsKey = new Context.Key<>();
  47 
  48      /** Registry map of available Intrinsic Processors */
  49     final Map<EntryKey, IntrinsicProcessor> registry = new HashMap<>();
  50     /** Lookup for resolving ConstantDesc */
  51     final MethodHandles.Lookup lookup = MethodHandles.lookup();
  52 
  53     public static Intrinsics instance(Context context) {
  54         Intrinsics instance = context.get(intrinsicsKey);
  55         if (instance == null)
  56             instance = new Intrinsics(context);
  57         return instance;
  58     }
  59 
  60     protected Intrinsics(Context context) {
  61         context.put(intrinsicsKey, this);
  62         String opt = Options.instance(context).get("intrinsify");
  63         boolean enableIntrinsics = opt != null && opt.equals("all");
  64         if (enableIntrinsics) {
  65             ServiceLoader<IntrinsicProcessor> serviceLoader =
  66                     ServiceLoader.load(IntrinsicProcessor.class);
  67             for (IntrinsicProcessor ip : serviceLoader) {
  68                 ip.register(this);
  69             }
  70         }
  71     }
  72 
  73     ClassDesc[] describeConstables(Class<?>[] types) {
  74         int length = types.length;
  75         ClassDesc[] classDescs = new ClassDesc[length];
  76         for (int i = 0; i < length; i++) {
  77             classDescs[i] = types[i].describeConstable().get();
  78         }
  79         return classDescs;
  80     }
  81 
  82     class EntryKey {
  83         final ClassDesc owner;
  84         final String methodName;
  85         final MethodTypeDesc methodType;
  86 
  87         EntryKey(ClassDesc owner,
  88                  String methodName,
  89                  MethodTypeDesc methodType) {
  90             this.owner = owner;
  91             this.methodName = methodName;
  92             this.methodType = methodType;
  93          }
  94 
  95         @Override
  96         public boolean equals(Object other) {
  97             if (this == other) {
  98                 return true;
  99             }
 100             if (other != null && other instanceof EntryKey) {
 101                 EntryKey otherKey = (EntryKey)other;
 102                 return owner.equals(otherKey.owner) &&
 103                        methodName.equals(otherKey.methodName) &&
 104                        methodType.equals(otherKey.methodType);
 105             }
 106             return false;
 107         }
 108 
 109         @Override
 110         public int hashCode() {
 111             return Objects.hash(owner, methodName, methodType);
 112         }
 113     }
 114 
 115     void register(IntrinsicProcessor processor,
 116                          Class<?> owner,
 117                          String methodName,
 118                          Class<?> returnType,
 119                          Class<?>... argTypes) {
 120         EntryKey key = new EntryKey(owner.describeConstable().get(),
 121                                     methodName,
 122                                     MethodTypeDesc.of(returnType.describeConstable().get(),
 123                                                       describeConstables(argTypes)));
 124         registry.put(key, processor);
 125 
 126     }
 127 
 128     Object getConstant(ClassDesc classDesc, ConstantDesc constantDesc) {
 129         try {
 130             Object constant = constantDesc.resolveConstantDesc(lookup);
 131             if (ConstantDescs.CD_boolean.equals(classDesc) ||
 132                     ConstantDescs.CD_Boolean.equals(classDesc)) {
 133                 int value = ((Number)constant).intValue();
 134                 constant = value == 0 ? Boolean.FALSE : Boolean.TRUE;
 135             } else if (ConstantDescs.CD_byte.equals(classDesc) ||
 136                     ConstantDescs.CD_Byte.equals(classDesc)) {
 137                 int value = ((Number)constant).intValue();
 138                 constant = (byte)value;
 139             } else if (ConstantDescs.CD_short.equals(classDesc) ||
 140                     ConstantDescs.CD_Short.equals(classDesc)) {
 141                 int value = ((Number)constant).intValue();
 142                 constant = (short)value;
 143             } else if (ConstantDescs.CD_char.equals(classDesc) ||
 144                     ConstantDescs.CD_Character.equals(classDesc)) {
 145                 int value = ((Number)constant).intValue();
 146                 constant = (char)value;
 147             }
 148             return constant;
 149         } catch (ReflectiveOperationException ex) {
 150             // Fall thru
 151         }
 152         return null;
 153     }
 154 
 155     Object[] getConstants(ClassDesc[] classDescs,
 156                                  ConstantDesc[] constantDescs,
 157                                  boolean skipReceiver) {
 158         int length = constantDescs.length;
 159         Object[] constants = skipReceiver ? new Object[length - 1] : new Object[length];
 160         int offset = skipReceiver ? 1 : 0;
 161         for (int i = offset; i < length; i++) {
 162             constants[i - offset] = getConstant(classDescs[i], constantDescs[i]);
 163         }
 164         return constants;
 165     }
 166 
 167     boolean isAllConstants(ConstantDesc[] constantDescs, boolean skipReceiver) {
 168         int length = constantDescs.length;
 169         for (int i = 0; i < length; i++) {
 170             if (constantDescs[i] == null && !(skipReceiver && i == 0)) {
 171                 return false;
 172             }
 173         }
 174         return true;
 175     }
 176 
 177     boolean isArrayVarArg(ClassDesc[] argClassDescs, int i) {
 178         return i + 1 == argClassDescs.length && argClassDescs[i].isArray();
 179     }
 180 
 181     int[] dropArg(int n, int k) {
 182         return IntStream.range(0, n)
 183                         .filter(i -> i != k)
 184                         .toArray();
 185     }
 186 
 187     /**
 188      * @param ownerDesc       method owner
 189      * @param methodName      method name
 190      * @param methodType      method type descriptor
 191      * @param argClassDescs   class descriptors for each argument (includes receiver)
 192      * @param constantArgs    constant value for each argument (includes receiver), null means unknown
 193      * @return IntrinsicProcessor.Result value
 194      */
 195     public IntrinsicProcessor.Result tryIntrinsify(ClassDesc ownerDesc,
 196                                                           String methodName,
 197                                                           MethodTypeDesc methodType,
 198                                                           boolean isStatic,
 199                                                           ClassDesc[] argClassDescs,
 200                                                           ConstantDesc[] constantArgs) {
 201         EntryKey key = new EntryKey(ownerDesc, methodName, methodType);
 202         IntrinsicProcessor processor = registry.get(key);
 203         if (processor != null) {
 204             return processor.tryIntrinsify(
 205                     ownerDesc,
 206                     methodName,
 207                     methodType,
 208                     isStatic,
 209                     argClassDescs,
 210                     constantArgs
 211                     );
 212         }
 213 
 214         return new IntrinsicProcessor.Result.None();
 215     }
 216 }