1 /*
   2  * Copyright (c) 2002, 2014, 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.javah;
  27 
  28 import java.io.ByteArrayOutputStream;
  29 import java.io.FileNotFoundException;
  30 import java.io.IOException;
  31 import java.io.InputStream;
  32 import java.io.OutputStream;
  33 import java.io.OutputStreamWriter;
  34 import java.io.PrintWriter;
  35 import java.io.UnsupportedEncodingException;
  36 import java.nio.file.NoSuchFileException;
  37 import java.util.ArrayList;
  38 import java.util.Arrays;
  39 import java.util.List;
  40 import java.util.Set;
  41 import java.util.Stack;
  42 
  43 import javax.annotation.processing.ProcessingEnvironment;
  44 import javax.lang.model.element.ExecutableElement;
  45 import javax.lang.model.element.Modifier;
  46 import javax.lang.model.element.TypeElement;
  47 import javax.lang.model.element.VariableElement;
  48 import javax.lang.model.util.ElementFilter;
  49 import javax.lang.model.util.Elements;
  50 import javax.lang.model.util.Types;
  51 import javax.tools.FileObject;
  52 import javax.tools.JavaFileManager;
  53 import javax.tools.JavaFileObject;
  54 import javax.tools.StandardLocation;
  55 
  56 /**
  57  * An abstraction for generating support files required by native methods.
  58  * Subclasses are for specific native interfaces. At the time of its
  59  * original writing, this interface is rich enough to support JNI and the
  60  * old 1.0-style native method interface.
  61  *
  62  * <p><b>This is NOT part of any supported API.
  63  * If you write code that depends on this, you do so at your own
  64  * risk.  This code and its internal interfaces are subject to change
  65  * or deletion without notice.</b></p>
  66  *
  67  * @author  Sucheta Dambalkar(Revised)
  68  */
  69 public abstract class Gen {
  70     protected String lineSep = System.getProperty("line.separator");
  71 
  72     protected ProcessingEnvironment processingEnvironment;
  73     protected Types types;
  74     protected Elements elems;
  75     protected Mangle mangler;
  76     protected Util util;
  77 
  78     protected Gen(Util util) {
  79         this.util = util;
  80     }
  81 
  82     /*
  83      * List of classes for which we must generate output.
  84      */
  85     protected Set<TypeElement> classes;
  86     static private final boolean isWindows =
  87         System.getProperty("os.name").startsWith("Windows");
  88 
  89 
  90     /**
  91      * Override this abstract method, generating content for the named
  92      * class into the outputstream.
  93      */
  94     protected abstract void write(OutputStream o, TypeElement clazz) throws Util.Exit;
  95 
  96     /**
  97      * Override this method to provide a list of #include statements
  98      * required by the native interface.
  99      */
 100     protected abstract String getIncludes();
 101 
 102     /*
 103      * Output location.
 104      */
 105     protected JavaFileManager fileManager;
 106     protected JavaFileObject outFile;
 107 
 108     public void setFileManager(JavaFileManager fm) {
 109         fileManager = fm;
 110     }
 111 
 112     public void setOutFile(JavaFileObject outFile) {
 113         this.outFile = outFile;
 114     }
 115 
 116 
 117     public void setClasses(Set<TypeElement> classes) {
 118         this.classes = classes;
 119     }
 120 
 121     void setProcessingEnvironment(ProcessingEnvironment pEnv) {
 122         processingEnvironment = pEnv;
 123         elems = pEnv.getElementUtils();
 124         types = pEnv.getTypeUtils();
 125         mangler = new Mangle(elems, types);
 126     }
 127 
 128     /*
 129      * Smartness with generated files.
 130      */
 131     protected boolean force = false;
 132 
 133     public void setForce(boolean state) {
 134         force = state;
 135     }
 136 
 137     /**
 138      * We explicitly need to write ASCII files because that is what C
 139      * compilers understand.
 140      */
 141     protected PrintWriter wrapWriter(OutputStream o) throws Util.Exit {
 142         try {
 143             return new PrintWriter(new OutputStreamWriter(o, "ISO8859_1"), true);
 144         } catch (UnsupportedEncodingException use) {
 145             util.bug("encoding.iso8859_1.not.found");
 146             return null; /* dead code */
 147         }
 148     }
 149 
 150     /**
 151      * After initializing state of an instance, use this method to start
 152      * processing.
 153      *
 154      * Buffer size chosen as an approximation from a single sampling of:
 155      *         expr `du -sk` / `ls *.h | wc -l`
 156      */
 157     public void run() throws IOException, ClassNotFoundException, Util.Exit {
 158         int i = 0;
 159         if (outFile != null) {
 160             /* Everything goes to one big file... */
 161             ByteArrayOutputStream bout = new ByteArrayOutputStream(8192);
 162             writeFileTop(bout); /* only once */
 163 
 164             for (TypeElement t: classes) {
 165                 write(bout, t);
 166             }
 167 
 168             writeIfChanged(bout.toByteArray(), outFile);
 169         } else {
 170             /* Each class goes to its own file... */
 171             for (TypeElement t: classes) {
 172                 ByteArrayOutputStream bout = new ByteArrayOutputStream(8192);
 173                 writeFileTop(bout);
 174                 write(bout, t);
 175                 writeIfChanged(bout.toByteArray(), getFileObject(t.getQualifiedName()));
 176             }
 177         }
 178     }
 179 
 180     /*
 181      * Write the contents of byte[] b to a file named file.  Writing
 182      * is done if either the file doesn't exist or if the contents are
 183      * different.
 184      */
 185     private void writeIfChanged(byte[] b, FileObject file) throws IOException {
 186         boolean mustWrite = false;
 187         String event = "[No need to update file ";
 188 
 189         if (force) {
 190             mustWrite = true;
 191             event = "[Forcefully writing file ";
 192         } else {
 193             InputStream in;
 194             byte[] a;
 195             try {
 196                 // regrettably, there's no API to get the length in bytes
 197                 // for a FileObject, so we can't short-circuit reading the
 198                 // file here
 199                 in = file.openInputStream();
 200                 a = readBytes(in);
 201                 if (!Arrays.equals(a, b)) {
 202                     mustWrite = true;
 203                     event = "[Overwriting file ";
 204 
 205                 }
 206             } catch (FileNotFoundException | NoSuchFileException e) {
 207                 mustWrite = true;
 208                 event = "[Creating file ";
 209             }
 210         }
 211 
 212         if (util.verbose)
 213             util.log(event + file + "]");
 214 
 215         if (mustWrite) {
 216             OutputStream out = file.openOutputStream();
 217             out.write(b); /* No buffering, just one big write! */
 218             out.close();
 219         }
 220     }
 221 
 222     protected byte[] readBytes(InputStream in) throws IOException {
 223         try {
 224             byte[] array = new byte[in.available() + 1];
 225             int offset = 0;
 226             int n;
 227             while ((n = in.read(array, offset, array.length - offset)) != -1) {
 228                 offset += n;
 229                 if (offset == array.length)
 230                     array = Arrays.copyOf(array, array.length * 2);
 231             }
 232 
 233             return Arrays.copyOf(array, offset);
 234         } finally {
 235             in.close();
 236         }
 237     }
 238 
 239     protected String defineForStatic(TypeElement c, VariableElement f)
 240             throws Util.Exit {
 241         CharSequence cnamedoc = c.getQualifiedName();
 242         CharSequence fnamedoc = f.getSimpleName();
 243 
 244         String cname = mangler.mangle(cnamedoc, Mangle.Type.CLASS);
 245         String fname = mangler.mangle(fnamedoc, Mangle.Type.FIELDSTUB);
 246 
 247         if (!f.getModifiers().contains(Modifier.STATIC))
 248             util.bug("tried.to.define.non.static");
 249 
 250         if (f.getModifiers().contains(Modifier.FINAL)) {
 251             Object value = null;
 252 
 253             value = f.getConstantValue();
 254 
 255             if (value != null) { /* so it is a ConstantExpression */
 256                 String constString = null;
 257                 if ((value instanceof Integer)
 258                     || (value instanceof Byte)
 259                     || (value instanceof Short)) {
 260                     /* covers byte, short, int */
 261                     constString = value.toString() + "L";
 262                 } else if (value instanceof Boolean) {
 263                     constString = ((Boolean) value) ? "1L" : "0L";
 264                 } else if (value instanceof Character) {
 265                     Character ch = (Character) value;
 266                     constString = String.valueOf(((int) ch) & 0xffff) + "L";
 267                 } else if (value instanceof Long) {
 268                     // Visual C++ supports the i64 suffix, not LL.
 269                     if (isWindows)
 270                         constString = value.toString() + "i64";
 271                     else
 272                         constString = value.toString() + "LL";
 273                 } else if (value instanceof Float) {
 274                     /* bug for bug */
 275                     float fv = ((Float)value).floatValue();
 276                     if (Float.isInfinite(fv))
 277                         constString = ((fv < 0) ? "-" : "") + "Inff";
 278                     else
 279                         constString = value.toString() + "f";
 280                 } else if (value instanceof Double) {
 281                     /* bug for bug */
 282                     double d = ((Double)value).doubleValue();
 283                     if (Double.isInfinite(d))
 284                         constString = ((d < 0) ? "-" : "") + "InfD";
 285                     else
 286                         constString = value.toString();
 287                 }
 288                 if (constString != null) {
 289                     StringBuilder s = new StringBuilder("#undef ");
 290                     s.append(cname); s.append("_"); s.append(fname); s.append(lineSep);
 291                     s.append("#define "); s.append(cname); s.append("_");
 292                     s.append(fname); s.append(" "); s.append(constString);
 293                     return s.toString();
 294                 }
 295 
 296             }
 297         }
 298         return null;
 299     }
 300 
 301     /*
 302      * Deal with the C pre-processor.
 303      */
 304     protected String cppGuardBegin() {
 305         return "#ifdef __cplusplus" + lineSep + "extern \"C\" {" + lineSep + "#endif";
 306     }
 307 
 308     protected String cppGuardEnd() {
 309         return "#ifdef __cplusplus" + lineSep + "}" + lineSep + "#endif";
 310     }
 311 
 312     protected String guardBegin(String cname) {
 313         return "/* Header for class " + cname + " */" + lineSep + lineSep +
 314             "#ifndef _Included_" + cname + lineSep +
 315             "#define _Included_" + cname;
 316     }
 317 
 318     protected String guardEnd(String cname) {
 319         return "#endif";
 320     }
 321 
 322     /*
 323      * File name and file preamble related operations.
 324      */
 325     protected void writeFileTop(OutputStream o) throws Util.Exit {
 326         PrintWriter pw = wrapWriter(o);
 327         pw.println("/* DO NOT EDIT THIS FILE - it is machine generated */" + lineSep +
 328                    getIncludes());
 329     }
 330 
 331     protected String baseFileName(CharSequence className) {
 332         return mangler.mangle(className, Mangle.Type.CLASS);
 333     }
 334 
 335     protected FileObject getFileObject(CharSequence className) throws IOException {
 336         String name = baseFileName(className) + getFileSuffix();
 337         return fileManager.getFileForOutput(StandardLocation.SOURCE_OUTPUT, "", name, null);
 338     }
 339 
 340     protected String getFileSuffix() {
 341         return ".h";
 342     }
 343 
 344     /**
 345      * Including super classes' fields.
 346      */
 347 
 348     List<VariableElement> getAllFields(TypeElement subclazz) {
 349         List<VariableElement> fields = new ArrayList<>();
 350         TypeElement cd = null;
 351         Stack<TypeElement> s = new Stack<>();
 352 
 353         cd = subclazz;
 354         while (true) {
 355             s.push(cd);
 356             TypeElement c = (TypeElement) (types.asElement(cd.getSuperclass()));
 357             if (c == null)
 358                 break;
 359             cd = c;
 360         }
 361 
 362         while (!s.empty()) {
 363             cd = s.pop();
 364             fields.addAll(ElementFilter.fieldsIn(cd.getEnclosedElements()));
 365         }
 366 
 367         return fields;
 368     }
 369 
 370     // c.f. MethodDoc.signature
 371     String signature(ExecutableElement e) {
 372         StringBuilder sb = new StringBuilder("(");
 373         String sep = "";
 374         for (VariableElement p: e.getParameters()) {
 375             sb.append(sep);
 376             sb.append(types.erasure(p.asType()).toString());
 377             sep = ",";
 378         }
 379         sb.append(")");
 380         return sb.toString();
 381     }
 382 }
 383