1 /*
   2  * Copyright (c) 2015, 2016, 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 package com.sun.tools.jextract;
  24 
  25 import java.foreign.memory.Callback;
  26 import java.foreign.memory.Pointer;
  27 import java.util.Objects;
  28 
  29 /**
  30  * A Java Type descriptor
  31  */
  32 public abstract class JType {
  33 
  34     /**
  35      * The descriptor of this type
  36      *
  37      * @return The type descriptor as defined in JVMS 4.3
  38      */
  39     public abstract String getDescriptor();
  40 
  41     public abstract String getSourceSignature(boolean isArgument);
  42 
  43     public final static JType Void = new PrimitiveType("V", of(Void.class), "void");
  44     public final static JType Byte = new PrimitiveType("B", of(Byte.class), "byte");
  45     public final static JType Bool = new PrimitiveType("Z", of(Boolean.class), "boolean");
  46     public final static JType Char = new PrimitiveType("C", of(Character.class), "char");
  47     public final static JType Short = new PrimitiveType("S", of(Short.class), "short");
  48     public final static JType Int = new PrimitiveType("I", of(Integer.class), "int");
  49     public final static JType Long = new PrimitiveType("J", of(Long.class), "long");
  50     public final static JType Float = new PrimitiveType("F", of(Float.class), "float");
  51     public final static JType Double = new PrimitiveType("D", of(Double.class), "double");
  52     public final static JType Object = of(java.lang.Object.class);
  53 
  54     public static JType of(final Class<?> cls) {
  55         if (cls.getEnclosingClass() != null) {
  56             throw new IllegalArgumentException("nested/inner class: " + cls.getName());
  57         }
  58 
  59         if (cls.isArray()) {
  60             return new ArrayType(JType.of(cls.getComponentType()));
  61         }
  62         if (cls.isPrimitive()) {
  63             switch (cls.getTypeName()) {
  64                 case "int":
  65                     return JType.Int;
  66                 case "long":
  67                     return JType.Long;
  68                 case "byte":
  69                     return JType.Byte;
  70                 case "char":
  71                     return JType.Char;
  72                 case "float":
  73                     return JType.Float;
  74                 case "double":
  75                     return JType.Double;
  76                 case "short":
  77                     return JType.Short;
  78                 case "boolean":
  79                     return JType.Bool;
  80                 case "void":
  81                     return JType.Void;
  82             }
  83         }
  84         if (cls == Object.class) {
  85             return JType.Object;
  86         }
  87         // assuming reference
  88         return new ClassType(binaryName(cls));
  89     }
  90 
  91     private static String binaryName(Class<?> cls) {
  92         return cls.getName().replace('.', '/');
  93     }
  94 
  95     public JType box() {
  96         return this;
  97     }
  98 
  99     public static class PrimitiveType extends JType {
 100         final String desc;
 101         final JType boxed;
 102         final String name;
 103 
 104         PrimitiveType(String desc, JType boxed, String name) {
 105             this.desc = desc;
 106             this.boxed = boxed;
 107             this.name = name;
 108         }
 109 
 110         @Override
 111         public JType box() {
 112             return boxed;
 113         }
 114 
 115         @Override
 116         public String getDescriptor() {
 117             return desc;
 118         }
 119 
 120         @Override
 121         public String getSourceSignature(boolean isArgument) {
 122             return name;
 123         }
 124     }
 125 
 126     public static class ClassType extends JType {
 127         // FIXME: for nested/inner types are just one-level deep.
 128         // If we generate deeply nested types, we have to revisit this.
 129 
 130         final String enclosingName;
 131         final String simpleName;
 132         final String clsName;
 133         final String externalName;
 134 
 135         ClassType(String clsName) {
 136             this.enclosingName = null;
 137             this.simpleName = null;
 138             this.clsName = Objects.requireNonNull(clsName);
 139             this.externalName = clsName.replace('/', '.');
 140         }
 141 
 142         ClassType(String enclosingName, String simpleName) {
 143             this.enclosingName = Objects.requireNonNull(enclosingName);
 144             this.simpleName = Objects.requireNonNull(simpleName);
 145             this.clsName = enclosingName + "$" + simpleName;
 146             this.externalName = enclosingName.replace('/', '.') + "." + simpleName;
 147         }
 148 
 149         @Override
 150         public String getDescriptor() {
 151             return "L" + clsName + ";";
 152         }
 153 
 154         @Override
 155         public String getSourceSignature(boolean isArgument) {
 156             // java.foreign.* is imported
 157             if (externalName.startsWith("java.lang.") ||
 158                 externalName.startsWith("java.foreign.")) {
 159                 return externalName.substring(externalName.lastIndexOf(".") + 1);
 160             } else {
 161                 return externalName;
 162             }
 163         }
 164 
 165         public String getSimpleName() {
 166             int innerEnd = clsName.lastIndexOf('$');
 167             int packageEnd = clsName.lastIndexOf('.');
 168             if (innerEnd != -1) {
 169                 return clsName.substring(innerEnd + 1);
 170             } else if (packageEnd != -1) {
 171                 return clsName.substring(packageEnd + 1);
 172             } else {
 173                 return clsName;
 174             }
 175         }
 176     }
 177 
 178     public final static class ArrayType extends JType {
 179         final JType elementType;
 180 
 181         ArrayType(JType type) {
 182             elementType = type;
 183         }
 184 
 185         @Override
 186         public String getDescriptor() {
 187             return JType.of(java.foreign.memory.Array.class).getDescriptor();
 188         }
 189 
 190         @Override
 191         public String getSourceSignature(boolean isArgument) {
 192             StringBuilder sb = new StringBuilder();
 193             sb.append("Array"); // java.foreign.memory.* will be imported
 194             sb.append("<");
 195             JType pt = elementType;
 196             sb.append(pt.box().getSourceSignature(isArgument));
 197             sb.append(">");
 198             return sb.toString();
 199         }
 200 
 201         public JType getElementType() {
 202             return elementType;
 203         }
 204     }
 205 
 206     public final static class Function extends JType {
 207         final JType returnType;
 208         final JType[] args;
 209         final boolean isVarArgs;
 210         final java.foreign.layout.Function layout;
 211 
 212         Function(java.foreign.layout.Function layout, boolean isVarArgs, JType returnType, JType... args) {
 213             this.layout = layout;
 214             this.returnType = returnType;
 215             this.args = args;
 216             for (int i = 0; i < args.length; i++) {
 217                 args[i] = arrayAsPointer(args[i]);
 218             }
 219             this.isVarArgs = isVarArgs;
 220         }
 221 
 222         private static JType arrayAsPointer(JType t) {
 223             return t instanceof ArrayType ?
 224                     GenericType.ofPointer(((ArrayType)t).elementType) :
 225                     t;
 226         }
 227 
 228         @Override
 229         public String getDescriptor() {
 230             StringBuilder sb = new StringBuilder();
 231             sb.append('(');
 232             // ensure sequence
 233             for (int i = 0; i < args.length; i++) {
 234                 sb.append(args[i].getDescriptor());
 235             }
 236             if (isVarArgs) {
 237                 sb.append("[Ljava/lang/Object;");
 238             }
 239             sb.append(')');
 240             sb.append(returnType.getDescriptor());
 241             return sb.toString();
 242         }
 243 
 244         @Override
 245         public String getSourceSignature(boolean isArgument) {
 246             throw new UnsupportedOperationException();
 247         }
 248 
 249         public String getNativeDescriptor() {
 250             return layout.toString();
 251         }
 252     }
 253 
 254     final static class FunctionalInterfaceType extends ClassType {
 255         final Function fn;
 256 
 257         FunctionalInterfaceType(String enclosingName, String name, Function fn) {
 258             super(enclosingName, name);
 259             this.fn = fn;
 260         }
 261 
 262         Function getFunction() {
 263             return fn;
 264         }
 265     }
 266 
 267     public static class GenericType extends ClassType {
 268         JType targ;
 269 
 270         GenericType(String base, JType targ) {
 271             super(base);
 272             this.targ = targ;
 273         }
 274 
 275         public JType getTypeArgument() {
 276             return targ;
 277         }
 278 
 279         @Override
 280         public String getSourceSignature(boolean isArgument) {
 281             StringBuilder sb = new StringBuilder();
 282             sb.append(super.getSourceSignature(isArgument));
 283             sb.append("<");
 284             if (targ == JType.Void && isArgument) {
 285                 sb.append('?');
 286             } else {
 287                 if (targ instanceof GenericType && isArgument) {
 288                     sb.append("? extends ");
 289                 }
 290                 sb.append(targ.box().getSourceSignature(isArgument));
 291             }
 292             sb.append(">");
 293             return sb.toString();
 294         }
 295 
 296         public static GenericType ofPointer(JType targ) {
 297             return new GenericType(JType.binaryName(Pointer.class), targ);
 298         }
 299 
 300         public static GenericType ofCallback(JType targ) {
 301             return new GenericType(JType.binaryName(Callback.class), targ);
 302         }
 303     }
 304 }