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