gt:         Assert.check(false, "jni unknown type");
context: /*
   * Copyright (c) 1999, 2013, Oracle and/or its affiliates. All rights reserved.
   * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   *
   * This code is free software; you can redistribute it and/or modify it
   * under the terms of the GNU General Public License version 2 only, as
   * published by the Free Software Foundation.  Oracle designates this
   * particular file as subject to the "Classpath" exception as provided
   * by Oracle in the LICENSE file that accompanied this code.
   *
   * This code is distributed in the hope that it will be useful, but WITHOUT
   * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
   * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
   * version 2 for more details (a copy is included in the LICENSE file that
   * accompanied this code).
   *
   * You should have received a copy of the GNU General Public License version
   * 2 along with this work; if not, write to the Free Software Foundation,
   * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
   *
   * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
   * or visit www.oracle.com if you need additional information or have any
   * questions.
   */
  
  package com.sun.tools.javac.jvm;
  
  import java.io.IOException;
  import java.io.Writer;
  import java.util.ArrayList;
  import java.util.List;
  import java.util.Stack;
  import java.util.StringTokenizer;
  
  import javax.lang.model.element.Element;
  import javax.lang.model.element.ExecutableElement;
  import javax.lang.model.element.Modifier;
  import javax.lang.model.element.Name;
  import javax.lang.model.element.TypeElement;
  import javax.lang.model.element.VariableElement;
  import javax.lang.model.type.ArrayType;
  import javax.lang.model.type.DeclaredType;
  import javax.lang.model.type.NoType;
  import javax.lang.model.type.PrimitiveType;
  import javax.lang.model.type.TypeKind;
  import javax.lang.model.type.TypeMirror;
  import javax.lang.model.type.TypeVariable;
  import javax.lang.model.type.TypeVisitor;
  import javax.lang.model.util.ElementFilter;
  import javax.lang.model.util.Elements;
  import javax.lang.model.util.SimpleTypeVisitor8;
  import javax.lang.model.util.Types;
  
  import javax.tools.FileObject;
  import javax.tools.JavaFileManager;
  import javax.tools.StandardLocation;
  
  import com.sun.tools.javac.code.Attribute;
  import com.sun.tools.javac.code.Flags;
  import com.sun.tools.javac.code.Kinds;
  import com.sun.tools.javac.code.Scope;
  import com.sun.tools.javac.code.Symbol.ClassSymbol;
  import com.sun.tools.javac.code.Symtab;
  import com.sun.tools.javac.model.JavacElements;
  import com.sun.tools.javac.model.JavacTypes;
  import com.sun.tools.javac.util.Assert;
  import com.sun.tools.javac.util.Context;
  import com.sun.tools.javac.util.Log;
  import com.sun.tools.javac.util.Options;
  
  import static com.sun.tools.javac.main.Option.*;
  
  /** This class provides operations to write native header files for classes.
   *
   *  <p><b>This is NOT part of any supported API.
   *  If you write code that depends on this, you do so at your own risk.
   *  This code and its internal interfaces are subject to change or
   *  deletion without notice.</b>
   */
  public class JNIWriter {
      protected static final Context.Key<JNIWriter> jniWriterKey =
          new Context.Key<JNIWriter>();
  
      /** Access to files. */
      private final JavaFileManager fileManager;
  
      JavacElements elements;
      JavacTypes types;
  
      /** The log to use for verbose output.
       */
      private final Log log;
  
      /** Switch: verbose output.
       */
      private boolean verbose;
  
      /** Switch: check all nested classes of top level class
       */
      private boolean checkAll;
  
      private Mangle mangler;
  
      private Context context;
  
      private Symtab syms;
  
      private String lineSep;
  
      private final boolean isWindows =
          System.getProperty("os.name").startsWith("Windows");
  
      /** Get the ClassWriter instance for this context. */
      public static JNIWriter instance(Context context) {
          JNIWriter instance = context.get(jniWriterKey);
          if (instance == null)
              instance = new JNIWriter(context);
          return instance;
      }
  
      /** Construct a class writer, given an options table.
       */
      private JNIWriter(Context context) {
          context.put(jniWriterKey, this);
          fileManager = context.get(JavaFileManager.class);
          log = Log.instance(context);
  
          Options options = Options.instance(context);
          verbose = options.isSet(VERBOSE);
          checkAll = options.isSet("javah:full");
  
          this.context = context; // for lazyInit()
          syms = Symtab.instance(context);
  
          lineSep = System.getProperty("line.separator");
      }
  
      private void lazyInit() {
          if (mangler == null) {
              elements = JavacElements.instance(context);
              types = JavacTypes.instance(context);
              mangler = new Mangle(elements, types);
          }
      }
  
      public boolean needsHeader(ClassSymbol c) {
          if (c.isLocal() || (c.flags() & Flags.SYNTHETIC) != 0)
              return false;
  
          if (checkAll)
              return needsHeader(c.outermostClass(), true);
          else
              return needsHeader(c, false);
      }
  
      private boolean needsHeader(ClassSymbol c, boolean checkNestedClasses) {
          if (c.isLocal() || (c.flags() & Flags.SYNTHETIC) != 0)
              return false;
  
          /* temporary code for backwards compatibility */
          for (Attribute.Compound a: c.annotations.getDeclarationAttributes()) {
              if (a.type.tsym == syms.nativeHeaderType_old.tsym)
                  return true;
          }
          /* end of temporary code for backwards compatibility */
  
          for (Scope.Entry i = c.members_field.elems; i != null; i = i.sibling) {
              if (i.sym.kind == Kinds.MTH && (i.sym.flags() & Flags.NATIVE) != 0)
                  return true;
              for (Attribute.Compound a: i.sym.annotations.getDeclarationAttributes()) {
                  if (a.type.tsym == syms.nativeHeaderType.tsym)
                      return true;
              }
          }
          if (checkNestedClasses) {
              for (Scope.Entry i = c.members_field.elems; i != null; i = i.sibling) {
                  if ((i.sym.kind == Kinds.TYP) && needsHeader(((ClassSymbol) i.sym), true))
                      return true;
              }
          }
          return false;
      }
  
      /** Emit a class file for a given class.
       *  @param c      The class from which a class file is generated.
       */
      public FileObject write(ClassSymbol c)
          throws IOException
      {
          String className = c.flatName().toString();
          FileObject outFile
              = fileManager.getFileForOutput(StandardLocation.NATIVE_HEADER_OUTPUT,
                  "", className.replaceAll("[.$]", "_") + ".h", null);
          Writer out = outFile.openWriter();
          try {
              write(out, c);
              if (verbose)
                  log.printVerbose("wrote.file", outFile);
              out.close();
              out = null;
          } finally {
              if (out != null) {
                  // if we are propogating an exception, delete the file
                  out.close();
                  outFile.delete();
                  outFile = null;
              }
          }
          return outFile; // may be null if write failed
      }
  
      public void write(Writer out, ClassSymbol sym)
              throws IOException {
          lazyInit();
          try {
              String cname = mangler.mangle(sym.fullname, Mangle.Type.CLASS);
              println(out, fileTop());
              println(out, includes());
              println(out, guardBegin(cname));
              println(out, cppGuardBegin());
  
              writeStatics(out, sym);
              writeMethods(out, sym, cname);
  
              println(out, cppGuardEnd());
              println(out, guardEnd(cname));
          } catch (TypeSignature.SignatureException e) {
              throw new IOException(e);
          }
      }
  
      protected void writeStatics(Writer out, ClassSymbol sym) throws IOException {
          List<VariableElement> classfields = getAllFields(sym);
  
          for (VariableElement v: classfields) {
              if (!v.getModifiers().contains(Modifier.STATIC))
                  continue;
              String s = null;
              s = defineForStatic(sym, v);
              if (s != null) {
                  println(out, s);
              }
          }
      }
  
      /**
       * Including super class fields.
       */
      List<VariableElement> getAllFields(TypeElement subclazz) {
          List<VariableElement> fields = new ArrayList<VariableElement>();
          TypeElement cd = null;
          Stack<TypeElement> s = new Stack<TypeElement>();
  
          cd = subclazz;
          while (true) {
              s.push(cd);
              TypeElement c = (TypeElement) (types.asElement(cd.getSuperclass()));
              if (c == null)
                  break;
              cd = c;
          }
  
          while (!s.empty()) {
              cd = s.pop();
              fields.addAll(ElementFilter.fieldsIn(cd.getEnclosedElements()));
          }
  
          return fields;
      }
  
      protected String defineForStatic(TypeElement c, VariableElement f) {
          CharSequence cnamedoc = c.getQualifiedName();
          CharSequence fnamedoc = f.getSimpleName();
  
          String cname = mangler.mangle(cnamedoc, Mangle.Type.CLASS);
          String fname = mangler.mangle(fnamedoc, Mangle.Type.FIELDSTUB);
  
          Assert.check(f.getModifiers().contains(Modifier.STATIC));
  
          if (f.getModifiers().contains(Modifier.FINAL)) {
              Object value = null;
  
              value = f.getConstantValue();
  
              if (value != null) { /* so it is a ConstantExpression */
                  String constString = null;
                  if ((value instanceof Integer)
                      || (value instanceof Byte)
                      || (value instanceof Short)) {
                      /* covers byte, short, int */
                      constString = value.toString() + "L";
                  } else if (value instanceof Boolean) {
                      constString = ((Boolean) value) ? "1L" : "0L";
                  } else if (value instanceof Character) {
                      Character ch = (Character) value;
                      constString = String.valueOf(((int) ch) & 0xffff) + "L";
                  } else if (value instanceof Long) {
                      // Visual C++ supports the i64 suffix, not LL.
                      if (isWindows)
                          constString = value.toString() + "i64";
                      else
                          constString = value.toString() + "LL";
                  } else if (value instanceof Float) {
                      /* bug for bug */
                      float fv = ((Float)value).floatValue();
                      if (Float.isInfinite(fv))
                          constString = ((fv < 0) ? "-" : "") + "Inff";
                      else
                          constString = value.toString() + "f";
                  } else if (value instanceof Double) {
                      /* bug for bug */
                      double d = ((Double)value).doubleValue();
                      if (Double.isInfinite(d))
                          constString = ((d < 0) ? "-" : "") + "InfD";
                      else
                          constString = value.toString();
                  }
  
                  if (constString != null) {
                      StringBuilder s = new StringBuilder("#undef ");
                      s.append(cname); s.append("_"); s.append(fname); s.append(lineSep);
                      s.append("#define "); s.append(cname); s.append("_");
                      s.append(fname); s.append(" "); s.append(constString);
                      return s.toString();
                  }
  
              }
          }
  
          return null;
      }
  
  
      protected void writeMethods(Writer out, ClassSymbol sym, String cname)
              throws IOException, TypeSignature.SignatureException {
          List<ExecutableElement> classmethods = ElementFilter.methodsIn(sym.getEnclosedElements());
          for (ExecutableElement md: classmethods) {
              if(md.getModifiers().contains(Modifier.NATIVE)){
                  TypeMirror mtr = types.erasure(md.getReturnType());
                  String sig = signature(md);
                  TypeSignature newtypesig = new TypeSignature(elements);
                  CharSequence methodName = md.getSimpleName();
                  boolean longName = false;
                  for (ExecutableElement md2: classmethods) {
                      if ((md2 != md)
                          && (methodName.equals(md2.getSimpleName()))
                          && (md2.getModifiers().contains(Modifier.NATIVE)))
                          longName = true;
  
                  }
                  println(out, "/*");
                  println(out, " * Class:     " + cname);
                  println(out, " * Method:    " +
                             mangler.mangle(methodName, Mangle.Type.FIELDSTUB));
                  println(out, " * Signature: " + newtypesig.getTypeSignature(sig, mtr));
                  println(out, " */");
                  println(out, "JNIEXPORT " + jniType(mtr) +
                             " JNICALL " +
                             mangler.mangleMethod(md, sym,
                                                 (longName) ?
                                                 Mangle.Type.METHOD_JNI_LONG :
                                                 Mangle.Type.METHOD_JNI_SHORT));
                  print(out, "  (JNIEnv *, ");
                  List<? extends VariableElement> paramargs = md.getParameters();
                  List<TypeMirror> args = new ArrayList<TypeMirror>();
                  for (VariableElement p: paramargs) {
                      args.add(types.erasure(p.asType()));
                  }
                  if (md.getModifiers().contains(Modifier.STATIC))
                      print(out, "jclass");
                  else
                      print(out, "jobject");
  
                  for (TypeMirror arg: args) {
                      print(out, ", ");
                      print(out, jniType(arg));
                  }
                  println(out, ");"
                          + lineSep);
              }
          }
      }
  
      // c.f. MethodDoc.signature
      String signature(ExecutableElement e) {
          StringBuilder sb = new StringBuilder("(");
          String sep = "";
          for (VariableElement p: e.getParameters()) {
              sb.append(sep);
              sb.append(types.erasure(p.asType()).toString());
              sep = ",";
          }
          sb.append(")");
          return sb.toString();
      }
  
      protected final String jniType(TypeMirror t) {
          TypeElement throwable = elements.getTypeElement("java.lang.Throwable");
          TypeElement jClass = elements.getTypeElement("java.lang.Class");
          TypeElement jString = elements.getTypeElement("java.lang.String");
          Element tclassDoc = types.asElement(t);
  
  
          switch (t.getKind()) {
              case ARRAY: {
                  TypeMirror ct = ((ArrayType) t).getComponentType();
                  switch (ct.getKind()) {
                      case BOOLEAN:  return "jbooleanArray";
                      case BYTE:     return "jbyteArray";
                      case CHAR:     return "jcharArray";
                      case SHORT:    return "jshortArray";
                      case INT:      return "jintArray";
                      case LONG:     return "jlongArray";
                      case FLOAT:    return "jfloatArray";
                      case DOUBLE:   return "jdoubleArray";
                      case ARRAY:
                      case DECLARED: return "jobjectArray";
                      default: throw new Error(ct.toString());
                  }
              }
  
              case VOID:     return "void";
              case BOOLEAN:  return "jboolean";
              case BYTE:     return "jbyte";
              case CHAR:     return "jchar";
              case SHORT:    return "jshort";
              case INT:      return "jint";
              case LONG:     return "jlong";
              case FLOAT:    return "jfloat";
              case DOUBLE:   return "jdouble";
  
              case DECLARED: {
                  if (tclassDoc.equals(jString))
                      return "jstring";
                  else if (types.isAssignable(t, throwable.asType()))
                      return "jthrowable";
                  else if (types.isAssignable(t, jClass.asType()))
                      return "jclass";
                  else
                      return "jobject";
              }
          }
  
  