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